Mithilfe von Layout Managern können wir Widgets innerhalb der GUI platzieren. Ein Layout Manager, den man hierfür besonders zu Beginn sehr häufig verwendet, ist der Layout Manager Pack. Welche Aufgabe dieser hat und wie man damit die Widgets an beliebigen Stellen platzieren kann, wirst du in diesem Beitrag erfahren.
Inhaltsverzeichnis
1. Was wirst du in diesem Beitrag lernen?
In diesem Beitrag werden wir uns den Tkinter Pack Layout Manager genauer ansehen. Da dies ein größeres Thema ist, sehen wir uns zunächst einmal kurz die Gliederung dieses Beitrags an.
Im ersten Schritt lernst du, welche Aufgaben ein Layout Manager allgemein hat und daraufhin erhältst du einen kurzen Überblick über die Basis Layout Manager, die Tkinter zur Verfügung stellt.
Danach sehen wir uns den Tkinter Pack Layout Manager im Detail an und machen uns mit dessen grundlegender Arbeitsweise vertraut. Abschließend lernst du, wie du die Platzierung der Widgets über den Pack Layout Manager mithilfe von Schlüsselwortargumenten beeinflussen kannst, sodass die Widgets dort platziert werden, wo du sie haben möchtest.
2. Was ist ein Layout Manager und welche Aufgabe hat er?
Alle Begriffe stehen allerdings für das Gleiche, denn sie beschreiben den Mechanismus, der für die Organisation, Position und Ausrichtung der Widgets im Tkinter Hauptfenster verantwortlich ist.
In Tkinter existieren die drei Basis Layout Manager pack, grid und place. Wir werden uns in diesem Einsteiger Crashkurs die wichtigsten Grundlagen des Tkinter Pack Layout Managers und etwas später die des Grid Layout Managers ansehen.
Bei jedem der Layout Manager handelt es sich nämlich um ein komplexes Thema, welches sich nicht vollständig in diesem Beitrag unterbringen lässt.
Ich empfehle dir deshalb, einen Blick auf unseren Tkinter Masterkurs zu werfen, in welchem wir jedem Layout Manager ein komplett eigenes Modul gewidmet haben. Dort lernst du dieses Thema wesentlich ausführlicher und Schritt für Schritt kennen. Zudem findest du darin Praxisprojekte, in welchen du dein Wissen direkt vertiefen kannst.
3. Der Tkinter Pack Layout Manager
Wie bereits erwähnt, geht es in diesem Beitrag um den Tkinter Pack Layout Manager, welchen wir bereits in unseren bisherigen Beispielen wiederholt verwendet haben.
Denn bisher haben wir immer erst ein Hauptfenster erstellt und in diesem bis zum jetzigen Zeitpunkt jedes Mal lediglich ein einzelnes Label Widget mithilfe des Pack Layout Managers platziert. Wir haben es also mit der Methode pack in die GUI gesetzt, wie das auch im folgenden Beispiel erkennbar ist:
Dabei haben wir ein Hauptfenster mit der Größe 400 x 400 Pixel erzeugt und ein Label Widget, das den Text Label 1 trägt, hinzugefügt, welches mithilfe von pack in der GUI platziert wurde.
4. Die Hintergrundfarbe des Labels mit bg ändern
Damit man die grundlegende Funktionsweise des Pack Layout Managers besser sehen kann, werden wir die Hintergrundfarbe des Labels verändern. Dafür gibt es beim Label Widget eine weitere Option, die sich „bg“ nennt und für „background“ steht.
Diese können wir als Schlüsselwortargument setzen, indem wir im Code „bg“ schreiben und als Wert die gewünschte Farbe zuweisen:
label1= tk.Label(root, text="Label 1", bg=)
label1.pack()
Dabei können wir entweder einen hexadezimalen Farbcode in Form eines Strings angeben oder den englischen Farbnamen. Möchten wir also, dass der Hintergrund grün ist, geben wir als String "green" an:
label1= tk.Label(root, text="Label 1", bg="green")
label1.pack()
Wenn wir das Programm an dieser Stelle ausführen, siehst du, dass das Label nun tatsächlich einen grünen Hintergrund besitzt.
5. Die Aufgabe des Tkinter Pack Layout Managers
Wie wir auch sehen können, wird es zentriert ganz oben im Fenster platziert. Doch woher weiß das Widget, dass es dort sein muss? Schließlich hätte es auch ganz unten in der Mitte oder rechts oben in der Ecke Platz.
Das Widget weiß, wo es platziert werden muss aufgrund des Layout Managers Pack. Dies ist auch der Grund, weshalb wir jedes Widget erst mit der pack-Methode zur GUI hinzufügen müssen, damit man es sehen kann. Ohne diesen Layout Manager wüsste das Widget also nicht, wo es platziert werden muss.
Stell dir nun einmal vor, wir erweitern das Programm noch um ein zweites Widget, das wir beispielhaft label2 nennen. Auch dieses soll in die root und der Text soll diesmal „Label 2“ lauten. Als Hintergrund für Label 2 legen wir die Farbe Rot fest:
label1= tk.Label(root, text="Label 1", bg="green")
label1.pack()
label2 = tk.label(root, text="Label 2", bg="red")
Auch dieses müssen wir natürlich jetzt mithilfe von pack in der GUI platzieren:
label1.pack()
label2 = tk.label(root, text="Label 2", bg="red")
label2.pack()
Nachdem wir das Programm noch einmal ausgeführt haben, sehen wir die folgende Ausgabe:
Das zweite Label hängt jetzt genau unter Label 1, wofür der Tkinter Pack Layout Manager verantwortlich ist.
Ein Layout Manager ist also nicht nur für die Widgets im Einzelnen wichtig, sondern auch für die Widgets in ihrer Gesamtheit.
Denn jedes Mal, wenn wir ein weiteres Widget zur GUI hinzufügen, muss der Pack Layout Manager alles umorganisieren, sodass alle Widgets sauber platziert sind und sich beispielsweise nicht überlappen.
Wie du sehen kannst, sind Layout Manager von großer Bedeutung. Dabei ist in Tkinter Pack einer der am meist genutzten, da er die Widgets auf einfache Art vollkommen automatisch im Fenster platziert basierend auf dem Platz, der darin noch verfügbar ist.
6. Das Schlüsselwortargument side
Wir können dem Pack Layout Manager jetzt auch noch mitteilen, dass er gewisse Widgets an bestimmte Positionen setzen soll. Denn nur so wird uns ermöglicht, GUIs zu bauen, die so aussehen, wie wir uns das vorstellen.
Das funktioniert mithilfe gewisser Schlüsselwortargumente, die wir beim pack-Methodenaufruf mitübergeben können. Aktuell werden die beiden Labels oben in der Mitte platziert, weil standardmäßig der Wert des sogenannten Schlüsselwortarguments „side“ auf top gesetzt ist.
Der pack-Methode könnten wir jetzt also auch das Schlüsselwortargument side direkt mitübergeben und ebenfalls auf top setzen:
label1 = tk.Label(root, text="Label 1", bg="green")
label1.pack(side="top")
Bei Label 2 machen wir es genauso:
label2 = tk.Label(root, text="Label 2", bg="red")
label2.pack(side="top")
Wenn wir das Programm jetzt noch einmal ausführen, sehen wir die exakt gleiche Ausgabe wie zuvor:
Was bedeutet "top"?
Denn standardmäßig, also wenn wir für die Option side selbst keinen Wert festlegen, ist der Wert immer auf top gesetzt. „top“ bedeutet aber nicht nur, dass das Label oben in der Mitte platziert wird, sondern es geschieht noch etwas anderes sehr Wichtiges dabei.
Tkinter allokiert nämlich für jedes so platzierte Widget ebenfalls die gesamte Breite der Applikation.
Was bedeutet das? Wie wir sehen, wird Label 1 zentriert, also mittig in der GUI angezeigt. Insgesamt ist für dieses Label allerdings auch der komplette horizontale Platz reserviert, welchen ich auf der folgenden Abbildung durch einen roten Kasten visualisiert habe:
Gleiches gilt für Label 2. Auch für dieses ist der gesamte horizontale Platz rechts und links vom Label reserviert. Das bringt uns zu einer weiteren Frage: Warum nutzen Label 1 und Label 2 nicht den kompletten Platz, der ihnen zur Verfügung steht?
Ganz einfach: Weil ein Label immer nur so viel Platz beansprucht, wie es benötigt, um den Inhalt des Labels darstellen zu können.
Wir schließen das Fenster nun also noch mal und passen den Text von Label 1 an, indem wir „beansprucht nun mehr Platz“ dazu schreiben:
label1 = tk.Label(root, text="Label 1 beansprucht nun mehr Platz", bg="green")
label1.pack(side="top")
Führen wir das Programm jetzt noch einmal aus, sehen wir, dass das Label nun in die Breite gewachsen ist, weil es jetzt mehr Platz benötigt, um den Text darstellen zu können:
Aus diesem Grund ist der gesamte horizontale Platz für das Label reserviert.
Ändern wir an dieser Stelle den Text aber wieder auf „Label 1“:
label1 = tk.Label(root, text="Label 1", bg="green")
label1.pack(side="top")
7. Das Schlüsselwortargument fill
Nun gibt es ein weiteres Schlüsselwortargument, mithilfe dessen wir dafür sorgen können, dass das Label trotz des kleinen Textes den kompletten horizontalen Platz füllt.
Es handelt sich dabei um das Schlüsselwortargument „fill“.
Wir können also der pack-Methode von Label 1 auch noch das Schlüsselwortargument „fill“ mitübergeben:
label1 = tk.Label(root, text="Label 1", bg="green")
label1.pack(side="top", fill=)
Dem Schlüsselwortargument fill weisen wir nun den Wert "x" als String zu:
label1 = tk.Label(root, text="Label 1", bg="green")
label1.pack(side="top", fill="x")
Wenn wir das Programm an dieser Stelle erneut ausführen, sehen wir, dass sich das Label 1 jetzt über die komplette Breite erstreckt, die es zur Verfügung hat:
Wir wählen deshalb "x", weil wir das Programm damit anweisen, den gesamten allokierten Platz in x-Richtung zu füllen. Neben x können wir auch y zuweisen. Damit geben wir vor, dass das Programm den kompletten Platz in y-Richtung füllen soll, also den Platz, den es vertikal allokiert hat.
Dafür ersetzen wir in unserem Code zunächst das "x" durch ein "y" und führen das Programm erneut aus:
label1.pack(side="top", fill="y")
Jetzt sehen wir, dass erst mal nichts passiert:
Das liegt daran, dass das Argument side des Labels auf „top“ gesetzt ist. Wenn das der Fall ist, allokiert das Widget zwar den komplett verfügbaren horizontalen Platz, allerdings nur den vertikalen Platz, den es benötigt, um den Inhalt darstellen zu können. Aus diesem Grund „klebt“ Label 2 direkt unter Label 1.
Denn rechts und links von Label 1 kann es schließlich nicht platziert werden, da der Platz für Label 1 bereits reserviert ist. Folglich wird es direkt darunter gesetzt und damit an die nächst höchstmögliche Stelle in der Applikation.
Wenn wir jetzt noch ein drittes Label oder anderweitiges Widget hinzufügen würden, würde dieses im Fall, dass wir side standardmäßig auf top gesetzt lassen, ebenfalls wieder direkt unter Label 2 platziert werden.
Noch einmal zur Wiederholung: Bei fill=y sehen wir keinen Unterschied, da Label 1 bereits den gesamten vertikalen Platz einnimmt, den es allokiert hat.
8. Das Schlüsselwortargument expand
Wir könnten Tkinter jetzt aber auch dazu anweisen, den Platz für Label 1 in y-Richtung, also in die vertikale Richtung um so viel Platz wie möglich zu vergrößern.
Das lässt sich mithilfe des Schlüsselwortarguments expand umsetzen. Dieses müssen wir auf den Wahrheitswert True setzen:
label1.pack(side="top", expand=True, fill="y")
An dieser Stelle führen wir das Programm aus und sehen die folgende Ausgabe:
Wir sagen damit also dem Pack Layout Manager, dass er Label 1 oben in das Hauptfenster setzen und nicht nur das allokieren soll, dass er standardmäßig mit der Zeile side=top bereits macht, nämlich den kompletten horizontalen Platz für das Label, sondern versucht, noch so viel Platz wie möglich in y-Richtung (vertikal) zu allokieren.
9. Was passiert, wenn man expand auf True setzt?
Beim Ausführen des Programms sehen wir, dass sich Label 1 den gesamten Platz in y-Richtung nimmt, den es beanspruchen kann, aber noch so viel Platz übrig lässt, dass Label 2 vollständig dargestellt werden kann.
Wenn man expand auf True setzt, muss man also nicht befürchten, dass die anderen Widgets innerhalb der GUI nicht mehr sichtbar sein werden, da es nur so viel Platz allokiert, dass die übrigen Widgets noch normal dargestellt werden können.
Jetzt könnte man auch auf die Idee kommen, beispielsweise Label 2 beim Aufruf der pack-Methode mitzuteilen, ebenfalls so viel Platz wie möglich zu allokieren. Das heißt, wir schreiben auch bei Label 2 expand und setzen dessen Wert auf True:
label2 = tk.Label(root, text="Label 2", bg="red")
label2.pack(side="top", expand=True)
Wenn wir das Programm nun erneut ausführen, sehen wir, dass der Pack Layout Manager dafür sorgt, dass sich beide Widgets den möglichen Platz teilen:
Denn wir schreiben vor, dass die beiden Widgets so viel Platz wie möglich einnehmen sollen und pack ist so intelligent, dass es diesen Platz für beide gleich aufteilt. So erhält Label 1 50 Prozent und Label 2 ebenfalls 50 Prozent des Platzes.
Wie bei Label 1 können wir auch bei Label 2 fill auf y setzen:
label2 = tk.Label(root, text="Label 2", bg="red")
label2.pack(side="top", expand=True, fill="y")
Dann führen wir das Programm erneut aus uns sehen in der Ausgabe noch deutlicher, dass sich beide Labels den Platz zur Hälfte teilen:
10. Widgets in beide Richtungen ausbreiten
Bisher haben wir bei fill immer entweder den Wert x oder y zugewiesen. Es gibt allerdings noch einen weiteren Wert namens both, den wir zuweisen können. Damit sorgen wir dafür, dass es den Platz in beide Richtungen, also sowohl in x- als auch in y-Richtung füllt.
Das können wir beispielsweise bei Label 2 umsetzen, indem wir „y“ durch „both“ ersetzen:
label2 = tk.Label(root, text="Label 2", bg="red")
label2.pack(side="top", expand=True, fill="both")
Anschließend führen wir das Programm erneut aus und sehen, dass der gesamte Platz, der für Label 2 reserviert wurde, nun mit der roten Hintergrundfarbe dargestellt wird. Das liegt daran, dass das Label nun den komplett allokierten Platz verwendet.
11. Wann man auf fill verzichten kann
Man muss nebenbei bemerkt, wenn man expand verwendet, nicht zwangsläufig auch fill als Argument übergeben. Das heißt, wir könnten beispielsweise fill löschen, das Programm noch einmal ausführen und sehen dann, dass der Platz dennoch in zwei Hälften geteilt wird:
label1.pack(side=”top”, expand=True, fill="y")
Es wird also dennoch der Platz entsprechend allokiert, allerdings erstreckt sich das Label nicht über den komplett allokierten Platz, sondern nutzt lediglich den minimalen Raum, den es benötigt, um den Inhalt sauber darstellen zu können.
Der Platz um das Label 1 herum ist aber immer noch für dieses reserviert.
12. Anmerkungen zum Schlüsselwortargument side
Damit hast du nun auch schon mal gesehen, wie sich pack verhält, wenn wir bei allen Widgets den Standardwert von side auf top setzen, was wir in jedem der eben vorgeführten Beispiele getan haben.
Wir können den Wert von side allerdings nicht nur auf top setzen, sondern auch auf bottom, sodass das Widget unten platziert wird. Auch left, also links und right (rechts) können wir angeben, um es an die jeweilige Position zu bringen.
Wenn man das so umsetzt und den Labels unterschiedliche side Values zuweist, bringt das in gewissen Konstellationen immer wieder bestimmte Implikationen mit sich.
Wie bereits erwähnt, sind im Python Tkinter Masterkurs sämtliche Fälle, die auftreten können, detailliert beschrieben und erklärt. In diesem Beitrag würde all das jedoch den Rahmen sprengen, weshalb es sich lohnt, einen Blick auf den Tkinter Masterkurs zu werfen.
An dieser Stelle empfehle ich dir, deine Entwicklungsumgebung zu öffnen und die eben gelernten Dinge selbst auszuprobieren. Denn nur so bekommst du ein Gefühl dafür, wie der Pack Layout Manager arbeitet. Ich weiß, dass das gerade anfangs etwas kompliziert klingt, aber wenn das Prinzip erst mal verstanden ist und du ein paar Widgets damit platziert hast, wirst du diesen intuitiv und ohne viel nachzudenken, nutzen können.