ttkbootstrap Logo

ttkbootstrap für Anfänger

Moderne Python GUIs mit Bootstrap-Look — Schritt für Schritt

10 Kapitel Python 3.8+ Viele Codebeispiele Offline verfügbar

1 · Was ist ttkbootstrap?

ttkbootstrap ist eine Python-Bibliothek, die die Standard-GUI tkinter mit modernen Bootstrap-Themes aufwertet. Statt grauer Systemdialoge bekommst du schöne, professionelle Oberflächen — mit minimalem Aufwand.

20+ Themes

Alle tkinter-Widgets

Drop-in-Ersatz

Open Source

💡 Tipp: Wenn du schon tkinter kennst, musst du fast nichts Neues lernen — ttkbootstrap ist ein direkter Ersatz mit besserem Aussehen.

2 · Installation

ttkbootstrap lässt sich mit pip in Sekunden installieren. Python 3.8 oder höher wird benötigt.

Terminal / Eingabeaufforderung
pip install ttkbootstrap
💡 Auf manchen Systemen heißt der Befehl pip3 install ttkbootstrap.
Installation prüfen
Python
import ttkbootstrap as ttk
print(ttk.__version__)  # z.B. 1.10.1
Empfohlene Entwicklungsumgebungen
EditorIdeal fürLink
VS CodeAlle Levelscode.visualstudio.com
PyCharm CEFortgeschrittenejetbrains.com/pycharm
ThonnyAnfängerthonny.org

3 · Erstes Fenster

Ein einfaches ttkbootstrap-Fenster benötigt nur wenige Zeilen. Der wichtigste Unterschied zu tkinter: ttk.Window statt tk.Tk().

hallo.py
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

# Fenster erstellen (Theme: "darkly" für dunkles Design)
app = ttk.Window(
    title="Mein erstes Fenster",
    themename="darkly",
    size=(400, 300)
)

# Label
label = ttk.Label(app, text="Hallo ttkbootstrap!", font=("Helvetica", 16))
label.pack(pady=20)

# Button
button = ttk.Button(app, text="Klick mich!", bootstyle=PRIMARY)
button.pack(pady=10)

# Fenster starten
app.mainloop()
💡 mainloop() startet die Event-Schleife — das Fenster bleibt offen, bis der Benutzer es schließt. Dieser Aufruf muss immer am Ende stehen.
Fenster-Optionen
Python
app = ttk.Window(
    title="Meine App",       # Fenstertitel
    themename="darkly",      # Theme
    size=(600, 400),         # Breite x Höhe in Pixel
    resizable=(True, True),  # Größe änderbar (Breite, Höhe)
    position=(100, 100)      # Position auf dem Bildschirm
)

# Mindest- und Maximalgröße
app.minsize(300, 200)
app.maxsize(1200, 800)

# Fenstergröße nicht änderbar
app.resizable(False, False)

4 · Themes

ttkbootstrap kommt mit über 20 fertigen Themes — helle und dunkle Varianten. Du wählst das Theme einmalig beim Start der App.

Alle verfügbaren Themes
# Helle Themes
"cosmo"      # Sauber & modern
"flatly"     # Flat Design
"journal"    # Zeitungsartig
"litera"     # Elegant
"lumen"      # Leuchtend hell
"minty"      # Mintgrün
"pulse"      # Lebhaft lila
"sandstone"  # Erdtöne
"simplex"    # Minimalistisch
"sketchy"    # Handgezeichnet
"spacelab"   # Sachlich
"united"     # Orange
"yeti"       # Nordisch

# Dunkle Themes
"darkly"     # Dunkel (Bootstrap-Stil)
"cyborg"     # Cyber / Sci-Fi
"superhero"  # Dunkelblau
"solar"      # Solarized
"vapor"      # Neon / Synthwave
Theme zur Laufzeit wechseln
import ttkbootstrap as ttk

app = ttk.Window(themename="darkly")

# Theme wechseln (z.B. nach Klick auf Button)
def theme_wechseln():
    app.style.theme_use("cyborg")

btn = ttk.Button(app, text="Theme wechseln", command=theme_wechseln)
btn.pack(pady=20)

app.mainloop()
💡 Für Apps im nihonnet-Stil empfiehlt sich darkly oder cyborg — beide passen gut zum dunklen Design.

5 · Widgets im Überblick

ttkbootstrap kennt alle Standard-tkinter-Widgets, ergänzt um bootstyle-Parameter für Farben. Das Farbschema entspricht Bootstrap: primary, secondary, success, danger, warning, info, light, dark.

Labels & Text
Python
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

app = ttk.Window(themename="darkly")

# Einfaches Label
ttk.Label(app, text="Normaler Text").pack(pady=5)

# Label mit Stil
ttk.Label(app, text="Primär",  bootstyle=PRIMARY).pack()
ttk.Label(app, text="Gefahr",  bootstyle=DANGER).pack()
ttk.Label(app, text="Erfolg",  bootstyle=SUCCESS).pack()

# Große Überschrift
ttk.Label(app, text="Titel", font=("Helvetica", 20, "bold")).pack(pady=10)

app.mainloop()
Buttons
Python
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

app = ttk.Window(themename="darkly")

# Verschiedene Button-Stile
ttk.Button(app, text="Primary",   bootstyle=PRIMARY).pack(pady=3)
ttk.Button(app, text="Danger",    bootstyle=DANGER).pack(pady=3)
ttk.Button(app, text="Success",   bootstyle=SUCCESS).pack(pady=3)
ttk.Button(app, text="Outline",   bootstyle="primary-outline").pack(pady=3)
ttk.Button(app, text="Groß",      bootstyle=PRIMARY, width=20).pack(pady=3)

# Deaktivierter Button
btn = ttk.Button(app, text="Deaktiviert", bootstyle=SECONDARY, state=DISABLED)
btn.pack(pady=3)

app.mainloop()
Eingabefelder
Python
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

app = ttk.Window(themename="darkly")

# Einzeiliges Eingabefeld
name_var = ttk.StringVar()
ttk.Label(app, text="Name:").pack()
ttk.Entry(app, textvariable=name_var, bootstyle=PRIMARY, width=30).pack(pady=5)

# Passwortfeld
pw_var = ttk.StringVar()
ttk.Label(app, text="Passwort:").pack()
ttk.Entry(app, textvariable=pw_var, show="*", width=30).pack(pady=5)

# Mehrzeiliges Textfeld
ttk.Label(app, text="Notiz:").pack()
text = ttk.ScrolledText(app, width=35, height=5)
text.pack(pady=5)

# Wert auslesen
def lesen():
    print("Name:", name_var.get())
    print("Text:", text.get("1.0", "end"))

ttk.Button(app, text="Auslesen", command=lesen, bootstyle=SUCCESS).pack()
app.mainloop()
Weitere wichtige Widgets
Python
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

app = ttk.Window(themename="darkly")

# Checkbox
check_var = ttk.BooleanVar()
ttk.Checkbutton(app, text="Ich stimme zu", variable=check_var,
                bootstyle="success-round-toggle").pack(pady=5)

# Radiobuttons
auswahl = ttk.StringVar(value="A")
ttk.Radiobutton(app, text="Option A", variable=auswahl, value="A").pack()
ttk.Radiobutton(app, text="Option B", variable=auswahl, value="B").pack()

# Dropdown (Combobox)
ttk.Label(app, text="Stadt:").pack(pady=(10,0))
combo = ttk.Combobox(app, values=["Wien", "Innsbruck", "Salzburg", "Graz"])
combo.current(0)
combo.pack(pady=5)

# Schieberegler
ttk.Label(app, text="Lautstärke:").pack()
slider = ttk.Scale(app, from_=0, to=100, bootstyle=DANGER)
slider.pack(fill="x", padx=20, pady=5)

# Fortschrittsbalken
pb = ttk.Progressbar(app, bootstyle="success-striped", value=65)
pb.pack(fill="x", padx=20, pady=5)

app.mainloop()

6 · Layout-Manager

tkinter/ttkbootstrap kennt drei Layout-Manager: pack, grid und place. Für die meisten GUIs empfiehlt sich grid.

pack — einfach & schnell
Python
import ttkbootstrap as ttk

app = ttk.Window(themename="darkly", size=(300,200))

# Widgets werden untereinander gestapelt
ttk.Label(app, text="Oben").pack(pady=5)
ttk.Label(app, text="Mitte").pack()
ttk.Label(app, text="Unten").pack(pady=5)

# Nebeneinander mit side=LEFT
frame = ttk.Frame(app)
frame.pack(pady=10)
ttk.Button(frame, text="Links",   bootstyle="primary").pack(side="left", padx=5)
ttk.Button(frame, text="Rechts",  bootstyle="danger").pack(side="left", padx=5)

app.mainloop()
grid — für Formulare & strukturierte Layouts
Python
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

app = ttk.Window(themename="darkly", size=(350,200))
app.columnconfigure(1, weight=1)  # Spalte 1 dehnt sich aus

# row=Zeile, column=Spalte, sticky=Ausrichtung
ttk.Label(app, text="Vorname:").grid(row=0, column=0, padx=10, pady=8, sticky="w")
ttk.Entry(app, bootstyle=PRIMARY).grid(row=0, column=1, padx=10, sticky="ew")

ttk.Label(app, text="Nachname:").grid(row=1, column=0, padx=10, pady=8, sticky="w")
ttk.Entry(app, bootstyle=PRIMARY).grid(row=1, column=1, padx=10, sticky="ew")

ttk.Label(app, text="E-Mail:").grid(row=2, column=0, padx=10, pady=8, sticky="w")
ttk.Entry(app, bootstyle=PRIMARY).grid(row=2, column=1, padx=10, sticky="ew")

# Button über beide Spalten
ttk.Button(app, text="Speichern", bootstyle=SUCCESS).grid(
    row=3, column=0, columnspan=2, pady=15
)

app.mainloop()
💡 sticky funktioniert wie Himmelsrichtungen: "w" = links, "e" = rechts, "ew" = dehnt sich horizontal aus, "nsew" = dehnt sich in alle Richtungen.

7 · Events & Callbacks

Events sind Benutzeraktionen (Klick, Tastendruck, Mausbewegung). Ein Callback ist eine Funktion, die beim Event ausgeführt wird.

Python — Einfacher Button-Klick
import ttkbootstrap as ttk
from ttkbootstrap.constants import *

app = ttk.Window(themename="darkly", size=(350,250))

zaehler = ttk.IntVar(value=0)

def klick():
    zaehler.set(zaehler.get() + 1)
    label.config(text=f"Geklickt: {zaehler.get()}x")

label = ttk.Label(app, text="Geklickt: 0x", font=("Helvetica", 14))
label.pack(pady=20)

ttk.Button(app, text="Klick mich!", command=klick, bootstyle=PRIMARY).pack()

app.mainloop()
Tastatur- & Maus-Events
Python
import ttkbootstrap as ttk

app = ttk.Window(themename="darkly", size=(400,300))
info = ttk.Label(app, text="Warte auf Eingabe...", font=("Helvetica", 12))
info.pack(pady=20)

# Tastendruck
def taste(event):
    info.config(text=f"Taste gedrückt: {event.keysym}")

# Mausklick
def mausklick(event):
    info.config(text=f"Mausklick bei: x={event.x}, y={event.y}")

# Maus drüber
def hover(event):
    info.config(text="Maus ist über dem Fenster")

app.bind("<Key>", taste)
app.bind("<Button-1>", mausklick)   # Linksklick
app.bind("<Motion>", hover)          # Mausbewegung
app.bind("<Return>", lambda e: info.config(text="Enter gedrückt!"))

app.mainloop()
StringVar & Trace (reaktiv)
Python
import ttkbootstrap as ttk

app = ttk.Window(themename="darkly", size=(350,200))

name_var = ttk.StringVar()
vorschau = ttk.Label(app, text="Vorschau: ", font=("Helvetica", 13))
vorschau.pack(pady=20)

# Automatisch reagieren wenn Eingabe sich ändert
def bei_aenderung(*args):
    vorschau.config(text=f"Hallo, {name_var.get()}!")

name_var.trace_add("write", bei_aenderung)

ttk.Entry(app, textvariable=name_var, width=30).pack()
app.mainloop()

8 · Formulare bauen

Ein vollständiges Eingabeformular mit Validierung — das Herzstück vieler Desktop-Apps.

formular.py
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from ttkbootstrap.dialogs import Messagebox

app = ttk.Window(title="Registrierung", themename="darkly", size=(420,380))
app.columnconfigure(1, weight=1)

# --- Variablen ---
name_var   = ttk.StringVar()
email_var  = ttk.StringVar()
pw_var     = ttk.StringVar()
land_var   = ttk.StringVar()
agb_var    = ttk.BooleanVar()

# --- Felder ---
felder = [
    ("Vorname:",   name_var,  False),
    ("E-Mail:",    email_var, False),
    ("Passwort:",  pw_var,    True),
]

for i, (text, var, geheim) in enumerate(felder):
    ttk.Label(app, text=text).grid(row=i, column=0, padx=15, pady=8, sticky="w")
    show = "*" if geheim else ""
    ttk.Entry(app, textvariable=var, show=show, width=28).grid(
        row=i, column=1, padx=15, sticky="ew")

# Dropdown
ttk.Label(app, text="Land:").grid(row=3, column=0, padx=15, pady=8, sticky="w")
ttk.Combobox(app, textvariable=land_var,
             values=["Österreich","Deutschland","Schweiz"]).grid(
    row=3, column=1, padx=15, sticky="ew")

# Checkbox
ttk.Checkbutton(app, text="AGB akzeptieren", variable=agb_var,
                bootstyle="success-round-toggle").grid(
    row=4, column=0, columnspan=2, pady=10)

# --- Validierung & Absenden ---
def absenden():
    if not name_var.get():
        Messagebox.show_warning("Bitte Vorname eingeben.", "Fehler")
        return
    if "@" not in email_var.get():
        Messagebox.show_warning("Ungültige E-Mail-Adresse.", "Fehler")
        return
    if len(pw_var.get()) < 6:
        Messagebox.show_warning("Passwort muss mind. 6 Zeichen haben.", "Fehler")
        return
    if not agb_var.get():
        Messagebox.show_warning("Bitte AGB akzeptieren.", "Fehler")
        return
    Messagebox.show_info(f"Willkommen, {name_var.get()}!", "Erfolg")

ttk.Button(app, text="Registrieren", command=absenden,
           bootstyle=SUCCESS, width=20).grid(
    row=5, column=0, columnspan=2, pady=15)

app.mainloop()

9 · Dialoge & Popups

ttkbootstrap bringt fertige Dialoge mit — für Meldungen, Fragen, Dateiauswahl und mehr.

Python — Messagebox
from ttkbootstrap.dialogs import Messagebox
import ttkbootstrap as ttk

app = ttk.Window(themename="darkly", size=(400,300))

def info():
    Messagebox.show_info("Das ist eine Info-Meldung.", "Info")

def warnung():
    Messagebox.show_warning("Achtung! Etwas stimmt nicht.", "Warnung")

def fehler():
    Messagebox.show_error("Ein Fehler ist aufgetreten.", "Fehler")

def frage():
    antwort = Messagebox.yesno("Möchtest du wirklich löschen?", "Bestätigung")
    if antwort == "Yes":
        print("Gelöscht!")

ttk.Button(app, text="Info",     command=info,    bootstyle="info").pack(pady=5)
ttk.Button(app, text="Warnung",  command=warnung, bootstyle="warning").pack(pady=5)
ttk.Button(app, text="Fehler",   command=fehler,  bootstyle="danger").pack(pady=5)
ttk.Button(app, text="Frage",    command=frage,   bootstyle="primary").pack(pady=5)

app.mainloop()
Datei-Dialog
Python
import ttkbootstrap as ttk
from tkinter import filedialog

app = ttk.Window(themename="darkly", size=(350,200))
pfad_label = ttk.Label(app, text="Kein Pfad gewählt")
pfad_label.pack(pady=20)

def datei_oeffnen():
    pfad = filedialog.askopenfilename(
        title="Datei auswählen",
        filetypes=[("Textdateien", "*.txt"), ("Alle Dateien", "*.*")]
    )
    if pfad:
        pfad_label.config(text=pfad)

def datei_speichern():
    pfad = filedialog.asksaveasfilename(
        defaultextension=".txt",
        filetypes=[("Textdateien", "*.txt")]
    )
    if pfad:
        with open(pfad, "w") as f:
            f.write("Hallo Welt!")
        pfad_label.config(text=f"Gespeichert: {pfad}")

ttk.Button(app, text="Datei öffnen",    command=datei_oeffnen,    bootstyle="primary").pack(pady=5)
ttk.Button(app, text="Datei speichern", command=datei_speichern,  bootstyle="success").pack(pady=5)
app.mainloop()

10 · Praxisprojekt: Notiz-App

Ein vollständiges Praxisprojekt: eine einfache Notiz-App mit Speichern, Laden und Dark Theme.

notiz_app.py
import ttkbootstrap as ttk
from ttkbootstrap.constants import *
from ttkbootstrap.dialogs import Messagebox
from tkinter import filedialog
import os

class NotizApp:
    def __init__(self):
        self.app = ttk.Window(
            title="Notiz-App",
            themename="darkly",
            size=(600, 500)
        )
        self.app.columnconfigure(0, weight=1)
        self.app.rowconfigure(1, weight=1)
        self.aktueller_pfad = None
        self._ui_aufbauen()

    def _ui_aufbauen(self):
        # --- Toolbar ---
        toolbar = ttk.Frame(self.app)
        toolbar.grid(row=0, column=0, sticky="ew", padx=5, pady=5)

        ttk.Button(toolbar, text="📄 Neu",       command=self.neu,       bootstyle="secondary", width=8).pack(side="left", padx=2)
        ttk.Button(toolbar, text="📂 Öffnen",    command=self.oeffnen,   bootstyle="primary",   width=10).pack(side="left", padx=2)
        ttk.Button(toolbar, text="💾 Speichern", command=self.speichern, bootstyle="success",   width=12).pack(side="left", padx=2)
        ttk.Button(toolbar, text="🗑 Löschen",   command=self.loeschen,  bootstyle="danger",    width=10).pack(side="left", padx=2)

        # Zeichenzähler rechts
        self.zaehler_label = ttk.Label(toolbar, text="0 Zeichen", bootstyle="secondary")
        self.zaehler_label.pack(side="right", padx=10)

        # --- Textbereich ---
        self.textfeld = ttk.ScrolledText(self.app, font=("Consolas", 12))
        self.textfeld.grid(row=1, column=0, sticky="nsew", padx=5, pady=(0,5))
        self.textfeld.bind("<KeyRelease>", self._zaehlen)

        # --- Statusleiste ---
        self.status = ttk.Label(self.app, text="Bereit", bootstyle="inverse-secondary")
        self.status.grid(row=2, column=0, sticky="ew")

    def _zaehlen(self, *args):
        text = self.textfeld.get("1.0", "end-1c")
        self.zaehler_label.config(text=f"{len(text)} Zeichen")

    def neu(self):
        self.textfeld.delete("1.0", "end")
        self.aktueller_pfad = None
        self.app.title("Notiz-App — Neue Datei")
        self.status.config(text="Neue Datei")
        self._zaehlen()

    def oeffnen(self):
        pfad = filedialog.askopenfilename(filetypes=[("Textdateien", "*.txt"), ("Alle", "*.*")])
        if pfad:
            with open(pfad, "r", encoding="utf-8") as f:
                inhalt = f.read()
            self.textfeld.delete("1.0", "end")
            self.textfeld.insert("1.0", inhalt)
            self.aktueller_pfad = pfad
            self.app.title(f"Notiz-App — {os.path.basename(pfad)}")
            self.status.config(text=f"Geöffnet: {pfad}")
            self._zaehlen()

    def speichern(self):
        if not self.aktueller_pfad:
            pfad = filedialog.asksaveasfilename(defaultextension=".txt")
            if not pfad:
                return
            self.aktueller_pfad = pfad
        with open(self.aktueller_pfad, "w", encoding="utf-8") as f:
            f.write(self.textfeld.get("1.0", "end-1c"))
        self.app.title(f"Notiz-App — {os.path.basename(self.aktueller_pfad)}")
        self.status.config(text=f"Gespeichert: {self.aktueller_pfad}")

    def loeschen(self):
        if Messagebox.yesno("Inhalt wirklich löschen?", "Bestätigung") == "Yes":
            self.neu()

    def starten(self):
        self.app.mainloop()

if __name__ == "__main__":
    NotizApp().starten()
🎉 Glückwunsch! Du hast eine vollständige Desktop-App mit Dateioperationen, Toolbar und Statusleiste gebaut.

🎓 Glückwunsch!

Du kennst jetzt alle Grundlagen von ttkbootstrap.

Installation ✓ Themes ✓ Widgets ✓ Layout ✓ Events ✓ Formulare ✓ Dialoge ✓ Projekt ✓
Offizielle Docs