Outils pour utilisateurs

Outils du site


issue86:python

1

This is the second in a multi-part tutorial on creating a Cross Stitch pattern generator. In the first part (FCM85), we created a database containing the DMC™ floss colors with their closest RGB values. In this part, we will create the GUI using Tkinter. We will also use PIL (Python Imaging Library) and PMW (Python Mega Widgets). You’ll need to download those libraries and install them before we go too far. For PIL, go to the Pillow fork at https://github.com/python-imaging/Pillow and download the latest version. For PMW, go to http://pmw.sourceforge.net/ and download from there. You will also need two image files. One is a simple grey rectangle 500×400 pixels. You can use GIMP or some other image manipulating program to create it. Name it default.jpg, and place it into your source code directory along with the database. The other is an image of a folder for the open image button. I got one from open clipart and searched for the word “folder”. I found a reasonable one at https://openclipart.org/detail/177890/file-folder-by-thebyteman-177890. Open it in GIMP, resize it to 30×30 and save it in the same directory as the other two files as “open.gif”.

Voici la deuxième partie d'un long tutoriel sur la création d'un générateur de motifs de point de croix. Dans la première partie (le FCM n° 85), nous avons créé une base de données contenant les couleurs de fils DMC™ avec leurs valeurs RVB les plus proches. Dans cette partie, nous allons créer l'interface graphique en utilisant Tkinter. Nous allons également utiliser PIL (Python Imaging Library) et PMW (Python Mega Widgets). Vous aurez besoin de télécharger les bibliothèques et les installer avant d'aller plus loin. Pour PIL, récupérez la dernière version du fork Pillow sur https://github.com/python-imaging/Pillow. Pour PMW, téléchargez-le sur http://pmw.sourceforge.net/.

Vous aurez également besoin de deux fichiers images. L'un est un simple rectangle gris de 500×400 pixels. Vous pouvez utiliser GIMP ou un autre programme de manipulation d'images pour le créer. Nommez-le default.jpg, et placez-le dans votre répertoire de code source avec la base de données. L'autre est une image d'un dossier pour le bouton d'ouverture d'image. J'ai cherché le mot « folder » sur openclipart. J'en ai trouvé un pas mal ici https://openclipart.org/detail/177890/file-folder-by-thebyteman-177890. Ouvrez-le dans GIMP, redimensionnez-le à 30×30 et enregistrez-le dans le même répertoire que les deux autres fichiers en tant que « open.gif».

2

Above is a screenshot of what the finished GUI will look like. There are four main frames in the GUI. Three on the left side and one on the right. When we go through the build widget process, I refer to them as Top Frame, Middle Frame, Bottom Frame and Side Frame. The top frame deals with the original image. The middle frame deals with the processing of the image. The bottom frame shows the original image on the left and the processed image on the right, and the side frame displays the colors and floss required. It seems from first glance there is a lot of wasted space here, but when you see the program run, it doesn’t really have that much empty space, once we get through the processing portion. Now we are ready to start working on the code. Here is our long list of imports…

Ci-dessous une capture d'écran de ce à quoi ressemblera l'interface graphique terminée. Il y a quatre fenêtres principales : trois sur le côté gauche et une à droite. Lorsque nous suivrons le processus de construction des widgets, je les nommerai fenêtre du haut, fenêtre du milieu, fenêtre du bas et fenêtre de côté. La fenêtre du haut contient l'image originale. La fenêtre du milieu sert au traitement de l'image. La fenêtre du bas montre l'image originale sur la gauche et l'image traitée sur la droite, et la fenêtre de côté affiche les couleurs et fils nécessaires. Il semble au premier abord qu'il y ait beaucoup d'espace perdu, mais quand vous verrez le programme fonctionner, il n'y aura pas tant d'espace vide que ça, une fois qu'on arrive à la partie de traitement.

Maintenant, nous pouvons commencer à travailler sur le code. Voici notre longue liste des importations…

3

from Tkinter import * import tkFileDialog import tkCommonDialog import tkMessageBox import ttk from PIL import Image,ImageTk,ImageOps import Pmw import apsw # Database Access import math # Math library import sys

from Tkinter import *

import tkFileDialog

import tkCommonDialog

import tkMessageBox

import ttk

from PIL import Image,ImageTk,ImageOps

import Pmw

import apsw # Database Access

import math # Math library

import sys

4

From the sheer number of imports, you can tell this is going to be a long program. In fact, the UI portion of the code will be over 300 lines, including comments. The “good” news is that about 200 of the lines of code deal with the Tkinter portion of the program, the actual GUI itself. The majority of the remaining lines of code in this portion are stubs for functions needed for the next article. We’ll create a class to hold all of our UI processing code (next page, top right). First, we have the class definition and next we have the init function which we pass the TopLevel “root” window into. We create the TopLevel window in the last four lines of the program. Within the init function we are defining all the global variables and doing some initial assignments before we start the other functions. The first thing we do is create a list of Tuples that hold the picture file formats that we need when we call the OpenFile dialog. The next two lines below, define and ready the two image files we just created (open folder GIF file, and the grey rectangle – which will be used as placeholders for our images used to create the pattern.

En voyant toutes ces importations, vous vous doutez que cela va être un long programme. En fait, la partie de code pour l'interface utilisateur fera plus de 300 lignes, commentaires compris. La « bonne » nouvelle c'est qu'environ 200 de ces lignes concernent la partie Tkinter du programme, l'interface graphique elle-même. La plupart des lignes restantes dans cette partie sont les préparatifs pour les fonctions du prochain article.

Nous allons créer une classe pour contenir tout le code de l'interface utilisateur (page suivante, en haut à droite).

Nous avons d'abord la définition de la classe et à côté nous avons la fonction init à laquelle nous passons la fenêtre « racine ». Nous créons la fenêtre racine dans les quatre dernières lignes du programme. Dans la fonction init nous définissons toutes les variables globales et faisons quelques affectations initiales avant de commencer les autres fonctions. La première chose que nous faisons est de créer une liste de tuples qui contiennent les formats de fichiers images dont nous avons besoin lorsque nous appelons le dialogue OpenFile. Les deux lignes suivantes ci-dessous définissent et préparent les deux fichiers images que nous venons de créer (le fichier GIF de dossier ouvert et le rectangle gris, qui seront utilisés comme des espaces réservés pour nos images utilisées pour créer le motif).

5

self.openimage = PhotoImage(file='open.gif') self.DefaultImage =ImageTk.PhotoImage(self.Thumbnail(“default.jpg”,450,450)) Now we get into the global definitions (middle right). If you remember, when you use Tkinter, if you have a widget like a text entry box or combo box that you want to retrieve the information selected or entered, you define a global variable and then assign it to a Variable Class (BooleanVar, DoubleVar, IntVar or StringVar). This will then “track” changes to the values within the widget values so you can access them with the .get() or .set() methods. In the next lines of code, we create the global variable name, then assign it to the proper wrapper class. I put some comments into the code to try to help you keep track of what we are doing.

self.openimage = PhotoImage(file='open.gif')

self.DefaultImage =ImageTk.PhotoImage(self.Apercu(“default.jpg”,450,450))

Maintenant nous entrons dans les définitions globales (au milieu à droite). Vous vous souvenez peut-être que lorsque vous utilisez Tkinter, si vous avez un widget comme une boîte de saisie de texte ou une liste déroulante et que vous souhaitez récupérer les informations sélectionnées ou saisies, vous définissez une variable globale, puis l'assignez à une classe de variables (BooleanVar, DoubleVar, IntVar ou StringVar). Elle « pistera » alors les modifications dans les valeurs du widget afin que vous puissiez y accéder avec les méthodes .get() ou .set(). Dans les prochaines lignes de code, nous créons le nom de la variable globale, puis l'affectons à la classe correspondante. J'ai mis quelques commentaires dans le code pour essayer de vous aider à suivre ce que nous faisons.

6

As you can see, we are setting a variable called OriginalFilename, which holds the image that we want to create the pattern from, OriginalColorCount which holds the number of colors in the original image file, and OriginalSize which holds the size in pixels of the original image. As they say on tv… “BUT WAIT! THERE’S MORE!” (bottom right): The ComboStitch variable is set by a combobox, and handles the stitch size of the aida that you wish to use for your project. The ComboSize variable is also set by a combo box and holds the size of the aida fabric. FabricHeight and FabricWidth are the breakdowns from the aida size. MaxColors is a value from an entry box to set the number of colors, and BorderSize is a floating point value that specifies the amount of unused aida for framing.

Comme vous pouvez le voir, nous créons des variables : NomFichierOriginal, qui contient l'image à partir de laquelle nous voulons créer le motif, NombreCouleursOriginal qui détient le nombre de couleurs de l'original et TailleOriginal qui détient la taille en pixels de l'original. Comme ils disent à la télé… « Mais attendez, il y en a encore plus ! » (en bas à droite) :

La variable ComboTaillePoints est réglée par une liste déroulante et gère la taille des points de la toile aïda que vous souhaitez utiliser pour votre projet. La variable ComboTaille est également définie par une zone de liste déroulante et contient la taille de la toile aïda. LargeurTissu et HauteurTissu sont les dimensions de la toile aïda. MaxCouleurs est réglée à partir d'une zone de saisie pour définir le nombre de couleurs et TailleBordure est une valeur en virgule flottante qui indique la quantité d'aïda utilisé pour le cadre.

7

global ProcessedColors ProcessedColors = StringVar() global ProcessedSize ProcessedSize = StringVar() global DmcColor DmcColor = StringVar() The final ‘variable class’ variables are used for information once we have processed the original image to the desired parameters.

global CouleursTraitees

CouleursTraitees = StringVar()

global TailleTraitee

TailleTraitee = StringVar()

global CouleurDMC

CouleurDMC = StringVar()

Les dernières variables de type « classes de variables » sont utilisées pour les informations une fois que nous avons traité l'image originale avec les paramètres souhaités.

8

The next set of globals is (top right) used for easy access throughout the program. For the most part, they are either obvious by their name, or will become obvious once we use them. There are three not-so-obvious variables here. backgroundColor1 and backgroundColor2 are tuples that are used in the gridding process, and the ReadyToProcess variable is used to designate that the original image is loaded and everything is ready to go – just in case the user presses the Process button too early. Finally we have assigned all our globals, and now have the code that actually creates the GUI. We open the database, create the menu, set up the widgets, and finally place the widgets into the proper places. Just to give you a heads-up, we will be using the Grid geometry placement manager. More on that later.

La prochaine série de variables globales (en haut à droite) est utilisée pour faciliter l'accès tout au long du programme. Pour la plupart, leur nom est explicite, ou le deviendra une fois que nous les utiliserons. Il y a trois variables pas si évidentes. couleurFond1 et couleurFond2 sont des tuples utilisés dans le processus de maillage, et la variable PretPourTraitement est utilisée pour indiquer que l'image d'origine est chargée et que tout est prêt pour commencer - juste au cas où l'utilisateur appuie sur le bouton Traitement trop tôt.

Voilà, nous avons créé toutes nos variables globales et arrivons au code qui crée réellement l'interface graphique. Nous ouvrons la base de données, créons le menu, mettons en place les widgets et enfin plaçons les widgets aux endroits appropriés. Juste pour vous donner un aperçu, nous utiliserons le gestionnaire de placement en grille. Nous verrons cela plus tard.

9

#————————- self.OpenDB() self.MakeMenu(master) frm = self.BuildWidgets(master) self.PlaceWidgets(frm) The next portion of our code (middle right) will set up the menu bar. I’ve tried to lay it out logically so it will be easy to understand. We define a function called MakeMenu, and pass in the TopLevel window. We then define the three menu sets we will be creating. One for File, one for Process, and one for Help. menu.add_cascade(label=“File”, menu=filemenu) menu.add_cascade(label=“Process”,menu=process) menu.add_cascade(label=“Help”,menu=help)

self.OuvrirBase()

self.FabriquerMenu(principal)

frm = self.ConstruireWidgets(principal)

self.PlacerWidgets(frm)

La prochaine partie de notre code (au milieu à droite) met en place la barre de menu. J'ai essayé de rester logique pour qu'il soit facile à comprendre.

Nous définissons une fonction appelée FabriquerMenu, avec pour argument la fenêtre racine. Nous définissons ensuite les trois jeux de menus que nous allons créer. Un menu Fichier, un pour le traitement et le dernier pour l'aide.

          menu.add_cascade(label="Fichier", menu=menuFichier)
          menu.add_cascade(label="Traitement",menu=Traitement)
          menu.add_cascade(label="Aide",menu=Aide)

10

Now we set up the File menu options (bottom right). Open will open our image and uses a function called “GetFileName”. Save will create the output PDF file and uses the FileSave function. We add a separator and finally an Exit function. Now we have the Process option and the Help functions (next page, top right). All of the options in the menu bar are also available from various buttons within the program. Now we will make our BuildWidgets function. This is where we create all the widgets that will be used on the GUI. def BuildWidgets(self,master): self.frame = Frame(master,width=900,height=850)

Maintenant, nous mettons en place les options du menu Fichier (en bas à droite). Ouvrir permet d'ouvrir notre image et utilise une fonction appelée RecupererNomFichier. Sauver va créer le fichier PDF de sortie et utilise la fonction SauverFichier. Nous ajoutons un séparateur et enfin une ligne pour Quitter.

Maintenant, nous avons l'option de traitement et les fonctions d'aide (page suivante, en haut à droite).

Toutes les options de la barre de menu sont également disponibles à partir de divers boutons dans le programme. Maintenant, nous allons écrire notre fonction ConstruireWidgets. C'est là que nous créons tous les widgets qui seront utilisés sur l'interface graphique.

def ConstruireWidgets(self,principal):

      self.frame = Frame(principal,width=900,height=850)

11

We start with the function (bottom right) definition, passing in the TopLevel window (master) and placing a frame that holds all of our other widgets. I’ve added comments to help realize which part of code deals with which frame. We’ll deal with the top frame first. Assuming you remember or refreshed your memory on Tkinter, it should be fairly straight-forward. Let’s look at the first label as a discussion item. self.label1 = Label(self.frm1,text = “Original Filename: ”) First, we define the name of the widget (self.label1 =). Next we set that variable to which widget type we want to use; in this case Label. Finally we set the parameters we want to apply to that widget starting with the parent widget (self.frm1), and in this case, the text that will show up in the label. Now let’s take a moment to look at the button self.btnGetFN. self.btnGetFN = Button(self.frm1, width=28, image=self.openimage, command=self.GetFileName)

Nous commençons par la définition de la fonction (en bas à droite), qui prend en argument la fenêtre racine (principale) et crée un cadre qui contient tous nos autres widgets. J'ai ajouté des commentaires pour aider à comprendre quelle partie du code traite de quelle fenêtre. Nous allons commencer avec la fenêtre supérieure.

En supposant que vous vous en souvenez ou avez rafraîchi votre mémoire sur Tkinter, cela devrait être assez simple. Regardons la première étiquette pour l'expliquer :

self.label1 = Label(self.frm1,text = “Fichier original : ”)

Premièrement, nous définissons le nom du widget (self.label1 =). Ensuite, nous réglons cette variable au type de widget que nous voulons utiliser ; dans ce cas Label (étiquette). Enfin, nous définissons les paramètres que nous voulons appliquer à ce widget, à commencer par le widget parent (self.frm1) et, dans ce cas, le texte qui apparaîtra sur l'étiquette. Maintenant, nous allons prendre un moment pour regarder le bouton self.btnNomFic.

self.btnNomFic = Button(self.frm1, width=28, image=self.openimage,

            command=self.RecupererNomFichier)

12

First thing to notice is that this is broken into two lines. You can safely place everything on one line…it is just too long to fit into a 72-character line. We’ll really pay attention to the parameters we use here. First the parent (frm1), next the width which is set at 28. When we use a widget that has the option of text or an image, we have to be careful setting the width. If it will contain text, the width parameter is the number of characters it will hold. If it is to display an image, it will be set at the number of pixels. Finally, we set the command parameter, which tells the system what function to call when the button is clicked. One more thing to look at is the textvariable parameter. This tells us what variable will hold the information that will be displayed in the widget. We set these in the init function earlier. One other thing to mention is that the frame itself has two parameters you might not remember. The Relief parameter sets the border type of the frame, which in this case is GROOVE, and the bd parameter sets the border width. Border width defaults at 0 so if you want to see the effect, you have to set the border width (bd is a shortcut).

La première chose à remarquer est que c'est scindé en deux lignes. Vous pouvez placer le tout sur une seule ligne sans problème… mais c'est tout simplement trop long pour tenir sur une ligne de 72 caractères. Nous allons vraiment faire attention aux paramètres que nous utilisons ici. D'abord, le parent (frm1), puis la largeur qui est fixée à 28. Lorsque nous utilisons un widget qui a l'option de contenir un texte ou une image, il faut faire attention au réglage de la largeur. S'il contient du texte, le paramètre de largeur représente le nombre de caractères qu'il contiendra. Si c'est pour afficher une image, il correspond au nombre de pixels. Enfin, nous réglons le paramètre de commande, qui indique au système quelle fonction appeler lorsque le bouton est cliqué.

Une autre chose à regarder est le paramètre textvariable. Il indique la variable qui contiendra l'information qui sera affichée dans le widget. Nous avons réglé ces variables dans la fonction init plus tôt. Une autre chose à mentionner est que le cadre lui-même a deux paramètres que vous pourriez oublier. Le paramètre Relief définit le type de bordure, qui dans ce cas est GROOVE, et le paramètre bd définit la largeur de la bordure. La largeur de la bordure vaut 0 par défaut, donc si vous voulez voir l'effet, vous devez définir la largeur de bordure (bd est un raccourci).

13

Now we’ll deal with the middle frame widgets. The last 6 lines of this section (previous page, middle right) deal with the two combo boxes in the UI. Each combo box uses three lines (the way I programmed it to make it easy to understand). In the first line, we set the basic parameters. The next line, we bind the combobox selection-changed event to the function StitchSizeSelect, and the last line has a list of the values that will be available for the pulldown. Everything else above is pretty “normal” stuff. Now we set our defaults for the widgets that need them. Again, we are using the global variables that we set up in the init function and wrapped to the widget variable class.

Maintenant, nous allons nous occuper des widgets de la fenêtre du milieu.

Les six dernières lignes de cette section (page précédente, au milieu à droite) gère les deux listes déroulantes de l'interface utilisateur. Chaque liste déroulante est sur trois lignes (je les ai écrites ainsi pour les rendre faciles à comprendre). La première ligne contient les paramètres de base. Sur la ligne suivante, nous relions l'événement de « changement de choix » à la fonction ChoixTaillePoints, et la dernière ligne contient la liste des valeurs disponibles dans le menu déroulant.

Tout le reste ci-dessus est assez « classique ». Maintenant, nous réglons nos valeurs par défaut pour les widgets qui en ont besoin. Encore une fois, nous utilisons les variables globales mises en place dans la fonction init et associées aux classes de variables de widgets.

14

ComboStitch.set(14) ComboSize.set(“15×18”) FabricWidth.set(15) FabricHeight.set(18) MaxColors.set(50) BorderSize.set(1.0) Now we deal with the bottom frame. This is really simple, since we have to set up only the frame and two labels which we will use to hold our images.

ComboTaillePoints.set(14)

ComboTaille.set(“15×18”)

LargeurTissu.set(15)

HauteurTissu.set(18)

MaxCouleurs.set(50)

TailleBordure.set(1.0)

Maintenant, nous gérons la fenêtre du bas. C'est très simple, puisque nous n'avons à mettre en place que le cadre et deux étiquettes que nous allons utiliser pour contenir nos images.

15

Finally we deal with the side frame. The side frame will hold a ScrolledFrame from the PMW library. It’s really easy to use and provides a nice interface to the information about the floss that should be used. You can research the ScrolledFrame on your own, since we still have a lot to cover here. That’s all for the widgets. Now we have to place them. As I said earlier, we will be using the Grid geometry manager, rather than the absolute or pack managers. The Grid method places the widgets in (you guessed it) a grid, referenced by row and column designations. I’ll use the top frame as an example (shown top right). First we place the frame.

Enfin, nous traitons la fenêtre latérale, qui contiendra une ScrolledFrame (fenêtre à ascenseurs) de la bibliothèque PMW. C'est vraiment facile à utiliser et fournit une interface agréable pour l'information sur les fils qui devront être utilisés. Vous pouvez vous documenter vous-mêmes sur la ScrolledFrame, car nous avons encore beaucoup à faire ici.

C'est tout pour les widgets. Maintenant, nous devons les placer. Comme je l'ai dit plus tôt, nous utiliserons le gestionnaire en « grille », plutôt que les gestionnaires « absolu » ou « paquet ».

La méthode Grille place les widgets (vous l'aurez deviné) sur une grille, par rangées et colonnes. Je vais utiliser la fenêtre supérieure à titre d'exemple (illustré en haut à droite).

D'abord, nous plaçons le cadre.

16

You can see that we place the widget by using the {widgetname}.grid command, then the row and column positions. Notice that we are telling the entry widget to span 5 columns. Padx and pady values will place some extra space on both the right and left sides (padx) or the top and bottom (pady). The sticky parameter is similar to a justify command for text. The middle frame is a bit more complicated, but basically the same as the top frame. You might notice an extra frame in the middle of the code (self.frmLine). This gives us a nice divider between the options section and the display section. Since there is no horizontal or vertical line widget, I cheated and used a frame with a width of 6 pixels and border width of 3, making it just look like a fat line. The bottom frame is simple since we have only the frame and the two labels to hold the images.

Vous pouvez voir que nous plaçons le widget en utilisant la commande {nomwidget}.grid, puis la position de ligne et de colonne. Notez que nous indiquons au widget de saisie de couvrir 5 colonnes. Les valeurs padx et pady mettront un espace supplémentaire à la fois sur les côtés droit et gauche (padx) et haut et bas (pady). Le paramètre sticky est similaire à une commande « justifier » pour le texte.

La fenêtre du milieu est un peu plus compliquée, mais similaire à celle du haut. Vous remarquerez peut-être un cadre supplémentaire au milieu du code (self.frmLine). Cela nous donne un beau séparateur entre la section des options et la section d'affichage. Comme il n'y a pas de widget ligne horizontale ou verticale, j'ai triché et utilisé un cadre d'une largeur de 6 pixels et une largeur de bordure de 3, ce qui le fait ressembler à une ligne épaisse.

La fenêtre du bas est simple puisque nous n'avons que le cadre et les deux étiquettes pour contenir les images.

17

The side frame is pretty much the same thing, except the ScrolledFrame allows for a frame to be set to the interior of the scrolled frame widget. We then create three widgets here and place them in their grids as column headers. We do this since we assigned the interior frame for the scroll frame here and we have to assign the parent (self.sfFrame) after we have created it. That’s all the hard work for now. At this point, we will create all of the functions that we need to get the GUI to run, stubbing most of them until next month. There are a few we will go ahead and complete, but they are fairly short. The first function will be the Exit option from the menu bar. It’s under the File menu option. def DoExit(self): sys.exit()

La fenêtre de côté est à peu près identique, sauf que la ScrolledFrame permet qu'un cadre soit défini à l'intérieur du widget. Nous créons ensuite trois widgets et les plaçons dans la grille comme des en-têtes de colonnes. Nous faisons cela, car nous avons réglé le cadre intérieur de la ScrolledFrame et nous devons assigner le parent (self.sfFrame) après sa création.

Le travail difficile est fini pour le moment. Maintenant nous allons créer toutes les fonctions dont nous aurons besoin pour obtenir une interface graphique qui fonctionne ; la plupart seront vides jusqu'au mois prochain. Nous en écrirons certaines, mais elles sont assez courtes.

La première fonction sera l'option Quitter de la barre de menu, dans le menu Fichier.

 def Quitter(self):
      sys.exit()

18

The only other one is the Thumbnail function. We need this to fill the grey rectangles into the labels in the bottom frame. We pass the filename and the width and height that we want the thumbnail image to be. Since this article is so long, I’m going to give you a list of function names and all you have to do is stub it out by using the pass command. We’ll fill them in next month. I’ll give you the first one as an example, but you should already know how to do it. def GetFileName(self): pass For the rest of the functions, I’ll just give you the def lines. Be sure to include them all in your code.

La seule autre est la fonction Apercu. Nous en avons besoin pour remplir les rectangles gris dans les étiquettes de la fenêtre du bas. Nous lui passons le nom du fichier et la largeur et la hauteur que nous souhaitons pour l'aperçu.

Comme cet article est déjà long, je vais vous donner une liste de noms de fonctions et tout ce que vous avez à faire est de les remplir en utilisant la commande pass. Nous les compléterons le mois prochain. Je vais vous donner le premier comme un exemple, mais vous devriez déjà savoir comment le faire.

    def RecupererNomFichier(self):

      pass

Pour le reste des fonctions, je vais vous donner les lignes def. Assurez-vous de tous les inclure dans votre code.

19

You can see, we have a large amount of work to do next month. We still have four more lines to write to finish up for this month. This is out of our class code. root = Tk() root.title(“Cross Stitch Pattern Creator”) test = XStitch(root) root.mainloop() The first line sets up the root TopLevel window. The next line sets the title on the top line. The third line instantiates our XStitch class, and the last line starts the main loop that shows the UI and gives control over to it.

Comme vous pouvez le voir, nous avons pas mal de travail à faire le mois prochain. Nous avons encore quatre lignes à écrire pour finir pour ce mois, en dehors de notre code de classe :

racine = Tk()

racine.title(“Generateur de motifs de point de croix”)

test = XStitch(racine)

racine.mainloop()

La première ligne met en place la fenêtre racine. La ligne suivante définit le titre sur la ligne supérieure. La troisième ligne instancie notre classe XStitch, et la dernière ligne démarre la boucle principale qui montre l'interface utilisateur et lui donne le contrôle.

20

Well that’s a lot for this month, but we are finally done. You can actually run the program to see the GUI. As always, the code is available on Pastebin at http://pastebin.com/XtBawJps. Next month we will flesh out the code. See you then.

Eh bien, ça fait beaucoup pour ce mois-ci, mais nous sommes arrivés au bout. Vous pouvez effectivement exécuter le programme pour voir l'interface graphique.

Comme toujours, le code est disponible sur Pastebin : http://pastebin.com/RM7CgtbT (http://pastebin.com/XtBawJps pour l'original).

Rendez-vous le mois prochain pour étoffer le code.

MORCEAUX DE CODE PAGE 11

class XStitch:

  def __init__(self, principal):
      self.formatsImages = [
      ('JPEG / JFIF','*.jpg'),
      ('Portable Network Graphics','*.png'),
      ('CompuServer GIF','*.gif'),
      ('Windows Bitmap','*.bmp'),
      ('Tous les types *.*','*.*'),
      ]
      #-------------------------------------------
      #            Definitions globales
      #-------------------------------------------
      #          pour l'interface graphique
      global NomFichierOriginal
      NomFichierOriginal = StringVar()
      global NombreCouleursOriginal
      NombreCouleursOriginal = StringVar()
      global TailleOriginal
      global ComboTaillePoints
      ComboTaillePoints = IntVar()
      global ComboTaille
      ComboTaille = StringVar()
      global LargeurTissu
      LargeurTissu = DoubleVar()
      global HauteurTissu
      HauteurTissu = DoubleVar()
      global MaxCouleurs
      MaxCouleurs = IntVar()
      global TailleBordure
      TailleBordure = DoubleVar()

MORCEAUX DE CODE PAGE 12

      #-------------------------------------------
      global AfficherGrille
      AfficherGrille = True
      global ImageTraitee
      ImageTraitee = ""
      global GrilleImage
      GrilleImage = ""
      global couleurFond1
      couleurFond1 = (120,)*3
      global couleurFond2
      couleurFond2 = (0,)*3
      global PretPourTraitement
      PretPourTraitement = False

#

# DEBUT DEFINITION INTERFACE #

  def FabriquerMenu(self,principal):
      menu = Menu(principal)
      racine.config(menu=menu)
      menuFichier = Menu(menu, tearoff=0)
      Traitement = Menu(menu,tearoff=0)
      Aide = Menu(menu,tearoff=0)
      #-------------------------------------------
      #              Menu Fichier
      #-------------------------------------------
      menuFichier.add_command(label="Nouveau")
      menuFichier.add_command(label="Ouvrir", command=self.RecupererNomFichier)
      menuFichier.add_command(label="Sauver", command=self.SauverFichier)
      menuFichier.add_separator()
      menuFichier.add_command(label="Quitter", command=self.Quitter)

MORCEAUX DE CODE PAGE 13

      #-------------------------------------------
      #               Menu Traitement
      #-------------------------------------------
      Traitement.add_command(label="Tous",command=self.Traitement)
      #-------------------------------------------
      #                Menu Aide
      #-------------------------------------------
      Aide.add_command(label="Aide",command=self.AfficherAide)
      Aide.add_separator()
      Aide.add_command(label="A propos",command=self.AfficherApropos)

Attention : il faudra inverser les 2 cadres de code ci-dessous pour mettre la fenêtre haut avant la fenêtre milieu (sinon ça ne va pas avec la suite (page 14) qui correspond à la fenêtre milieu)

      # --------------- FENETRE MILIEU --------------------
      self.frm2 = Frame(self.frame,width=900,height=160,bd=4,relief=GROOVE)
      self.lbl4 = Label(self.frm2,text="Taille des points de l'aida : ")
      self.lbl5 = Label(self.frm2,text="Taille du tissu Aida : ")
      self.TCombobox1 = ttk.Combobox(self.frm2,textvariable=ComboTaillePoints,width=8)
      self.TCombobox1.bind('<<ComboboxSelected>>', self.ChoixTaillePoints)
      self.TCombobox1['values'] = (7,10,11,12,14,16,18,22)
      self.TCombobox2 = ttk.Combobox(self.frm2,textvariable=ComboTaille,width = 8)
      self.TCombobox2.bind('<<ComboboxSelected>>',self.ChoixTailleAida)
      self.TCombobox2['values'] = ("12x18","15x18","30")
      # ---------------- FENETRE HAUT ---------------------
      self.frm1 = Frame(self.frame,width=900,height=100,bd=4,relief=GROOVE)
      self.label1 = Label(self.frm1,text = "Fichier original : ")
      self.entNomFic = Entry(self.frm1,width=50,textvariable=NomFichierOriginal)
      self.btnNomFic = Button(self.frm1,width=28,image=self.openimage,command=self.RecupererNomFichier)
      self.label2 = Label(self.frm1,text = "Nb couleurs de l'original : ")
      self.lblNombreCouleursOriginal = Label(self.frm1,text="",width=10,textvariable=NombreCouleursOriginal)
      self.label3 = Label(self.frm1,text = "Taille de l'original : ")
      self.lblTailleOriginal = Label(self.frm1,text="",width=10,textvariable=TailleOriginal)

MORCEAUX DE CODE PAGE 14

      self.lbl6 = Label(self.frm2,text="Nb max de couleurs : ")
      self.entMaxCouleurs = Entry(self.frm2,textvariable=MaxCouleurs,width=3)
      self.lbl7 = Label(self.frm2,text="Taille bordure : ")
      self.entTailleBordure = Entry(self.frm2,textvariable=TailleBordure,width = 8)
      self.frmLine = Frame(self.frm2,width=6,height=80,bd=3,relief="raised")
      self.lbl8 = Label(self.frm2,text="Couleurs traitees : ")
      self.lbl9 = Label(self.frm2,text="Nb points traites : ")
      self.lblCouleursTraitees=Label(self.frm2,width=10,textvariable=CouleursTraitees,justify=LEFT)
      self.lblTailleTraitee=Label(self.frm2,width=10,textvariable=TailleTraitee,justify=LEFT)
      self.btnDoIt = Button(self.frm2,text="Traitement",width=11,command = self.Traitement)
      self.btnAfficherGrille = Button(self.frm2,text="Masquer grille",width=11,command=self.AfficherMasquerGrille)
      self.btnCreerPDF=Button(self.frm2,text="Creer PDF",width=11,command=self.CreerPDF)
      # --------------- FENETRE BAS -----------------------
      self.frm3 = Frame(self.frame,width=450,height=450,bd=4,relief=GROOVE)
      self.lblImageL = Label(self.frm3,image=self.DefaultImage,height=400,width=400,borderwidth=2,relief=GROOVE)
      self.lblImageR = Label(self.frm3,image=self.DefaultImage,height=400,width=400,borderwidth=2,relief=GROOVE)
      #---------------- FENETRE COTE ----------------------
      self.frm4 = Frame(self.frame,width = 300,height=580,bd=4,relief=GROOVE)
      # Cree la fenetre deroulante
      self.sf = Pmw.ScrolledFrame(self.frm4,
              labelpos = 'n', label_text = 'Liste couleurs traitees',
              usehullsize = 1,
              hull_width = 300,
              hull_height = 567,)
      return self.frame

MORCEAUX DE CODE PAGE 15

cadre du haut, remplacer (et décaler avec des espaces pour aligner) :

  • ROW par LIGNE
  • entFileName par entNomFic
  • btnGenFN par btnNomFic
  • lblOriginalColorCount par lblNombreCouleursOriginal
  • lblOriginalSize par lblTailleOriginal
  def PlacerWidgets(self,fenetre):
      fenetre.grid(column = 0, row = 0)
      # ---------------- FENETRE HAUT ---------------------
      self.frm1.grid(column=0,row=0,rowspan=2,sticky="new")
      self.label1.grid(column=0,row=0,sticky='w')
      self.entNomFic.grid(column=1,row=0,sticky='w',columnspan = 5)
      self.btnNomFic.grid(column=7,row = 0,sticky='w')
      self.label2.grid(column=9,row=0,sticky='w',padx=10)
      self.lblNombreCouleursOriginal.grid(column=10,row=0,sticky='w')
      self.label3.grid(column=9,row=1,sticky='w',padx=10,pady=5)
      self.lblTailleOriginal.grid(column=10,row=1,sticky='w')
      # ---------------- FENETRE MILIEU -------------------
      self.frm2.grid(column=0,row=2,rowspan=2,sticky="new")
      self.lbl4.grid(column=0,row=0,sticky="new",pady=5)
      self.lbl5.grid(column=0,row=1,sticky="new")
      self.TCombobox1.grid(column=1,row=0,sticky="new",pady=5)
      self.TCombobox2.grid(column=1,row=1,sticky="new")
      self.lbl6.grid(column=2,row = 0,sticky="new",padx=5,pady=5)
      self.entMaxCouleurs.grid(column=3,row=0,sticky="new",pady=5)
      self.lbl7.grid(column=2,row=1,sticky='new',padx=5)
      self.entTailleBordure.grid(column=3,row=1,sticky='new')
      self.frmLine.grid(column=4,row=0,rowspan=2,sticky='new',padx=15)
      self.lbl8.grid(column=5,row=0,sticky='new',pady=5)
      self.lbl9.grid(column=5,row=1,sticky='new')
      self.lblCouleursTraitees.grid(column=6,row=0,sticky='w')
      self.lblTailleTraitee.grid(column=6,row=1,sticky='new')
      self.btnDoIt.grid(column=7,row=0,sticky='e',padx=5,pady = 5)
      self.btnAfficherGrille.grid(column=7,row=1,sticky='e',padx=5,pady = 5)
      self.btnCreerPDF.grid(column=8,row=0,rowspan=2,sticky='ew',padx=10)
      # ---------------- FENETRE BAS ----------------------
      self.frm3.grid(column=0,row=4,sticky="nsew")
      self.lblImageL.grid(column=0,row=0,sticky="w")
      self.lblImageR.grid(column=1,row=0,sticky="e")

MORCEAUX DE CODE PAGE 16

# —————- FENETRE COTE ——————— self.frm4.grid(column=2,row=0,rowspan=12,sticky=“new”) self.sf.grid(column=0,row=1) self.sfFrame = self.sf.interior() self.lblch1 = Label(self.sfFrame,text=“ Original”) self.lblch2 = Label(self.sfFrame,text=“ DMC”) self.lblch3 = Label(self.sfFrame,text=“Nom/Numero”) self.lblch1.grid(column=0,row=0,sticky='w') self.lblch2.grid(column=1,row=0,sticky='w') self.lblch3.grid(column=2,row=0,sticky=“w”)

  def Apercu(self,fichier,tailleH,tailleV):
      taille = tailleH,tailleV
      posExt = fichier.rfind(".")
      fichierSortie = fichier[:posExt] + ".thumbnail"
      im = Image.open(fichier)
      im.thumbnail(taille)
      im.save(fichierSortie,"JPEG")
      return im

def AfficherAide(self):, def AfficherApropos(self):, def OuvrirBase(self):, def AfficherMasquerGrille(self): def ChoixTaillePoints(self,p):, def ChoixTailleAida(self,p):, def Traitement(self): def CreerPDF(self):, def InfoOriginal(self,fichier):, def RecupererNbCouleurs(self,fichier): def RecupererHauteurLargeur(self,fichier):, def RecupererHauteurLargeur2(self,fichier):, def RecupererCouleurs(self,image): def Pixeliser(self,im,taillePixel):, def ReduireCouleurs(self,NomImage): def DessinerLignes(self,im,taillePixel):, def DessinerLignes2(self,im,taillePixel): def Rgb2Hex(self,rgb):, def RemplirListeDeroulante(self,nomFic): def TrouverMeilleureDistance(self,r1,g1,b1):

issue86/python.txt · Dernière modification : 2014/12/31 17:50 de d52fr