1 - Grundlagen
Tkinter ist die Standard-Bibliothek für die Erstellung von grafischen Benutzeroberflächen (GUIs) in Python. Sie ermöglicht es dir, professionelle Desktop-Anwendungen mit Fenstern, Buttons, Eingabefeldern und vielen anderen Elementen zu erstellen – ohne externe Abhängigkeiten.
Inhaltsverzeichnis
Einführung
Tkinter (Tk Interface) ist Pythons Standard GUI-Bibliothek und eine Python-Schnittstelle für Tk GUI Toolkit. Tk wurde ursprünglich für die Programmiersprache Tcl entwickelt, wurde aber für viele Sprachen portiert, darunter Python.
Kernmerkmale
- Plattformübergreifend: Läuft auf Window, macOS und Linux
- Standardbibliothek: In Python bereits integriert
- Ausgereift und stabil: Seit 1991 in Entwicklung
- Event-basiert: Reagiert auf Benutzerinteraktionen
- Widget-basiert: Modularer Aufbau aus UI-Komponenten
Installation & Setup
Verfügbarkeit prüfen
Tkinter ist normalerweise bereits mit Python installiert. Um dies zu prüfen, kann man folgendes kleines Python-Programm (Script) verwenden.
Dafür können wir beispielsweise eine Datei namens test_tkinter.py mit nachfolgendem Inhalt erstellen. Danach kann man dieses Script einfach mit python3 test_tkinter.py ausführen.
import tkinter as tk
print(f"Tkinter Version: {tk.TkVersion}")
print(f"Tcl Version: {tk.TclVersion}")
print(f"Tkinter ist verfügbar")Tkinter Version: 9.0
Tcl Version: 9.0
Tkinter ist verfügbarInstallation (falls nicht vorhanden)
macOS
brew install python-tk@3.14Linux (Ubuntu/Debian)
sudo apt update
sudo apt install python3-tkLinux (Fedora)
sudo dnf install python3-tkinterWindows
Tkinter wird automatisch mit Python installiert. Hier ist wichtig sicherzustellen, dass “tcl/tk and IDLE” während der Installation aktiviert ist.
Erstes Tkinter Programm
In diesem Beispiel schreiben wir das minimalste und erste Tkinter Programm.
import tkinter as tk
# Hauptfenster erstellen
root = tk.Tk()
# Event-Loop starten
root.mainloop()Wenn wir das Programm ausführen, erhalten wir unser App-Fenster.

Was passiert hier genau?
import tkinter as tk- Importiert Tkinter (mittkAlias)root = tk.Tk()- Erstellt das Hauptfenster (Root-Window)root.mainloop()- Startet die Event-Loop (Programm wartet auf Events)
Programm mit Label
Im nächsten Schritt platzieren wir zur Veranschaulichung ein Label-Widget. Diese werden später genauer erläutert.
Dazu erstellen wir die Datei basic_label_widget.py mit folgendem Inhalt.
import tkinter as tk
# Hauptfenster erstellen
root = tk.Tk()
# Titel des Fensters setzen
root.title("Basic Widgets")
# Größe des Fensters festlegen
root.geometry("500x300") # Breite x Höhe
# Label-Widget erstellen
label_one = tk.Label(
master=root,
text="Hallo Tkinter",
font=("Arial", 24),
fg="#246990"
)
# Label im Fenster platzieren
label_one.pack(pady=50)
# Event-Loop starten
root.mainloop()Nach der Ausführung mit python3 basic_label_widget.py erhalten wir folgendes Programm.

Was ist hier neu?
root.title()- Setzt den Fenster-Titelroot.geometry()- Setzt die Fenstergröße (Format: “BreitexHöhe”)tk.Label()- Erstellt ein Text-Widgetlabel_one.pack()- Platziert das Widget im Fensterpady=50- Fügt vertikalen Abstand hinzu
Programm mit Label und Button
Nun bauen wir unser Beispiel ein wenig aus und fügen einen Button hinzu, auf den wir klicken und eine Aktion ausführen können.
import tkinter as tk
# Funktion für Button
# Wird aufgerufen, wenn der Button geklickt wird
def button_action():
label_one.configure(text="Button geklickt!")
button_one.configure(state="disabled")
# Hauptfenster
root = tk.Tk()
root.title("Label und Button")
root.geometry("500x300")
# Label
label_one = tk.Label(
master=root,
text="Klicke den Button!"
font=("Arial", 18)
)
label_one.pack(pady=30)
# Button
button_one = tk.Button(
master=root,
text="Klick mich",
font=("Arial", 14),
bg="#246990",
fg="white",
padx=20,
pady=10,
command=button_action
)
button_one.pack()
# Event-Loop
root.mainloop()Mit diesem Code erhalten wir eine Applikation, welche uns einen Label und einen Button bereitstellt. Mit dem Klick auf den Button können wir sowohl den Label als auch den Button aktualisieren.


Event-Loop
Was ist die Event-Loop
Die Event-Loop ist das Herzstück vieler GUI-Anwendungen.
+-------------------------------------------+
| Start: root.mainloop() |
+----------------------+--------------------+
|
v
+----------------------+
| Warte auf Event |<-----------+
+----------+-----------+ |
| |
v |
+----------------------+ |
| Event aufgetreten? | |
+----------+-----------+ |
| |
+--------+--------+ |
| | |
Ja Nein |
| | |
v +------------+
+-------------------+
| Event |
| verarbeiten |
+---------+---------+
|
v
+-------------------+
| Callbacks |
| ausführen |
+---------+---------+
|
v
+-------------------+
| GUI |
| aktualisieren |
+---------+---------+
|
+------------------------------>Tkinter ist kein reines Python-GUI-Toolkit. Es ist ein Python-Binding zu Tcl/Tk. Das bedeutet:
- Die Python-Methoden (z.B.
button = tk.Button(...)) erzeugen Tcl/Tk-Objekte - Tkinter kommuniziert per eingebettetem Tcl-Interpreter
- Die Event-Loop gehört dem Tcl/Tk-Interpreter, nicht Python
Die zentrale Schleife (Loop) ist Tk.mainloop(), aber intern ist es Tcl/Tk, nicht Python.
Ablauf
Wir haben folgenden Code.
root = tk.Tk()
root.mainloop()Wenn wir diesen Code ausführen, passiert Folgendes:
- Tkinter übergibt die Kontrolle an Tcl/Tk
- Tcl/Tk startet seine Hauptschleife:
- Überprüft Input-Events (X11 / WinAPI / macOS EventQueue)
- Führt Callbacks aus (
command=...,bind=..., Timer-Events) - Zeichent die Widgets neu
- Plant das Neu-Zeichnen, Idle-Tasks, interne Updates
- Python blockiert, bis die Schleife beendet wird
Während mainloop() läuft, ruft NICHT Python Tkinter auf - Tcl/Tk ruft die definierten Python-Callbacks auf.
Beispiel - Event-Loop
Schauen wir uns ein Basis-Beispiel für eine Event-Loop an.
import tkinter as tk
from datetime import datetime
def update_time():
current_time = datetime.now().strftime("%H:%M:%S")
time_label.configure(text=f"Aktuelle Zeit: {current_time}")
root.after(1000, update_time)
def on_mouse_enter(event):
hover_label.configure(
text="Maus über dem Label",
bg="lightblue"
)
def on_mouse_leave(event):
hover_label.configure(
text="Bewege die Maus hierher",
bg="lightgrey"
)
root = tk.Tk()
root.title("Event-Loop Demo")
root.geometry("500x300")
time_label = tk.Label(
master=root,
font=("Arial", 16)
)
time_label.pack(pady=20)
# Starte die Zeit-Aktualisierung
update_time()
hover_label = tk.Label(
master=root,
text="Bewege die Maus hierher",
font=("Arial", 16),
bg="lightgrey",
padx=20,
pady=20
)
hover_label.pack(pady=20)
hover_label.bind("<Enter>", on_mouse_enter)
hover_label.bind("<Leave>", on_mouse_leave)
click_count = 0
counter_label = tk.Label(
master=root,
text=f"Klicks: {click_count}",
font=("Arial", 16)
)
counter_label.pack(pady=20)
def increment_counter():
global click_count
click_count += 1
counter_label.configure(text=f"Klicks: {click_count}")
counter_button = tk.Button(
master=root,
text="Klick mich",
command=increment_counter
)
counter_button.pack()
root.mainloop()Wenn wir diesen Code ausführen, erhalten wir eine Anwendung mit ein paar Widgets, mit welchen wir interagieren können. Dabei aktualisiert sich die jeweiligen Zustände jeweils.


Was in diesem Beispiel neu war, sind folgende Konzepte.
after(ms, callback)- Ruft eine Funktion nach einer Verzögerung aufbind(event, handler)- Verbindet Events mit Handler-Funktionen- Event-Handler erhalten ein
eventObjekt mit Details
Grundstruktur einer Tkinter-Anwendung
Objektorientierter Ansatz
Für größere Anwendungen ist eine klare Struktur wichtig. Schauen wir uns ein Beispiel an.
import tkinter as tk
class Application(tk.Tk):
"""Haupt-Anwendungsklasse"""
def __init__(self):
# Initialisierung
super().__init__()
# Fenster-Konfiguration
self.title("Tkinter-App")
self.geometry("500x300")
# UI erstellen
self.create_widgets()
def create_widgets(self):
# Erstellt UI-Komponenten
# Header
header = tk.Label(
master=self,
text="Willkommen",
font=("Arial", 20, bold),
bg="lightblue",
pady=10
)
header.pack(fill="x")
# Haupt-Bereich
main_frame = tk.Frame(
master=self,
bg="white"
)
main_frame.pack(
fill="both",
expand=True,
padx=20,
pady=20
)
# Inhalt
tk.Label(
master=main_frame,
text="Das ist eine strukturierte Anwendung",
font=("Arial", 12),
bg="white"
).pack(pady=10)
# Button
tk.Button(
master=main_frame,
text="Aktion ausführen",
command=self.handle_button_click
).pack(pady=10)
# Status-Label
self.status_label = tk.Label(
master=main_frame,
text="Bereit",
font=("Arial", 10),
fg="green",
bg="white"
)
self.status_label.pack(pady=10)
def handle_button_click(self):
# Führt eine Aktion aus
self.status_label.configure(
text="Aktion wurde ausgeführt",
fg="#246990"
)
if __name__ == "__main__":
app = Application()
app.mainloop()Mit diesem Ansatz ist es für uns möglich, Anwendungen mit mehr Objekt-orientiertem Ansatz zu schreiben. Das gibt dem Code mehr Struktur und bessere Aufteilung.


Tkinter vs Ttk
Tkinter bietet zwei Sets von Widgets.
- tkinter - Klassische Widgets (
tk.Button,tk.Label, etc.) - ttk - Themed Widgets (
ttk.Button,ttk.Label, etc.)
| Aspekt | tkinter | ttk |
|---|---|---|
| Aussehen | Platform-basic | Platform-native / Themed |
| Styling | Über Widget-Optionen | Über ttk.Style |
| Performance | Gut | Besser |
| Modernität | Altmodisch | Modern |
| Optionen | Mehr direkte Optionen | Weniger direkte Optionen |
Verfügbare ttk-Widgets
- Button, Checkbutton, Radiobutton
- Label, Entry
- Frame, LabelFrame
- Combobox (Dropdown)
- Progressbar
- Scrollbar
- Notebook (Tabs)
- Treeview
- Separator
- Sizegrip
- Scale
Nur in tkinter verfügbar
- Canvas
- Text
- Listbox
- Menu
- Spinbox (auch in ttk, aber anders)
- OptionMenu
- Toplevel
Nun schauen wir uns den Vergleich direkt an einem Beispiel an, damit wir auch visuell sehen, wie sich die Widgets unterscheiden.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.title("Vergleich tk vs. ttk")
root.geometry("600x400")
# Überschrift
tk.Label(
master=root,
text="Widget-Vergleich: tk vs. ttk",
font=("Arial", 16, "bold")
).pack(pady=10)
# Container
container = tk.Frame(master=root)
container.pack(
fill="both",
expand=True,
padx=20,
pady=10
)
# Linke Spalte: tkinter
tk_frame = tk.LabelFrame(
master=container,
text="tkinter Widgets",
font=("Arial", 12, "bold"),
padx=10,
pady=10
)
tk_frame.pack(
side="left",
fill="both",
expand=True,
padx=(0, 5)
)
tk.Label(
master=tk_frame,
text="Label (tkinter)"
).pack(pady=5)
tk.Button(
master=tk_frame,
text="Button (tkinter)"
).pack(pady=5)
tk.Checkbutton(
master=tk_frame,
text="Checkbutton (tkinter)"
).pack(pady=5)
tk.Radiobutton(
master=tk_frame,
text="Radiobutton (tkinter)"
).pack(pady=5)
tk.Entry(master=tk_frame).pack(pady=5)
tk.Scale(
master=tk_frame,
from_=0,
to=100,
orient="horizontal"
).pack(pady=5)
# Rechte Spalte: ttk
ttk_frame = ttk.LabelFrame(
master=container,
text="ttk Widgets (Themed)",
padding=10
)
ttk_frame.pack(
side="right",
fill="both",
expand=True,
padx=(5, 0)
)
ttk.Label(
master=ttk_frame,
text="Label (ttk)"
).pack(pady=5)
ttk.Button(
master=ttk_frame,
text="Button (ttk)"
).pack(pady=5)
ttk.Checkbutton(
master=ttk_frame,
text="Checkbutton (ttk)"
).pack(pady=5)
ttk.Radiobutton(
master=ttk_frame,
text="Radiobutton (ttk)"
).pack(pady=5)
ttk.Entry(master=ttk_frame).pack(pady=5)
ttk.Scale(
master=ttk_frame,
from_=0,
to=100,
orient="horizontal"
).pack(pady=5)
# Zusätzlich ttk-spezifische Widgets
ttk.Separator(
master=ttk_frame,
orient="horizontal"
).pack(fill="x", pady=10)
ttk.Progressbar(
master=ttk_frame,
mode="determinate",
value=50
).pack(pady=5)
ttk.Combobox(
master=ttk_frame,
values=[
"Option 1",
"Option 2",
"Option 3"
]
).pack(pady=5)
root.mainloop()Mit dieser Applikation haben wir Sektionen, in welchen wir die Widgets nebeneinander sehen. Bei ttk sehen wir, dass auch das dunkle Design des Betriebssystems automatisch unterstützt wird.


Praktisches Beispiel
Zum Abschluß der Einführung und Übersicht über Tkinter, bauen wir ein Beispiel mit verschiedenen Widgets auf. Man muss nicht alle Widgets an dieser Stelle verstehen, da diese im späteren Verlauf im Detail erklärt werden.
import tkinter as tk
from tkinter import ttk, messagebox
from datetime import datetime
class PersonalInfoApp(tk.Tk):
def __init__(self):
super().__init__()
# Daten-Storage
self.persons = []
# Fenster-Setup
self.setup_window()
# UI erstellen
self.create_widgets()
# Willkommensnachricht
self.update_status("Bereit - Geben Sie Ihre Daten ein")
def setup_window(self):
"""
Konfiguriert das Hauptfenster
"""
self.title("Personal Info Manager")
self.geometry("700x500")
# Minimum-Größe festlegen
self.minsize(600, 400)
# Beim Schließen nachfragen
self.protocol("WM_DELETE_WINDOW", self.on_closing)
def create_widgets(self):
"""
Erstellt alle UI-Komponenten
"""
# Haupt-Container mit Padding
main_container = ttk.Frame(
master=self,
padding=10
)
main_container.pack(fill="both", expand=True)
# Titel
title_label = tk.Label(
master=main_container,
text="📋 Personal Info Manager",
font=("Arial", 18, "bold"),
bg="#4CAF50",
fg="white",
pady=10
)
title_label.pack(fill="x")
# Eingabebereich
self.create_input_section(main_container)
# Listen-Bereich
self.create_list_section(main_container)
# Statusbar
self.status_bar = ttk.Label(
master=self,
text="Bereit",
relief="sunken",
anchor="w"
)
self.status_bar.pack(side="bottom", fill="x")
def create_input_section(self, parent):
"""
Erstellt den Eingabebereich
"""
input_frame = ttk.LabelFrame(
master=parent,
text="Neue Person hinzufügen",
padding=10
)
input_frame.pack(fill="x", pady=(10, 5))
# Grid-Layout für Formular
# - Name
ttk.Label(
master=input_frame,
text="Name:"
).grid(
row=0,
column=0,
sticky="w",
pady=5
)
self.name_var = tk.StringVar()
self.name_entry = ttk.Entry(
master=input_frame,
textvariable=self.name_var,
width=30
)
self.name_entry.grid(
row=0,
column=1,
sticky="ew",
pady=5,
padx=5
)
# - Alter
ttk.Label(
master=input_frame,
text="Alter:"
).grid(
row=1,
column=0,
sticky="w",
pady=5
)
self.age_var = tk.StringVar()
self.age_spinbox = ttk.Spinbox(
master=input_frame,
from_=0,
to=120,
textvariable=self.age_var,
width=10
)
self.age_spinbox.grid(
row=1,
column=1,
sticky="w",
pady=5,
padx=5
)
# - Stadt
ttk.Label(
master=input_frame,
text="Stadt:"
).grid(
row=2,
column=0,
sticky="w",
pady=5
)
self.city_var = tk.StringVar()
self.city_combo = ttk.Combobox(
master=input_frame,
textvariable=self.city_var,
values=[
"Berlin",
"München",
"Hamburg",
"Köln",
"Frankfurt"
],
width=28
)
self.city_combo.grid(
row=2,
column=1,
sticky="ew",
pady=5,
padx=5
)
# Button-Frame
button_frame = ttk.Frame(master=input_frame)
button_frame.grid(
row=3,
column=0,
columnspan=2,
pady=10
)
ttk.Button(
master=button_frame,
text="Hinzufügen",
command=self.add_person
).pack(side="left", padx=5)
ttk.Button(
master=button_frame,
text="Felder leeren",
command=self.clear_fields
).pack(side="left", padx=5)
# Grid-Gewichtung für Responsive-Verhalten
input_frame.columnconfigure(1, weight=1)
# Enter-Taste zum Hinzufügen
self.name_entry.bind("<Return>", lambda e: self.add_person())
self.age_spinbox.bind("<Return>", lambda e: self.add_person())
self.city_combo.bind("<Return>", lambda e: self.add_person())
def create_list_section(self, parent):
"""
Erstellt den Listen-Bereich
"""
list_frame = ttk.LabelFrame(
master=parent,
text="Gespeicherte Personen",
padding=10
)
list_frame.pack(
fill="both",
expand=True,
pady=(5, 10)
)
# Treeview mit Scrollbar
tree_container = ttk.Frame(master=list_frame)
tree_container.pack(fill="both", expand=True)
# Scrollbars
vsb = ttk.Scrollbar(
master=tree_container,
orient="vertical"
)
vsb.pack(side="right", fill="y")
hsb = ttk.Scrollbar(
master=tree_container,
orient="horizontal"
)
hsb.pack(side="bottom", fill="x")
# Treeview
self.tree = ttk.Treeview(
master=tree_container,
columns=(
"Name",
"Alter",
"Stadt",
"Hinzugefügt"
),
show="headings",
yscrollcommand=vsb.set,
xscrollcommand=hsb.set
)
# Scrollbars konfigurieren
vsb.configure(command=self.tree.yview)
hsb.configure(command=self.tree.xview)
# Spalten-Header
self.tree.heading("Name", text="Name")
self.tree.heading("Alter", text="Alter")
self.tree.heading("Stadt", text="Stadt")
self.tree.heading("Hinzugefügt", text="Hinzugefügt am")
# Spaltenbreiten
self.tree.column("Name", width=150)
self.tree.column("Alter", width=80)
self.tree.column("Stadt", width=120)
self.tree.column("Hinzugefügt", width=150)
self.tree.pack(fill="both", expand=True)
# Button-Frame unter der Liste
button_frame = ttk.Frame(master=list_frame)
button_frame.pack(fill="x", pady=(10, 0))
ttk.Button(
master=button_frame,
text="Ausgewählte löschen",
command=self.delete_selected
).pack(side="left", padx=5)
ttk.Button(
master=button_frame,
text="Alle löschen",
command=self.delete_all
).pack(side="left", padx=5)
self.count_label = ttk.Label(
master=button_frame,
text="Gesamt: 0"
)
self.count_label.pack(side="right", padx=5)
def add_person(self):
"""
Fügt eine Person zur Liste hinzu
"""
name = self.name_var.get().strip()
age = self.age_var.get().strip()
city = self.city_var.get().strip()
# Validierung
if not name:
messagebox.showwarning("Warnung", "Bitte geben Sie einen Namen ein.")
self.name_entry.focus()
return
if not age or not age.isdigit():
messagebox.showwarning("Warnung", "Bitte geben Sie ein gültiges Alter ein.")
self.age_spinbox.focus()
return
if not city:
messagebox.showwarning("Warnung", "Bitte wählen Sie eine Stadt.")
self.city_combo.focus()
return
# Zeitstempel
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# Zur Liste hinzufügen
person = {
"name": name,
"age": age,
"city": city,
"timestamp": timestamp
}
self.persons.append(person)
# In Treeview einfügen
self.tree.insert(
"",
"end",
values=(name, age, city, timestamp)
)
# Felder leeren
self.clear_fields()
# Status aktualisieren
self.update_count()
self.update_status(f"'{name}' wurde hinzugefügt")
# Focus zurück zum Namen-Feld
self.name_entry.focus()
def clear_fields(self):
"""
Leert alle Eingabefelder
"""
self.name_var.set("")
self.age_var.set("")
self.city_var.set("")
self.name_entry.focus()
def delete_selected(self):
"""
Löscht die ausgewählten Einträge
"""
selected_items = self.tree.selection()
if not selected_items:
messagebox.showinfo("Info", "Bitte wählen Sie einen Eintrag aus.")
return
# Bestätigung
if messagebox.askyesno(
"Bestätigung",
f"Möchten Sie {len(selected_items)} Eintrag/Einträge löschen?"
):
for item in selected_items:
self.tree.delete(item)
self.update_count()
self.update_status(f"{len(selected_items)} Eintrag/Einträge gelöscht")
def delete_all(self):
"""
Löscht alle Einträge
"""
if not self.tree.get_children():
messagebox.showinfo("Info", "Liste ist bereits leer.")
return
if messagebox.askyesno(
"Bestätigung",
"Möchten Sie wirklich alle Einträge löschen?"
):
self.tree.delete(*self.tree.get_children())
self.persons.clear()
self.update_count()
self.update_status("Alle Einträge gelöscht")
def update_count(self):
"""
Aktualisiert die Anzahl-Anzeige
"""
count = len(self.tree.get_children())
self.count_label.configure(text=f"Gesamt: {count}")
def update_status(self, message):
"""
Aktualisiert die Statusbar
"""
self.status_bar.configure(
text=f"{message} | {datetime.now().strftime('%H:%M:%S')}"
)
def on_closing(self):
"""
Wird beim Schließen des Fensters aufgerufen
"""
if self.persons:
if messagebox.askyesno(
"Beenden",
"Es gibt ungespeicherte Daten. Wirklich beenden?"
):
self.destroy()
else:
self.destroy()
if __name__ == "__main__":
app = PersonalInfoApp()
app.mainloop()Wenn wir dieses Programm ausführen, erhalten wir folgende schöne Applikation, welche bereits einiges an Layouting und Funktionalität demonstriert.
