Interface graphique
Nos programmes jusqu’à présent était en mode console. Cette façon de programmer permet d’apprendre tous les aspects du langage sans avoir à se soucier de l’affichage, ni du son. Elle est plus facile et le code est performant mais l’interaction est peu attirante, peu ergonomique. L’interface graphique, est seulement une « surcouche » ajoutée au langage, permettant d’afficher des fenêtres, des images ou des boutons par exemple, pour un meilleur confort d’utilisation au détriment de la performance due à la gestion de l’affichage graphique.
Différence de conception
En mode console généralement le programme s’exécute au fur et à mesure du fichier. C’est votre programme qui pilote les évènements, qui demande une information, qui guide pas à pas le déroulement des choses.
Avec une interface graphique, c’est différent, le programme ne « pilote » plus les évènements, mais il est cette fois « piloté » par ceux-ci. Il est en quelque sorte au service de l’utilisateur et doit agir en fonction des actions de celui-ci sur l’environnement : clavier, souris … on parle de gestion d’événement, généralement codée à l’aide d’une boucle, qui s’effectue tant qu’aucun événement ne se produit.
L’interface graphique installée par défaut est : tkinter
Un élément graphique comme un bouton, une case à cocher ou une fenêtre de saisie est appelé widget et se caractérise par :
- des options qui sont ses propriétés
- et des méthodes qui désignent des actions qu’on peut lui appliquer.
Les options sont précisées entre parenthèses au moment de la création du widget. Les méthodes sont invoqués ensuite.
Exemple 1 : Dans un script Python testez les lignes suivantes :
1 2 3 4 5 6 7 8 9 10 |
from tkinter import * # Charger la bibliothèque tkinter fp = Tk() # Créer la fenêtre principale sous le nom fp B1 = Button(fp, text = "Bouton 1") # créer le bouton B1 dans la fenetre fp # (fp désigne le père du bouton). # L'option text permet de définir le texte affiché sur le bouton B1.pack() # La méthode pack positionne le bouton B2 = Button(fp, text = "Bouton 2") # créer le bouton B2 dans la fenetre fp B2.pack() # positionner le bouton dans la fenêtre fp.mainloop() # activer le gestionnaire d'événements qui va surveiller # ce qui se passe dans la fenêtre |
Remarque : Le fait d'avoir utilisé le positionneur pack() sans aucun paramètre, les widgets sont positionnés automatiquement l'un en dessous de l'autre. |
Exemple 2 : Testez les lignes suivantes et commentez-les.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
from tkinter import * root = Tk() W1 = Toplevel(root, bg='gold', cursor = 'X_cursor') W2 = Toplevel(root, bg='azure', cursor = 'man', pady=10) root.geometry('400x100+100+100') W1.geometry('400x100+100+250') W2.geometry('400x200+100+400') root.title('Fenêtre Principale') W1.title('Fenêtre 1') W2.title('Fenêtre 2') B1 = Button(root, text= ' BOUTON FP').pack(pady = 10) B2 = Button(W1, text= ' BOUTON W1').pack(pady = 10) B3 = Button(W2, text= ' BOUTON W2').pack(pady = 10) W1.maxsize(500, 400) W2.minsize(150, 100) root.mainloop() |
Les Widgets : Label, Entry, Text , …
nom = Label(parent, option=valeur, option=valeur, . . .)
Ce widget permet d’afficher un texte sur une ou plusieurs lignes. Il peut aussi servir à afficher une image.
nom = Entry(parent, option=valeur, option=valeur, . . .)
Ce widget d’une seule ligne permet de saisir un texte.
nom = Text(parent, option=valeur, option=valeur, . . .)
Ce widget permet d’éditer un texte de plusieurs lignes. Le texte peut être formaté (fonte, couleur, . . .). Pour récupérer le contenu du widget, on utilise la méthode nom.get(), qui retourne une chaîne.
Exemple 3 : Testez et commentez les lignes suivantes
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
from tkinter import * def hello(): E3.insert(INSERT, 'Bonjour '+ E1.get() + ' ' + E2.get()+'\n') fp = Tk() fp.title('Widgets Button Entry Text') L1 = Label(fp, text = 'Nom') E1 = Entry(fp, width=20, bg = 'yellow') E1.insert(INSERT , 'Pain') L2 = Label(fp, text = '\nPrénom') # le \n permet d'espacer verticalement E2 = Entry(fp, width=15 , bg ='#ffcc00') E2.insert(INSERT,'Desucre') B1 = Button(fp, text='Saluer', bd=5, command = hello) B2 = Button(fp, text='EXIT', bd=5, command=fp.destroy) E3 = Text(fp, width=30, height=15) E3.pack(side=RIGHT, fill= BOTH, padx = 5, pady = 10, expand=False) L1.pack() E1.pack() L2.pack() E2.pack() B1.pack(pady=20) B2.pack(pady=5) fp.mainloop() |
La méthode pack() place les widgets dans l’espace qui n’est pas encore utilisé et adapte la taille de la fenêtre pour contenir tous les widgets. Par défaut, les widgets sont placés les uns en dessous des autres en commençant par le haut.
L’option side permet de positionner le widget dans l’espace disponible. Elle peut prendre la valeur LEFT, RIGHT, TOP ou BOTOM.
Au début, l’espace disponible est constitué de la totalité du widget père (conteneur). Une fois le widget positionné, on se retrouve avec un nouvel espace disponible dans lequel on recommence de la même manière.
tkinter possède de nombreux autres widgets comme Checkbutton (qui retourne une valeur parmi deux selon qu’il est ou pas coché) ou Radiobutton, (qui permet de faire un choix parmi plusieurs) ou encore Optionmenu (qui permet de sélectionner une valeur dans une liste déroulante).
Le positionneur pack
Autres options :
- l’option fill précise comment le widget va remplir l’espace disponible. Cette option peut prendre la valeur X pour horizontal, Y pour vertical, BOTH pour les deux et NONE pour aucun.
- L’option expand permet préciser si le widget doit suivre le redimensionnement de son conteneur. Cette option peut prendre la valeur 1 ou 0
- Les options padx et pady permettent de définir les marges extérieures (en pixel) horizontales et verticales autour du widget (couleur marron) … ipadx et ipady pour les marges intérieures.
- L’option anchor. Par défaut, quand le widget ne remplit pas son espace, il est centré. Cette option permet de l’ancrer sur l’un des quatre cotés ou l’un des quatre coins.
Pour plus de souplesse, on peut commencer par placer des frames qui pourront à leur tour recevoir des widgets. A l’intérieur de ces nouveaux conteneurs, rien ne nous oblige d’utiliser le positionneur pack(), on peut très bien utiliser le positionneur grid() ou place()
Les positionneurs grid() et place()
La méthode grid() permet de positionner les widgets dans un conteneur comme une fenêtre ou un frame. Le conteneur est organisé comme une grille de cellules. Pour placer un widget dans une cellule: widget.grid(row=valeur, column=valeur)
Au départ, on ne crée pas la grille en définissant un nombre de lignes ou de colonnes. La grille est crée au momment où on y place des widgets. Si on place un widget dans la cellule (1,1) et un autre dans la cellule (4, 3) alors une grille à quatre ligne et 3 colonnes est crée.
Une ligne ou une colonne dont toutes les cellules sont vide n’est pas visible à moins qu’on définisse une taille minimum obligatoire:
1 2 |
conteneur.grid_rowconfigure(7, minsize = 20) conteneur.grid_columnconfigure(3, minsize = 60) |
L’option rowspan permet d’occuper plusieurs cellules :
1 |
widget.grid(row=2, column=3, rowspan=5) |
On peut aussi imposer une marge horizontale et verticale par rapport aux bords de la cellule à l’aides des options padx et pady
Le positionneur place() permet définir les positions absolues (x,y) des widget ainsi que leur dimensions.
Le widget : Canvas
Un canevas est une surface rectangulaire délimitée, dans laquelle on peut installer ensuite divers dessins et images à l’aide de méthodes spécifiques : can = Canvas(parent,option=valeur,option=valeur,…)
Exemple 4 : Testez et commentez les lignes suivantes
1 2 3 4 5 6 7 8 9 |
from tkinter import * Mafenetre = Tk() Mafenetre.title('France') photo = PhotoImage(file="image.gif") Can = Canvas(Mafenetre,width = photo.width(), height =photo.height()) # Création d'un widget Canvas (zone graphique) item = Can.create_image(0,0,anchor=NW, image=photo) Can.create_oval(150, 150, 200, 200, outline='black', fill='black') Can.pack() Mafenetre.mainloop() |
voir le site de Fabrice Sincère.
Les Frames
nom = Frame(parent, option=valeur, option=valeur, . . .)
Ce widget est un conteneur qui peut recevoir d’autres widgets. Le positionnement des widgets à l’intérieur de chaque frame est indépendant du positionneur utilisé dans le parent du Frame.
nom = LabelFrame(parent, option=valeur, option=valeur, . . .)
Ce widget est le même que le widget Frame avec en plus la possibilité d’afficher un texte sur la bordure du frame.
Exemple 5 : Testez et commentez les lignes suivantes
1 2 3 4 5 6 7 8 9 10 11 |
from tkinter import * fp =Tk() F1 = Frame(fp, bg='gold', bd=5,relief = RAISED) F2 = LabelFrame(fp, text = 'Groupe 2', labelanchor = N) B1 = Button(F1,text='Bouton 1 F1') B2 = Button(F2,text='Bouton 1 F2') B1.pack(padx = 10, pady = 10) B2.pack(padx = 10, pady = 10) F1.pack(padx =10, pady = 10) F2.pack(padx =10, pady = 10) fp.mainloop() |
Gestion des événements clavier et souris : la méthode .bind()
Tkinter offre la possibilité de lier un widget avec un événement:
widget.bind(événement , fonctions)
L’événement peut venir du clavier ou de la souris mais pas seulement. Chaque fois que l’événement survient, la fonction est appelée. L’événement est représenté par une chaîne le plus souvent comme cela : <événement> ou ‘<événement>’
Evénement Clavier
- ‘<KeyPress>’, ‘<KeyPress-A>’, ‘<A>’, ’A’, ‘<KeyRelease>’ , ‘<KeyRelease-B>’
- ‘<Up>’, ‘<Down>’, ‘<Left>’, ‘<Right>’
- ‘<Tab>’, ‘<Escape>’, ‘<Return>’, ‘<Caps_Lock>’, ‘<Num_Lock>’, ‘<Alt_R>’, ‘<Control_L>’, ‘<Shift_R>’
Evénement Souris
- <Button>,<Button-n> ,<ButtonRelease-n> –> Le bouton n a été cliqué ou relâché (1=gauche, 2=centre, 3=droite)
- <Bn-Motion> –> La sourie a bougé avec le bouton n appuyé
- <Double-Button-n> –> Le bouton n a été double cliqué ou relâché (1=gauche, 2=centre, 3=droite)
- <Enter>, <Leave> –> Le pointeur de la souris vient d’entrer ou de sortir du widget
Le module tkinter permet également la gestion :
- de messages dans des fenêtres surgissantes (tkinter.messagebox / showinfo)
- de menu déroulant (Menu(tk))
- de boîte de dialogue pour les fichiers (tkinter.filedialog / askopenfilename)
Exemple 6 : Testez cet exemple permettant d’afficher une image gif dans une fenêtre et commentez les fonctions Clic et Annuler.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
from tkinter import * import tkinter.messagebox import tkinter.filedialog def Ouvrir(): Canevas.delete(ALL) # on efface la zone graphique filename = tkinter.filedialog.askopenfilename(title="Ouvrir une image",filetypes=[('gif files','.gif'),('all files','.*')]) photo = PhotoImage(file=filename) gifdict[filename] = photo # référence Canevas.create_image(0,0,anchor=NW,image=photo) Canevas.config(height=photo.height(),width=photo.width()) Mafenetre.title("Image "+str(photo.width())+" x "+str(photo.height())) def Fermer(): Canevas.delete(ALL) Mafenetre.title("Image") def Apropos(): tkinter.messagebox.showinfo("A propos","Exemple de messagebox") def Clic(event): X = event.x Y = event.y Balle = Canevas.create_oval(X-10,Y-10,X+10,Y+10,width=1,fill='black') Listeballe.append(Balle) def Annuler(): n=len(Listeballe) if n>0: Canevas.delete(Listeballe[n-1]) del(Listeballe[n-1]) # Utilisation d'un dictionnaire pour conserver la référence de l'image afin qu'elle existe en dehors de la fonction ouvrir gifdict={} # Utilisation d'une liste pour conserver les références des items de balle Listeballe=[] # Création de la fenetre principale Mafenetre = Tk() Mafenetre.title("Image") # Création d'un widget Menu associé à la fenetre principale menubar = Menu(Mafenetre) menufichier = Menu(menubar,tearoff=0) menufichier.add_command(label="Ouvrir une image",command=Ouvrir) menufichier.add_command(label="Fermer l'image",command=Fermer) menufichier.add_separator() menufichier.add_command(label="Quitter",command=Mafenetre.destroy) menubar.add_cascade(label="Fichier", menu=menufichier) menuaide = Menu(menubar,tearoff=0) menuaide.add_command(label="A propos",command=Apropos) menubar.add_cascade(label="Aide", menu=menuaide) # Affichage du menu Mafenetre.config(menu=menubar) # Création d'un widget Canvas dans la fenetre principale Canevas = Canvas(Mafenetre) Canevas.pack(padx=5,pady=5) # La méthode bind() permet de lier un événement avec une fonction : # un clic gauche sur la zone graphique provoquera l'appel de la fonction utilisateur Clic() Canevas.bind('<Button-1>', Clic) # Création d'un widget Button (bouton Annuler) BoutonAnnuler = Button(Mafenetre, text ='Annuler clic', command = Annuler) BoutonAnnuler.pack(side = LEFT, padx = 5, pady = 5) # On lance le gestionnaire d'évènement Mafenetre.mainloop() |
Enfin, voici un petit mémo bien pratique pour toutes les instructions en Tkinter.