#!/usr/bin/env python3 """ Kapitel 14: Grafische Desktop-Anwendung (Startbares Skript) 🖥️✨ ================================================================ Führe diese Datei auf deinem Desktop aus (z.B. in VS Code auf Mac/Windows/Linux): python3 gui_app.py Features dieser erweiterten Version: - Saubere Model-View-Controller (MVC) Architektur - Farb-Feedback (Grün bei positiven, Rot bei negativen Zahlen) - Tastatur-Shortcuts: [Pfeil nach oben] -> Erhöhen [Pfeil nach unten] -> Verringern [R] -> Zurücksetzen - Warnung bei Grenzwert-Überschreitungen """ import sys from typing import Optional try: import tkinter as tk from tkinter import messagebox TK_AVAILABLE = True except ImportError: TK_AVAILABLE = False class _MockWidget: def __init__(self, *args, **kwargs): pass def pack(self, *args, **kwargs): pass def grid(self, *args, **kwargs): pass def columnconfigure(self, *args, **kwargs): pass def bind(self, *args, **kwargs): pass def config(self, *args, **kwargs): pass def insert(self, *args, **kwargs): pass def delete(self, *args, **kwargs): pass def get(self): return "1" class _MockTkModule: Tk = _MockWidget Label = _MockWidget Button = _MockWidget Entry = _MockWidget Frame = _MockWidget LabelFrame = _MockWidget tk = _MockTkModule() messagebox = None class ZaehlerLogik: """Modell für Zähler, Grenzwerte und Schrittweite.""" def __init__(self, startwert: int = 0, min_wert: int = -100, max_wert: int = 100): self.wert: int = startwert self.min_wert: int = min_wert self.max_wert: int = max_wert self.schrittweite: int = 1 self.klick_anzahl: int = 0 def erhoehen(self) -> int: self.klick_anzahl += 1 self.wert = min(self.max_wert, self.wert + self.schrittweite) return self.wert def verringern(self) -> int: self.klick_anzahl += 1 self.wert = max(self.min_wert, self.wert - self.schrittweite) return self.wert def zuruecksetzen(self) -> int: self.klick_anzahl += 1 self.wert = 0 return self.wert def setze_schrittweite(self, schritt: int) -> bool: if schritt > 0: self.schrittweite = schritt return True return False def ist_gerade(self) -> bool: return self.wert % 2 == 0 def __str__(self) -> str: return f"Zählerstand: {self.wert} (Schritt: {self.schrittweite})" class DesktopZaehlerGUI: """Moderne grafische Desktop-Benutzeroberfläche mit Tkinter.""" def __init__(self, root: tk.Tk): self.root = root self.root.title("Python OOP • Desktop Zähler 🔢") self.root.geometry("420x440") self.root.minsize(360, 400) self.root.configure(bg="#f8fafc") self.logik = ZaehlerLogik(startwert=0, min_wert=-100, max_wert=100) # UI Komponenten aufbauen self._erstelle_widgets() self._binde_tasten() self.aktualisiere_anzeige() def _erstelle_widgets(self): # 1. Header Frame frame_header = tk.Frame(self.root, bg="#4f46e5", pady=14) frame_header.pack(fill="x") self.label_titel = tk.Label( frame_header, text="🔢 Python Desktop Zähler", font=("Helvetica", 16, "bold"), bg="#4f46e5", fg="white", ) self.label_titel.pack() self.label_sub = tk.Label( frame_header, text="Kapitel 14 • Tkinter & OOP", font=("Helvetica", 9), bg="#4f46e5", fg="#c7d2fe", ) self.label_sub.pack() # 2. Haupt-Anzeige Frame frame_card = tk.Frame(self.root, bg="#ffffff", bd=1, relief="solid", padx=20, pady=15) frame_card.pack(fill="x", padx=25, pady=15) self.label_anzeige = tk.Label( frame_card, text="0", font=("Helvetica", 42, "bold"), bg="#ffffff", fg="#4f46e5", ) self.label_anzeige.pack(pady=(5, 0)) self.label_info = tk.Label( frame_card, text="Klicks: 0 | Gerade Zahl", font=("Helvetica", 10), bg="#ffffff", fg="#64748b", ) self.label_info.pack(pady=(0, 5)) # 3. Button Steuerungsbereich frame_buttons = tk.Frame(self.root, bg="#f8fafc") frame_buttons.pack(fill="x", padx=25, pady=5) frame_buttons.columnconfigure(0, weight=1) frame_buttons.columnconfigure(1, weight=1) self.btn_plus = tk.Button( frame_buttons, text="➕ Erhöhen [↑]", font=("Helvetica", 11, "bold"), bg="#10b981", fg="white", activebackground="#059669", activeforeground="white", relief="flat", pady=8, cursor="hand2", command=self.klick_plus, ) self.btn_plus.grid(row=0, column=0, padx=5, pady=5, sticky="ew") self.btn_minus = tk.Button( frame_buttons, text="➖ Verringern [↓]", font=("Helvetica", 11, "bold"), bg="#ef4444", fg="white", activebackground="#dc2626", activeforeground="white", relief="flat", pady=8, cursor="hand2", command=self.klick_minus, ) self.btn_minus.grid(row=0, column=1, padx=5, pady=5, sticky="ew") self.btn_reset = tk.Button( frame_buttons, text="↺ Zurücksetzen [R]", font=("Helvetica", 10), bg="#e2e8f0", fg="#1e293b", activebackground="#cbd5e1", relief="flat", pady=6, cursor="hand2", command=self.klick_reset, ) self.btn_reset.grid(row=1, column=0, columnspan=2, padx=5, pady=5, sticky="ew") # 4. Schrittweite Konfiguration frame_settings = tk.LabelFrame( self.root, text=" ⚙️ Einstellungen ", font=("Helvetica", 9, "bold"), bg="#f8fafc", fg="#475569", padx=10, pady=8, ) frame_settings.pack(fill="x", padx=25, pady=10) tk.Label(frame_settings, text="Schrittweite:", bg="#f8fafc", font=("Helvetica", 10)).pack( side="left", padx=5 ) self.entry_schritt = tk.Entry( frame_settings, width=6, font=("Helvetica", 11), justify="center" ) self.entry_schritt.insert(0, "1") self.entry_schritt.pack(side="left", padx=5) self.btn_schritt = tk.Button( frame_settings, text="Übernehmen", font=("Helvetica", 9), bg="#6366f1", fg="white", relief="flat", cursor="hand2", command=self.klick_schritt_setzen, ) self.btn_schritt.pack(side="left", padx=5) def _binde_tasten(self): """Bindet bequeme Tastatur-Shortcuts an das Fenster.""" self.root.bind("", lambda event: self.klick_plus()) self.root.bind("", lambda event: self.klick_minus()) self.root.bind("", lambda event: self.klick_reset()) self.root.bind("", lambda event: self.klick_reset()) self.root.bind("", lambda event: self.klick_schritt_setzen()) def klick_plus(self) -> None: self.logik.erhoehen() self.aktualisiere_anzeige() def klick_minus(self) -> None: self.logik.verringern() self.aktualisiere_anzeige() def klick_reset(self) -> None: self.logik.zuruecksetzen() self.aktualisiere_anzeige() def klick_schritt_setzen(self) -> None: eingabe = self.entry_schritt.get().strip() try: schritt = int(eingabe) if self.logik.setze_schrittweite(schritt): self.aktualisiere_anzeige() else: self.label_info.config( text="⚠️ Schrittweite muss größer als 0 sein!", fg="#ef4444" ) except ValueError: self.label_info.config( text="⚠️ Bitte eine ganze Zahl eingeben!", fg="#ef4444" ) def aktualisiere_anzeige(self) -> None: # Zählerwert darstellen self.label_anzeige.config(text=str(self.logik.wert)) # Farb-Feedback if self.logik.wert > 0: self.label_anzeige.config(fg="#10b981") # Grün elif self.logik.wert < 0: self.label_anzeige.config(fg="#ef4444") # Rot else: self.label_anzeige.config(fg="#4f46e5") # Neutral # Info-Text paritaet = "Gerade" if self.logik.ist_gerade() else "Ungerade" info = f"Klicks: {self.logik.klick_anzahl} | {paritaet} | Schritt: {self.logik.schrittweite}" if self.logik.wert >= self.logik.max_wert: info += " ⚠️ Max erreicht!" elif self.logik.wert <= self.logik.min_wert: info += " ⚠️ Min erreicht!" self.label_info.config(text=info, fg="#64748b") def main(): if not TK_AVAILABLE: print("=" * 60) print("⚠️ Hinweis: Tkinter ist in dieser Linux-Umgebung nicht installiert.") print("Um die grafische Oberfläche mit echtem Fenster zu sehen:") print(" 1. Klone oder öffne diesen Ordner auf deinem PC/Laptop (Windows/Mac/Linux).") print(" 2. Führe 'python3 gui_app.py' aus!") print("=" * 60) return try: root = tk.Tk() app = DesktopZaehlerGUI(root) print("🖥️ GUI-Fenster gestartet! Schließe das Fenster zum Beenden.") root.mainloop() except Exception as e: print(f"⚠️ Konnte GUI nicht initialisieren (kein X11-Display gefunden): {e}") print("💡 Teste deinen Code headless mit: python3 test_aufgabe.py") if __name__ == "__main__": main()