Ceci est une ancienne révision du document !
After our last meeting you should have a fairly good idea of how to use Page. If not, please read last month's article. We'll continue this time by creating a file list application with a GUI. The goal here is to create a GUI application that will recursively walk through a directory, looking for files with a defined set of extensions, and display the output in a treeview. For this example we will look for media files with the extensions of “.avi”, “.mkv”, “.mv4”, “.mp3” and “.ogg”. This time, the text might seem a bit terse in the design portion. All I'm going to do is give you directions for placement of widgets and the required attributes and values like this… Widget Attribute: Value
Après notre dernière rencontre, vous devriez avoir une assez bonne idée de la façon d'utiliser Page. Sinon allez vite lire l'article du mois dernier. Nous allons continuer cette fois-ci en créant une application de liste de fichiers avec une interface graphique. Le but ici est de créer une application graphique qui va récursivement parcourir un répertoire, en cherchant des fichiers avec un ensemble défini d'extensions, et afficher le résultat dans une vue arborescente. Pour cet exemple, nous allons chercher les fichiers multimédias avec les extensions .avi, .mkv, .mv4, .mp3 et .ogg.
Cette fois, le texte peut sembler un peu laconique dans la partie conception. Tout ce que je vais faire, c'est vous donner des indications pour le placement des widgets ainsi que les attributs et les valeurs requises de cette façon…
Widget
Attribut: valeur
I will only quote text string when it is needed. For example for one of the buttons, the text should be set to “…”. Here's what the GUI of our application will look like… As you can see, we have our main form, an exit button, a text entry box with a button that will call up an ask for directory dialog box, 5 check boxes for extension selecting extension types, a “GO!” button to actually start the processing and a treeview to display our output. So, let's get started. Fire up Page and create a new top level widget. Using the Attribute Editor set the following attributes. Alias: Searcher Title: Searcher
Je ne mettrai les chaînes entre guillemets que lorsque cela sera nécessaire. Par exemple, pour l'un des boutons, le texte doit être réglé sur « … ».
Voici à quoi va ressembler l'interface graphique de notre application…
Comme vous pouvez le voir, nous avons notre formulaire principal, un bouton pour quitter, une boîte de saisie de texte avec un bouton qui va appeler une boîte de dialogue pour demander le répertoire, cinq cases à cocher pour sélectionner les types d'extension, un bouton « ALLER ! » pour effectivement commencer le traitement et une arborescence pour afficher notre production.
Nous pouvons commencer. Lancez Page et créez un nouveau widget principal. En utilisant l'éditeur d'attributs, définissez les attributs suivants.
Alias : Rechercher Titre : Rechercher
Be sure to save often. When you save the file, save it as “Searcher”. Remember, Page puts the .tcl extension for you and when you finally generate the python code, it will be saved in the same folder. Next add a frame. It should go at the very top of the main frame. Set the attributes as follows. Width: 595 Height: 55 x position: 0 y position: 0 In this frame, add a button. This will be our Exit button. Alias: btnExit Text: Exit Move this close to the center of the frame or close to the frame's right side. I set mine to X 530 and Y 10.
Assurez-vous de sauvegarder souvent. Lorsque vous enregistrez le fichier, donnez-lui le nom « Rechercher ». Rappelez-vous, Page ajoute l'extension .tcl pour vous et quand vous générerez le code python il sera sauvegardé dans le même dossier.
Ensuite, ajoutez un cadre. Il devrait se placer tout en haut du cadre principal. Définissez les attributs comme suit.
Largeur : 595 Hauteur : 55 Position x : 0 Position y : 0
Dans ce cadre, ajoutez un bouton. Ce sera notre bouton Quitter.
Alias : btnExit Texte : Quitter
Déplacez-le au centre de la fenêtre, ou alors sur le côté droit. J'ai mis le mien à X = 530 et Y = 10.
Create another frame. Width: 325 Height: 185 y position: 60 Here is what this frame will look like, to give you a guide going forward through this section. In this frame, add a label. Set the text attribute to “Path:”. Move it close to the top left of the frame. In the same frame, add an entry widget. Alias: txtPath Text: FilePath Width: 266 Height: 21
Créez un autre cadre.
Largeur : 325 Hauteur : 185 Position y : 60
Voici à quoi ce cadre va ressembler, pour vous guider à travers cette section.
Dans ce cadre, ajoutez une étiquette. Définissez l'attribut texte à « Chemin : ». Déplacez-le en haut à gauche de la fenêtre.
Dans le même cadre, ajoutez un widget de saisie.
Alias : txtChemin Texte : CheminFichier Largeur : 266 Hauteur : 21
Add a button to the right of the entry widget. Alias: btnSearchPath Text: “…” (no quotes) Add five (5) check buttons. Put them in the following order… x x x x x The three check buttons on the left are for video files and the two on the right are for audio files. We will deal with the three on the left first, then the two on the right.
Ajoutez un bouton à droite de la zone de saisie.
Alias : btnCheminRecherche Texte : « … » (sans guillemets)
Ajoutez cinq (5) cases à cocher. Mettez-les dans l'ordre suivant…
x x x x x
Les trois cases à cocher de gauche sont pour les fichiers vidéo et les deux de droite sont pour les fichiers audio. Nous allons d'abord traiter les trois de gauche, puis les deux de droite.
Alias: chkAVI Text: “.avi” (no quotes) Variable: VchkAVI Alias: chkMKV Text: “.mkv” (no quotes) Variable: VchkMKV Alias: chkMV4 Text: “.mv4” (no quotes) Variable: VchkMV4 Alias: chkMP3 Text: “.mp3” (no quotes) Variable: VchkMP3 Alias: chkOGG Text: “.ogg” (no quotes) Variable: VchkOGG
Alias : chkAVI Texte : « .avi » (sans guillemets) Variable : VchkAVI
Alias : chkMKV Texte : « .mkv » (sans guillemets) Variable : VchkMKV
Alias : chkMV4 Texte : « .mv4 » (sans guillemets) Variable : VchkMV4
Alias : chkMP3 Texte : « .mp3 » (sans guillemets) Variable : VchkMP3
Alias : chkOGG Texte : « .ogg » (sans guillemets) Variable : VchkOGG
Finally, in this frame add a button somewhere below the five check boxes and somewhat centered within the frame. Alias: btnGo Text: GO! Now add one more frame below our last frame. Width: 565 Height: 265 I placed mine around X 0 Y 250. You might have to resize your main form to have the entire frame show. Within this frame, add a Scrolledtreeview widget. Width: 550 Height: 254 X Position: 10 Y Position: 10
Enfin ajoutez dans ce cadre un bouton quelque part en-dessous des cinq cases à cocher et un peu centré à l'intérieur du cadre.
Alias : btnAller Texte : ALLER !
Maintenant, ajoutez un autre cadre en-dessous du précédent.
Largeur : 565 Hauteur : 265
J'ai placé le mien à environ X = 0 et Y = 250. Vous pourriez avoir à redimensionner votre formulaire principal pour voir l'affichage en entier. Dans ce cadre, ajoutez un widget Scrolledtreeview (vue arborescente avec ascenseur).
Largeur : 550 Hauteur : 254 Position x : 10 Position y : 10
There. We've designed our GUI. Now all that is left to do is create our function list and bind the functions to our buttons. In the Function list window, click the New button (the far left button). This brings up the new function editor. Change the text in the Function entry box from “py: xxx” to “py:btnExitClick()”. In the arguments entry box type “p1”. In the bottom multiline entry box, change the text to: def btnExitClick(p1): sys.exit() Notice that this is not indented. Page will do that for us when it creates the python file.
Voilà. Nous avons conçu notre interface graphique. Maintenant tout ce qu'il reste à faire est de créer notre liste de fonctions et de lier ces fonctions à nos boutons.
Dans la fenêtre de liste des fonctions, cliquez sur le bouton Nouveau (le bouton le plus à gauche). Ceci nous amène à l'éditeur de nouvelle fonction. Modifiez le texte dans la zone de saisie Fonction en remplaçant « py: xxx » par « py: btnClicQuitter()“. Dans la zone de texte de l'argument saisissez « p1 ». Dans la zone de saisie multilignes du bas, changez le texte en :
def btnClicQuitter(p1):
sys.exit ()
Notez que ce n'est pas indenté. Page le fera pour nous quand il créera le fichier python.
Next create another function called btnGoClick. Remember to add a passed parameter of “p1”. Leave the “pass” statement. We'll change that later. Finally, add another function called “btnSearchPath”. Again, leave the pass statement. Lastly, we need to bind the buttons to the functions we just created. Right-click on the exit button we created, select Bind. A large box will pop up. Click on the New binding button, Click on Button-1 and change the word “TODO” in the right text entry box to “btnExitClick”. Do NOT include the parens () here. Bind the GO button to btnGoClick and the “…” button to btnSearchPathClick. Save your GUI and generate the python code.
Ensuite, créez une autre fonction appelée btnClicAller. N'oubliez pas d'ajouter un paramètre nommé « p1 ». Laissez l'instruction « pass » ; nous changerons cela plus tard.
Enfin, ajoutez une autre fonction appelée « btnCheminRecherche ». Encore une fois, laissez l'instruction « pass ».
Enfin, nous devons relier les boutons et les fonctions que nous venons de créer.
Faites un clic droit sur le bouton Quitter que nous avons créé, sélectionnez Lier. Une grande boîte apparaîtra. Cliquez sur le bouton Nouvelle liaison, cliquer sur bouton-1 et remplacez le mot « A FAIRE » dans la boîte de saisie de texte de droite par « btnClicQuitter ». NE METTEZ PAS les parenthèses () ici.
Liez le bouton ALLER à la fonction btnClicAller et le bouton « … » à btnClicCheminRecherche.
Sauvegardez votre interface graphique et générez le code python.
Now all we have left is to create the code that “glues” the GUI together.
Open up the code we just generated in your favorite editor. Let's start off by examining what Page created for us.
At the top of the file is our standard python header and a single import statement to import the sys library. Next is some rather confusing (at first glance) code. This basically looks at the version of python you are trying to run the application in and then to import the correct versions of the tkinter libraries. Unless you are using python 3.x, you can basically ignore the last two.
We'll be modifying the 2.x code portion to import other tkinter modules in a few moments.
Next is the “vp_start_gui()” routine. This is the program's main routine. This sets up our gui, sets the variables we need, and then calls the tkinter main loop. You might notice the line “w = None” below this. It is not indented and it isn't supposed to be.
Next are two routines (create_Searcher and destroy_Searcher) that are used to replace the main loop routine if we are calling this application as a library. We don't need to worry about these.
Next is the “set_Tk_var” routine. We define the tkinter variables used that need to be set up before we create the widgets. You might recognize these as the text variable for the FilePath entry widget and the variables for our check boxes. The next three routines here are the functions we created using the function editor and an “init()” function.
Run the program now. Notice that the check buttons have grayed out checks in them. We don't want that in our “release” app, so we'll create some code to clear them before the form is displayed to the user. The only functioning thing other than the check boxes is the Exit button.
Go ahead and end the program.
Now, we'll take a look at the class that actually holds the GUI definition. That would be “class Searcher”. Here is where all the widgets are defined and placed in our form. You should be familiar with this by now.
Two more classes are created for us that hold the code to support the scrolled tree view. We don't have to change any of this. It was all created by Page for us.
Now let's go back to the top of the code and start modifying.
We need to import a few more library modules, so under the “import sys” statement, add…
import os
from os.path import join, getsize, exists
Now find the section that has the line “py2 = True”. As we said before, this is the section that deals with the tkinter imports for Python version 2.x. Below the “import ttk”, we need to add the following to support the FileDialog library. We also need to import the tkFont module.
import tkFileDialog
import tkFont
Next we need to add some variables to the “set_Tk_var()” routine. At the bottom of the routine, add the following lines…
global exts, FileList
exts = []
FileList=[]
Here we create two global variables (exts and FileList) that will be accessed later on in our code. Both are lists. “exts” is a list of the extensions that the user selects from the GUI. “FileList” holds a list of lists of the matching files found when we do our search. We'll use that to populate the treeview widget.
Since our “btnExitClick” is already done for us by Page, we'll deal with the “btnGoClick” routine. Comment out the pass statement and add the code so it looks like this…
def btnGoClick(p1) :
#pass
BuildExts()
fp = FilePath.get()
e1 = tuple(exts)
Walkit(fp,e1)
LoadDataGrid()
This is the routine that will be called when the user clicks the “GO!” button. We call a routine called “BuildExts” which creates the list of the extensions that the user has selected. Then we get the path that the user has selected from the AskDirectory dialog and assign that to the fp variable. We then create a tuple from the extension list, which is needed when we check for files. We then call a routine called “Walkit”, passing the target directory and the extension tuple.
Finally we call a routine called “LoadDataGrid”.
Next we need to flesh out the “btnSearchPathClick” routine. Comment out the pass statement and change the code to look like this…
def btnSearchPathClick(p1) :
#pass
path = tkFileDialog.askdirectory() #**self.file_opt)
FilePath.set(path)
The init routine is next. Again, make the code look like this…
def init():
#pass
# Fires AFTER Widgets and Window are created...
global treeview
BlankChecks()
treeview = w.Scrolledtreeview1
SetupTreeview()
Here we create a global called “treeview”. We then call a routine that will clear the gray checks from the check boxes, assign the “treeview” variable to point to the Scrolled treeview in our form and call “SetupTreeview” to set the headers for the columns. Here's the code for the BlankChecks routine which needs to be next.
def BlankChecks():
VchkAVI.set('0')
VchkMKV.set('0')
VchkMP3.set('0')
VchkMV4.set('0')
VchkOGG.set('0')
Here, all we are doing is setting the variables (which automatically sets the check state in our check boxes) to “0”. If you remember, whenever the check box is clicked, this variable is automatically updated. If the variable is changed by our code, the check box responds as well. Now (above right) we'll deal with the routine that builds the list of extensions from what the user has clicked.
Cast your memory back to my ninth article in FCM#35. We wrote some code to create a catalog of MP3 files. We'll use a shortened version of that routine (middle right). Refer back to FCM#35 if you have questions about this routine.
Next (bottom right) we call the SetupTreeview routine. It's fairly straightforward. We define a variable “ColHeads” with the headings we want in each column of the treeview. We do this as a list. We then set the heading attribute for each column. We also set the column width to the size of this header.
Finally we have to create the “LoadDataGrid” routine (next page, top right) which is where we load our data into the treeview. Each row of the treeview is one entry in the FileList list variable. We also adjust the width of each column (again) to match the size of the column data.
That's it for the first blush of the application. Give it a run and see how we did. Notice that if you have a large number of files to go through, the program looks like it's not responding. This is something that needs to be fixed. We'll create routines to change our cursor from the default to a “watch” style cursor and back so when we do something that takes a long time, the user will notice.
In the “set_Tk_var” routine, add the following code at the bottom.
global busyCursor,preBusyCursors,busyWidgets
busyCursor = 'watch'
preBusyCursors = None
busyWidgets = (root, )
What we do here is set up global variables, assign them and then we set the widget(s) (in busyWidgets) we wish to respond to the cursor change. In this case we set it to root which is our full window. Notice that this is a tuple.
Next we create two routines to set and unset the cursor. First the set routine, which we will call “busyStart”. After our “LoadDataGrid” routine, insert the code shown middle right.
We first check to see if a value was passed to “newcursor”. If not, we default to the busyCursor. Then we walk through the busyWidgets tuple and set the cursor to whatever we want.
Now put the code shown bottom right below it.
In this routine, we basically reset the cursor for the widgets in our busyWidget tuple back to our default cursor.
Save and run your program. You should find that the cursor changes whenever you have a long list of files to go through.
While this application doesn't really do much but show you how to use Page to create really fast code development. From today's article, you can see how having a good design of your GUI ahead of time can make the development process easy and fairly painless.
The tcl file is saved in pastebin at http://pastebin.com/AA1kE4Dy and the python code is saved at http://pastebin.com/VZm5un3e.
See you next time.