web-dev-qa-db-fra.com

Tkinter Grid: Comment positionner les widgets pour qu'ils ne soient pas collés ensemble

J'essaie de créer deux widgets Label qui se trouvent dans les coins supérieur gauche et supérieur droit de mon interface de test. Le problème est que les widgets sont collés ensemble et j'aimerais qu'il y ait un espace entre eux.

Lors de mes recherches, je suis tombé sur des suggestions pour utiliser les options sticky, padx et pady. Mais peu importe les arguments que je transmets à .grid (), il semble que je ne puisse pas créer d'espace entre mes widgets. Je comprends que, quel que soit le nombre de colonnes et de lignes entre deux widgets, si ces lignes/colonnes sont vides, c’est comme si elles n’existaient pas et les widgets semblent collés ensemble.

À l'aide de la méthode .grid (), comment puis-je positionner les widgets afin qu'ils ne soient pas collés ensemble?

Voici mon code jusqu'à présent:

#!/usr/bin/python
from Tkinter import *

class MyApp:
    def __init__(self, parent):
        self.myParent = parent
        self.main_container = Frame(parent)
        self.main_container.grid(row=0, rowspan=2, column=0, columnspan=4)

        self.top_frame = Frame(self.main_container)
        self.top_frame.grid(row=0, column=0, columnspan=4)

        self.top_left = Frame(self.top_frame)
        self.top_left.grid(row=0, column=0, columnspan=2)

        self.top_right = Frame(self.top_frame)
        self.top_right.grid(row=0, column=2, columnspan=2)

        self.bottom_frame = Frame(self.main_container)
        self.bottom_frame.grid(row=2, column=0, columnspan=4)

        self.top_left_label = Label(self.top_left, text="Top Left")
        self.top_left_label.grid(row=0, column=0, sticky='W', padx=2, pady=2)

        self.top_right_label = Label(self.top_right, text="Top Right")
        self.top_right_label.grid(row=0, column=4, sticky='E', padx=2, pady=2)

        self.text_box = Text(self.bottom_frame, height=5, width=40)
        self.text_box.grid(row=0, column=0)

root = Tk()
root.title("Test UI")
myapp = MyApp(root)
root.mainloop()

~ ~ Mise à jour ~ ~

J'ai essayé ce qui suit mais cela n'a pas fonctionné:

    self.top_left = Frame(self.top_frame)
    self.top_left.grid(row=0, column=0, columnspan=2)
    for c in range(2):
        self.top_left.columnconfigure(c, weight=2)

    self.top_right = Frame(self.top_frame)
    self.top_right.grid(row=0, column=2, columnspan=2)
    for c in range(2):
        self.top_right.columnconfigure(c, weight=2)
17
Dirty Penguin

Vous devez utiliser grid_columnconfigure pour donner du poids aux colonnes du milieu. Avec plus de poids que les autres colonnes, elles s'agrandiront et se contracteront pour occuper tout l'espace supplémentaire dont vous disposez. Cependant, dans votre cas, il n’est vraiment pas nécessaire d’utiliser la grille. Tout dans votre interface graphique est aligné sur des côtés particuliers pour lesquels --variable - est un ajustement plus naturel. 

Je vais vous montrer comment utiliser à la fois Pack et Grille, en commençant par Pack car c'est le plus simple. Même si vous insistez pour utiliser pack, lisez la section suivante pour comprendre comment décomposer un gros problème de présentation en de nombreux problèmes de présentation plus petits. 

Diviser et conquérir

La meilleure façon de faire la mise en page dans Tkinter est "diviser pour régner". Commencez par les widgets les plus externes et obtenez-les exactement comme vous le souhaitez. Ensuite, abordez chacun de ces objectifs un à la fois.

Dans votre cas, vous avez un widget le plus externe - le conteneur principal. Comme il s'agit du seul widget de la fenêtre, pack est le moyen le plus simple de l'obtenir pour remplir l'intégralité du conteneur. Vous pouvez également utiliser la grille, mais cela nécessite un peu de travail supplémentaire:

self.main_container = Frame(parent. background="bisque")
self.main_container.pack(side="top", fill="both", expand=True)

Il est utile de donner temporairement des couleurs distinctives à vos cadres afin que vous puissiez les visualiser au fur et à mesure de votre développement. Ajoutez simplement le code ci-dessus à votre __init__, exécutez votre application, puis redimensionnez la fenêtre pour voir que le cadre principal grossit et diminue correctement.

Ensuite, vous avez deux cadres - top_frame et bottom_frame. Par leur nom et par la manière dont vous avez essayé d’utiliser la grille, je suppose qu’ils devraient remplir l’interface graphique dans la direction x. En outre, je suppose que le cadre supérieur est une sorte de barre d’outils et que le cadre inférieur est la véritable "partie principale" de votre interface graphique. Ainsi, faisons en sorte que le widget du bas occupe tout l'espace supplémentaire. 

Puisque ceux-ci sont empilés les uns sur les autres, grid est à nouveau le choix naturel. Ajoutez le code suivant - et uniquement le code suivant - pour vous assurer que ces zones occupent les parties de la fenêtre que vous attendez et qu'elles ont le comportement de redimensionnement approprié. 

    self.top_frame = Frame(self.main_container, background="green")
    self.bottom_frame = Frame(self.main_container, background="yellow")
    self.top_frame.pack(side="top", fill="x", expand=False)
    self.bottom_frame.pack(side="bottom", fill="both", expand=True)

Viennent ensuite les cadres pour les étiquettes. Encore une fois, ceux-ci occupent un espace le long des bords de leur conteneur, donc pack a tout son sens. Et encore une fois, ajoutez juste le morceau de code suivant, lancez votre programme et assurez-vous que les choses sont toujours en train de redimensionner et de remplir les bonnes parties de la fenêtre:

    self.top_left = Frame(self.top_frame, background="pink")
    self.top_right = Frame(self.top_frame, background="blue")
    self.top_left.pack(side="left", fill="x", expand=True)
    self.top_right.pack(side="right", fill="x", expand=True)

Ensuite, vous avez vos étiquettes de "coin". Encore une fois, comme le conteneur n’est qu’une rangée de widgets, l'option pack facilite les choses. Puisque vous voulez les étiquettes dans les coins, nous allons définir l'attribut pack un peu différent pour chacun:

    self.top_left_label = Label(self.top_left, text="Top Left")
    self.top_right_label = Label(self.top_right, text="Top Right")
    self.top_left_label.pack(side="left")
    self.top_right_label.pack(side="right")

Enfin, vous avez le widget de texte. Il remplit tout le cadre du bas, alors encore une fois - variable - notre ami:

    self.text_box = Text(self.bottom_frame, height=5, width=40, background="gray")
    self.text_box.pack(side="top", fill="both", expand=True)

pack ou grille?

Vous avez utilisé grid pour votre code d'origine et vous avez demandé comment le corriger. Pourquoi ai-je utilisé pack pour mes exemples? 

Lorsque vous utilisez sticky, toutes les options de configuration peuvent être regroupées en un seul appel. Avec l'option pack, en plus de placer les widgets dans leurs conteneurs, vous devez également veiller à donner du poids à vos différentes colonnes et rangées afin qu'elles soient redimensionnées correctement. Lorsque vous empilez simplement des widgets ou que vous les alignez sur une ligne, l'option pack est beaucoup plus facile à utiliser. 

Dans mes interfaces graphiques, j'utilise presque toujours une combinaison de grid et pack. Ils sont à la fois puissants et excellents à différentes choses. La seule chose à retenir - et ceci est crucial - est que vous ne pouvez pas les utiliser dans le même parent. En utilisant votre code comme exemple, vous ne pouvez pas utiliser grid pour la trame top_left et pack pour la trame top_right, car elles partagent le même parent. Vous pouvez, cependant, les mélanger dans la même application.Encore une fois, en utilisant la grille.

Ok, alors peut-être que vous voulez vraiment utiliser pack: c'est peut-être une tâche scolaire, ou peut-être voulez-vous simplement vous concentrer sur un gestionnaire de géométrie à la fois. C'est super. Voici comment je le ferais. Encore une fois, vous devez diviser et conquérir:

Commencez avec le cadre principal. Remplacez la déclaration où nous emballons le conteneur principal avec les lignes suivantes. Notez que nous devons configurer les lignes et les colonnes sur le parent, pas le cadre que nous avons créé.

self.main_container.grid(row=0, column=0, sticky="nsew") self.myParent.grid_rowconfigure(0, weight=1) self.myParent.grid_columnconfigure(0, weight=1)

self.top_frame.grid(row=0, column=0, sticky="ew") self.bottom_frame.grid(row=1, column=0,sticky="nsew") self.main_container.grid_rowconfigure(1, weight=1) self.main_container.grid_columnconfigure(0, weight=1)

self.top_left.grid(row=0, column=0, sticky="w") self.top_right.grid(row=0, column=2, sticky="e") self.top_frame.grid_columnconfigure(1, weight=1)

    self.top_left_label.grid(row=0, column=0, sticky="w")
    self.top_right_label.grid(row=0, column=0, sticky="e")

self.text_box.grid(row=0, column=0, sticky="nsew") self.bottom_frame.grid_rowconfigure(0, weight=1) self.bottom_frame.grid_columnconfigure(0, weight=1)

pack nécessite quelques lignes de code supplémentaires, mais c'est le bon choix lorsque vous avez réellement une grille. Dans votre cas, vous aviez déjà subdivisé votre interface graphique en sections. Ainsi, même si le résultat final était une grille, chaque section était soit placée au-dessus ou au-dessous d'une autre, soit sur les bords gauche ou droit. Dans ces cas, pack est un peu plus facile à utiliser car vous n'avez pas à vous soucier de la pondération des lignes et des colonnes. 

As you can see, grid requires a few more lines of code but it's the right choice when you really do have a grid. In your case you had already sub-divided your GUI into sections, and so even though the end result was a grid, each section was either packed on top or below another, or to the left or right edges. In these cases, pack is a bit easier to use since you don't have to worry about row and column weights.

64
Bryan Oakley

Voici un moyen facile de le faire:

  • Commencez par concevoir votre interface graphique sur papier ou à l’aide de tout outil qui vous convient.
  • Utiliser le gestionnaire de disposition de grille
  • Où que vous souhaitiez créer un espace vide, utilisez columnspan ou rowspan property de la présentation, en combinaison avec la propriété sticky Columnspan ou rowspan vous permet d'affecter plusieurs cellules de la grille à un composant d'interface graphique. forcer cet élément à coller d'un côté et laisser un espace vide de l'autre côté
1
Farhad Maleki

Si vous ajoutez bg = "red" aux constructeurs top_left et top_right, vous verrez comment la variable Frames reste bloquée au centre, même à l'aide de l'option sticky. S'ils ne contiendront rien d'autre qu'un widget, je vous recommande de ne pas les utiliser.

#!/usr/bin/python
from Tkinter import *

class MyApp:
    def __init__(self, parent):
        self.top_left_label = Label(parent, text="Top Left")
        self.top_left_label.grid(row=0, column=0, padx=2, pady=2, sticky=N+S+W)

        self.top_right_label = Label(parent, text="Top Right")
        self.top_right_label.grid(row=0, column=1, padx=2, pady=2, sticky=N+S+E)

        self.text_box = Text(parent, height=5, width=40)
        self.text_box.grid(row=1, column=0, columnspan=2)

root = Tk()
root.title("Test UI")
myapp = MyApp(root)
root.mainloop()
1
A. Rodas

Mieux vaut tard que jamais ;)

La "grille" de Tkinter veut tout mettre en place. C'est pourquoi vous ne pouvez pas sauter de cellules. Vous devez spécifier les largeurs, puis ancrer le texte. J'ai ajouté de la couleur pour aider à montrer les cellules. Je mets le bd dans le cadre. Ensuite, je devais donner de la largeur aux cellules gauche et droite pour que la grille leur donne du poids. Ensuite, ancrez le texte Ouest ou Est. Je ne suis pas sûr de la raison pour laquelle j'ai dû ajouter 2 espaces supplémentaires pour chaque cellule, mais j'ai pensé qu'il s'agissait d'un problème de police de caractères. 

Je crois que Rodas est plus simple et plus propre, mais j’ai essayé de rester dans les paramètres que vous demandez. 

Pack est plus facile et plus rapide pour les petites choses.

from tkinter import *

class MyApp:
    def __init__(self, parent):
        self.myParent = parent

        self.main_container = Frame(parent, bg="green")
        self.main_container.grid()

        self.top_frame = Frame(self.main_container)
        self.top_frame.grid()

        self.top_left = Frame(self.top_frame, bd=2)
        self.top_left.grid(row=0, column=0)

        self.top_right = Frame(self.top_frame, bd=2)
        self.top_right.grid(row=0, column=2)

        self.top_left_label = Label(self.top_left, bd=2, bg="red", text="Top Left", width=22, anchor=W)
        self.top_left_label.grid(row=0, column=0)

        self.top_right_label = Label(self.top_right, bd=2, bg="blue", text="Top Right", width=22, anchor=E)
        self.top_right_label.grid(row=0, column=0)

        self.bottom_frame = Frame(self.main_container, bd=2)
        self.bottom_frame.grid(row=2, column=0)

        self.text_box = Text(self.bottom_frame, width=40, height=5)
        self.text_box.grid(row=0, column=0)

root = Tk()
root.title("Test UI")
myapp = MyApp(root)
root.mainloop()
1
James

Si vous avez besoin d'un contrôle total sur le placement de votre composant d'interface graphique, essayez de placer la disposition; Une approche très simple consiste à organiser vos composants dans plusieurs cadres, chacun avec une disposition de grille ou de pack, puis à organiser tous ces cadres à l'aide d'une disposition en lieux.

0
Farhad Maleki