Layout pack
Überblick
Der Layout-Manager „pack“ arbeitet ähnlich des GridBagLayouts. Nur werden die Elemente beim Einfügen positioniert.
Parameter der pack-Methode:
| Parameter | Werte | Erläuterung |
| after | Widget | Das Element wird nach dem Wigdet-Element gepackt. |
| anchor | "n", "ne", "e", "se", "s", "sw", "w", "nw","center" | Anchor à la GridbagLayout. Damit können Elemente, die nicht "gezoomt" werden, zum Beispiel nach links/oben positioniert werden. |
| before | Widget | Das Element wird vor dem Wigdet-Element gepackt. |
| expand | True / False |
wx bzw. wy à la GridbagLayout. Dieser Wert sollte nur beim Wert fill="y" oder fill="both" gesetzt werden. Label, Entry, Checkbox, Buttons brauch kein expand. |
| fill | "x", "y", "both", "none" | fill-Attribut à la GridbagLayout. Wenn man das Fenster vergrößert, wird das UI-Element mitvergrößert. |
| in | Widget | Das Element wird in das Wigdet-Element gepackt. Meist ein LabelFrame. Diese Eigenschaft wird meistens über den Konstruktor gesetzt. |
| ipadx | int | innerer Rand in Pixel |
| ipady | int | innerer Rand in Pixel |
| padx | int | äußerer Rand in Pixel |
| pady | int | äußerer Rand in Pixel |
| side | "left", "right", "top", "bottom" | Layout à la DockPanel von WPF. |
Erstes Tkinter-Beispiel
01:# coding=utf8
02: import tkinter
03:
04: class MyApp(tkinter.Frame):
05:
06: def __init__(self, master=None):
07: tkinter.Frame.__init__(self, master)
08: self.pack()
09: self.setGUI()
10:
11: def setGUI(self):
12: self.nameEntry = tkinter.Entry(self)
13: self.nameEntry.pack()
14:
15: self.inputui = tkinter.StringVar()
16: self.inputui.set("Ihr Name...")
17: self.nameEntry["textvariable"] = self.inputui
18:
19: self.bnOk = tkinter.Button(self)
20: self.bnOk["text"] = "Ok"
21: self.bnOk["command"] = self.quit
22: self.bnOk.pack(side="right")
23:
24: self.bnAction = tkinter.Button(self)
25: self.bnAction["text"] = "Action"
26: self.bnAction["command"] = self.onAction
27: self.bnAction.pack(side="right")
28:
29: def onAction(self):
30: s = self.inputui.get()
31: self.inputui.set( "Text: "+s )
# 1. Aufruf-Möglichkeit
root = tkinter.Tk()
app = MyApp(root)
app.mainloop()
# 2. Aufruf-Möglichkeit
root = tkinter.Tk()
root.title("Mein Fenster")
root.geometry("250x100")
app = MyApp(root)
app.mainloop()
Erläuterung des Beispiels
- 01: Sinnvoll bei der Verwendung der deutschen Umlauten
- 02: Einfügen des Moduls tkinter
- 04: Die Klasse braucht die Oberklasse Frame (à la JFrame)
- 06: Konstruktur
- 07: Aufruf des Konstrukturs der Oberfläche. Damit ist das Fenster sichtbar.
- 07: Das Fenster hat aber nur die Mindestgröße (1. Aufruf)
- Samples1-a.png
- 07: Mit dem Aufruf des Fensters in der zweiten Variante hat das Fenster nun eine feste Größe.
- Samples1-b.png
- 11: Aufbau der internen Struktur
- 12: Erzeugen einer Textbox.
- 13: Einfügen in den Layout-Manager.
- 15: Um auf das Entry zugreifen zu können, benötigt man einen Mittler (StringVar)
- 16: mit "set" setzt man einen Wert. Aber noch nicht im Textfeld.
- 17: Hier wird die Verknüpfung "Variable" zum Textfeld gesetzt (Hashtable-Prinzip).
- 19: Erzeugen eines Schalters
- 20: Setzen eines Textes.
- 21: ActionListener definieren. Wenn angeklickt, wird die interne Methode "quit" aufgerufen.
- 22: Einfügen in den Layout-Manager mit der Ausrichtung "rechts".
- 24: Erzeugen eines Schalters (bnAction)
- 25: Setzen eines Textes.
- 26: ActionListener definieren. Wenn angeklickt, wird die Methode "onAction" aufgerufen.
- 27: Einfügen in den Layout-Manager mit der Ausrichtung "rechts".
- 29: onClick-Methoden des zweiten Schalters
- 30: Holen des Inhaltes des Textfeldes
- 31: Setzen des Inhaltes
- Quellcode: Sample1.py
- Samples1-c.png (1. Aufruf)
- Samples1-d.png (2. Aufruf)
Empfehlung
Im Bild Samples1-e.png sieht man, dass das Textfeld nicht komplett im Fenster positioniert ist.
Der Layout-Manager ist mit gelben Hintergrund dargestellt.
Auch eine Vergrößerung des Fensters bewirkt nichts.
Abhilfe:
Der Aufruf des pack-Layouts im Konstruktur wird erweitert.
self.pack(expand=True, fill="both", padx="5", pady="5")
Ergebnis:
Samples1-f.png
Nun müssen natürlich auch die UI-Elemente angepasst werden:
mit
self.nameEntry.pack(fill="x", padx="5", pady="5")
wird das Textfeld komplett im Fenster ausgefüllt.
Ein Vergrößern vergrößert auch das Textfeld!
Bild Samples1-g.png
Optimale Lösung
Damit man es einfacher hat sollte man UI-Elemente mit dem Frame-Element zusammenfügen.
Ein "Frame-Element" ist nichts anderes als ein "JPanel" bzw. ein DockPanel.
1) Im Konstruktur verwendet man immer
self.pack(expand=True, fill="both", padx="5", pady="5")
2) Jede "Zeile" im Fenster fügt man in einem Frane ein
inputframe = tkinter.Frame(
self
) # frame wird eingefügt in self
inputframe.background = "red" #"#FF0000"
inputframe.pack(fill="x", side="top" )
self.label1 = tkinter.Label(i
nputframe
) # label wird eingefügt in frame
self.label1["text"] = "Eingabe"
self.label1.config(foreground = "blue" )
self.label1.config(background = "#FFFF00")
self.label1.pack(side="left")
self.inputui = tkinter.Entry(
inputframe
) # Entry wird eingefügt in frame
self.inputui.pack(fill="x",padx="5",pady="5")
Ergebnis:
Bild Samples1-h.png
Bei Vergrößerung:
Bild Samples1-i.png
3) Jeder "Frame" mit fill="both" sollte erst am Schluss eingefügt werden
Der Sinn liegt im DockPanel-Prinzip von WPF.
Beispiel:
Fügt man eine Zeile mit einer Entry ein.
Fügt man eine Zeile mit einer Listbox ein (fill="both")
Fügt man einen Button ein.
Durch die Listbox hat der Schalter keinen Platz mehr.
Erst ab einer bestimmten Größe ist er wieder sichtbar.
Editor mit Frames und Scrollbars
Editor
In der oberen Abbildung sieht man die drei Frames:
- rot: Eingabezeile
- grün: Texteditor
- blau: "Schalterleiste"
Quellcode des Editors
# coding=utf8
import tkinter
from tkinter import messagebox
class MyApp(tkinter.Frame):
def __init__(self, master=None):
tkinter.Frame.__init__(self, master)
self.pack(expand=True, fill="both") # dialog zoomt
self.setGUI()
def setGUI(self):
# pack ist wie das DockPanel in WPF
# erst muessen die "normalen" UI-Elemente eintgetragen werden
# am Schluss werden die UI-Elemente eingetragen, die fill="both" haben
inputframe = tkinter.Frame(self) # „JPanel“ fuer die Eingabe
inputframe.config(background = "red") #"#FF0000"
inputframe.pack(fill="x", side="top" ) # ohne expand, da fill=“x“
self.label1 = tkinter.Label(inputframe, fg="blue", bg="#FFFF00")
self.label1["text"] = "Eingabe"
self.label1.pack(side="left")
self.inputui = tkinter.Entry(inputframe)
self.inputui.pack(expand=True,fill="x",padx="5",pady="5")
self.var_name = tkinter.StringVar()
self.var_name.set("Ihr Name...")
self.inputui["textvariable"] = self.var_name
buttonframe = tkinter.Frame(self) # „JPanel“ fuer die Eingabe
buttonframe.config(background = "blue") #"#FF0000"
# ohne expand, da fill=“x“
buttonframe.pack(fill="x", side="bottom" )
self.bnEsc = tkinter.Button(buttonframe)
self.bnEsc["text"] = "Beenden"
self.bnEsc["command"] = self.quit
self.bnEsc.pack(padx="5", side="right")
self.bnAction = tkinter.Button(buttonframe)
self.bnAction["text"] = "Action"
self.bnAction["command"] = self.onAction
self.bnAction.pack(side="right")
# nun ein Editor mit fill=both
editorframe = tkinter.Frame(self) # „JPanel“ fuer den Editor
editorframe.config(background = "green") #"#FF0000"
editorframe.pack(expand=True,fill="both", side="top" )
sbx = tkinter.Scrollbar(editorframe, orient="horizontal")
sbx.pack(fill="x", side="bottom")
sby = tkinter.Scrollbar(editorframe)
sby.pack(fill="y", side="right")
self.editor = tkinter.Text(editorframe)
self.editor.config(wrap="none") # wrap="word" word char
self.editor.pack(expand=True, fill="both", padx="5",pady="5")
self.editor["xscrollcommand"] = sbx.set
sbx["command"] = self.editor.xview
self.editor["yscrollcommand"] = sby.set
sby["command"] = self.editor.yview
def onAction(self):
str = self.var_name.get()
messagebox.showinfo( "Hello Python", str)
self.editor.insert("end",str+"\r\n")
root = tkinter.Tk()
root.title("Sample2.py (Editor)")
root.geometry("450x400")
app = MyApp(root)
app.mainloop()