Geometrie-Manager (auch: Layout-Manager) lösen folgendes Problem: man hat eine Reihe von Widgets, beispielsweise 4 Buttons, 1 Eingabezeile und 1 Textwidget, und man möchte die Widgets in einer bestimmten Art und Weise auf dem Bildschirm in einem Fenster anordnen. Die Antwort auf dieses Problem heißt Geometrie-Manager, Beispiel:
Hier das Python-Skript (pack.py, Auszug!) mit den Aufrufen des Geometrie-Managers pack()
:
root = Tk()
root.title('Chatten mit Python')
textfenster = Text(root,width=90)
textfenster.pack(fill=BOTH,expand=YES)
eingabe = Entry(root,width=60)
eingabe.pack(side=LEFT,fill=BOTH,expand=YES)
but1 = Button(root,text='Senden', command = senden)
but1.pack(side = LEFT,expand=NO)
but2 = Button(root,text='Verbinden', command = verbinde)
but2.pack(side = LEFT,expand=NO)
but3 = Button(root,text='Info', command = info)
but3.pack(side = LEFT,expand=NO)
but4 = Button(root,text='Beenden', command = ende)
but4.pack(side = LEFT,expand=NO)
Wie funktioniert der Geometrie-Manager? pack()
versucht, die Widgets in
einem Rechteck anzuordnen, dabei bestimmt die Option side
, wo das
nächste Widget platziert wird. Die Voreinstellung ist TOP
, fehlt also
die Angabe von side
wie in der folgenden Zeile
textfenster.pack(fill=BOTH,expand=YES)
so wird das Widget im Rechteck oben angeordnet. side
kann die folgenden
vier Werte annehmen: LEFT, RIGHT, TOP
und BOTTOM
(denke an die
vier Himmelsrichtungen). Zu den anderen beiden Optionen:
fill
kann die Werte Y, X, BOTH
und NONE
annehmen. Die Option bestimmt, ob und wie der Freiraum zwischen den einzelnen Widgets im Fenster gefüllt wird. Bei fill=NONE
passiert gar nichts, bei fill=BOTH
wird das Widget sowohl in x-Richtung (horizontal) als auch in y-Richtung (vertikal) so vergrößert, dass es den zur Verfügung stehenden Platz im Rechteck ausfüllt. expand
kann die Werte YES
bzw. NO
annehmen. Die Option bestimmt, was beim Vergrößern des Fensters mit den Widgets passiert. Im Beispiel oben: wird das chat-Fenster vergrößert, so wachsen Text-Widget und Eingabezeile mit (expand=YES
), während die Buttons so groß bleiben, wie sie sind (expand=NO
). Ein Geometrie-Manager wirkt entweder auf das Toplevel-Widget (im Beispiel
oben: root
) oder auf ein Frame-Widget. Ein Frame-Widget kann nicht
direkt auf dem Bildschirm gezeichnet werden, es dient vielmehr als Behälter
(Container) für andere Widgets. Beispiel (frame_02.py, Auszug!):
foben = Frame(root,width=500)
foben.pack(expand=YES, fill=BOTH)
textfenster = Text(foben,width=90,height=18)
textfenster.pack(fill=BOTH,expand=YES)
funten = Frame(root,width=500)
funten.pack(side=BOTTOM,expand=YES, fill=BOTH)
eingabe = Entry(funten,width=60)
eingabe.pack(side=LEFT,fill=BOTH,expand=YES)
Im Beispiel gibt es zwei Frames (anschaulich: Bildschirm-Bereiche): Das Frame-
Widget foben
enthält das Text-Widget. Unten ist das Frame-Widget funten
mit dem Entry-Widget (und den Buttons). Im Aussehen unterscheidet sich das
Skript frame_02.py
nicht von dem Beispiel pack.py
oben.
Neben dem pack
-Layout-Manager bietet Tkinter noch zwei weitere
Geometrie-Manager an:
place()
: erlaubt eine Anordnung der Widgets in absoluten Koordinaten, anschaulich in Pixeln.
grid()
: dieser Geometrie-Manager verwaltet ein Fenster wie eine Tabelle mit Zeilen (row
) und Spalten (column
), (schlechtes) Beispiel: but4.grid(row=1,column=4)
Die Zählung beginnt bei 0, nicht bei 1, und das Beispiel ist deshalb
schlecht, weil es die gemeinsame Verwendung des grid-
und des
pack
-Layout-Manager nahe legt, ein verhängnisvoller Fehler! Deshalb
hier die Faustregel:
Verwende in einem Fenster immer nur einen Geometrie-Manager, nie mischen!
Keine Regel ohne Ausnahme, aber dafür gibt es die Dokumentation (und das Beispiel-Skript frame_grid.py
)!
Der Mausklick auf einen Button ist ein Ereignis, das eine Aktion auslöst, Beispiel:
but1 = Button(root,text='Senden', command = senden)
Wird der Button but1
angeklickt, so wird die Aktion senden
ausgelöst, mit
dem Button ist also eine Aktion verknüpft. Nun will man bestimmte Aktionen
auch mit bestimmten Tastenkombinationen verbinden, beispielsweise die
Tastenkombinationen ALT + F4
zum Beenden des Programms. Diese Aktion ist
in tkinter schon per Voreinstellung an die Tastenkombinationen ALT + F4
gebunden, aber will man beispielsweise den Inhalt einer Eingabezeile beim
Drücken auf die <Return>
-Taste auslesen (mit Hilfe der get()
-Methode), so
muss man die <Return>
-Taste an die Eingabezeile binden, Beispiel (aus
dem Skript **bind.py**
):
eingabe = Entry(root,width=60)
## . . .
eingabe.bind('<Return>',callback)
eingabe.bind('<F1>',callback)
Der Methode callback
wird über einen Parameter mitgeteilt, dass sie auf ein
Ereignis (=Event
) reagieren soll. Tritt das betreffende Ereignis ein, so
wird die Ereignisbehandlungs-Routine callback
(=Event-Handler
) ausgeführt:
def callback(event):
tkMessageBox.showinfo('Senden','May be later!')
Binding sorgt also bei einem Widget für die Verknüpfung einer Methode (die Ereignisbehandlungs-Routine callback
) mit einem Ereignis (<Return>
-Taste gedrückt). Alternativ zu <Return>
kann man verwenden:
eingabe.bind('<Alt-y>',callback) ## Achtung: kleines y
eingabe.bind('<Control-E>',callback) ## Achtung: GROSSES E
eingabe.bind('<Key-Escape>',callback)
## Die Pfeiltasten:
textfenster.bind('<Shift-Up>',callback)
textfenster.bind('<Shift-Down>',callback)
## Jetzt die rechte Maustaste:
textfenster.bind('<Button-3>',callback)
Die letzten Beispiele beziehen sich auf das Text-Widget, die Informationen zu der korrekten Schreibweise der Tasten- und Mauskombinationen findet man in der Dokumentation!
pack()
vertraut! Überprufe vor allem: was passiert, wenn man das side
-Argument verändert (statt LEFT
RIGHT
oder BOTTOM
). Benutze für deine Versuche in IDLE das Skript pack.py
, verändere es vorsichtig und speichere es unter einem anderen Namen ab. pack.py
so um, das bei den Buttons der grid()
-Manager benutzt wird, etwa so: but1 = Button(root,text='Senden', command = senden) but1.grid(row=1,column=1) but2 = Button(root,text='Verbinden', command = verbinde) but2.grid(row=1,column=2) but3 = Button(root,text='Info', command = info) but3.grid(row=1,column=3) but4 = Button(root,text='Beenden', command = ende) but4.grid(row=1,column=4)Abspeichern (unter einem anderen Namen!) und ab geht's in die Katastrophe! Vergleiche deine Bemühungen mit dem Skript
frame_grid.py
, wo liegt der Unterschied? Welche Schlussfolgerung kann man daraus ziehen?pack.py
Frames ein und vergleiche das Ergebnis mit den Skripten frame_01.py
und frame_02.py
. Experimentiere auch hier mit dem Geometrie-Manager pack()
. callback
-Methode weitere Methoden, und binde sie an irgendwelche Widgets und Tastenkombinationen! Geht auch die linke Maus-Taste? Oder beide Maus-Tasten? Wie? Vergleiche deine Ergebnisse mit dem Skript bind.py
. → sp, 2023-07-28