Python Interface Graphique - Tkinter J

Python

Interface Graphique - Tkinter

J usqu'? pr?sent, l'ensemble des exemples que nous avons trait?s dans les diff?rents sujets trait?s se faisait uniquement en mode console. Nous profitons de cette ?tude pour ?laborer des interfaces graphiques (fen?tres, boutons, zones de saisie, etc.) ? l'aide de la biblioth?que ? tkinter ? qui est fournie par d?faut avec Python.

Ce n'est certainement pas la plus sophistiqu?e puisque bon nombre de fonctionnalit?s n'existent pas, mais elle nous permettra d'?laborer facilement des applications fen?tr?es rudimentaires de tout genre.

Nous aborderons syst?matiquement une approche orient? objet. Ce choix nous permet de cr?er nos propres composants sp?cifiques en h?ritant de ceux existants. Nous apporterons ainsi les modifications justes n?cessaires sans tout r?inventer. Par ailleurs, nous ?vitons, pour la composition de la fen?tre principale, de d?clarer syst?matiquement plein de variables globales pour l'?laboration des m?thodes qui g?rent la partie ?v?nementielle.

CLES COMPOSANTS GRAPHIQUES DE TKINTER ? LES WIDGETS omme toute interface graphique, il existe un certain nombre de composants graphiques pr?fabriqu?s, des ? widgets ? pour composer notre fen?tre principale avec tous les ?l?ments n?cessaires pour r?soudre l'application que nous souhaitons mettre en oeuvre. Voici, ci-dessous une liste non exhaustif des widgets les plus utilis?s.

Widget

Caract?ristiques associ?es

Tk Button Canvas CheckButton

Entry Frame

Label Listbox

MenuButton Menu Message

RadioButton

Scale

Scrollbar

Text TopLevel SpinBox PanedWindow tkMessageBox

Fen?tre principale d'une application python avec la biblioth?que ? tkinter ?.

Un bouton classique, ? utiliser pour provoquer l'ex?cution d'une commande.

Un espace pour dessiner des formes, comme des lignes, des ovales, des polygones, des rectangles, etc.

Une case ? cocher qui peut prendre uniquement deux ?tats distincts (la case est coch?e ou non). Un clic sur ce widget provoque le changement d'?tat. ? l'aide de ces types de bouton, l'utilisateur peut s?lectionner plusieurs options ? la fois.

Zone de saisie sur une ligne pour accepter des valeurs (par d?faut des textes) provenant de l'utilisateur.

Une surface rectangulaire dans la fen?tre pr?vue pour disposer d'autres widgets afin que l'apparence globale soit plus agr?able. Cette surface peut ?tre color?e et d?cor?e avec une bordure (pour d?finir des zones particuli?res).

Un texte (un libell?) utilis? pour fournir une l?gende pour d'autres widgets. Il peut ?galement contenir des images.

Une liste de choix propos?s ? l'utilisateur. Il est possible de configurer cette liste de telle mani?re que ces diff?rents choix soient pr?sent?s comme une s?rie de ? boutons radio ? ou de ? cases ? cocher ?.

Utilis? pour impl?menter des menus d?roulants dans votre application.

Utilis? pour fournir diff?rentes commandes ? l'utilisateur. Ces commandes sont contenues ? l'int?rieur du ? MenuButton ?.

Permet d'afficher un texte. Ce widget est une variante du widget ? Label ?, qui permet d'adapter automatiquement le texte affich? ? une certaine taille ou ? un certain rapport ? largeur/hauteur ?.

Repr?sente (par un point noir dans un petit cercle) une des valeurs d'une variable qui poss?de plusieurs options possibles. Cliquer sur un bouton radio donne la valeur correspondante ? la variable et d?sactive automatiquement les autres boutons radios associ?s ? cette variable. l'utilisateur peut ainsi s?lectionner une seule option ? la fois.

Widget curseur qui permet de faire varier de mani?re tr?s visuelle la valeur d'une variable, en d?pla?ant avec la souris le curseur le long d'une r?gle.

Ascenseur ou barre de d?filement que nous associons ? d'autres widgets (Canvas, Entry, Listbox, etc.) afin d'augmenter la surface de travail dans une zone tr?s limit?e.

Utilis? pour afficher ou ?diter du texte sur plusieurs lignes.

Affiche une autre fen?tre au premier plan en plus de la fen?tre principale d'application.

Zone de saisie qui peut ?tre utilis?e pour s?lectionner une valeur par rapport ? une fourchette de valeurs pr?-?tablies.

Conteneur qui peut contenir un nombre quelconque de panneaux, dispos?s horizontalement ou verticalement.

Bo?te de messages, d'avertissement, etc.

APREMI?RE APPLICATION FEN?TR?E fin de bien comprendre le m?canisme inh?rent aux applications avec interface graphique, je vous propose de r?aliser un premier projet qui impl?mente notre premi?re application fen?tr?e. Pour cela, nous allons reprendre un projet que nous avons d?j? mis en oeuvre en mode console, qui permettait de conna?tre la surface d'un disque et le volume d'une sph?re ? partir du rayon d'un cercle. Bien s?r, nous gardons la classe ? Cercle ? qui effectue automatiquement tous les calculs n?cessaires. Seuls, la partie graphique va ?tre rajout?e.

cercle.py

from math import pi, sqrt, pow

class Cercle: # Constructeur

def __init__(self, rayon=50): self.rayon = rayon

# D?finition du diam?tre du cercle @property def diam?tre(self): return 2 * self.rayon @diam?tre.setter def diam?tre(self, nouveau): self.rayon = nouveau//2 # D?finition de la surface d'un disque

BAC S-ISN

Page 1/17

Python

Interface Graphique - Tkinter

@property def surface(self): return pi * self.rayon**2 @surface.setter def surface(self, nouvelle): self.rayon = sqrt(nouvelle/pi)

# D?finition du volume d'une sph?re @property def volume(self): return 4/3 * pi * self.rayon**3 @volume.setter def volume(self, nouveau): self.rayon = pow(3*nouveau/(4*pi), 1/3)

Nous avons ?limin? les membres de classe, attribut et m?thode, qui permettait de conna?tre le nombre de cercles cr??s. Ici, un seul cercle sera g?n?r? pour r?aliser les calculs n?cessaires ? l'application.

saisiefloat.py

from tkinter import *

# Nouvelle classe de saisie pr?vue pour les nombres r?els class SaisieFloat(Entry):

def __init__(self, conteneur, libell?, valeur=0.0, ?tat=DISABLED): Label(conteneur, text=libell?, anchor=W, width=30).pack() self.__valeur = DoubleVar(conteneur, valeur) super().__init__(conteneur, state=?tat, textvariable=self.__valeur, justify='right', width=30) self.pack()

@property def valeur(self): return self.__valeur.get() @valeur.setter def valeur(self, nouvelle): self.__valeur.set(nouvelle)

Un deuxi?me module s'occupe de la mise en oeuvre d'une zone de saisie personnalis?e ? SaisieFloat ? qui permet de r?cup?rer directement une valeur de type ? float ?. Lorsque vous d?sirez cr?er un widget personnalis?, la technique est tr?s simple, il suffit d'h?riter du composant avec lequel vous souhaitez rajouter d'autres fonctionnalit?s. Ainsi, vous n'avez pas tout ? refaire. C'est vraiment l'int?r?t de la programmation objet.

Ce nouveau composant est d?crit ? l'aide de d'une propri?t? ? valeur ? en lecture et en ?criture et d'un constructeur ? __init__ ? qui nous permet de le fa?onner pour qu'il soit capable de manipuler des valeurs r?elles et de le configurer pour que son apparence soit conforme ? notre d?sir.

Ce qui est particulier ici, c'est que ce composant est en r?alit? compos? de deux ?l?ments, d'une part d'une ?tiquette ? Label ? et d'autre part de la zone d'?dition ? Entry ? proprement dite qui sont plac?s l'un au dessus de l'autre.

? propos de placement, il est possible de pr?ciser quel est l'?l?ment conteneur (autre composant) qui va s'occuper de le placer, ici ce sera bien entendu la fen?tre (si le conteneur est la fen?tre principale, vous n'?tes pas oblig? de donner cette pr?cision). C'est syst?matiquement le premier argument que nous devons pr?ciser ? chacun des objets que nous cr?ons, quelle que soit leur nature.

Le placement du composant proprement dit se fait ? l'aide d'une m?thode sp?cifique ? pack() ? qui positionne par d?faut les composants les uns au-dessus des autres, ce qui convient parfaitement ici. Nous verrons plus tard qu'il existe bien entendu d'autres solutions alternatives.

Lorsque nous cr?ons nos widgets, chacun poss?de plein de param?tres dans la phase de construction qui permettent de r?gler finement l'apparence et le comportement de chacun. Pour cela, vous devez qualifier le param?tre qui vous int?resse en donnant l'argument correspondant. Nous ne les montrerons pas tous, mais voici ceux que j'ai pris en compte pour notre application :

Dans le cas du ? Label ? nous pr?cisons l'intitul? du texte ? text=libell? ?, le positionnement du texte par rapport ? la dimension du composant, ici ? l'ouest ? anchor=W ? et la dimension du composant ? width=30 ?.

Dans le cas de ? Entry ?, nous sp?cifions l'?tat du composant (par d?faut d?sactiv? ? gris?) ? state=?tat ?, la variable qui correspond ? la valeur ? traiter ? textvariable=self.__valeur ?, la justification (le placement) de la valeur par rapport ? toute la zone d'?dition, ici ? droite ? justify='right' ? et la dimension du composant ? width=30 ?.

Par d?faut, une zone de saisie traite du texte, mais il possible, comme nous venons de le voir, de fournir un argument au param?tre ? textvariable ? correspondant au type ? traiter. Il existe quatre classes sp?cialis?es pour cela, chacune traitant d'un type sp?cifique, ? StringVar ? pour le type ? str ?, ? BooleanVar ? pour le type ? bool ?, ? IntVar ? pour le type ? int ? et ? DoubleVar ? pour le type ? float ?.

L'utilisation de ces classes est tr?s simple. Elles sont toutes capables de passer d'une cha?ne de caract?re, mode par d?faut vers le type choisi, et vice versa. Elles poss?dent chacune une m?thode ? set ? pour soumettre une nouvelle valeur, et une m?thode ? get ? pour r?cup?rer la valeur en cours.

Pour que cela soit relativement transparent et tr?s facile ? manipuler, nous avons pr?vu une propri?t? ? valeur ? qui nous permettra de r?aliser tous les calculs n?cessaires ? notre application, notamment pour la fen?tre principale que nous allons maintenant d?crire.

principal.py

from tkinter import * from saisiefloat import SaisieFloat from cercle import Cercle # Fen?tre principale de l'application

BAC S-ISN

Page 2/17

Python

Interface Graphique - Tkinter

class Fen?tre(Tk): def __init__(self, titre): super().__init__() self.title(titre) self.cercle = Cercle() self.placerComposants() self.mainloop()

def placerComposants(self): self.rayon = SaisieFloat(self, 'Rayon :', self.cercle.rayon, ?tat="normal") self.diam?tre = SaisieFloat(self, 'Diam?tre :', self.cercle.diam?tre) self.surface = SaisieFloat(self, 'Surface :', self.cercle.surface) self.volume = SaisieFloat(self, 'Volume :', self.cercle.volume) Button(self, text='Calculer', command=self.calcul, width=28).pack() self.rayon.focus()

def calcul(self): self.cercle.rayon = self.rayon.valeur self.diam?tre.valeur = self.cercle.diam?tre self.surface.valeur = self.cercle.surface self.volume.valeur = self.cercle.volume

# Programme principal if __name__ == '__main__' :

programme = Fen?tre('Calculs g?om?triques')

Lorsque vous d?sirez cr?er une nouvelle application fen?tr?e, n'h?sitez pas ? cr?er une nouvelle classe, ici ? Fen?tre ? qui h?rite de la classe ? Tk ? qui repr?sente une classe principale d'application que nous personnalisons pour r?pondre simplement ? notre attente. Trois m?thodes sont impl?ment?es :

? placerComposants() : comme son nom l'indique, cette m?thode s'occupe de placer tous les composants graphiques n?cessaires ? la vue de notre application. C'est ? ce moment-l? que nous cr?ons tous les objets relatifs ? la classe que nous venons de mettre en oeuvre ? SaisieFloat ?.

? calcul() : une autre m?thode sp?cialis?e qui s'occupe uniquement de tous les calculs relatifs aux traitements demand?s. C'est dans cette m?thode que nous utilisons la propri?t? ? valeur ? de la classe ? SaisieFloat ? que nous venons d'?voquer. C'est aussi l? que nous utilisons les comp?tences de la classe ? Cercle ? puisque c'est elle qui dispose de toutes les m?thodes adapt?es aux diff?rents traitements.

? __init__() : constructeur qui permet dans l'ordre : de pr?ciser un titre ? la fen?tre, de cr?er l'attribut ? cercle ? qui s'occupe des calculs, d'activer la m?thode ? placerComposants ? et surtout d'activer la gestion ?v?nementielle gr?ce ? l'appel de la m?thode ? mainloop ?.

Cette m?thode ? mainloop ? est fondamentale, elle permet de faire en sorte de tenir compte de tous les ?v?nements issus de la souris ou d'une touche du clavier. Elle attend une demande sp?cifique de cl?ture de l'application gr?ce au menu syst?me de la fen?tre principale de l'application. Entre temps, si d'autres ?v?nements sont propos?s, ils sont tout simplement pris en compte, c'est-?-dire que les m?thodes ou les fonctions de rappel sont sollicit?es suivant les cas de figure.

Justement, ici nous proposons un ?v?nement associ? au clic du bouton ? Calculer ?. Sur ce composant, gr?ce ? l'?tiquette ? command ?, nous associons la m?thode ? calcul() ? de la classe ? Fen?tre ?. Ainsi, ? chaque fois que l'utilisateur cliquera sur ce bouton, la m?thode associ?e sera automatiquement appel?e (m?thode dite de rappel).

Pour conclure, vous voyez que la fin du module poss?de une ?criture bien particuli?re ? if __name__ == `__main__'' ?. Cela veut simplement dire que le programme principal de l'application commence ? cet endroit-l?. Ce programme comprend une seule ligne d'instruction qui consiste ? cr?er l'objet ? programme ? relatif ? la classe principale de l'application ? Fen?tre ?.

Le mod?le de programmation que nous venons de mettre en oeuvre s'appelle un mod?le ? MVC ? (Mod?le-Vue-Contr?leur) qui permet de s?parer les diff?rents ?l?ments, chacun ayant sa propre fonction. La ? vue ? comme son nom l'indique ne s'int?resse qu'? l'apparence (impl?ment?e ici par la m?thode ? placeComposants() ?, le ? mod?le ? ne s'int?resse qu'au traitement de fond (impl?ment?e ici par la classe ? Cercle ?, et le ? contr?leur ? assure la coh?sion entre les deux gr?ce ? la gestion ?v?nementielle inh?rent ? la structure de la classe ?Tk? avec la m?thode ? mainloop ? d'une part et l'?tiquette ? command ? d'autre part.

LCHOISIR LE POSITIONNEMENT DES COMPOSANTS INTERNES ? POSITIONNEMENT AUTOMATIQUE e chapitre pr?c?dent nous a permis de bien comprendre les principes g?n?raux de la conception d'une interface graphique. Nous allons nous int?resser maintenant au placement automatique des widgets ? l'int?rieur de la fen?tre (ou d'autres widgets). Il existe trois m?thodes sp?cifiques qui sont automatiquement int?gr?es ? chacun des widgets :

? pack() : tr?s souvent utilis?, ce gestionnaire de placement organise les widgets dans les blocs avant de les placer dans le widget parent. La taille de chacun des widgets sera diff?rente suivant leur propre dimension et suivant d'autres crit?res possibles.

? grid(): ce gestionnaire place les widgets dans une grille dans le widget parent ? l'image d'un tableau ? deux dimensions. Lorsque vous rajouter un widget dans la fen?tre, vous devez alors pr?ciser ? quelle ligne et dans quelle colonne il doit ?tre plac? et ?ventuellement s'il prend plusieurs colonnes ou plusieurs lignes ou les deux.

? place() : ce gestionnaire de disposition organise les widgets en les pla?ant dans une position pr?cis?e par le d?veloppeur dans le widget parent.

BAC S-ISN

Page 3/17

Python

Interface Graphique - Tkinter

RPOSITIONNER UN WIDGET RELATIVEMENT AVEC LA M?THODE ? PACK() ? eprenons le projet pr?c?dent en utilisant toujours la m?thode ? pack() ? pour placer chacun des widgets dans la fen?tre principale, mais cette fois-ci nous proposons un certain nombre d'options int?ressantes qui permettra d'?viter de donner syst?matiquement une m?me largeur ? width ? ? tous les widgets que nous positionnons. Voici d'ailleurs quelques param?tres utiles que nous utiliserons au cours de notre ?tude :

? padx et pady : propose une marge externe suivant l'axe des X et des Y autour du widget.

? ipadx et ipady : propose une marge interne suivant l'axe des X et des Y ? l'int?rieur du widget.

? expand : place des espaces suppl?mentaires au widget, la valeur doit ?tre sup?rieure ? ? 0 ? et vous pr?cisez le nombre d'espace que vous souhaitez.

? fill : ?tend le composant au maximum jusqu'? atteindre le bord du widget parent, soit dans l'axe des ? X ?, soit dans l'axe des ? Y ?, soit les deux ? BOTH ? ou tout simplement aucune extension (par d?faut ? default ?).

? side : propose un positionnement du widget dans le parent suivant les points cardinaux ? N, NE, E, SE, S, SW, W, NW ?.

Voici ci-dessous une possibilit? d'utilisation en prenant en compte quelques crit?res ?voqu?s ci-dessus :

saisiefloat.py

from tkinter import *

# Nouvelle classe de saisie pr?vue pour les nombres r?els class SaisieFloat(Entry):

def __init__(self, conteneur, libell?, valeur=0.0, ?tat=DISABLED): Label(conteneur, text=libell?, anchor=SW).pack(fill=X, padx=4, pady=3, ipady=3) self.__valeur = DoubleVar(conteneur, valeur) super().__init__(conteneur, state=?tat, textvariable=self.__valeur, justify='right', width=30) self.pack(padx=5)

@property def valeur(self): return self.__valeur.get() @valeur.setter def valeur(self, nouvelle): self.__valeur.set(nouvelle)

principal.py

from tkinter import * from saisiefloat import SaisieFloat from cercle import Cercle

# Fen?tre principale de l'application class Fen?tre(Tk):

def __init__(self, titre): super().__init__() self.title(titre) self.cercle = Cercle() self.placerComposants() self.mainloop()

def placerComposants(self): self.rayon = SaisieFloat(self, 'Rayon :', self.cercle.rayon, ?tat="normal") self.diam?tre = SaisieFloat(self, 'Diam?tre :', self.cercle.diam?tre) self.surface = SaisieFloat(self, 'Surface :', self.cercle.surface) self.volume = SaisieFloat(self, 'Volume :', self.cercle.volume) Button(self, text='Calculer', command=self.calcul).pack(fill=X, padx=3, pady=5) self.rayon.focus()

def calcul(self): self.cercle.rayon = self.rayon.valeur self.diam?tre.valeur = self.cercle.diam?tre self.surface.valeur = self.cercle.surface self.volume.valeur = self.cercle.volume

# Programme principal if __name__ == '__main__' :

programme = Fen?tre('Calculs g?om?triques')

CPOSITIONNER LE WIDGET DANS UNE GRILLE AVEC LA M?THODE ? GRID() ? omme c'est le cas dans notre projet, lorsque vous avez ? constituer une interface graphique sous l'aspect d'un formulaire, il est souvent plus judicieux de placer les composants align?s les uns par rapport aux autres en prenant plusieurs colonnes, ce qui correspond plus ? un positionnement sous forme de grille. C'est ce que propose la m?thode ? grid() ? dont voici quelques param?tres int?ressants :

? padx et pady : propose une marge externe suivant l'axe des X et des Y autour du widget.

? ipadx et ipady : propose une marge interne suivant l'axe des X et des Y ? l'int?rieur du widget.

BAC S-ISN

Page 4/17

Python

Interface Graphique - Tkinter

? column : num?ro de la colonne o? vous souhaitez placer votre widget en partant de z?ro. Sa valeur par d?faut est ? 0 ?.

? row : Le num?ro de ligne o? vous souhaitez placer votre widget en partant de z?ro. Sa valeur par d?faut est le num?ro de de la premi?re ligne inoccup?e.

? columnspan : Normalement un widget occupe seulement une cellule. Cependant, gr?ce ? ce param?tre, vous pouvez regrouper plusieurs cellules d'une ligne en indiquant le nombre de cellules que vous d?sirez couvrir.

? rowspan : Normalement un widget occupe seulement une cellule. Cependant, gr?ce ? ce param?tre, vous pouvez regrouper plusieurs cellules d'une colonne en indiquant le nombre de cellules que vous d?sirez couvrir.

? sticky : Cette option d?termine la fa?on de distribuer l'espace inoccup? par un widget ? l'int?rieur d'une cellule. Si vous ne donnez aucune valeur ? l'attribut ? sticky ?, le comportement par d?faut est de centrer le widget dans sa cellule. Vous pouvez positionner le widget dans un des coins de la cellule en indiquant ? sticky='ne' ? (nord-est: en haut ? droite), ? 'se' ? (en bas ? droite), ? 'sw' ? (en bas ? gauche), ou ? 'nw' ? (en haut ? gauche). Vous pouvez centrer le widget contre l'un des bords de la cellule en utilisant ? sticky='n' ? (centr? en haut), ? 'e' ? (centr? ? droite), ? `s' ? (centr? en bas), ou ? `w' ? (centr? ? gauche). Utilisez ? sticky='ns' ? pour l'?tirer verticalement tout en le laissant centr? horizontalement. Utilisez ? sticky='ew' ? pour l'?tirer horizontalement tout en le laissant centr? verticalement. Utilisez ? sticky='nesw' ? pour l'?tirer dans les deux directions afin de remplir la cellule. Les autres combinaisons fonctionneront aussi. Par exemple, ? sticky='nsw' ? l'?tirera verticalement en le pla?ant contre le bord gauche de la cellule.

Voici ci-dessous une possibilit? d'utilisation en prenant en compte les crit?res ?voqu?s ci-dessus :

saisiefloat.py

from tkinter import *

# Nouvelle classe de saisie pr?vue pour les nombres r?els class SaisieFloat(Entry):

# Attribut de classe ligne = 0

def __init__(self, conteneur, libell?, valeur=0.0, ?tat=DISABLED): Label(conteneur, text=libell?, anchor=W).grid(row=SaisieFloat.ligne, column=0, sticky=E) self.__valeur = DoubleVar(conteneur, valeur) super().__init__(conteneur, state=?tat, textvariable=self.__valeur, justify='right') self.grid(row=SaisieFloat.ligne, column=1, pady=5) SaisieFloat.ligne += 1

@property def valeur(self): return self.__valeur.get() @valeur.setter def valeur(self, nouvelle): self.__valeur.set(nouvelle)

principal.py

from tkinter import * from saisiefloat import SaisieFloat from cercle import Cercle

# Fen?tre principale de l'application class Fen?tre(Tk):

def __init__(self, titre): super().__init__() self.title(titre) self.cercle = Cercle() self.placerComposants() self.mainloop()

def placerComposants(self): self.rayon = SaisieFloat(self, 'Rayon :', self.cercle.rayon, ?tat="normal") self.diam?tre = SaisieFloat(self, 'Diam?tre :', self.cercle.diam?tre) self.surface = SaisieFloat(self, 'Surface :', self.cercle.surface) self.volume = SaisieFloat(self, 'Volume :', self.cercle.volume) Button(self, text='Calculer', command=self.calcul, padx=100).grid(row=SaisieFloat.ligne, columnspan=2, padx=5, pady=7) self.rayon.focus()

def calcul(self): self.cercle.rayon = self.rayon.valeur self.diam?tre.valeur = self.cercle.diam?tre self.surface.valeur = self.cercle.surface self.volume.valeur = self.cercle.volume

# Programme principal if __name__ == '__main__' :

programme = Fen?tre('Calculs g?om?triques')

BAC S-ISN

Page 5/17

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download