Différences entre les versions de « Programmation Python/Utilisation de fenêtres et de graphismes »

pre -> source="Python"
(pre -> source="Python")
Pour la suite des explications, nous supposerons bien évidemment que le module ''Tkinter'' a déjà été installé sur votre système. Pour pouvoir en utiliser les fonctionnalités dans un script Python, il faut que l'une des premières lignes de ce script contienne l'instruction d'importation :
 
<source lang="Python">
<pre>
from Tkinter import *
</presource>
 
[[Image:Apprendre à programmer avec Python 15.png|right]]
Comme toujours sous Python, il n'est même pas nécessaire d'écrire un script. Vous pouvez faire un grand nombre d'expériences directement à la ligne de commande, en ayant simplement lancé Python en mode interactif. Dans l'exemple qui suit, nous allons créer une fenêtre très simple, et y ajouter deux ''widgets''<ref>''widget'' est le résultat de la contraction de l'expression ''window gadget''. Dans certains environnements de programmation, on appellera cela plutôt un « contrôle » ou un « composant graphique ». Ce terme désigne en fait toute entité susceptible d'être placée dans une fenêtre d'application, comme par exemple un bouton, une case à cocher, une image, etc., et parfois aussi la fenêtre elle-même.</ref> typiques : un bout de texte (ou ''label'') et un bouton (ou ''button'').
 
<source lang="Python">
<pre>
>>> from Tkinter import *
>>> fen1 = Tk()
>>> bou1.pack()
>>> fen1.mainloop()
</presource>
 
Note : Suivant la version de Python utilisée, vous verrez déjà apparaître la fenêtre d'application immédiatement après avoir entré la deuxième commande de cet exemple, ou bien seulement après la septième<ref>Si vous effectuez cet exercice sous ''Windows'', nous vous conseillons d'utiliser de préférence une version standard de Python dans une fenêtre DOS ou dans IDLE plutôt que ''PythonWin''. Vous pourrez mieux observer ce qui se passe après l'entrée de chaque commande.</ref>.
<li>Réfléchissez !</li>
<li>
<source lang="Python">
<pre>
from Tkinter import *
 
i = i +1
base.mainloop()
</presource>
Variante :
<source lang="Python">
<pre>
from Tkinter import *
 
Button(base, text='5', command = a5).pack(side =LEFT)
base.mainloop()
</presource>
[[Image:Apprendre à programmer avec Python 72.png|center|fenetre avec les 5 anneaux olympiques]]
</li>
|}
 
<source lang="Python">
<pre>
from Tkinter import *
 
b2.pack(side =RIGHT, padx =3, pady =3)
fen.mainloop()
</presource>
 
Commençons par analyser le programme principal, à la fin du script :
<li>Voir ci dessous</li>
<li>
<source lang="Python">
<pre>
# Dessin d'un damier, avec placement de pions au hasard
b2.pack(side =RIGHT, padx =3, pady =3)
fen.mainloop()#
</presource>
</li>
</ol>
Les langages interprétés disposent donc toujours de fonctions permettant d'évaluer une chaîne de caractères comme une suite d'instructions du langage lui-même. Il devient alors possible de construire en peu de lignes des structures de programmes très dynamiques. Dans l'exemple ci-dessous, nous utilisons la fonction intégrée <code>eval()</code> pour analyser l'expression mathématique entrée par l'utilisateur dans le champ prévu à cet effet, et nous n'avons plus ensuite qu'à afficher le résultat.
 
<source lang="Python">
<pre>
# Exercice utilisant la bibliothèque graphique Tkinter et le module math
 
 
fenetre.mainloop()
</presource>
 
Au début du script, nous commençons par importer les modules ''Tkinter'' et ''math'', ce dernier étant nécessaire afin que la dite calculatrice puisse disposer de toutes les fonctions mathématiques et scientifiques usuelles : sinus, cosinus, racine carrée, etc.
<code>eval()</code> fait appel à l'interpréteur pour évaluer une expression Python qui lui est transmise dans une chaîne de caractères. Le résultat de l'évaluation est fourni en retour. Exemple :
 
<source lang="Python">
<pre>
chaine = "(25 + 8)/3" # chaîne contenant une expression mathématique
res = eval(chaine) # évaluation de l'expression contenue dans la chaîne
print res +5 # => le contenu de la variable res est numérique
</presource>
 
<code>str()</code> transforme une expression numérique en chaîne de caractères. Nous devons faire appel à cette fonction parce que la précédente renvoie une valeur numérique, que nous convertissons à nouveau en chaîne de caractères pour pouvoir l'incorporer au message « Résultat = ».
Attention, à présent : afin que ce dernier widget puisse vraiment faire son travail, c'est-à-dire transmettre au programme l'expression que l'utilisateur y aura encodée, ''nous lui associons un événement'' à l'aide de la méthode <code>bind()</code><ref>En anglais, le mot ''bind'' signifie « lier »</ref> :
 
<source lang="Python">
<pre>
entree.bind("<Return>",evaluer)
</presource>
 
Cette instruction signifie : « Lier l'événement “pression sur la touche Return” à l'objet <entree>, le gestionnaire de cet événement étant la fonction <evaluer> ».
Profitons de l'occasion pour observer encore une fois la syntaxe des instructions destinées à mettre en œuvre une méthode associée à un objet :
 
<source lang="Python">
<pre>
objet.méthode(arguments)
</presource>
 
On écrit d'abord le nom de l'objet sur lequel on désire intervenir, puis le point (qui fait office d'opérateur), puis le nom de la méthode à mettre en œuvre ; entre les parenthèses associées à cette méthode, on indique enfin les arguments qu'on souhaite lui transmettre.
Nous n'allons pas entrer dans trop de détails. Si vous voulez bien encoder et expérimenter le petit script ci-dessous, vous aurez vite compris le principe.
 
<source lang="Python">
<pre>
# Détection et positionnement d'un clic de souris dans une fenêtre :
 
 
fen.mainloop()
</presource>
 
[[Image:Apprendre à programmer avec Python 23.png|200px|right]]
Nous pourrions effectuer un certain nombre de tentatives en fournissant à la méthode <code>pack()</code> des arguments de type « side = », comme nous l'avons déjà fait précédemment, mais cela ne nous mène pas très loin. Essayons par exemple :
 
<source lang="Python">
<pre>
from Tkinter import *
 
 
fen1.mainloop()
</presource>
 
... mais le résultat n'est pas vraiment celui que nous recherchions !!! :
 
[[Image:Apprendre à programmer avec Python 26.png|right]]
<source lang="Python">
<pre>
from Tkinter import *
 
entr2.grid(row =1, column =1)
fen1.mainloop()
</presource>
 
Dans ce script, nous avons donc remplacé la méthode <code>pack()</code> par la méthode <code>grid()</code>. Comme vous pouvez le constater, l'utilisation de la méthode <code>grid()</code> est très simple. Cette méthode considère la fenêtre comme un tableau (ou une grille). Il suffit alors de lui indiquer dans quelle ligne (''row'') et dans quelle colonne (''column'') de ce tableau on souhaite placer les widgets. On peut numéroter les lignes et les colonnes comme on veut, en partant de zéro, ou de un, ou encore d'un nombre quelconque : ''Tkinter'' ignorera les lignes et colonnes vides. Notez cependant que si vous ne fournissez aucun numéro pour une ligne ou une colonne, la valeur par défaut sera zéro.
''Tkinter'' détermine automatiquement le nombre de lignes et de colonnes nécessaire. Mais ce n'est pas tout : si vous examinez en détail la petite fenêtre produite par le script ci-dessus, vous constaterez que nous n'avons pas encore tout à fait atteint le but poursuivi. Les deux chaînes apparaissant dans la partie gauche de la fenêtre sont centrées, alors que nous souhaitions les aligner l'une et l'autre par la droite. Pour obtenir ce résultat, il nous suffit d'ajouter un argument dans l'appel de la méthode <code>grid()</code> utilisée pour ces widgets. L'option <code>sticky</code> peut prendre l'une des quatre valeurs <code>N</code>, <code>S</code>, <code>W</code>, <code>E</code> (les quatre points cardinaux en anglais). En fonction de cette valeur, on obtiendra un alignement des widgets par le haut, par le bas, par la gauche ou par la droite. Remplacez donc les deux premières instructions <code>grid()</code> du script par :
 
<source lang="Python">
<pre>
txt1.grid(row =0, sticky =E)
txt2.grid(row =1, sticky =E)
</presource>
 
… et vous atteindrez enfin exactement le but recherché.
Le code correspondant est le suivant :
 
<source lang="Python">
<pre>
from Tkinter import *
 
# démarrage :
fen1.mainloop()
</presource>
 
Pour pouvoir faire fonctionner ce script, il vous faudra probablement remplacer le nom du fichier image (''Martin_P.gif'') par le nom d'une image de votre choix. Attention : la bibliothèque ''Tkinter'' standard n'accepte qu'un petit nombre de formats pour cette image. Choisissez de préférence le format GIF.
Vous pouvez par exemple assez fréquemment utiliser la composition d'instructions pour appliquer la méthode de mise en page des widgets (grid(), pack() ou place()) au moment même où vous créez ces widgets. Le code correspondant devient alors un peu plus simple, et parfois plus lisible. Vous pouvez par exemple remplacer les deux lignes :
 
<source lang="Python">
<pre>
txt1 = Label(fen1, text ='Premier champ :')
txt1.grid(row =1, sticky =E)
</presource>
 
du script précédent par une seule, telle que :
 
<source lang="Python">
<pre>
Label(fen1, text ='Premier champ :').grid(row =1, sticky =E)
</presource>
 
Dans cette nouvelle écriture, vous pouvez constater que nous faisons l'économie de la variable intermédiaire <code>txt1</code>. Nous avions utilisé cette variable pour bien dégager les étapes successives de notre démarche, mais elle n'est pas toujours indispensable. Le simple fait d'invoquer la classe <code>Label()</code> provoque en effet l'instanciation d'un objet de cette classe, même si l'on ne mémorise pas la référence de cet objet dans une variable (''Tkinter'' la conserve de toute façon dans sa représentation interne de la fenêtre). Si l'on procède ainsi, la référence est perdue pour le restant du script, mais elle peut tout de même être transmise à une méthode de mise en page telle que <code>grid()</code> au moment même de l'instanciation, en une seule instruction composée. Voyons cela un peu plus en détail :
Jusqu'à présent, nous avons créé des objets divers (par instanciation à partir d'une classe quelconque), en les affectant à chaque fois à des variables. Par exemple, lorsque nous avons écrit :
 
<source lang="Python">
<pre>
txt1 = Label(fen1, text ='Premier champ :')
</presource>
 
Nous avons créé une instance de la classe <code>Label()</code>, que nous avons assignée à la variable <code>txt1</code>.
Lorsque ce genre de situation se présente, il est plus judicieux d'utiliser la composition d'instructions. Par exemple, on préférera le plus souvent remplacer les deux instructions :
 
<source lang="Python">
<pre>
somme = 45 + 72
print somme
</presource>
 
par une seule instruction composée, telle que :
 
<source lang="Python">
<pre>
print 45 + 72
</presource>
 
On fait ainsi l'économie d'une variable.
Et dans ce cas, il faut obligatoirement utiliser deux instructions distinctes, l'une pour instancier le widget et l'autre pour lui appliquer ensuite la méthode de mise en page. Vous ne pouvez pas, par exemple, construire une instruction composée telle que :
 
<source lang="Python">
<pre>
entree = Entry(fen1).pack() # faute de programmation !!!
</presource>
 
En apparence, cette instruction devrait instancier un nouveau widget et l'assigner à la variable entree, la mise en page s'effectuant dans la même opération à l'aide de la méthode <code>pack()</code>.
Pour obtenir une vraie référence du widget, vous devez utiliser deux instructions :
 
<source lang="Python">
<pre>
entree = Entry(fen1) # instanciation du widget
entree.pack() # application de la mise en page
</presource>
 
Note : Lorsque vous utilisez la méthode <code>grid()</code>, vous pouvez simplifier encore un peu votre code, en omettant l'indication de nombreux numéros de lignes et de colonnes. À partir du moment où c'est la la méthode <code>grid()</code> qui est utilisée pour positionner les widgets, ''Tkinter'' considère en effet qu'il existe forcément des lignes et des colonnes11. Si un numéro de ligne ou de colonne n'est pas indiqué, le widget correspondant est placé dans la première case vide disponible.
Le script ci-dessous intègre les simplifications que nous venons d'expliquer :
 
<source lang="Python">
<pre>
from Tkinter import *
fen1 = Tk()
# démarrage :
fen1.mainloop()
</presource>
 
== Modification des propriétés d'un objet - Animation ==
Veuillez donc écrire, tester, puis analyser le script ci-dessous :
 
<source lang="Python">
<pre>
from Tkinter import *
 
# démarrage du réceptionnaire d'évènements (boucle principale) :
fen1.mainloop()
</presource>
 
[[Image:Apprendre à programmer avec Python 28.png|center]]
<ol>
<li>
<source lang="Python">
<pre>
# Simulation du phénomène de gravitation universelle
 
 
fen.mainloop()
</presource>
[[Image:Apprendre à programmer avec Python 73.png|center|capture d'écran du résultat final]]
</li>
<li>Réfléchissez !</li>
<li>
<source lang="Python">
<pre>
# Conversions de températures Fahrenheit <=> Celsius
 
 
fen.mainloop()
</presource>
[[Image:Apprendre à programmer avec Python 74.png|center|capture d'écran du résultat final]]
</li>
<li>Voir ci dessous.</li>
<li>
<source lang="Python">
<pre>
# Cercles et courbes de Lissajous
 
 
fen.mainloop()
</presource>
[[Image:Apprendre à programmer avec Python 75.png|center|capture d'écran du résultat final]]
</li>
Pour conclure cette première prise de contact avec l'interface graphique ''Tkinter'', voici un dernier exemple d'animation, qui fonctionne cette fois de manière autonome dès qu'on l'a mise en marche.
 
<source lang="Python">
<pre>
from Tkinter import *
 
# démarrage du réceptionnaire d'évènements (boucle principale) :
fen1.mainloop()
</presource>
 
[[Image:Apprendre à programmer avec Python 30.png|center]]
<li>Réfléchissez !</li>
<li>
<source lang="Python">
<pre>
 
# Chutes et rebonds
 
fen.mainloop()
</presource>
[[Image:Apprendre à programmer avec Python 76.png|center|capture d'écran du résultat final]]
</li>
Utilisateur anonyme