Tkinter – Grid Layout Manager

Im vorherigen Blogbeitrag zum Thema Tkinter ging es um die Verwendung einer Klasse. Hier nochmal der Code zum dort gezeigten Beispiel:

#!/usr/bin/env python3

import tkinter as tk

class MainWindow():
    def __init__(self):

        self.window = tk.Tk()
        self.window.title("Main Window")

        # Set width and height of the window
        window_width = 500
        window_height = 300

        # Get the screen dimension
        screen_width = self.window.winfo_screenwidth()
        screen_height = self.window.winfo_screenheight()

        # Find the center point
        center_x = int(screen_width / 2 - window_width / 2)
        center_y = int(screen_height / 2 - window_height / 2)

        # Set the position of the window to the center of the screen
        self.window.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}")

        # Window is not resizable
        self.window.resizable(False, False)

    def mainloop(self):
        self.window.mainloop()


if __name__ == "__main__":
    app_instance = MainWindow()
    app_instance.mainloop()

Widgets hinzufügen

Dieses Beispiel wird nunmehr um Widgets erweitert. Anstatt die Widgets direkt zum Programmfensters hinzuzufügen, soll hier aber ein Frame-Widget verwendet werden. Alle übrigen Widgets sollen dann Bestandteil dieses Frames sein:

self.content = ttk.Frame(self.window, padding=(10, 10, 10, 10))

Dabei wird mit padding für ausreichend Abstand zu den Kanten des Programmfensters gesorgt.

Nacheinander folgen jetzt zwei Entry-, ein Button- und ein Label-Widget:

# Entry
self.first_input_field = ttk.Entry(self.content)
self.second_input_field = ttk.Entry(self.content)

# Button
self.calculate_button = ttk.Button(self.content, text="Calculate", command=self.calculate)

# Label
self.result_label = ttk.Label(self.content)

Mit

command=self.calculate

wird definiert, welche Funktion nach einem Klick auf den Button ausgeführt werden soll. Damit der bisherige Code ausgeführt werden kann, ohne dass eine Fehlermeldung erscheint, wird jetzt noch zusätzlich diese Funktion hinzugefügt. Hinsichtlich des Funktionskörpers belassen wir es aber bei einem pass.

def calculate():
    pass

Grid Layout verwenden

Wird dieses einfache Programm ausgeführt, erscheint (weiterhin) ein leeres Fenster ohne Widgets. Das hängt damit zusammen, dass in einem zweiten Schritt mithilfe eines Layout-Managers (auch als Geometrie-Manager bezeichnet) die Widgets zum Fenster (bzw. zum Frame) hinzugefügt bzw. angeordnet werden müssen. In den bisherigen in diesem Blog vorgestellten Tkinter-Tutorials wurde hierfür pack verwendet. Dieser ist so populär, dass er bis heute in zahlreichen Tutorials zu finden ist, obwohl es einen neueren Layout-Manager gibt. Denn im Jahre 1996 wurde Grid eingeführt, den ich in diesem Beispiel verwenden möchte.

Grid funktioniert dergestalt, dass den Widgets Spalten und Zeilen zugeordnet werden. Widgets, die derselben Spalte zugeordnet werden, sind vertikal angeordnet. Widgets, die derselben Zeile zugeordnet werden, sind horizontal angeordnet.

In diesem Programm sollen an oberster Stelle, also in der ersten Zeile, die beiden Eingabefelder (Entry Widgets) stehen. In der Zeile darunter soll sich der Button befinden und in einer Zeile darunter wiederum das Label-Widget. Das mag zwar nicht sonderlich schön aussehen, sollte aber das Prinzip verdeutlichen.

Tkinter grid layout
Grid Layout

In Code gegossen sieht dies wie folgt aus:

# Grid Layout manager
# Frame and entries
self.content.grid(column=0, row=0, sticky="nsew")
self.first_input_field.grid(column=0, row=0, sticky="nsew")
self.second_input_field.grid(column=1, row=0, sticky="nsew")

# Button
self.calculate_button.grid(column=0, row=1, sticky="nsew")

# Label
self.result_label.grid(column=0, row=2, sticky="nsew")

Über column und row wird festgelegt, in welcher Spalte und in welcher Zeile ein Widget platziert werden soll. Darüber hinaus lässt sich auch festlegen, dass sich ein Widget über mehrere Spalten und Zeilen erstrecken soll. Dies wird mit columnspan= und rowspan= erreicht.

Des Weiteren kann ein Widget mit sticky innerhalb einer Zelle ausgerichtet werden. Dabei steht „nsew“ für die vier Himmelsrichtungen. Würde man nur „w“ (= west) angeben, dann würde das Widget auf der linken Seite angeheftet werden (und Freiraum auf der echten Seite lassen). Die Angabe von „nsew“ führt also zu einer gleichmäßigen Anordnung, denn das Widget wird in alle Himmelsrichtungen gestreckt und füllt so den gesamten zur Verfügung stehenden Platz aus.

Damit steht nun das Layout für dieses einfache Programm. Zugegeben, es sieht nicht besonders schön aus, aber das Prinzip sollte deutlich geworden sein. Am besten ist es, wenn man etwas experimentiert, um sich mit dem Grid-Layout-Manger vertraut zumachen. Zum Abschluss nochmal der komplette Code zu diesem Beispiel:

#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk


class MainWindow:
    def __init__(self):
        self.window = tk.Tk()
        self.window.title("Main Window")

        # Set width and height of the window
        window_width = 400
        window_height = 100

        # Get the screen dimension
        screen_width = self.window.winfo_screenwidth()
        screen_height = self.window.winfo_screenheight()

        # Find the center point
        center_x = int(screen_width / 2 - window_width / 2)
        center_y = int(screen_height / 2 - window_height / 2)

        # Set the position of the window to the center of the screen
        self.window.geometry(f"{window_width}x{window_height}+{center_x}+{center_y}")

        # Window is not resizable
        self.window.resizable(False, False)

        # All widgets will be part of this frame.
        self.content = ttk.Frame(self.window, padding=(10, 10, 10, 10))

        # Entry
        self.first_input_field = ttk.Entry(self.content)
        self.second_input_field = ttk.Entry(self.content)

        # Button
        self.calculate_button = ttk.Button(
            self.content,
            text="Calculate",
            command=self.calculate
        )

        # Label
        self.result_label = ttk.Label(
            self.content,
            text="Result label"
        )

        # Layout manager
        # Add frame and entries
        self.content.grid(column=0, row=0, sticky="nsew")
        self.first_input_field.grid(column=0, row=0)
        self.second_input_field.grid(column=1, row=0)

        # Add button
        self.calculate_button.grid(column=0, row=1, sticky="nsew")

        # Add label
        self.result_label.grid(column=0, row=2, sticky="nsew")

    def calculate():
        pass 

    def mainloop(self):
        self.window.mainloop()


if __name__ == "__main__":
    app_instance = MainWindow()
    app_instance.mainloop()