« Programmation Python/Et pour quelques widgets de plus... » : différence entre les versions

Contenu supprimé Contenu ajouté
Tavernier (discussion | contributions)
Tavernier (discussion | contributions)
découpage en paragraphes
Ligne 1 :
{{todo|mettre en forme}}
 
Les pages qui suivent contiennent des indications et et des exemples complémentaires qui pourront vous être utiles pour le développement de vos projets personnels. Il ne s'agit évidemment pas d'une documentation de référence complète sur Tkinter. Pour en savoir plus, vous devrez tôt ou tard consulter des ouvrages spécialisés, comme par exemple l'excellent Python and Tkinter programming de John E. Grayson, dont vous trouverez la référence complète à la page 8.
 
1.1Les== Les « boutons radio » ==
 
Les widgets « boutons radio » permettent de proposer à l'utilisateur un ensemble de choix mutuellement exclusifs. On les appelle ainsi par analogie avec les boutons de sélection que l'on trouvait jadis sur les postes de radio. Ces boutons étaient conçus de telle manière qu'un seul à la fois pouvait être enfoncé : tous les autres ressortaient automatiquement.
La caractéristique essentielle de ces widgets est qu'on les utilise toujours par groupes. Tous les boutons radio faisant partie d'un même groupe sont associés à une seule et même variable Tkinter, mais chacun d'entre eux se voit aussi attribuer une valeur particulière.
Ligne 39 ⟶ 43 :
33.if __name__ == '__main__':
34. RadioDemo().mainloop()
 
;Commentaires :
 
Ligne 3 : Cette fois encore, nous préférons construire notre petite application comme une classe dérivée de la classe Frame(), ce qui nous permettrait éventuellement de l'intégrer sans difficulté dans une application plus importante.
Ligne 8 : En général, on applique les méthodes de positionnement des widgets (pack(), grid(), ou place()) après instanciation de ceux-ci, ce qui permet de choisir librement leur disposition à l'intérieur des fenêtres maîtresses. Comme nous le montrons ici, il est cependant tout à fait possible de déjà prévoir ce positionnement dans le constructeur du widget.
Ligne 53 ⟶ 59 :
"President 18 italic"
Voir également les exemples de la page 223.
 
1.2Utilisation== Utilisation des cadres (frames) pour la composition d'une fenêtre ==
 
Vous avez déjà abondamment utilisé la classe de widgets Frame() (« cadre », en français), notamment pour créer de nouveaux widgets complexes par dérivation.
Le petit script ci-dessous vous montre l'utilité de cette même classe pour regrouper des ensembles de widgets et les disposer d'une manière déterminée dans une fenêtre. Il vous démontre également l'utilisation de certaines options décoratives (bordures, relief, etc.).
Ligne 101 ⟶ 109 :
Lignes 22-23 : Préparation du cadre f2 (cadre de droite). Sa couleur sera une variété de jaune, et nous l'entourerons d'une bordure décorative ayant l'aspect d'un sillon.
Lignes 25 à 28 : Le cadre f2 contiendra un canevas et un bouton. Notez encore une fois l'utilisation des options padx et pady pour ménager des espaces autour des widgets (Considérez par exemple le cas du bouton, pour lequel cette option n'a pas été utilisée : de ce fait, il entre en contact avec la bordure du cadre qui l'entoure). Comme nous l'avons fait pour les cadres, nous avons placé une bordure autour du canevas. Sachez que d'autres widgets acceptent également ce genre de décoration : boutons, champs d'entrée, etc.
 
1.3Comment== Comment déplacer des dessins à l'aide de la souris ==
 
Le widget canevas est l'un des points forts de la bibliothèque graphique Tkinter. Il intègre en effet un grand nombre de dispositifs très efficaces pour manipuler des dessins. Le script ci-après est destiné à vous montrer quelques techniques de base. Si vous voulez en savoir plus, notamment en ce qui concerne la manipulation de dessins composés de plusieurs parties, veuillez consulter l'un ou l'autre ouvrage de référence traitant de Tkinter.
Au démarrage de notre petite application, une série de dessins sont tracés au hasard dans un canevas (il s'agit en l'occurrence de simples ellipses colorées). Vous pouvez déplacer n'importe lequel de ces dessins en les « saisissant » à l'aide de votre souris.
Ligne 171 ⟶ 181 :
Draw().mainloop()
 
;Commentaires :
 
Le script contient essentiellement la définition d'une classe graphique dérivée de Frame().
Comme c'est souvent le cas pour les programmes exploitant les classes d'objets, le corps principal du script se résume à une seule instruction composée, dans laquelle on réalise deux opérations consécutives : instanciation d'un objet de la classe définie précédemment, et activation de sa méthode mainloop() ( laquelle démarre l'observateur d'événements).
Ligne 185 ⟶ 196 :
La méthode mouseUp() termine le travail. Lorsque le dessin transporté est arrivé à destination, il reste à annuler la sélection et rendre au contour son épaisseur initiale. Ceci ne peut être envisagé que s'il existe effectivement une sélection, bien entendu.
 
1.4Python== Python Mega Widgets ==
 
Les modules Pmw constituent une extension intéressante de Tkinter. Entièrement écrits en Python, ils contiennent toute une bibliothèque de widgets composites, construits à partir des classes de base de Tkinter. Dotés de fonctionnalités très étendues, ces widgets peuvent se révéler fort précieux pour le développement rapide d'applications complexes. Si vous souhaitez les utiliser, sachez cependant que les modules Pmw ne font pas partie de l'installation standard de Python : vous devrez donc toujours vérifier leur présence sur les machines cibles de vos programmes.
Il existe un grand nombre de ces méga-widgets. Nous n'en présenterons ici que quelques-uns parmi les plus utiles. Vous pouvez rapidement vous faire une idée plus complète de leurs multiples possibilités, en essayant les scripts de démonstration qui les accompagnent (lancez par exemple le script all.py , situé dans le répertoire .../Pmw/demos).
 
1.4.1=== « Combo Box » ===
 
Les méga-widgets s'utilisent aisément. La petite application ci-après vous montre comment mettre en œuvre un widget de type ComboBox (boîte de liste combinée à un champ d'entrée). Nous l'avons configuré de la manière la plus habituelle (avec une boîte de liste déroulante).
Lorsque l'utilisateur de notre petit programme choisit une couleur dans la liste déroulante (il peut aussi entrer un nom de couleur directement dans le champ d'entrée), cette couleur devient automatiquement la couleur de fond pour la fenêtre maîtresse.
Ligne 220 ⟶ 234 :
26.
27.fen.mainloop()
 
;Commentaires :
 
Lignes 1 & 2 : On commence par importer les composants habituels de Tkinter, ainsi que le module Pmw.
Ligne 14 : Pour créer la fenêtre maîtresse, il faut utiliser de préférence la méthode Pmw.initialise(), plutôt que d'instancier directement un objet de la classe Tk(). Cette méthode veille en effet à mettre en place tout ce qui est nécessaire afin que les widgets esclaves de cette fenêtre puissent être détruits correctement lorsque la fenêtre elle-même sera détruite. Cette méthode installe également un meilleur gestionnaire des messages d'erreurs.
Ligne 226 ⟶ 242 :
Ligne 14 : L'option selectioncommand transmet un argument à la fonction invoquée : l'item sélectionné dans la boîte de liste. Vous pourrez également retrouver cette sélection à l'aide de la méthode get(), comme nous le faisons à la ligne 8 pour actualiser le libellé.
 
1.4.2Remarque=== Remarque concernant l'entrée de caractères accentués ===
 
Nous vous avons déjà signalé précédemment que Python est tout à fait capable de prendre en charge les alphabets du monde entier (grec, cyrillique, arabe, japonais, etc. - voir notamment page 41). Il en va de même pour Tkinter. En tant que francophone, vous souhaiterez certainement que les utilisateurs de vos scripts puissent entrer des caractères accentués dans les widgets Entry, Text et leurs dérivés (ComboBox, ScrolledText).
Veuillez donc prendre bonne note que lorsque vous entrez dans l'un de ces widgets une chaîne contenant un ou plusieurs caractères non-ASCII (tel qu'une lettre accentuée, par exemple), Tkinter encode cette chaîne suivant la norme UTF-8. Si votre ordinateur utilise plutôt le codage Latin-1 par défaut (ce qui est très souvent le cas), vous devrez convertir la chaîne avant de pouvoir l'afficher.
Ligne 249 ⟶ 266 :
Essayez ce petit script en entrant des chaînes avec caractères accentués dans le champ d'entrée.
Essayez encore, mais en remplaçant l'instruction « print ch2 » par « print ch1 ». Concluez.
 
1.4.3=== « Scrolled Text » ===
 
Ce méga-widget étend les possibilités du widget Text sandard, en lui associant un cadre, un libellé (titre) et des barres de défilement.
Comme le démontrera le petit script ci-dessous, il sert fondamen-talement à afficher des textes, mais ceux-ci peuvent être mis en forme et intégrer des images.
Ligne 312 ⟶ 331 :
51.fen.mainloop()
 
;Commentaires :
 
 
Commentaires :
Lignes 4-7 : Cette fonction est un gestionnaire d'événement, qui est appelé lorsque l'utilisateur effectue un clic de souris sur le nom de l'auteur (cfr. lignes 27 & 29). A la ligne 6, on utilise la méthode tag_nextrange() du widget pour trouver les index de la portion de texte associée à la balise « cible ». La recherche de ces index est limitée au domaine défini par les 2e & 3e arguments (dans notre exemple, on recherche du début à la fin du texte entier). La méthode tag_nextrange() renvoie une liste de deux index (ceux des premier et dernier caractères de la portion de texte associée à la balise « cible »). A la ligne 7, nous nous servons d'un seul de ces index (le premier) pour activer la méthode see(). Celle-ci provoque un défilement automatique du texte (scrolling), de telle manière que le caractère correspondant à l'index transmis devienne visible dans le widget (avec en général un certain nombre des caractères qui suivent).
Lignes 9 à 23 : Construction classique d'une fenêtre destinée à afficher un seul widget. Dans le code d'instanciation du widget, nous avons inclus un certain nombre d'options destinées à vous montrer une petite partie des nombreuses possibilités de configuration.
Ligne 327 ⟶ 345 :
Lignes 43-44 : Ces instructions insèrent des fragments de texte (respectivement au début et à la fin du texte préexistant), en associant une balise à chacun d'eux.
Ligne 49 : L'association des balises au texte est dynamique. A tout moment, vous pouvez activer une nouvelle association (comme nous le faisons ici en rattachant la balise « lien » à une portion de texte préexistante). Note : pour « détacher » une balise, utilisez la méthode tag_delete().
 
1.4.4=== « Scrolled Canvas » ===
 
Le script ci-après vous montre comment vous pouvez exploiter le méga-widget ScrolledCanvas, lequel étend les possibilités du widget Canvas standard en lui associant des barres de défilement, un libellé et un cadre. Notre exemple constitue en fait un petit jeu d'adresse, dans lequel l'utilisateur doit réussir à cliquer sur un bouton qui s'esquive sans cesse. (Note : si vous éprouvez vraiment des difficultés pour l'attraper, commencez d'abord par dilater la fenêtre).
Le widget Canvas est très versatile : il vous permet de combiner à volonté des dessins, des images bitmap, des fragments de texte, et même d'autres widgets, dans un espace parfaitement extensible. Si vous souhaitez développer l'un ou l'autre jeu graphique, c'est évidemment le widget qu'il vous faut apprendre à maîtriser en priorité.
Ligne 397 ⟶ 417 :
60. FenPrinc().mainloop()
 
;Commentaires :
 
Ligne 6 : Tous ces noms de couleurs sont acceptés par Tkinter. Vous pourriez bien évidemment les remplacer par des descriptions hexadécimales, comme nous l'avons expliqué page 203.
Lignes 12 à 17 : Ces options sont très similaires à celles que nous avons décrites plus haut pour le widget ScrolledText. Le présent méga-widget intègre un composant Frame, un composant Label, un composant Canvas et deux composants Scrollbar. On accède aux options de configuration de ces composants à l'aide d'une syntaxe qui relie le nom du composant et celui de l'option par l'intermédiaire d'un caractère « souligné ».
Ligne 409 ⟶ 430 :
Lignes 48 à 55 : Ces deux gestionnaires d'événement sont associés au bouton en alternance. Ils servent évidemment à démarrer et à arrêter l'animation.
 
1.4.5Barres=== Barres d'outils avec bulles d'aide - expressions lambda ===
 
De nombreux programmes comportent une ou plusieurs « barres d'outils » (toolbar) constituées de petits boutons sur lesquels sont représentés des pictogrammes (icônes). Cette façon de faire permet de proposer à l'utilisateur un grand nombre de commandes spécialisées, sans que celles-ci n'occupent une place excessive à l'écran (un petit dessin vaut mieux qu'un long discours, dit-on).
La signification de ces pictogrammes n'est cependant pas toujours évidente, surtout pour les utilisateurs néophytes. Il est donc vivement conseillé de compléter les barres d'outils à l'aide d'un système de bulles d'aide (tool tips), qui sont des petits messages explicatifs apparaissant automatiquement lorsque la souris survole les boutons concernés.
Ligne 463 ⟶ 485 :
46.Application().mainloop()
 
=== Métaprogrammation. Expressions lambda : ===
 
Métaprogrammation. Expressions lambda :
Vous savez qu'en règle générale, on associe à chaque bouton une commande, laquelle est une méthode ou une fonction particulière qui se charge d'effectuer le travail lorsque le bouton est activé. Or dans l'application présente, tous les boutons doivent faire à peu près la même chose (recopier un dessin dans le canevas), la seule différence entre eux étant le dessin concerné.
Pour simplifier notre code, nous voudrions donc pouvoir associer l'option command de tous nos boutons avec une seule et même méthode (ce sera la méthode action() ), mais en lui transmettant à chaque fois la référence du bouton particulier utilisé, de manière à ce que l'action accomplie puisse être différente pour chacun d'eux.
Ligne 502 ⟶ 524 :
 
Nous ne détaillerons pas davantage ici la question des expressions lambda, car elle déborde du cadre que nous nous sommes fixés pour cet ouvrage d'initiation. Si vous souhaitez en savoir plus, veuillez donc consulter l'un ou l'autre des ouvrages de référence cités dans la bibliographie.
 
1.5Fenêtres== Fenêtres avec menus ==
 
Nous allons décrire à présent la construction d'une fenêtre d'application dotée de différents types de menus « déroulants », chacun de ces menus pouvant être « détaché » de l'application principale pour devenir lui-même une petite fenêtre indépendante, comme dans l'illustration ci-dessous.
Cet exercice un peu plus long nous servira également de révision, et nous le réaliserons par étapes, en appliquant une stratégie de programmation que l'on appelle développement incrémental.
Ligne 510 ⟶ 534 :
Il reste également impératif de commenter convenablement le code produit, au fur et à mesure de son élaboration. S'efforcer de rédiger de bons commentaires est en effet nécessaire, non seulement pour que votre code soit facile à lire (et donc à maintenir plus tard, par d'autres ou par vous-même), mais aussi pour que vous soyez forcés d'exprimer ce que vous souhaitez vraiment que la machine fasse (Cfr. Erreurs sémantiques, page 16)
 
;Cahier des charges de l'exercice :
 
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.
 
1.5.1Première=== 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 :
 
Ligne 556 ⟶ 583 :
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. A 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.yh
 
;Analyse du script :
 
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édemment2, le corps principal du programme (lignes 35 à 37) comporte l'instruction classique : if __name__ == '__main__':
Les deux instructions qui suivent consistent seulement à instancier un objet app et à faire fonctionner sa méthode mainloop(). Comme vous le savez certainement, nous aurions pu également condenser ces deux instructions en une seule.
Ligne 578 ⟶ 607 :
Toutes ces références sont hiérarchisées à l'aide de la qualification des noms par points.
 
1.5.2Ajout=== Ajout de la rubrique « Musiciens » ===
 
Continuez le développement de ce petit programme, en ajoutant les lignes suivantes dans le constructeur de la classe MenuBar() (après la ligne 18) :
##### Menu <Musiciens> #####
Ligne 610 ⟶ 640 :
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.
 
;Analyse du script
 
Analyse du script
Les seules nouveautés introduites dans ces lignes concernent l'utilisation de polices de caractères bien déterminées (option font), ainsi que de couleurs pour l'avant-plan (option foreground) et le fond (option background) des textes affichés.
Veuillez noter encore une fois l'utilisation de l'option underline 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 command 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 boss.
La méthode create_text() 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.)
 
1.5.3Ajout=== 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 MenuBar() :
##### Menu <Peintres> #####
Ligne 657 ⟶ 688 :
font =('President', 20), fill ='purple')
 
;Analyse du script :
 
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 me1). L'intégration est assurée ensuite à l'aide de la méthode add_cascade().
L'un des items est désactivé (option state = DISABLED). L'exemple suivant vous montrera comment vous pouvez activer ou désactiver à volonté des items, par programme.
 
1.5.4Ajout=== 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.
Ligne 704 ⟶ 737 :
self.musi.configure(state =[DISABLED, NORMAL][m])
 
;Analyse du script
 
a);Menu avec « cases à cocher »
 
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 « chekbutton » :
Ligne 723 ⟶ 758 :
 
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.
 
b);Menu avec choix exclusifs
 
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. A 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 :
Ligne 741 ⟶ 778 :
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é.
 
c);Contrôle du flux d'exécution à l'aide d'une liste
 
Veuillez à présent considérer la définition de la méthode reliefBarre() :
A la première ligne, la méthode get() 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 : ».
Ligne 771 ⟶ 810 :
Notez cependant que ce que l'on gagne en compacité se paie d'une certaine perte de lisibilité.
 
d);Pré-sélection d'une rubrique
 
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 Application()