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


tkinter
Layout grid