Accès rapide :
Présentation de la stratégie grid
Mise en oeuvre d'une grille simple
Peaufinons notre grille
L'exemple de la calculatrice
Cette stratégie permet de positionner chacun de vos widgets dans une ou plusieurs cellules d'une grille. La grille est organisée en lignes et en colonnes : vous pouvez, bien entendu, contrôler le nombre de lignes et de colonnes. Il est aussi à noter qu'il est possible qu'un widget occupe plusieurs cellules de la grille. Voici une capture d'écran montrant une grille un peu sophistiquée.
Nous allons commencer par un exemple très simple. Nous allons chercher à afficher 4 boutons (2 lignes et 2 colonnes) dans la fenêtre. Voici le code à produire pour réaliser cela.
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 |
#!/usr/bin/python3 from tkinter import Tk, Button # On définit une classe qui dérive de la classe Tk (la classe de fenêtre). class MyWindow(Tk): def __init__(self): # On appelle le constructeur parent super().__init__() button1 = Button(self, text="Button 1") button1.grid(column=0, row=0) button2 = Button(self, text="Button 2") button2.grid(column=1, row=0) button3 = Button(self, text="Button 3") button3.grid(column=0, row=1) button4 = Button(self, text="Button 4") button4.grid(column=1, row=1) # On dimensionne la fenêtre (400 pixels de large par 400 de haut). self.geometry("400x400") # On ajoute un titre à la fenêtre self.title("Positionnement de widgets via grid") # On crée notre fenêtre et on l'affiche window = MyWindow() window.mainloop() |
grid
est disponible sur n'importe quel widget.
Mais en réalité, c'est la classe Grid
qui porte cette méthode
et par le jeu subtil de l'héritage cette méthode est transmise à toutes les classes de widgets.
Et voici le résultat obtenu en exécutant ce programme.
Comme vous le constater, par défaut, la grille ne cherche pas à occuper 100% de la superficie de la fenêtre. Nous allons tenter de remédier à ce point.
Pour cela, il faut indiquer à Tkinter les dimensions de la grille : nombre de lignes et nombre de colonnes.
On réalise cela en invoquant les méthodes
grid_rowconfigure
et
grid_columnconfigure
.
Afin qu'une colonne (ou une ligne) ne prenne pas l'avantage sur une autre, il est nécessaire de jouer avec la notion de poids (weight
).
Dans l'exemple suivant, toutes les lignes ont le même poids (1
) et idem pour les colonnes.
N'oubliez pas qu'en Python, les indices commencent souvent à 0.
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 |
#!/usr/bin/python3 from tkinter import Tk, Button # On définit une classe qui dérive de la classe Tk (la classe de fenêtre). class MyWindow(Tk): def __init__(self): # On appelle le constructeur parent super().__init__() # Pour que la grille occupe 100% de la fenêtre, il faut que tkinter # connaisse à l'avance le nombre de lignes et le nombre de colonnes. # Le paramètre weight indique que chaque colonne (et chaque ligne) # à le même poids. L'une ne prendra pas l'avantage sur l'autre. self.grid_rowconfigure(0, weight=1) self.grid_rowconfigure(1, weight=1) self.grid_columnconfigure(0, weight=1) self.grid_columnconfigure(1, weight=1) # On place les 4 boutons dans chacune des cases button1 = Button(self, text="Button 1") button1.grid(column=0, row=0) button2 = Button(self, text="Button 2") button2.grid(column=1, row=0) button3 = Button(self, text="Button 3") button3.grid(column=0, row=1) button4 = Button(self, text="Button 4") button4.grid(column=1, row=1) # On dimensionne la fenêtre (400 pixels de large par 400 de haut). self.geometry("400x400") # On ajoute un titre à la fenêtre self.title("Positionnement de widgets via grid") # On crée notre fenêtre et on l'affiche window = MyWindow() window.mainloop() |
Et voici l'affichage produit par l'exemple ci-dessus.
Par défaut, un widget occupe le minimum de place dans sa cellule et il est centré en plein milieu de cette cellule.
On peut changer cela grâce au paramètre sticky
. Ce paramètre doit contenir une chaîne de caractères composée des caractères
n
, s
, e
et w
. Ces caractères correspondent au quatre points cardinaux (north, south, east et west).
Selon la valeur du paramètre sticky
, le widget sera attaché sur un ou plusieurs côtés de la cellule qui lui est associée.
Ainsi, la valeur sticky="n"
collera le widget au nord de la cellule.
De même, la valeur sticky="w"
collera le widget à l'ouest de la cellule.
Plus subtil, la valeur sticky="we"
attachera le widget à l'ouest et à l'est de la cellule : il prendra donc toute la largeur de la cellule.
La valeur sticky="ns"
permettra au widget de prendre toute la hauteur. Enfin, la valeur sticky="nsew"
lui permettra de prendre toute
la place disponible dans la cellule. Voici un exemple d'utilisation.
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 |
#!/usr/bin/python3 from tkinter import Tk, Button # On définit une classe qui dérive de la classe Tk (la classe de fenêtre). class MyWindow(Tk): def __init__(self): # On appelle le constructeur parent super().__init__() # Pour que la grille occupe 100% de la fenêtre, il faut que tkinter # connaisse à l'avance le nombre de lignes et le nombre de colonnes. # Le paramètre weight indique que chaque colonne (et chaque ligne) # à le même poids. L'une ne prendra pas l'avantage sur l'autre. self.grid_rowconfigure(0, weight=1) self.grid_rowconfigure(1, weight=1) self.grid_columnconfigure(0, weight=1) self.grid_columnconfigure(1, weight=1) # On place les 4 boutons dans chacune des cases button1 = Button(self, text="Button 1") button1.grid(column=0, row=0) button2 = Button(self, text="Button 2") button2.grid(column=1, row=0, sticky="w") button3 = Button(self, text="Button 3") button3.grid(column=0, row=1, sticky="ns") button4 = Button(self, text="Button 4") button4.grid(column=1, row=1, sticky="nswe") # On dimensionne la fenêtre (400 pixels de large par 400 de haut). self.geometry("400x400") # On ajoute un titre à la fenêtre self.title("Positionnement de widgets via grid") # On crée notre fenêtre et on l'affiche window = MyWindow() window.mainloop() |
sticky
n'a aucune importance. Vous pouvez l'écrire comme vous le souhaitez.
Le paramètre sticky="nsew"
aura donc le même effet que le paramètre sticky="wens"
.
Et voici l'affichage produit par cet exemple.
Nous allons maintenant chercher à peaufiner notre grille. Pour ce faire, chaque bouton va recevoir le paramètre sticky="nsew"
comme vu précédemment, mais nous allons aussi y rajouter des marges autour de chaque bouton. Les paramètres padx
et pady
permettent justement de contrôler ces marges.
Dans le même ordre d'idée, nous allons aussi chercher à décorer nos boutons. Voici un petit exemple d'utilisation.
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 |
#!/usr/bin/python3 from tkinter import Tk, Button # On définit une classe qui dérive de la classe Tk (la classe de fenêtre). class MyWindow(Tk): def __init__(self): # On appelle le constructeur parent super().__init__() # Pour que la grille occupe 100% de la fenêtre, il faut que tkinter # connaisse à l'avance le nombre de lignes et le nombre de colonnes. # Le paramètre weight indique que chaque colonne (et chaque ligne) # à le même poids. L'une ne prendra pas l'avantage sur l'autre. self.grid_rowconfigure(0, weight=1) self.grid_rowconfigure(1, weight=1) self.grid_columnconfigure(0, weight=1) self.grid_columnconfigure(1, weight=1) # On place les 4 boutons dans chacune des cases button1 = Button(self, text="Button 1", bg="#22cbff", fg="white") button1.grid(column=0, row=0, sticky="nswe", padx=10, pady=10) button2 = Button(self, text="Button 2", bg="#22cbff", fg="white") button2.grid(column=1, row=0, sticky="nswe", padx=10, pady=10) button3 = Button(self, text="Button 3", bg="#22cbff", fg="white") button3.grid(column=0, row=1, sticky="nswe", padx=10, pady=10) button4 = Button(self, text="Button 4", bg="#22cbff", fg="white") button4.grid(column=1, row=1, sticky="nswe", padx=10, pady=10) # On dimensionne la fenêtre (400 pixels de large par 400 de haut). self.geometry("400x400") # On ajoute un titre à la fenêtre self.title("Positionnement de widgets via grid") # On crée notre fenêtre et on l'affiche window = MyWindow() window.mainloop() |
Voici le résultat produit par cet exemple de code.
Comme vous le constatez, le look commence à prendre une part importante du code, des paramètres étant souvent répliqués sur tous les appels de méthodes
(constructeur ou la méthode grid
, dans notre exemple).
Il est possible de simplifier ce code en déclarant les valeurs des paramètres communs dans un dictionnaire puis de repasser ce dictionnaire à chaque appel. Ainsi, si vous voulez changer les marges de vos boutons ou les couleurs, un seul endroit dans le code devra être impacté. Voici déjà un petit exemple de code et quelques explications suivront.
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 |
#!/usr/bin/python3 from tkinter import Tk, Button # On définit une classe qui dérive de la classe Tk (la classe de fenêtre). class MyWindow(Tk): def __init__(self): # On appelle le constructeur parent super().__init__() # Pour que la grille occupe 100% de la fenêtre, il faut que tkinter # connaisse à l'avance le nombre de lignes et le nombre de colonnes. # Le paramètre weight indique que chaque colonne (et chaque ligne) # à le même poids. L'une ne prendra pas l'avantage sur l'autre. self.grid_rowconfigure(0, weight=1) self.grid_rowconfigure(1, weight=1) self.grid_columnconfigure(0, weight=1) self.grid_columnconfigure(1, weight=1) # On définit deux dictionnaires pour les informations de styles button_dict = {"bg": "#22cbff", "fg": "white", "font": ("Arial", 25, "bold")} grid_dict = {"sticky": "nswe", "padx":10, "pady":10} # On place les 4 boutons dans chacune des cases en y injectant nos dictionnaires. button1 = Button(self, text="Button 1", **button_dict) button1.grid(column=0, row=0, **grid_dict) button2 = Button(self, text="Button 2", **button_dict) button2.grid(column=1, row=0, **grid_dict) button3 = Button(self, text="Button 3", **button_dict) button3.grid(column=0, row=1, **grid_dict) button4 = Button(self, text="Button 4", **button_dict) button4.grid(column=1, row=1, **grid_dict) # On dimensionne la fenêtre (400 pixels de large par 400 de haut). self.geometry("400x400") # On ajoute un titre à la fenêtre self.title("Positionnement de widgets via grid") # On crée notre fenêtre et on l'affiche window = MyWindow() window.mainloop() |
La syntaxe **button_dict
permet d'injecter les valeurs du dictionnaire dans l'appel du constructeur (ou d'une méthode).
Cela est possible, car le constructeur de la classe Button
accepte un nombre variable de paramètres nommés. Il en va de même pour la méthode grid
.
J'en ai profité pour rajouter une police de caractères pour les quatre boutons.
Afin de terminer sur un exemple un peu plus sérieux, je voulais vous proposer le code permettant d'afficher la calculatrice présentée au début de ce chapitre. Si l'idée vous en dit, vous pouvez d'abord remonter en haut de la page pour voir le visuel shouhaité et vous pouvez essayer de réaliser cette calculatrice par vous-même. Ensuite vous pourrez jeter un coup d'oeil à ma proposition ci-dessous ;-)
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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
#!/usr/bin/python3 from tkinter import Tk, Button, Label # On définit une classe qui dérive de la classe Tk (la classe de fenêtre). class MyWindow(Tk): def __init__(self): # On appelle le constructeur parent super().__init__() # On prépare une grille de six lignes et 4 colonnes # La première ligne cherchera à saturer l'espace restant dans la fenêtre. self.grid_rowconfigure(0, weight=1) self.grid_rowconfigure(1, weight=0) self.grid_rowconfigure(2, weight=0) self.grid_rowconfigure(3, weight=0) self.grid_rowconfigure(4, weight=0) self.grid_rowconfigure(5, weight=0) # On force la taille des colonne avec le paramètre uniform. "same_group" est texte libre. # Le fait que les 4 colonnes utilisent la même chaîne force la même taille. # Les paramètres weight servent uniquement pour que les 4 colonnes utilisent 100% de la # largeur de la fenêtre. self.grid_columnconfigure(0, weight=1, uniform="same_group") self.grid_columnconfigure(1, weight=1, uniform="same_group") self.grid_columnconfigure(2, weight=1, uniform="same_group") self.grid_columnconfigure(3, weight=1, uniform="same_group") # On définit quelques éléments de style pour nos boutons. default_button_style = { "bg": "#595959", "fg": "white", "highlightthickness": 0, "font": ("Arial", 25, "bold") } equal_button_style = default_button_style | {"bg": "#f05a2D"} default_button_grid = {"padx": 10, "pady": 10, "sticky": "nsew"} # Première ligne result_label = Label(self, text="0", anchor='e', # e => est bg="#a2af77", fg="white", font=("Arial", 25), padx=10) result_label.grid(column=0, row=0, columnspan=4, **default_button_grid) # Seconde ligne mc = Button(self, text="MC", **default_button_style) mc.grid(column=0, row=1, **default_button_grid) mplus = Button(self, text="M+", **default_button_style) mplus.grid(column=1, row=1, **default_button_grid) div = Button(self, text="/", **default_button_style) div.grid(column=2, row=1, **default_button_grid) mul = Button(self, text="*", **default_button_style) mul.grid(column=3, row=1, **default_button_grid) # Troisième ligne d7 = Button(self, text="7", **default_button_style) d7.grid(column=0, row=2, **default_button_grid) d8 = Button(self, text="8", **default_button_style) d8.grid(column=1, row=2, **default_button_grid) d9 = Button(self, text="9", **default_button_style) d9.grid(column=2, row=2, **default_button_grid) sub = Button(self, text="-", **default_button_style) sub.grid(column=3, row=2, **default_button_grid) # Quatrième ligne d4 = Button(self, text="4", **default_button_style) d4.grid(column=0, row=3, **default_button_grid) d5 = Button(self, text="5", **default_button_style) d5.grid(column=1, row=3, **default_button_grid) d6 = Button(self, text="6", **default_button_style) d6.grid(column=2, row=3, **default_button_grid) add = Button(self, text="+", **default_button_style) add.grid(column=3, row=3, **default_button_grid) # Cinquième ligne d1 = Button(self, text="1", **default_button_style) d1.grid(column=0, row=4, **default_button_grid) d2 = Button(self, text="2", **default_button_style) d2.grid(column=1, row=4, **default_button_grid) d3 = Button(self, text="3", **default_button_style) d3.grid(column=2, row=4, **default_button_grid) equal = Button(self, text="=", **equal_button_style) equal.grid(column=3, row=4, rowspan=2, **default_button_grid) # Cinquième ligne d0 = Button(self, text="0", **default_button_style) d0.grid(column=0, row=5, columnspan=2, **default_button_grid) dot = Button(self, text=".", **default_button_style) dot.grid(column=2, row=5, **default_button_grid) # On change la couleur de fond et les marges de la fenêtre. self.configure(bg="#333333", padx=10, pady=10) # On dimensionne la fenêtre (500 pixels de large par 200 de haut). self.geometry("400x500") # On ajoute un titre à la fenêtre self.title("Positionnement de widgets via grid") # On crée notre fenêtre et on l'affiche window = MyWindow() window.mainloop() |
|
, utilisé en ligne 34, sert à fusionner deux dictionnaires.
Cet opérateur est disponible sur les dictionnaires depuis la version 3.9 de Python.
Si vous utilisez une version antérieure, veuillez adapter le code.
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :