Comme nous l'avons déjà expliqué précédemment, cette méthode consiste à commencer l'écriture d'un programme par une ébauche, qui ne comporte que quelques lignes seulement mais qui est déjà fonctionnelle. On teste alors cette ébauche soigneusement afin d'en éliminer les "bugs" éventuels. Lorsque l'ébauche fonctionne correctement, on y ajoute une fonctionnalité supplémentaire. On teste ce complément jusqu'à ce qu'il donne entière satisfaction, puis on en ajoute un autre, et ainsi de suite...
"Cela ne signifie pas que vous pouvez commencer directement à programmer" sans avoir au préalable effectué une analyse sérieuse du projet, dont au moins les grandes lignes devront être convenablement décrites dans un "cahier des charges" clairement rédigé.
Notre application comportera simplement une barre de menus et un canevas. Les différentes rubriques et options des menus ne serviront qu'à faire apparaître des fragments de texte dans le canevas ou à modifier des détails de décoration, mais ce seront avant tout des exemples variés, destinés à donner un aperçu des nombreuses possibilités offertes par ce type de "widget", accessoire indispensable de toute application moderne d'une certaine importance.
Nous souhaitons également que le code produit dans cet exercice soit bien structuré. Pour ce faire, nous ferons usage de deux classes : une classe pour l'application principale, et une autre pour la barre de menus. Nous voulons procéder ainsi afin de bien mettre en évidence la construction d'une application type incorporant plusieurs classes d'objets interactifs.
Première ébauche du programme :.
Lorsque l'on construit l'ébauche d'un programme, il faut tâcher d'y faire apparaître le plus tôt possible la structure d'ensemble, avec les relations entre les principaux blocs qui constitueront l'application définitive. C'est ce que nous nous sommes efforcés de faire dans l'exemple ci-dessous :
from Tkinter import * #1
 #2
class MenuBar(Frame): #3
 """Barre de menus déroulants""" #4
 def __init__(self, boss =None): #5
 Frame.__init__(self, borderwidth =2) #6
 #7
 ##### Menu <Fichier> ##### #8
 fileMenu = Menubutton(self, text ='Fichier') #9
 fileMenu.pack(side =LEFT) #10
 # Partie "déroulante" : #11
 me1 = Menu(fileMenu) #12
 me1.add_command(label ='Effacer', underline =0, #13
 command = boss.effacer) #14
 me1.add_command(label ='Terminer', underline =0, #15
 command = boss.quit) #16
 # Intégration du menu : #17
 fileMenu.configure(menu = me1) #18
 #19
class Application(Frame): #20
 """Application principale""" #21
 def __init__(self, boss =None): #22
 Frame.__init__(self) #23
 self.master.title('Fenêtre avec menus') #24
 mBar = MenuBar(self) #25
 mBar.pack() #26
 self.can = Canvas(self, bg='light grey', height=190, #27
 width=250, borderwidth =2) #28
 self.can.pack() #29
 self.pack() #30
 #31
 def effacer(self): #32
 self.can.delete(ALL) #33
 #34
if __name__ == '__main__': #35
 app = Application() #36
 app.mainloop() #37
Veuillez donc encoder ces lignes et en tester l'exécution. Vous devriez obtenir une fenêtre avec un canevas gris clair surmonté d'une barre de menus. À ce stade, la barre de menus ne comporte encore que la seule rubrique « Fichier ».
Cliquez sur la rubrique « fichier » pour faire apparaître le menu correspondant : l'option « Effacer » n'est pas encore fonctionnelle (elle servira à effacer le contenu du canevas), mais l'option « Terminer » devrait déjà vous permettre de fermer proprement l'application.
Comme tous les menus gérés par "Tkinter", le menu que vous avez créé peut être converti en menu « flottant » : il suffit de cliquer sur la ligne pointillée apparaissant en-tête de menu. Vous obtenez ainsi une petite fenêtre satellite, que vous pouvez alors positionner où bon vous semble sur le bureau.
La structure de ce petit programme devrait désormais vous apparaître familière : afin que les classes définies dans ce script puissent éventuellement être (ré)utilisées dans d'autres projets par importation, comme nous l'avons déjà expliqué précédemment, le corps principal du programme (lignes 35 à 37) comporte l'instruction classique : codice_124 :
Les deux instructions qui suivent consistent seulement à instancier un objet codice_125 et à faire fonctionner sa méthode codice_38. Comme vous le savez certainement, nous aurions pu également condenser ces deux instructions en une seule.
L'essentiel du programme se trouve cependant dans les définitions de classes qui précèdent :
La classe codice_127 contient la description de la barre de menus. Dans l'état présent du script, elle se résume à une ébauche de constructeur.
La classe codice_144 contient la description de la fenêtre principale du programme ainsi que les méthodes gestionnaires d'événements qui lui sont associées.
Ajout de la rubrique « Musiciens ».
Continuez le développement de ce petit programme, en ajoutant les lignes suivantes dans le constructeur de la classe codice_127 (après la ligne 18) :
... ainsi que les définitions de méthodes suivantes à la classe Application() (après la ligne 33) :
Lorsque vous y aurez ajouté toutes ces lignes, sauvegardez le script et exécutez-le.
Votre barre de menus comporte à présent une rubrique supplémentaire : la rubrique « Musiciens ».
Le menu correspondant propose deux items qui sont affichés avec des couleurs et des polices personnalisées. Vous pourrez vous inspirer de ces techniques décoratives pour vos projets personnels. À utiliser avec modération !
Les commandes que nous avons associées à ces items sont évidemment simplifiées afin de ne pas alourdir l'exercice : elles provoquent l'affichage de petits textes sur le canevas.
Les seules nouveautés introduites dans ces lignes concernent l'utilisation de polices de caractères bien déterminées (option codice_150), ainsi que de couleurs pour l'avant-plan (option codice_151) et le fond (option codice_152) des textes affichés.
Veuillez noter encore une fois l'utilisation de l'option codice_153 pour désigner les caractères correspondant à des raccourcis claviers (en n'oubliant pas que la numérotation des caractères d'une chaîne commence à partir de zéro), et surtout que l'option codice_121 de ces "widgets" accède aux méthodes de l'autre classe, par l'intermédiaire de la référence mémorisée dans l'attribut codice_134.
La méthode codice_156 du canevas doit être utilisée avec deux arguments numériques, qui sont les coordonnées X et Y d'un point dans le canevas. Le texte transmis sera positionné par rapport à ce point, en fonction de la valeur choisie pour l'option anchor : Celle-ci détermine comment le fragment de texte doit être « ancré » au point choisi dans le canevas, par son centre, par son coin supérieur gauche, etc., en fonction d'une syntaxe qui utilise l'analogie des points cardinaux géographiques (NW = angle supérieur gauche, SE = angle inférieur droit, CENTER = centre, etc.)
Ajout de la rubrique « Peintres » :.
Cette nouvelle rubrique est construite d'une manière assez semblable à la précédente, mais nous lui avons ajouté une fonctionnalité supplémentaire : des menus « en cascade ». Veuillez donc ajouter les lignes suivantes dans le constructeur de la classe codice_127 :
... et les définitions suivantes dans la classe Application() :
Vous pouvez réaliser aisément des menus en cascade, en enchaînant des sous-menus les uns aux autres jusqu'à un niveau quelconque (il vous est cependant déconseillé d'aller au-delà de 5 niveaux successifs : vos utilisateurs s'y perdraient).
Un sous-menu est défini comme un menu « esclave » du menu de niveau précédent (dans notre exemple, me2 est défini comme un menu « esclave » de codice_158). L'intégration est assurée ensuite à l'aide de la méthode codice_159.
L'un des items est désactivé (option codice_160). L'exemple suivant vous montrera comment vous pouvez activer ou désactiver à volonté des items, par programme.
Ajout de la rubrique « Options » :.
La définition de cette rubrique est un peu plus compliquée, parce que nous allons y intégrer l'utilisation de variables internes à "Tkinter".
Les fonctionnalités de ce menu sont cependant beaucoup plus élaborées : les options ajoutées permettent en effet d'activer ou de désactiver à volonté les rubriques « Musiciens » et « Peintres », et vous pouvez également modifier à volonté l'aspect de la barre de menus elle-même.
Veuillez donc ajouter les lignes suivantes dans le constructeur de la classe codice_127 :
... ainsi que les définitions de méthodes suivantes (toujours dans la classe codice_127) :
Notre nouveau menu déroulant comporte deux parties. Afin de bien les mettre en évidence, nous avons inséré une ligne de séparation ainsi que deux « faux items » (« Activer : » et « Relief : ») qui servent simplement de titres. Nous faisons apparaître ceux-ci en couleur pour que l'utilisateur ne les confonde pas avec de véritables commandes.
Les items de la première partie sont dotées de « cases à cocher ». Lorsque l'utilisateur effectue un clic de souris sur l'un ou l'autre de ces items, les options correspondantes sont activées ou désactivées, et ces états « actif / inactif » sont affichés sous la forme d'une coche. Les instructions qui servent à mettre en place ce type de rubrique sont assez explicites. Elles présentent en effet ces items comme des "widgets" de type codice_163 :
Il est important de comprendre ici que ce type de "widget" comporte nécessairement une variable interne, destinée à mémoriser l'état « actif / inactif » du "widget". Cette variable ne peut pas être une variable Python ordinaire, parce que les classes de la bibliothèque "Tkinter" sont écrites dans un autre langage. Et par conséquent, on ne pourra accéder à une telle variable interne qu'à travers une interface. Cette interface, appelée « "variable Tkinter" », est en fait un objet, que l'on crée à partir d'une classe particulière, qui fait partie du module "Tkinter" au même titre que les classes de "widgets". L'utilisation de ces « objets-variables » est relativement simple :
Tout ce qui précède peut vous paraître un peu compliqué. Considérez simplement qu'il s'agit de votre première rencontre avec les problèmes d"'interfaçage" entre deux langages de programmation différents, utilisés ensemble dans un projet composite.
La deuxième partie du menu « Options » permet à l'utilisateur de choisir l'aspect que prendra la barre de menus, parmi six possibilités. Il va de soi que l'on ne peut activer qu'une seule de ces possibilités à la fois. Pour mettre en place ce genre de fonctionnalité, on fait classiquement appel appel à des "widgets" de type « boutons radio ». La caractéristique essentielle de ces "widgets" est que plusieurs d'entre eux doivent être associés à une seule et même "variable Tkinter". À chaque bouton radio correspond alors une valeur particulière, et c'est cette valeur qui est affectée à la variable lorsque l'utilisateur sélectionne le bouton.
Ainsi, l'instruction :
configure un item du menu «Options» de telle manière qu'il se comporte comme un bouton radio.
Lorsque l'utilisateur sélectionne cet item, la valeur 3 est affectée à la "variable Tkinter" codice_164 (celle-ci étant désignée à l'aide de l'option codice_165 du "widget"), et un appel est lancé en direction de la méthode codice_166. Celle-ci récupère alors la valeur mémorisée dans la "variable Tkinter" pour effectuer son travail.
Dans le contexte particulier de ce menu, nous souhaitons proposer 6 possibilités différentes à l'utilisateur. Il nous faut donc six « boutons radio », pour lesquels nous pourrions encoder six instructions similaires à celle que nous avons reproduite ci-dessus, chacune d'elles ne différant des cinq autres que par ses options codice_167 et codice_168. Dans une situation de ce genre, la bonne pratique de programmation consiste à placer les valeurs de ces options dans une liste, et à parcourir ensuite cette liste à l'aide d'une boucle codice_21, afin d'instancier les "widgets" avec une instruction commune :
La liste utilisée est une liste de six tuples (valeur, libellé). À chacune des 6 itérations de la boucle, un nouvel item codice_170 est instancié, dont les options codice_168 et codice_167 sont extraites de la liste par l'intermédiaire des variables codice_173 et codice_174.
Dans vos projets personnels, il vous arrivera fréquemment de constater que vous pouvez ainsi remplacer des suites d'instructions similaires, par une structure de programmation plus compacte (en général, la combinaison d'une liste et d'une boucle, comme dans l'exemple ci-dessus).
Vous découvrirez petit à petit encore d'autres techniques pour alléger votre code : nous en fournissons encore un exemple dans le paragraphe suivant. Tâchez cependant de garder à l'esprit cette règle essentielle, qu'un bon programme doit avant tout rester "lisible" et "commenté".
Veuillez à présent considérer la définition de la méthode codice_166 :
À la première ligne, la méthode codice_64 nous permet de récupérer l'état d'une "variable Tkinter" qui contient le numéro du choix opéré par l'utilisateur dans le sous-menu « Relief : ».
À la seconde ligne, nous utilisons le contenu de la variable codice_177 pour extraire d'une liste de six éléments celui qui nous intéresse. Par exemple, si codice_177 contient la valeur codice_179, c'est l'option codice_180 qui sera utilisée pour reconfigurer le "widget".
La variable codice_177 est donc utilisée ici comme un index, servant à désigner un élément de la liste. En lieu et place de cette construction compacte, nous aurions pu programmer une série de tests conditionnels, comme par exemple :
D'un point de vue strictement fonctionnel, le résultat serait exactement le même. Vous admettrez cependant que la construction que nous avons choisie est d'autant plus efficiente, que le nombre de possibilités de choix est élevé. Imaginez par exemple que l'un de vos programmes personnels doive effectuer une sélection dans un très grand nombre d'éléments : avec une construction du type ci-dessus, vous seriez peut-être amené à encoder plusieurs pages de « codice_182 » !
Nous utilisons encore la même technique dans la méthode codice_183. Ainsi l'instruction :
utilise le contenu de la variable codice_184 comme index pour désigner lequel des deux états codice_185, codice_186 doit être sélectionné pour reconfigurer le menu « Peintres ».
Lorsqu'elle est appelée, la méthode codice_183 reconfigure donc les deux rubriques « Peintres » et « Musiciens » de la barre de menus, pour les faire apparaître « normales » ou « désactivées » en fonction de l'état des variables codice_188 et codice_184, lesquelles sont elles-mêmes le reflet de variables "Tkinter".
Ces variables intermédiaires codice_188 et codice_184 ne servent en fait qu'à clarifier le script. Il serait en effet parfaitement possible de les éliminer, et de rendre le script encore plus compact, en utilisant la composition d'instructions. On pourrait par exemple remplacer les deux instructions :
par une seule, telle que :
Notez cependant que ce que l'on gagne en compacité se paie d'une certaine perte de lisibilité.
Pour terminer cet exercice, voyons encore comment vous pouvez déterminer à l'avance certaines sélections, ou bien les modifier par programme.
Veuillez donc ajouter l'instruction suivante dans le constructeur de la classe codice_144 (juste avant l'instruction codice_193, par exemple) :
Lorsque vous exécutez le script ainsi modifié, vous constatez qu'au départ la rubrique « Musiciens » de la barre de menus est active, alors que la rubrique « Peintres » ne l'est pas. Programmées comme elles le sont, ces deux rubriques devraient être actives toutes deux par défaut. Et c'est effectivement ce qui se passe si nous supprimons l'instruction :
Nous vous avons suggéré d'ajouter cette instruction au script, pour vous montrer comment vous pouvez effectuer "par programme" la même opération que celle que l'on obtient normalement avec un clic de souris.
L'instruction ci-dessus "invoque" le "widget" codice_194 en actionnant la commande associée au "deuxième" item de ce "widget". En consultant le listing, vous pouvez vérifier que ce deuxième item est bien l'objet de type checkbutton qui active/désactive le menu « Peintres » (Rappelons encore une fois que l'on numérote toujours à partir de zéro).
Au démarrage du programme, tout se passe donc comme si l'utilisateur effectuait tout de suite un premier clic sur la rubrique « Peintres » du menu « Options », ce qui a pour effet de désactiver le menu correspondant.Mais si vous voulez le réactiver , vous devrez éteinde votre ordinateur et le tour sera joué !