Theorie: Geometrie-Manager & Binding



Geometrie-Manager

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:

Mehrere Widgets in Aktion

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:

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:

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)!


Binding

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!


Aufgaben

  1. Mach dich mit dem Geometrie-Manager 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.

  2. Oben steht: Verwende in einem Fenster immer nur einen Geometrie-Manager, nie mischen! Das wollen wir doch erst mal sehen: Schreibe das Skript 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?

  3. Baue in das Skript 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().

  4. Zeit für's Binding: Schreibe nach dem Muster der 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