Gridlayout
Gridlayout
Das Grid-Layout basiert darauf, dass man Elemente in einer Art Tabelle anordnet. Es gibt Zeilen
und Spalten. Elemente werden mit grid()
in Zellen gelegt.
Per rowspan
und columnspan
können
mehrere Zeilen/Spalten von einem Widget überdeckt werden. Dann sorgt die Option
sticky
für das Ausdehnen. Dieses funktioniert mit Einschränkungen: Es müssen Elemente in den span-Zeilen
enthalten sein, damit die Geometrie sichtbar funktioniert. Alternativ können Zeilen und Spalten
konfiguriert werden, wie es in den nächsten Abschnitten gezeigt wird. Folgendes Beispiel verdeutlicht das:
import tkinter as tk
from tkinter import ttk
class A(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("300x200")
self._createWidgets()
def _createWidgets(self):
label00 = ttk.Label(self, relief=tk.GROOVE, text="abc")
label00.grid(row=0, column=0, sticky=tk.N+tk.S+tk.W+tk.E)
label01 = ttk.Label(self, relief=tk.GROOVE, text="def")
label01.grid(row=0, column=1, sticky=tk.N+tk.S+tk.W+tk.E)
label02 = ttk.Label(self, relief=tk.GROOVE, text='ghi')
label02.grid(row=0, column=2, rowspan=2, sticky=tk.N+tk.S+tk.W+tk.E)
# Kommentiert man dieses Label aus, dann wird auch label02
# nicht mehr über 2 Reihen angezeigt
label10 = ttk.Label(self, relief=tk.GROOVE, text='aaa')
label10.grid(row=1, column=0, sticky=tk.N+tk.S+tk.W+tk.E)
button = ttk.Button(self, text='Quit', command=self.destroy)
button.grid(row=2, column=0, columnspan=3, sticky=tk.W+tk.E)
if __name__ == '__main__':
window = A()
window.mainloop()
Parameter der Grid-Methode
Die Methode grid()
kennt folgende Parameter:
Parametername | Beschreibung |
---|---|
column | Spaltennummer, optional, default=0 |
columnspan | Anzahl der zusätzlich benötigten Spalten |
in_ | Das Widget wird in ein anderes Widget eingefügt |
ipadx, ipady | inneres Padding, zusätzlicher Platz links und rechts bzw. oben und unten innerhalb des Widgets. Diesen Platz sieht man nicht, wenn sich das Widget voll ausdehnt. |
padx, pady | äußeres Padding, zusätzlicher Platz links und rechts bzw. oben und unten. Diesen Platz sieht man auch, wenn das Widget sich voll ausdehnen darf. |
row | Zeilennummer, optional, default=0 |
rowspan | Anzahl der zusätzlich benötigten Zeilen |
sticky | Kombination aus tk.N, tk.S, tk.W und tk.E.
|
Das folgende Beispiel zeigt äußeres und inneres Padding:
label00
wird mit einem
Außenabstand zum Rand gezeichnet, obwohl es sich ausdehnen darf.
label01
darf
sich nicht ausdehnen, bekommt aber durch inneres Padding etwas mehr Innenraum.
label02
hingegen darf sich ausdehnen,
bekommt aber keinen Padding.
import tkinter as tk
from tkinter import ttk
class A(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("300x200")
self._createWidgets()
def _createWidgets(self):
self.rowconfigure(0, weight=1)
self.rowconfigure(1, weight=1)
self.rowconfigure(2, weight=1)
self.columnconfigure(0, weight=1)
self.columnconfigure(1, weight=1)
self.columnconfigure(2, weight=1)
label00 = ttk.Label(self, relief=tk.GROOVE, text="abc")
label00.grid(row=0, column=0, sticky=tk.N+tk.S+tk.W+tk.E, padx=10, pady=10)
label01 = ttk.Label(self, relief=tk.GROOVE, text="def")
label01.grid(row=0, column=1, ipadx=10, ipady=10)
label02 = ttk.Label(self, relief=tk.GROOVE, text='ghi')
label02.grid(row=0, column=2, sticky=tk.N+tk.S+tk.W+tk.E)
button = ttk.Button(self, text='Quit', command=self.destroy)
button.grid(row=1, column=0, columnspan=3)
if __name__ == '__main__':
window = A()
window.mainloop()
Gridlayout bei variabler Fenstergröße
Das eigentliche Ziel des Layouts ist es, die Widgets in einem Fenster so anzuordnen, dass
auch bei sich ändernder Fenstergröße die Widgets passend angeordnet werden. Das Ziel kann
leicht erreicht werden, wenn man mit rowconfigure()
und
columnconfigure()
Zeilen und Spalten eine Größe oder
alternativ ein Gewicht zuordnen. Gewicht bedeutet eine relative Breite oder Höhe.
import tkinter as tk
from tkinter import ttk
class A(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("300x200")
self._createWidgets()
def _createWidgets(self):
self.rowconfigure(0, weight=1)
self.rowconfigure(1, weight=1)
self.rowconfigure(2, weight=1)
self.columnconfigure(0, weight=1)
self.columnconfigure(1, weight=1)
self.columnconfigure(2, weight=1)
label00 = ttk.Label(self, relief=tk.GROOVE, text="abc")
label00.grid(row=0, column=0, sticky=tk.N+tk.S+tk.W+tk.E)
label01 = ttk.Label(self, relief=tk.GROOVE, text="def")
label01.grid(row=0, column=1, sticky=tk.N+tk.S+tk.W+tk.E)
label02 = ttk.Label(self, relief=tk.GROOVE, text='ghi')
label02.grid(row=0, column=2, rowspan=2, sticky=tk.N+tk.S+tk.W+tk.E)
# Unabhängig von den folgenden beiden Zeilen wird label02
# angezeigt
#label10 = ttk.Label(self, relief=tk.GROOVE, text='aaa')
#label10.grid(row=1, column=0, sticky=tk.N+tk.S+tk.W+tk.E)
button = ttk.Button(self, text='Quit', command=self.destroy)
button.grid(row=2, column=0, columnspan=3, sticky=tk.W+tk.E)
if __name__ == '__main__':
window = A()
window.mainloop()
Gridlayoutmethoden im Überblick
Funktion | Parameter | Rückgabewert | Beschreibung |
---|---|---|---|
w.columnconfigure(spalte, option=wert, …) | spalte: Nummer der Spalte, option=wert: Optionen für Spalte. Optionen können minsize (Mindestbreite), pad (Extraabstand in Pixeln) oder weight (Relative Breite Spalte) sein | - | Verändert die Konfiguration der Spalte |
w.grid_bbox(spalte1, reihe1, spalte2, reihe2) | spalte, reihe optionale Spalten- und Reihennummern | 4-Tupel | Gibt die Geometrie als Rechteck der Elemente in den angegebenen Spalten/Reihen zurück |
w.grid_forget() | - | - | w wird aus dem Grid gelöst, ist aber weiterhin da und kann per grid() wieder eingefügt werden |
w.grid_info() | - | Dictionary | Beschreibt die Lage eines Widgets, beispielsweise eines Labels, innerhalb des Grid-Layouts. Es werden alle Gridattribute mit ihren zugehörigen Werten zurückgeliefert. |
w.grid_location(x, y) | x, y Screenkoordinaten | 2-Tupel | Gibt die Zelle (spalte, reihe) zurück |
w.grid_propagate(prop) | True oder False | - | False: Das Widget teilt dem Fenster seine Größe nicht mit. Es wird also nicht in die Gridlayout-Größenberechnung miteinbezogen. Nützlich, wenn man festgelegte konstante Breiten/Höhen erreichen will. |
w.grid_remove() | - | - | wie grid_forget(), die Gridproportionen werden aber gemerkt und genutzt, wenn das Widget erneut eingefügt wird |
w.grid_size() | - | 2-Tupel | Gibt die Anzahl der Spalten und Reihen im Grid-Layout von w an |
w.grid_slaves(reihe, spalte) | reihe: optionale Reihe, spalte: optionale Spalte | Liste von Widgets | Gibt eine Liste von Widgets zurück, die sich in der Reihe/Spalte oder im gesamten Grid befinden |
w.rowconfigure(zeile, option=wert, …) | wie w.columnconfigure(…), nur mit Zeilen |
Packlayout
Packlayout
Beim Packlayout werden die Widgets von links nach rechts, oder oben nach unten mit
pack()
eingefügt. Der Parameter
fill
bestimmt die Richtung, in die sich ein
Widget ausdehnen darf, der Parameter expand
bestimmt,
ob das Widget extra Raum bekommen darf.
import tkinter as tk
from tkinter import ttk
class A(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("300x200")
self._createWidgets()
def _createWidgets(self):
label00 = ttk.Label(self, relief=tk.GROOVE, text="abc")
label00.pack(fill=tk.X)
label01 = ttk.Label(self, relief=tk.GROOVE, text="def")
label01.pack(fill=tk.Y, expand=tk.YES)
label02 = ttk.Label(self, relief=tk.GROOVE, text='ghi')
label02.pack(fill=tk.BOTH, expand=tk.YES)
button = ttk.Button(self, text='Quit', command=self.destroy)
button.pack()
if __name__ == '__main__':
window = A()
window.mainloop()
Hier darf sich label00
horizontal ausdehnen
und den verfügbaren Platz einnehmen, label01
und
label02
dürfen sich vertikal ausdehnen und
konkurrieren um den verfügbaren Platz. Während label01
sich nicht horizontal ausdehnen darf, darf label02
sich
in beide Richtungen erstrecken.
Parameter der Pack-Methode
Parametername | Beschreibung |
---|---|
after, before | Ändert die Pack-Reihenfolge. Dieses Widget wird nach / vor einem mit after / before anzugebenen Widget gepackt |
anchor | Gibt es mehrere Möglichkeiten für ein Widget, sich zu plazieren, kann mit anchor bestimmt werden, wohin es plaziert wird. tk.N, tk.S, tk.W, tk.E, tk.CENTER (default) |
expand | tk.YES, tk.NO (default), gibt an, ob das Widget seinen Freiraum nutzen darf |
fill | tk.NONE: nichts ausfüllen (default), tk.X: in X-Richtung den Platz füllen, tk.Y: in Y-Richtung, tk.BOTH: in X- und Y-Richtung den Platz ausfüllen |
in_ | Das Widget wird in ein anderes Widget eingefügt |
ipadx, ipady | Zusätzlicher Innenabstand |
padx, pady | Zusätzlicher Außenabstand |
side | TK.BOTTOM, tk.TOP (default), tk.LEFT, tk.RIGHT, Gibt die Seite an, an die das Widget gepackt wird |
Im folgenden Beispiel kommt es sehr auf die Reihenfolge der Labels an. Ändert man diese bei gleichen Pack-Parametern, dann ändert sich auch gleichzeitig die Ausdehnung:
import tkinter as tk
from tkinter import ttk
class A(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("300x200")
self._createWidgets()
def _createWidgets(self):
labelLeft = ttk.Label(self, relief=tk.GROOVE, text="LEFT")
labelLeft.pack(side=tk.LEFT, fill=tk.BOTH, expand=tk.YES)
labelRight = ttk.Label(self, relief=tk.GROOVE, text='RIGHT')
labelRight.pack(side=tk.RIGHT, fill=tk.BOTH, expand=tk.YES)
labelTop = ttk.Label(self, relief=tk.GROOVE, text="TOP")
labelTop.pack(side=tk.TOP, fill=tk.BOTH, expand=tk.YES)
labelCenter = ttk.Label(self, relief=tk.GROOVE, text="CENTER")
labelCenter.pack(anchor=tk.CENTER, fill=tk.BOTH, expand=tk.YES)
labelBottom = ttk.Label(self, relief=tk.GROOVE, text='BOTTOM')
labelBottom.pack(side=tk.BOTTOM, fill=tk.BOTH, expand=tk.YES)
if __name__ == '__main__':
window = A()
window.mainloop()
Packlayoutmethoden im Überblick
Funktion | Parameter | Rückgabewert | Beschreibung |
---|---|---|---|
w.pack_forget(), w.forget() | - | - | Entfernt das Widget aus dem Layout und vergisst dabei alle pack()-Parameter. |
w.info() | - | Dictionary mit allen möglichen Pack-Parametern | Gibt die aktuelle Pack-Konfiguration zurück. |
l.pack_propagate(prop) | True / False, tk.YES / tk.NO, 1/0 | - | True: Größe des Layout-Widgets ändert sich, wenn Widgets dem Layout hinzugefügt werden. (Default) Sonst nicht |
l.pack_slaves(), l.slaves() | - | Array von Widgets | Gibt ein Array zurück, das alle im Layout-Widget enthaltenen Widgets zurückliefert. |
Im folgenden Beispiel bekommt man nichts zu sehen, wenn der Parameter von
f.pack_propagate(1)
auf "0" gesetzt wird: Die Größe
des Frames ändert sich dann nicht, wenn die Labels hinzugefügt werden:
import tkinter as tk
from tkinter import ttk
class A(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("200x200")
self._createWidgets()
def _createWidgets(self):
f = ttk.Frame(self, relief=tk.SUNKEN)
f.pack()
f.pack_propagate(1)
label1 = ttk.Label(f, relief=tk.GROOVE, text="Label1")
label1.pack(padx=10, pady=10, side=tk.LEFT, expand=tk.YES, fill=tk.BOTH)
label2 = ttk.Label(f, relief=tk.GROOVE, text='Label2')
label2.pack(padx=10, pady=10, side=tk.LEFT, expand=tk.YES, fill=tk.BOTH)
if __name__ == '__main__':
window = A()
window.mainloop()
Gibt eine Liste von im Layout enthaltener Widgets aus:
import tkinter as tk
from tkinter import ttk
class A(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("200x200")
self._createWidgets()
def _createWidgets(self):
f = ttk.Frame(self, relief=tk.SUNKEN)
f.pack(side=tk.LEFT, expand=tk.YES, fill=tk.BOTH)
label1 = ttk.Label(f, relief=tk.GROOVE, text="Label1")
label1.pack(padx=10, pady=10, side=tk.LEFT, expand=tk.YES, fill=tk.BOTH)
label2 = ttk.Label(f, relief=tk.GROOVE, text='Label2')
label2.pack(padx=10, pady=10, side=tk.LEFT, expand=tk.YES, fill=tk.BOTH)
print(f.pack_slaves())
if __name__ == '__main__':
window = A()
window.mainloop()
Placelayout
Placelayout - absolut
Das Placelayout basiert darauf, dass Widgets mit place()
punktgenau positioniert werden. Tkinter
braucht hierfür nichts weiter zu berechnen und der Anwender ist für Größe und Position
der Widgets veantwortlich. Ferner gibt es die Möglichkeit, ein Widget relativ zur Größe
des Layout-Widgets zu positionieren.
Im folgenden Beispiel werden zwei Widgets positioniert. Eines oben links, das Andere unten rechts. Der
Parameter anchor
bestimmt, welcher Ort des Labels an den
angegebenen Punkt plaziert wird.
import tkinter as tk
from tkinter import ttk
class A(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("200x200")
self._createWidgets()
def _createWidgets(self):
label1 = ttk.Label(self, relief=tk.GROOVE, text="Label1")
label1.place(x=0, y=0)
label2 = ttk.Label(self, relief=tk.GROOVE, text="Label2")
label2.place(anchor=tk.SE, x=200, y=200)
if __name__ == '__main__':
window = A()
window.mainloop()
Placelayout - relativ
Neben der absoluten Positionierung bietet place()
die Möglichkeit,
ein Widget relativ zur Größe des Layout-Widgets zu positionieren.
import tkinter as tk
from tkinter import ttk
class A(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("200x200")
self._createWidgets()
def _createWidgets(self):
label1 = ttk.Label(self, relief=tk.GROOVE, text="Oben-Mitte")
label1.place(relx=0.5, y=0)
label2 = ttk.Label(self, relief=tk.GROOVE, text="Links-Mitte")
label2.place(x=0, rely=0.5)
label3 = ttk.Label(self, relief=tk.GROOVE,
text="Dieses Widget liegt in der Mitte")
label3.place(relx=0.5, rely=0.5, anchor=tk.CENTER)
label4 = ttk.Label(self, relief=tk.GROOVE, text="Unten-Mitte")
label4.place(relx=0.5, rely=1, anchor=tk.S)
if __name__ == '__main__':
window = A()
window.mainloop()
Und schließlich lässt sich noch die Größe des Widgets relativ zum Layout-Widget per
relwidth
, relheight
angeben:
import tkinter as tk
from tkinter import ttk
class A(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("200x200")
self._createWidgets()
def _createWidgets(self):
label1 = ttk.Label(self, relief=tk.GROOVE, text="Oben-Mitte")
label1.place(x=0, y=0, relwidth=0.5, relheight=0.5)
label2 = ttk.Label(self, relief=tk.GROOVE, text="Unten")
label2.place(relx=0.5, rely=1, relwidth=0.5, anchor=tk.S)
if __name__ == '__main__':
window = A()
window.mainloop()
Parameter der Place-Methode
Parametername | Beschreibung |
---|---|
anchor | Ort im Widget, der positioniert werden soll: tk.NW, …, tk.SE, tk.CENTER |
bordermode | Bei tk.INSIDE (default): Rahmen des Layout-Widgets wird berücksichtigt, tk.OUTSIDE: Rahmen im Layout-Widget bleibt komplett unberücksichtigt |
height, width | Absolute Höhe/Breite eines Widgets |
in_ | Angabe eines anderen Layout-Widgets, in welches das Widget eingefügt werden soll |
relheight, relwidth | Relative Höhe/Breite eines Widgets, 0..1 |
relx, rely | Relative Positionsangabe eines Widgets, 0..1 |
x, y | Absolute Positionsangabe eines Widgets |
Im folgenden Beispiel werden Label in einem Frame mit einem 10 Pixel Rahmen plaziert. Die Label unterscheiden
sich im bordermode
-Parameter:
import tkinter as tk
from tkinter import ttk
class A(tk.Tk):
def __init__(self):
super().__init__()
self.geometry("200x200")
self._createWidgets()
def _createWidgets(self):
f = ttk.Frame(self, relief=tk.RAISED, borderwidth=10)
f.pack(fill=tk.BOTH, expand=tk.YES)
label1 = ttk.Label(f, relief=tk.GROOVE, text="INSIDE")
label1.place(x=0, y=0, bordermode=tk.INSIDE)
label2 = ttk.Label(f, relief=tk.GROOVE, text="OUTSIDE")
label2.place(x=0, rely=0.5, bordermode=tk.OUTSIDE)
if __name__ == '__main__':
window = A()
window.mainloop()
Packlayoutmethoden im Überblick
Funktion | Parameter | Rückgabewert | Beschreibung |
---|---|---|---|
w.place_forget() | - | - | Entfert Widget aus dem Layout |
w.place_info() | - | Dictionary mit Place-Optionen | Gibt ein Dictionary mit allen Place-Optionen zurück. |
w.place_slaves() | - | Array mit Widgets | Gibt ein Array mit allen Widgets im aktuellen Layout zurück. |
Fensterlayout
Fensterlayout - kein echtes Layout
Das Fensterlayout ist kein echtes Layout, weil es sich nur auf die Verwaltung eines
einzelnen Fensters stützt. Zentrale Methode ist hier geometry()
mit
folgenden Möglichkeiten:
- w.geometry("100x200") - Fenster wird mit der Größe 100X200 Pixel erzeugt,
- w.geometry("100x200+20+50") - Fenster wird zusätzlich um x=+20 und y=+50 Pixel von der oberen linken Bildschirmecke positioniert.
Hat man mehrere Fenster und möchte deren Anordnung über- oder untereinander festlegen ("Stacking Order"), helfen die folgenden Funktionen:
- w.lower(belowThis=None) - Fenster wird nach unten (unter das Fenster
belowThis
) verschoben - w.tkraise(aboveThis=None) - Fenster wird nach oben (über das Fenster
aboveThis
) verschoben
Folgendes Programm stellt drei Toplevel-Fenster vor, die nach jeweils 500 ms neu übereinander angeordnet werden:
import tkinter as tk
from tkinter import ttk
class A(tk.Tk):
def __init__(self):
super().__init__()
self.geometry('400x200+100+100')
self._createWidgets()
def _createWidgets(self):
self._currentTop = 0
# drei Toplevel-Fenster mit unterschiedlichem Hintergrund
self.w1 = tk.Toplevel(self, background='green')
self.w2 = tk.Toplevel(self, background='red')
self.w3 = tk.Toplevel(self, background='blue')
# Geometrie, sollte überlappen
self.w1.geometry('100x100+120+120')
self.w2.geometry('100x100+150+150')
self.w3.geometry('100x100+180+180')
# Hauptfenster unter das erste Toplevel schieben
self.lower(belowThis=self.w1)
# Starte den Timer
self.after(500, self._lifter)
def _lifter(self):
self._currentTop = (self._currentTop + 1) % 3
if self._currentTop == 0:
self.w1.tkraise()
elif self._currentTop == 1:
self.w2.tkraise()
else:
self.w3.tkraise()
self.after(500, self._lifter)
if __name__ == '__main__':
window = A()
window.mainloop()