Ceci est une ancienne révision du document !
Table des matières
Correction Last month, in part 21, you were told to save what you have as “PlaylistMaker.glade”, but, in the code, it was referred to as: “playlistmaker.glade”. I’m sure you noticed that one has capitals and the other does not. The code will run only if you use both the call and file name with, or both without, the capitals.
Je propose de ne pas mettre ce paragraphe en VF : nous n'avions pas d'erreur de nom de fichier dans la VF !
To start off on the right foot, you need to have the playlistmaker.glade and playlistmaker.py from last month. If you don't, jump over to the last issue and get the goodies. Before we get to the code, let's take a look at what a playlist file is. There are multiple versions of play lists, and they all have different extensions. The one we will be creating will be a *.m3u type playlist. In its simplest form, it's just a text file that starts with “#EXTM3U”, and then has an entry for each song file you want to play - including the full path. There's also an extension that can be added before each entry that includes the length of the song, the album name the song comes from, the track number, and the song name. We'll bypass the extension for now and just concentrate on the basic version. Here is an example of a M3U playlist file.. . #EXTM3U Adult Contemporary/Chris Rea/Collection/02 - On The Beach.mp3 Adult Contemporary/Chris Rea/Collection/07 - Fool (If You Think It's Over).mp3 Adult Contemporary/Chris Rea/Collection/11 - Looking For The Summer.mp3
Pour commencer du bon pied, vous avez besoin d'avoir les fichiers playlistmaker.glade et playlistmaker.py du mois dernier. Si ce n'est pas le cas, sautez sur le numéro précédent pour les récupérer. Avant de passer au code, nous allons jeter un oeil à ce qu'est un fichier de liste de lecture. Il y a plusieurs versions des listes de lecture, qui ont toutes des extensions différentes. Le fichier que nous allons créer sera de type *.m3u. Dans sa forme la plus simple, c'est juste un fichier texte qui commence par « #EXTM3U “ et qui contient une entrée pour chaque fichier audio que vous voulez écouter - avec le chemin d'accès complet. Il y a aussi une extension qui peut être ajoutée avant chaque entrée contenant la longueur de la chanson, le nom de l'album d'où vient la chanson, le numéro de piste et le nom du morceau. Nous allons ignorer l'extension pour l'instant et se concentrer uniquement sur la version de base.
Voici un exemple d'un fichier de liste de lecture M3U. . #EXTM3U Adult Contemporary/Chris Rea/Collection/02 - On The Beach.mp3 Adult Contemporary/Chris Rea/Collection/07 - Fool (If You Think It's Over).mp3 Adult Contemporary/Chris Rea/Collection/11 - Looking For The Summer.mp3
All path names are relative to the location of the playlist file. OK…now let's get to coding. Shown right is the opening of the source code from last month. Now, we need to create an event handler routine for each of our events that we have set up. Notice that on_MainWindow_destroy and on_tbtnQuit_clicked are already done for us, so we need to have only 10 more (shown top right). Just make stubs for now. We'll modify these stubbed routines in a few minutes. For now, this should get us up and running with an application, and we can test things as we go. But, we need to add one more line to the init routine before we can run the app. After the self.wTree line, add… self.SetEventDictionary()
Tous les noms de chemin sont relatifs à l'emplacement du fichier de liste de lecture.
Bien… Maintenant passons au code. Vous voyez à droite le début du code source du mois dernier.
Maintenant, nous devons créer une routine de gestion d'événement pour chacun des événements que nous avons mis en place. Notez que on_FenetrePrincipale_destroy et on_boBtnQuitter_clicked sont déjà faits pour nous, il n'en reste donc que dix autres à écrire (voir en haut à droite). Écrivons juste des ébauches pour l'instant.
Nous modifierons ces ébauches de routines dans quelques minutes. Pour l'instant, cela devrait nous permettre de démarrer l'application, et nous pourrons tester les choses au fur et à mesure que nous avançons. Nous devons quand même ajouter une ligne supplémentaire à la routine __init__ avant que nous puissions démarrer l'application. Après la ligne self.wTree, ajouter :
self.DicoEvenements()
Now, you can run the application, see the window, and click the Quit toolbar button to exit the application properly. Save the code as “playlistmaker-1a.py” and give it a try. Remember to save it in the same folder as the glade file we created last time, or copy the glade file into the folder you saved this code in. We also need to define a few variables for future use. Add these after the SetEventDictionary call in the init function. self.CurrentPath = ”“ self.CurrentRow = 0 self.RowCount = 0 Now, we will create a function that allows us to display a popup dialog box whenever we need to give some information to our user. There is a built-in set of routines that we will use, but we'll make a routine of our own to make it easier for us. It is the gtk.MessageDialog routine, and the syntax is as follows… gtk.MessageDialog(parent,flags,MessageType,Buttons,message)
Maintenant, vous pouvez exécuter l'application, voir la fenêtre, puis cliquez sur le bouton Quitter de la barre pour quitter l'application correctement. Enregistrez le code sous le nom “CreateurListeDeLecture-1a.py” et essayez-le. Souvenez-vous qu'il faut l'enregistrer dans le même dossier que le fichier glade nous avons créé la dernière fois, ou bien copier le fichier glade dans le dossier dans lequel vous avez enregistré ce code.
Nous avons également besoin de définir quelques variables pour une utilisation future. Ajoutez ceci après l'appel à DicoEvenements() dans la fonction __init__.
self.CheminCourant = "" self.LigneCourante = 0 self.NombreDeLignes = 0
Maintenant, nous allons créer une fonction qui nous permet d'afficher une boîte de dialogue à chaque fois que nous avons besoin de donner des informations à nos utilisateurs. Il existe un ensemble de routines toutes faites que nous allons utiliser, mais nous allons faire une routine à nous pour nous faciliter les choses. C'est la routine gtk.MessageDialog, et la syntaxe est la suivante…
gtk.MessageDialog (parent, drapeaux, MessageType, boutons, message)
Some discussion is needed before we go too much further. The message type can be one of the following… GTK_MESSAGE_INFO - Informational message GTK_MESSAGE_WARNING - Nonfatal warning message GTK_MESSAGE_QUESTION - Question requiring a choice GTK_MESSAGE_ERROR - Fatal error message And the button types are… GTK_BUTTONS_NONE - no buttons at all GTK_BUTTONS_OK - an OK button GTK_BUTTONS_CLOSE - a Close button GTK_BUTTONS_CANCEL - a Cancel button GTK_BUTTONS_YES_NO - Yes and No buttons GTK_BUTTONS_OK_CANCEL - OK and Cancel Buttons
Une discussion est nécessaire avant d'aller trop loin. Le type de message peut être une des suivants…
GTK_MESSAGE_INFO - message d'information GTK_MESSAGE_WARNING - message d'avertissement GTK_MESSAGE_QUESTION - question nécessitant un choix GTK_MESSAGE_ERROR - message d'erreur fatale
Et les types de boutons sont…
GTK_BUTTONS_NONE - aucun bouton GTK_BUTTONS_OK - un bouton OK GTK_BUTTONS_CLOSE - un bouton Fermer GTK_BUTTONS_CANCEL - un bouton Annuler GTK_BUTTONS_YES_NO - boutons Oui et Non GTK_BUTTONS_OK_CANCEL - boutons OK et Annuler
Normally, you would use the following code , or similar, to create the dialog, display it, wait for a response, and then destroy it. dlg = gtk.MessageDialog(None,0,gtk.MESSAGE_INFO,gtk.BUTTONS_OK,”This is a test message…“) response = dlg.run() dlg.destroy() However, if you want to display a message box to the user more than once or twice, that's a LOT of typing. The general rule of thumb is that if you write a series of lines-of-code more than once or twice, it's usually better to create a function and then call that. Think of it this way: If we want to display a message dialog to the user, say ten times in your application, that's 10 X 3 (or 30) lines of code. By making a function to do this for us (using the example I just presented), we would have 10 + 3 (or 13) lines of code to write. The more we call a dialog, the less code we actually have to type, and the more readable our code is. Our function (top right) will allow us to call any of the four message dialog types with just one routine using different parameters.
Normalement, vous devez utiliser le code suivant, ou du code similaire, pour créer la boîte de dialogue, de l'afficher, attendre une réponse, puis le détruire.
dlg = gtk.MessageDialog (None, 0, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "Ceci est un message de test ...") reponse = dlg.run () dlg.destroy ()
Toutefois, si vous voulez afficher une boîte de message à l'utilisateur plus d'une ou deux fois, c'est beaucoup de dactylographie. La règle générale est que si vous écrivez une série de lignes de code plus d'une ou deux fois, il est généralement préférable de créer une fonction puis de l'appeler. Pensez-y de cette manière : si nous voulons afficher un message de dialogue à l'utilisateur par exemple dix fois dans votre application, cela représente 10 x 3 (soit 30) lignes de code. En faisant une fonction pour faire cela pour nous (en utilisant l'exemple que je viens de présenter), nous aurions 10 + 3 (soit 13) lignes de code à écrire. Plus nous appelons une boîte de dialogue, moins cela fait de code à taper, et plus lisible est notre code. Notre fonction (en haut à droite) nous permettra d'appeler l'un des quatre types de message de dialogue avec juste une routine en utilisant différents paramètres.
This is a very simple function that we would then call like this… self.MessageBox(“info”,”The button QUIT was clicked“) Notice that if we choose to use the MESSAGE_QUESTION type of dialog, there are two possible responses that will be returned by the message dialog - a “Yes” or a “No”. Whichever button the user clicks, we will receive the information back in our code. To use the question dialog, the call would be something like this… response = self.MessageBox(“question”,”Are you sure you want to do this now?“) if response == gtk.RESPONSE_YES: print “Yes was clicked” elif response == gtk.RESPONSE_NO: print “NO was clicked”
C'est une fonction très simple que nous pourrons ensuite appeler de cette façon…
self.MessageBox("info", "Le bouton QUITTER a été cliqué")
Notez que si nous choisissons d'utiliser le type de dialogue MESSAGE_QUESTION, il y a deux réponses possibles qui seront retournées par la fenêtre de dialogue - un « oui » ou un « non ». Quelle que soit le bouton cliqué par l'utilisateur, nous allons recevoir les informations de retour dans notre code. Pour utiliser la boîte de dialogue de question, l'appel ressemblera à ça …
reponse = self.MessageBox(« question », « Êtes-vous sûr de vouloir faire cela maintenant ? ")
if reponse == gtk.RESPONSE_YES:
print "Clic sur oui"
elif reponse == gtk.RESPONSE_NO:
print "clic sur non"
You can see how you can check the value of the button returned. So now, replace the “pass” call in each of our event handler routines with something like that shown below right. We won't keep it like this, but this gives you a visual indication that the buttons work the way we want. Save the code now as “playlistmaker-1b.py”, and test your program. Now we are going to create a function to set our widget references. This routine is going to be called only once, but it will make our code much more manageable and readable. Basically, we want to create local variables that reference the widgets in our glade window - so we can make calls to them whenever (if ever) we need to. Put this function (above right) below the SetEventDictionary function. Please notice that there is one thing that isn't referenced in our routine. That would be the treeview widget. We'll make that reference when we set up the treeview itself. Also of note is the last line of our routine. In order to use the status bar, we need to refer to it by its context id. We'll be using this later on. Next, let's set up the function that displays the “about” dialog when we click the About toolbar button. Again, there is a built-in routine to do this provided by the GTK library. Put this after the MessageBox function. Here's the code, below right.
Save your code and then give it a try. You should see a pop-up box, centered in our application, that displays everything we have set. There are more attributes that you can set for the about box (which can be found at http://www.pygtk.org/docs/pygtk/class-gtkaboutdialog.html), but these are what I would consider a minimum set. Before we go on, we need to discuss exactly what will happen from here. The general idea is that the user will click on the “Add” toolbar button, we'll pop up a file dialog box to allow them to add files to the playlist, and then display the file information into our treeview widget. From there, they can add more files, delete single file entries, delete all file entries, move a file entry up, down, or to the top or down to the bottom of the treeview. Eventually, they'll set the path that the file will be saved to, provide a filename with a “m3u” extension, and click the save file button. While this seems simple enough, there's a lot that happens behind the scenes. The magic all happens in the treeview widget, so let's discuss that. This will get pretty deep, so you might want to read carefully, since an understanding of this will keep you from making mistakes later on.
A treeview can be something as simple as a columnar list of data like a spreadsheet or database representation, or it could be more complex like a file-folder listing with parents and children, where the folder would be the parent and the files in that folder would be the children, or something even more complex. For this project, we'll use the first example, a columnar list. In the list, there will be three columns. One is for the name of the music file, one is for the extension of the file (mp3, ogg, wav, etc) and the final column is for the path. Combining this into a string (path, filename, extension) gives us the entry into the playlist we will be writing. You could, of course, add more columns as you wish, but for now, we'll deal with just three. A treeview is simply a visual storage container that holds and displays a model. The model is the actual “device” that holds and manipulates our data. There are two different pre-defined models that are used with a treeview, but you can certainly create your own. That having been said, for 98% of your work, one of the two pre-defined models will do what you need. The two types are GTKListStore and GTKTreeStore. As their names suggest, the ListStore model is usually used for lists, the TreeStore is used for Trees. For our application, we will be using a GTKListStore. The basic steps are: • Create a reference to the TreeView widget. • Add the columns. • Set the type of renderer to use. • Create the ListStore. • Set the model attribute in the Treeview to our model. • Fill in the data.
The third step is to set up the type of renderer the column will use to display the data. This is simply a routine that is used to draw the data into the tree model. There are many different cell renderers that come with GTK, but most of the ones that you would normally use include GtkCellRenderText and GtkCellRendererToggle. So, let's create a function (shown above) that sets up our TreeView widget. We'll call it SetupupTreeview. First we'll define some variables for our columns, set the variable reference of the TreeView itself, add the columns, set up the ListStore, and set the model. Here's the code for the function. Put it after the SetWidgetReferences function. The variables cFName, cFType and cFPath define the column numbers. The variables sFName, sFType and sFPath will hold the column names in our displayed view. The seventh line sets the variable reference of the treeview widget as named in our glade file. Next we call a routine (next page, top right), which we'll create in just a moment, for each column we want. Then we define our GTKListStore with three text fields, and finally set the model attribute of our TreeView widget to our GTKListStore. Let's create the AddPlaylistColumn function next. Put it after the SetupTreeview function.
Each column is created with this function. We pass in the title of the column (what's displayed on the top line of each column) and a columnID. In this case, the variables we set up earlier (sFName and cFname) will be passed here. We then create a column in our TreeView widget giving the title, what kind of cell renderer it will be using, and, finally, the id of the column. We then set the column to be resizable, set the sort id, and finally append the column into the TreeView. Add these two functions to your code. I choose to put them right after the SetWidgetReferences function, but you can put it anywhere within the PlayListCreator class. Add the following line after the call to SetWidgetReferences() in the init function to call the function. self.SetupTreeview() Save and run your program, and you will see that we now have three columns with headers in our TreeView widget.
There are so many things left to do. We have to have a way to get the music filenames from the user and put them into the TreeView as rows of data. We have to create our Delete, ClearAll, movement functions, save routine, and file path routines, plus a few “pretty” things that will make our application look more professional. Let's start with the Add routine. After all, that's the first button on our toolbar. When the user clicks the Add button, we want to pop up a “standard” open-file dialog that allows for multiple selections. Once the user has made their selection, we then want to take this data and add it into the treeview, as I stated above. So the first logical thing to do is work on the File Dialog. Again, GTK provides us a way to call a “standard” file dialog in code. We could hard code this as just lines in the on_tbtnAdd_clicked event handler, but let's make a separate class to handle this. While we are at it, we can make this class handle not only a file OPEN dialog, but a folder SELECT dialog as well. As before with the MessageBox function, you can pull this into a snippet file that has all kinds of reusable routines for later use. We'll start by defining a new class called FileDialog which will have only one function called ShowDialog. That function will take two parameters, one called 'which' (a '0' or a '1'), that designates whether we are creating an open-file or select-folder dialog, and the other is the path that should be used for the default view of the dialog called CurrentPath. Create this class just before our main code at the bottom of the source file. class FileDialog: def ShowDialog(self,which,CurrentPath):
The first part of our code should be an IF statement if which == 0: # file chooser … else: # folder chooser … Before going any further, let's explore how the file/folder dialog is actually called and used. The syntax of the dialog is as follows gtk.FileChooserDialog(title,parent,action,buttons,backend) and returns a dialog object. Our first line (under if which == 0) will be the line shown below. As you can see, the title is “Select files to add…”, the parent is set to None. We are requesting a File Open type dialog (action), and we want a Cancel and an Open button, both using “stock” type icons. We are also setting the return codes of gtk.RESPONSE_CANCEL and gtk.RESPONSE_OK for when the user makes their selections. The call for our Folder Chooser under the Else clause is similar.
Basically, the only thing that changed between the two definitions are the title (shown above right) and the action type. So our code for the class should now be the code shown middle right. These set the default response to be the OK button, and then to turn on the multiple select feature so the user can select (you guessed it) multiple files to add. If we didn't set this, the dialog would only allow one file to be selected at a time, since set_select_multiple is set to False by default. Our next lines are setting the current path, and then displaying the dialog itself. Before we type in the code, let me explain why we want to deal with the current path. Every time you pop up a file dialog box, and you DON'T set a path, the default is to the folder where our application resides. So, let's say that the music files that the user would be looking for are in /media/music_files/, and are then broken down by genre, and further by artist, and further by album. Let's further assume that the user has installed our application in /home/user2/playlistmaker. Each time we pop up the dialog, the starting folder would be /home/user2/playlistmaker. Quickly, the user would become frustrated by this, wanting the last folder he was in to be the starting folder next time. Make sense? OK. So, bottom right are our next lines of code.
Here we check the responses sent back. If the user clicked the 'Open' button which sends back a gtk.RESPONSE_OK, we get the name or names of the files the user selected, set the current path to the folder we are in, destroy the dialog, and then return the data back to the calling routine. If, on the other hand, the user clicked on the 'Cancel' button, we simply destroy the dialog. I put the print statement in there just to show you that the button press worked. You can leave it or take it out. Notice that when we return from the Open button part of the routine, we are returning two sets of values. 'fileselection' is a list of the files selected by the user, as well as the CurrentPath. In order to get the routine to do something, add the following line under the on_tbtnAdd_click routine… fd = FileDialog() selectedfiles,self.CurrentPath = fd.ShowDialog(0,self.CurrentPath) Here we retrieve the two return values that are sent from our return call. For now, add the following code to see what the information returned will look like. for f in selectedfiles: print “User selected %s” % f print “Current path is %s” % self.CurrentPath
When you run the program, click on the 'Add' button. You'll see the file dialog. Now move to somewhere where you have some files and select them. You can hold down the [ctrl] key and click on multiple files to select them individually, or the [shift] key to select multiple contiguous files. Click on the 'Open' button, and look at the response in your terminal window. Please note that if you click on the 'Cancel' button right now, you'll get an error message. That's because the above code assumes that there are no files selected. Don't worry about that right now - we'll handle that in a little bit. I just wanted to let you see what comes back if the 'Open' button is pressed. One thing we should do is add a filter to our file-open dialog. Since we expect the user to normally select music files, we should (1) give the option to display only music files, and (2) give the option to show all files just-in-case. We do this by using the filefilter attributes of the dialog. Here's the code for that which should go in the which == 0 section right after the dialog set line. filter = gtk.FileFilter() filter.set_name(“Music Files”) filter.add_pattern(“*.mp3”) filter.add_pattern(“*.ogg”) filter.add_pattern(“*.wav”) dialog.add_filter(filter) filter = gtk.FileFilter() filter.set_name(“All files”) filter.add_pattern(“*”) dialog.add_filter(filter)
We are setting up two “groups”, one for music files (filter.set_name(“Music Files”)), and the other for all files. We use a pattern to define the types of files we want. I have defined three patterns, but you can add or delete any that you wish. I put the music filter first, since that's what we will assume the user is going to be mainly concerned with. So the steps are… • Define a filter variable. • Set the name. • Add a pattern. • Add the filter to the dialog. You can have as many or as few filters as you wish. Also notice that once you have added the filter to the dialog, you can re-use the variable for the filter. Back in the on_tbtnAdd_clicked routine, comment out the last lines we added and replace them with this one line. self.AddFilesToTreeview(selectedfiles) so our routine now looks like the code shown on the next page.
So, when we get the response back from file dialog, we will send the list containing the selected files to this routine. Once here, we set up a counter variable (how many files we are adding), then parse the list. Remember that each entry contains the fully qualified filename with path and extension. We'll want to split the filename into path, filename, and extension. First we get the very last 'period' from the filename and assume that is the beginning of the extension and assign its position in the string to extStart. Next we find the very last '/' in the filename to determine the beginning of the filename. Then we break up the string into extension, filename and file path. We then stuff these values into a list named 'data' and append this into our playlist ListStore. We increment the counter since we have done all the work. Finally we increment the variable RowCount which holds the total number of rows in our ListStore, and then we print a message to the status bar. Now you can run the application and see the data in the TreeView. As always, the full code can be found at http://pastebin.com/JtrhuE71. Next time, we'll finalize our application, filling in the missing routines, etc.
CODE PAGE 7
#!/usr/bin/env python import sys from mutagen.mp3 import MP3 try: import pygtk pygtk.require("2.0") except: pass try: import gtk import gtk.glade except: sys.exit(1)
puis la définition de la classe
class CreateurListeDeLecture: def __init__(self): self.gladefile = "CreateurListeDeLecture.glade" self.wTree = gtk.glade.XML(self.gladefile,"FenetrePrincipale")
et la routine principale
if __name__ == "__main__": createurLDL = CreateurListeDeLecture() gtk.main()
Ensuite, nous avons le dictionnaire qui devrait se trouver après la routine __init__.
def DicoEvenements(self): dict = {"on_MainWindow_destroy": gtk.main_quit, "on_boBtnQuitter_clicked": gtk.main_quit, "on_boBtnAjouter_clicked": self.on_boBtnAjouter_clicked, "on_boBtnSupprimer_clicked": self.on_boBtnSupprimer_clicked, "on_boBtnEffacer_clicked": self.on_boBtnEffacer_clicked, "on_boBtnHaut_clicked": self.on_boBtnHaut_clicked, "on_boBtnMonter_clicked": self.on_boBtnMonter_clicked, "on_boBtnDescendre_clicked": self.on_boBtnDescendre_clicked, "on_boBtnBas_clicked":self.on_boBtnBas_clicked, "on_boBtnAPropos_clicked": self.on_boBtnAPropos_clicked, "on_btnNomRepertoire_clicked": self.on_btnNomRepertoire_clicked, "on_btnSauvegarderListe_clicked": self.on_btnSauvegarderListe_clicked} self.wTree.signal_autoconnect(dict)
CODE PAGE 8
def on_boBtnAjouter_clicked(self,widget): pass def on_boBtnSupprimer_clicked(self,widget): pass def on_boBtnEffacer_clicked(self,widget): pass def on_boBtnHaut_clicked(self,widget): pass def on_boBtnMonter_clicked(self,widget): pass def on_boBtnDescendre_clicked(self,widget): pass def on_boBtnBas_clicked(self,widget): pass def on_boBtnAPropos_clicked(self,widget): pass def on_btnNomRepertoire_clicked(self,widget): pass def on_btnSauvegarderListe_clicked(self,widget): pass
CODE PAGE 9 (haut)
def MessageBox(self,niveau,texte): if niveau == "info": dlg = gtk.MessageDialog(None,0,gtk.MESSAGE_INFO,gtk.BUTTONS_OK,texte) elif niveau == "warning": dlg = gtk.MessageDialog(None,0,gtk.MESSAGE_WARNING,gtk.BUTTONS_OK,texte) elif niveau == "error": dlg = gtk.MessageDialog(None,0,gtk.MESSAGE_ERROR,gtk.BUTTONS_OK,texte) elif niveau == "question": dlg = gtk.MessageDialog(None,0,gtk.MESSAGE_QUESTION,gtk.BUTTONS_YES_NO,texte) if niveau == "question": resp = dlg.run() dlg.destroy() return resp else: resp = dlg.run() dlg.destroy()
CODE PAGE 9 (bas)
def on_boBtnAjouter_clicked(self,widget): self.MessageBox("info","Clic sur bouton Ajouter...") def on_boBtnSupprimer_clicked(self,widget): self.MessageBox("info","Clic sur bouton Supprimer...") def on_boBtnEffacer_clicked(self,widget): self.MessageBox("info","Clic sur bouton Effacer...") def on_boBtnHaut_clicked(self,widget): self.MessageBox("info","Clic sur bouton Haut...") def on_boBtnMonter_clicked(self,widget): self.MessageBox("info","Clic sur bouton Monter...") def on_boBtnDescendre_clicked(self,widget): self.MessageBox("info","Clic sur bouton Descendre...") def on_boBtnBas_clicked(self,widget): self.MessageBox("info","Clic sur bouton Bas...") def on_boBtnAPropos_clicked(self,widget): self.MessageBox("info","Clic sur bouton À propos...") def on_btnNomRepertoire_clicked(self,widget): self.MessageBox("info","Clic sur bouton NomRepertoire...") def on_btnSauvegarderListe_clicked(self,widget): self.MessageBox("info","Clic sur bouton SauvegarderListe...")
CODE PAGE 10 (haut)
def ReferencesWidgets(self): self.txtFilename = self.wTree.get_widget("txtFilename") self.txtPath = self.wTree.get_widget("txtPath") self.boBtnAjouter = self.wTree.get_widget("boBtnAjouter") self.boBtnSupprimer = self.wTree.get_widget("boBtnSupprimer") self.boBtnEffacer = self.wTree.get_widget("boBtnEffacer") self.boBtnQuitter = self.wTree.get_widget("boBtnQuitter") self.boBtnAPropos = self.wTree.get_widget("boBtnAPropos") self.boBtnHaut = self.wTree.get_widget("boBtnHaut") self.boBtnMonter = self.wTree.get_widget("boBtnMonter") self.boBtnDescendre = self.wTree.get_widget("boBtnDescendre") self.boBtnBas = self.wTree.get_widget("boBtnBas") self.btnNomRepertoire = self.wTree.get_widget("btnNomRepertoire") self.btnSauvegarderListe = self.wTree.get_widget("btnSauvegarderListe") self.sbar = self.wTree.get_widget("statusbar3") self.context_id = self.sbar.get_context_id("Statusbar")
puis ajoutez un appel à ceci juste après l'appel à self.DicoEvenements() dans la routine __init__.
self.ReferencesWidgets()
CODE PAGE 10 (bas)
def AfficherAPropos(self): apropos = gtk.AboutDialog() apropos.set_program_name("Createur de liste de lecture") apropos.set_version("1.0") apropos.set_copyright("(c) 2011 by Greg Walters") apropos.set_comments("Ecrit pour le Full Circle Magazine") apropos.set_website("http://thedesignatedgeek.com") apropos.run() apropos.destroy()
Maintenant, commentez (ou retirez simplement) l'appel à MessageBox dans la routine on_boBtnAPropos_clicked, et remplacez-le par un appel à la fonction AfficherAPropos. Cela devrait ressembler à cela.
def on_boBtnAPropos_clicked(self,widget): #self.MessageBox("info","Clic sur bouton APropos...") self.AfficherAPropos()
CODE PAGE 11
def SetupTreeview(self): self.cNomFic = 0 self.cTypeFic = 1 self.cCheminFic = 2 self.sNomFic = "NomFichier" self.sTypeFic = "Type" self.sCheminFic = "Dossier" self.treeview = self.wTree.get_widget("treeview1") self.AjouterColonne(self.sNomFic,self.cNomFic) self.AjouterColonne(self.sTypeFic,self.cTypeFic) self.AjouterColonne(self.sCheminFic,self.cCheminFic) self.playList = gtk.ListStore(str,str,str) self.treeview.set_model(self.playList) self.treeview.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_BOTH)
CODE PAGE 12
def AjouterColonne(self,titre,idColonne): colonne = gtk.TreeViewColumn(titre,gtk.CellRendererText(),text=idColonne) colonne.set_resizable(True) colonne.set_sort_column_id(idColonne) self.treeview.append_column(colonne)
CODE PAGE 13 (haut)
dialog = gtk.FileChooserDialog("Choisir le repertoire de sauvegarde...",None, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
CODE PAGE 13 (milieu)
class FileDialog: def ShowDialog(self,which,CheminCourant): if which == 0: # choix de fichiers #gtk.FileChooserDialog(titre,parent,action,boutons,backend) dialog = gtk.FileChooserDialog("Choisir les fichiers a ajouter...",None, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) else: # choix de repertoire dialog = gtk.FileChooserDialog("Choisir le repertoire de sauvegarde...",None, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
Les deux lignes suivantes seront (en dehors de l'instruction if/else)…
dialog.set_default_response(gtk.RESPONSE_OK) dialog.set_select_multiple(True)
CODE PAGE 13 (bas)
if CheminCourant != "": dialog.set_current_folder(CheminCourant)
reponse = dialog.run()
Ensuite, nous devons gérer la réponse venant du dialogue.
if reponse == gtk.RESPONSE_OK: fileselection = dialog.get_filenames() CheminCourant = dialog.get_current_folder() dialog.destroy() return (fileselection,CheminCourant) elif reponse == gtk.RESPONSE_CANCEL: print 'Annulation, aucun fichier choisi' dialog.destroy()
CODE PAGE 15
def on_tbtnAdd_clicked(self,widget): fd = FileDialog() selectedfiles,self.CurrentPath = fd.ShowDialog(0,self.CurrentPath) self.AddFilesToTreeview(selectedfiles)
We now have to create the function that we just put the call to. Put this function after the on_btnSavePlaylist_clicked routine.
def AddFilesToTreeview(self,FileList): counter = 0 for f in FileList: extStart = f.rfind(".") fnameStart = f.rfind("/") extension = f[extStart+1:] fname = f[fnameStart+1:extStart] fpath = f[:fnameStart] data = [fname,extension,fpath] self.playList.append(data) counter += 1 self.RowCount += counter self.sbar.push(self.context_id,"%s files added for a total of %d" % (counter,self.RowCount))