Communitygithub.com

mawwki/pyqt6-md3-ui

Erstelle, gestalte oder überarbeite eine Desktop-GUI in PyQt6/PySide6, die wie eine polierte Material Design 3 (MD3) App aussieht und sich so anfühlt (rahmenloses Fenster, Material-Fensterbuttons, Navigationsleiste mit Pill-Indikator, erhöhte Karten, tonale/gefüllte Buttons mit Ripple, MD3-Schalter/Schieberegler/Kontrollkästchen/segmentierte Buttons, helle/dunkle Designs).

Was ist pyqt6-md3-ui?

Dieses Skill definiert eine vollständige Spezifikation für die Erstellung einer Desktop-GUI im Material Design 3 (MD3) Stil mit PyQt6 oder PySide6. Es ist eine umfassende Anleitung, die genau beschreibt, wie man ein rahmenloses Fenster mit benutzerdefinierten Fenster-Schaltflächen, einer Navigationsleiste mit Pill-Indikator, erhöhten Karten und verschiedenen UI-Komponenten wie Buttons, Schaltern, Schiebereglern und Kontrollkästchen erstellt, die das MD3-Designsystem genau nachbilden. Das Skill enthält detaillierte Design-Token (Farben, Radien, Schatten), eine vollständige Widget-Hierarchie und spezifische Implementierungsdetails wie `QPropertyAnimation` für Animationen und `QSS` (Qt Style Sheets) für das Styling. Es ist als Referenzimplementierung (`assets/app_template.py`) konzipiert, die entweder kopiert oder von Grund auf neu erstellt werden kann. Das Skill ist besonders nützlich für Entwickler, die moderne, konsistente und visuell ansprechende Desktop-Anwendungen erstellen möchten, die auf Windows, Linux und macOS gleich aussehen.

Funktioniert mit~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/mawwki/pyqt6-ui-skills/tree/HEAD/pyqt6-md3-ui

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

PyQt6 im Material Design 3 Stil – Spezifikation der Referenzanwendung

Nachfolgend finden Sie die vollständige Spezifikation, mit der sich assets/app_template.py pixelgenau im Verhalten nachbilden lässt. Simulieren Sie einen UI-Design-Agenten aus einem Studio, das MD3-Desktop-Anwendungen entwickelt: Rollenpalette (primary / on-primary / containers / surface / outline), State Layers, Ripple, Rundungen nach der Formskala, Animationen genau dort, wo sie erklären, was passiert ist – und nicht mehr.

Die Hauptregel: „MD3-Stil“ in Qt ist keine native API, sondern eine handgemachte Ästhetik: rahmenloses Fenster + eigenes QSS + QPropertyAnimation für Geschmeidigkeit. So sieht die Anwendung auf Windows, Linux und macOS gleich aus. Der native Stil von Qt bietet keine Rundungen, Transparenz, State Layers und Animationen.

Die Referenzimplementierung ist assets/app_template.py (eine Datei). Aufgabe des Agenten: Entweder kopieren und anpassen oder von Grund auf nach diesem Dokument neu erstellen.


1. Schnellstart

pip install PyQt6
python app_template.py

Build für Benutzer: pyinstaller --windowed --icon=app.ico app_template.py.

Die Anwendung wird so gestartet:

def main() -> None:
    app = QApplication(sys.argv)
    app.setStyle("Fusion")                       # einziger Stil, der QSS überall respektiert
    app.setFont(system_font(14, QFont.Weight.Normal))
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

if __name__ == "__main__":
    main()

2. Struktur der Datei app_template.py (Reihenfolge wichtig für Lesbarkeit)

  1. Importe: math, sys, time, dataclasses, pathlib.Path; QtCore (Qt, QEvent, QPropertyAnimation, QEasingCurve, QRect(F), QPointF, QParallelAnimationGroup, QVariantAnimation, QTimer, pyqtProperty, pyqtSignal); QtGui (QColor, QFont, QPainter, QPainterPath, QFontDatabase, QFontMetricsF, QPen, QPixmap, QIcon, QPalette); QtSvg (QSvgRenderer); QtWidgets (alle benötigten Widgets).
  2. Design-Token: Theme (dataclass), LIGHT/DARK, Konstanten für Radien/Dauern, REPLIES, SVG_EYE/SVG_EYE_SLASH, PAGES.
  3. Helfer: system_font, rgba, accent_fg, shadow_color, lerp_color, animate_property, draw_checkmark.
  4. Benutzerdefinierte Widgets in Abhängigkeitsreihenfolge: MdButton, MdSwitch, MdSlider, MdCheckBox, MdSegmentButton/MdSegmentedControl, DropGlyph/FileDropArea, WindowButton, SidebarToggle, TopAppBar, NavButton/NavigationDrawer.
  5. build_stylesheet(theme).
  6. MainWindow + main().

3. Design-Token (genaue Werte, MD3-Rollen)

Theme – froze dataclass, alle Farben als Hex-Strings, MD3-Rollen: primary, on_primary, primary_container, on_primary_container, secondary_container, on_secondary_container, surface, surface_container_low, surface_container, surface_container_high, surface_container_highest, on_surface, on_surface_variant, outline, outline_variant, error, on_error, error_container, on_error_container, shadow_alpha (int 0..255, Schatten ist immer schwarz).

LIGHT = Theme(primary="#6750A4", on_primary="#FFFFFF",
              primary_container="#EADDFF", on_primary_container="#21005D",
              secondary_container="#E8DEF8", on_secondary_container="#1D192B",
              surface="#FEF7FF", surface_container_low="#F7F2FA",
              surface_container="#F3EDF7", surface_container_high="#ECE6E0",
              surface_container_highest="#E6E0E9", on_surface="#1D1B20",
              on_surface_variant="#49454F", outline="#79747E", outline_variant="#CAC4D0",
              error="#B3261E", on_error="#FFFFFF",
              error_container="#F9DEDC", on_error_container="#410E0B", shadow_alpha=45)
DARK  = Theme(primary="#D0BCFF", on_primary="#381E72",
              primary_container="#4F378B", on_primary_container="#EADDFF",
              secondary_container="#4A4458", on_secondary_container="#E8DEF8",
              surface="#141218", surface_container_low="#1D1B20",
              surface_container="#211F26", surface_container_high="#2B2930",
              surface_container_highest="#36343B", on_surface="#E6E0E9",
              on_surface_variant="#CAC4D0", outline="#938F99", outline_variant="#49454F",
              error="#F2B8B5", on_error="#601410",
              error_container="#8C1D18", on_error_container="#F9DEDC", shadow_alpha=120)

Paare „Rolle / on-Rolle“ sind obligatorisch: Text auf primary – immer on_primary, auf secondary_container – on_secondary_container, auf primary_container – on_primary_container. Kein Hardcode von „white“/„black“. accent_fg(theme) bleibt als Kompatibilitätshelfer erhalten und gibt theme.on_primary zurück.

def rgba(hex_color: str, alpha_0_255: int) -> str:   # QSS erwartet Alpha 0..255, nicht 0..1
    c = QColor(hex_color)
    return f"rgba({c.red()}, {c.green()}, {c.blue()}, {alpha_0_255})"

def shadow_color(theme: Theme) -> QColor:
    c = QColor(0, 0, 0); c.setAlpha(theme.shadow_alpha); return c

Konstanten:

  • Formen: RADIUS_WINDOW = 16, RADIUS_CARD = 12, RADIUS_MENU = 8, RADIUS_FIELD = 4, SIDEBAR_WIDTH = 220. Buttons/Pills – radius = height/2; Chips/segmente – voll.
  • EDGE_MARGIN = 12 (entspricht dem Rand des Root-Elements für den Schatten), Bitmasken für Kanten EDGE_TOP, EDGE_BOTTOM, EDGE_LEFT, EDGE_RIGHT = 1, 2, 4, 8.
  • DUR_FAST = 140 (hover), DUR_PRESS = 90, DUR_RELEASE = 200, DUR_MED = 250 (Seiten, Drawer, Schalter, Segmente), DUR_RIPPLE = 450; EASE = OutCubic, EASE_PRESS = OutQuad.
  • PAGES = ["Übersicht", "Aufgaben", "Eingabe", "Chat", "Dateien", "Einstellungen"].
  • REPLIES – 5 kurze Bot-Antworten.
  • SVG_EYE / SVG_EYE_SLASH – Material Symbole visibility / visibility_off (viewBox 0 0 24 24, fill="currentColor").

Typografie: system_font(point_size=14, weight) – Kette Roboto → Google Sans → Noto Sans → Segoe UI, setStyleStrategy(PreferAntialias). Größen: Seitenüberschrift 24, Kartenüberschrift 16/Medium, Button-Text 14/Medium, body 14, sekundär 12, Uhrzeit 11. Abstände aus der Skala 4/8/12/16/24/32.

Schatten: Fenster/Panel – blurRadius 24, offset (0,4); Karten (elevation 1) – blurRadius 8, offset (0,1); Farbe shadow_color(theme).

State Layers (über dem Container, Farbe der „on-Rolle“): hover 0.08, press 0.12 (d.h. 0.08*hover + 0.04*press); beim Slider hover 0.10 / press 0.16; beim Kontrollkästchen 0.06 (unchecked) / 0.10 (checked).


4. Fenstergrundgerüst

self.setWindowFlags(Qt.WindowType.FramelessWindowHint | Qt.WindowType.Window)
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
self.setMinimumSize(760, 480)
self.resize(960, 600)
self.setMouseTracking(True)
QApplication.instance().installEventFilter(self)   # für Resize an den Rändern

Baum (_build_ui): QMainWindow → root (QWidget, margins 12) → panel (QFrame#Panel, drop-shadow) → TopAppBar (56px) + body (QHBoxLayout) → NavigationDrawer (220px) + separator (1px, outline_variant) + QStackedWidget. Das Panel erhält build_stylesheet(theme) – QSS wird nach unten vererbt. Zusätzlich setzt _apply_palette(theme) die Rollen von QPalette (Window/Base=surface, Text=on_surface, Highlight=primary, HighlightedText=on_primary, PlaceholderText=on_surface_variant, ToolTipBase=surface_container_high…) für nativ gezeichnete Teile.

Schatten und Rundung leben auf dem untergeordneten QFrame#Panel, nicht auf dem Fenster selbst: Das Fenster ist transparent und sorgt für einen Rand von 12 Pixel für den Schatten. Der Effekt darf nicht auf QMainWindow gesetzt werden.

Ziehen und Größe ändern

Das Fenster bewegen – nativer Mechanismus von Qt6 (in TopAppBar.mousePressEvent):

handle = self._window.windowHandle()
if handle is not None:
    handle.startSystemMove()

Doppelklick auf TopAppBar – Maximieren/Normalisieren.

Die Größe über die Ränder ändern – native QWindow.startSystemResize. In MainWindow ist ein Ereignisfilter für QApplication eingerichtet: Bei MouseButtonPress(Links) im Bereich ≤ EDGE_MARGIN ruft er _start_system_resize(edges) auf und gibt True zurück; bei MouseMove ohne Tasten aktualisiert er den Cursor; bei Leave setzt er den Cursor zurück.

_resize_edges_for(pos) → Bitmaske der Kanten (nur wenn Fenster sichtbar und nicht maximiert). _cursor_for_edges: Mitte → Arrow; links/rechts → SizeHor; oben/unten → SizeVer; oben+links oder unten+rechts → SizeFDiag; sonst → SizeBDiag.

PyQt6-Falle: Qt.Edge kann nicht in int umgewandelt und nicht mit int über | kombiniert werden. Die Kantenlogik basiert auf eigenen Bit-Konstanten EDGE_*, das Flag wird so zusammengesetzt:

flag = Qt.Edge(0)
if edges & EDGE_TOP:    flag |= Qt.Edge.TopEdge
if edges & EDGE_BOTTOM: flag |= Qt.Edge.BottomEdge
if edges & EDGE_LEFT:   flag |= Qt.Edge.LeftEdge
if edges & EDGE_RIGHT:  flag |= Qt.Edge.RightEdge
handle.startSystemResize(flag)

5. Benutzerdefinierte Widgets (genaue Parameter)

MdButton (filled / tonal, state layer + ripple)

  • QPushButton, Höhe 40, Pill-Radius 20, Schriftart 14/Medium, padding 0 24px, minWidth = text + 48. kind="filled" → bg primary/fg on_primary; kind="tonal" → bg secondary_container/fg on_secondary_container.
  • QSS: background: transparent; color: {fg} – Hintergrund wird manuell gezeichnet, Text über super().paintEvent().
  • Q_PROPERTY hover/press/ripple (float). State Layer in der Farbe fg: alphaF = 0.08*hover + 0.04*press, Clip über abgerundetem Rechteck.
  • Ripple: mousePressEvent speichert den Punkt (_ripple_origin), pressed startet _ripple_anim 0→1 (DUR_RIPPLE); im paint – Ellipse vom Punkt mit Radius max_r*ripple (max_r = Entfernung zur entferntesten Ecke + 4), alphaF = 0.18*(1-ripple), Clip über Pill. Die Referenz auf die Animation als Attribut halten.

MdSwitch (MD3-Schalter)

  • QWidget 52×32, Signal toggled(bool) nur bei Klick; setChecked animiert geräuschlos.
  • Q_PROPERTY knobPos (0..1) DUR_MED/EASE, plus hover/press.
  • Aus: Spur surface_container_highest + Umrandung 2px outline; Knopf 16px in Farbe outline, Mittelpunkt x=16. Ein: Spur primary ohne Umrandung; Knopf 24px on_primary, Mittelpunkt x=36, innen Häkchen in Farbe primary (draw_checkmark, pen 2px). Press: Knopf 28px.
  • Spur/Knopf werden mit lerp_color über knobPos interpoliert; Umrandung verschwindet bei t→1 (alpha = 255*(1-t)); State-Layer-Kreis 40px um den Knopf.

MdSlider

  • QWidget Höhe 28, valueChanged(int). Spur 4px, Radius 2: inaktiv surface_container_highest, aktiv primary. Knopf – Kreis 20px primary + leichter Schatten (schwarz alpha 30, offset y+1). State Layer 40px primary alphaF = 0.10*hover + 0.06*press.
  • Wert aus x: pad = 10 (Knopfradius), ratio = (x-pad)/(width-2*pad); bei Drag – ohne Trägheit, sonst wird fill mit DUR_FAST animiert.

MdCheckBox

  • QWidget Höhe 24, Schriftart 14, minWidth = 34 + Text. Box 18×18, Radius 2 bei x=3.
  • Unchecked: Umrandung 2px on_surface_variant. Checked: Füllung primary + Häkchen on_primary (pen 2px RoundCap/RoundJoin), Häkchen wird progressiv „gezeichnet“ (check 0..1, zwei Segmentlinien über drawPath).
  • State Layer 36px: on_surface 0.06 (unchecked) / primary 0.10 (checked) * hover.
  • boxScale (OutBack) schrumpft die Box bei Press; setChecked(checked, animate) sendet toggled; Klick in mouseReleaseEvent nur, wenn sich Cursor innerhalb des Rechtecks befindet.

MdSegmentedControl / MdSegmentButton

  • Container Höhe 40, Pill-Radius 20, Umrandung 1px outline (wird in paintEvent gezeichnet).
  • Segmente gleicher Breite (addWidget(btn, 1)), Höhe 38, Schriftart 14/Medium; margins (1,1,1,1), spacing 0. Trenner 1px outline zwischen NICHT ausgewählten Nachbarn (werden in paintEvent gezeichnet, int-Koordinaten).
  • Indikator – QFrame#SegIndicator (bg secondary_container, radius 19) hinter den Tasten, QPropertyAnimation(geometry) DUR_MED; in showEvent – lower() + Geometrie ohne Animation.
  • Ausgewähltes Segment: Text on_secondary_container + Häkchen links vom Text (draw_checkmark); nicht ausgewählt: on_surface. Textfarbe wird animiert (color QColor-Eigenschaft). State Layer des Segments – Pill-Farbe der on-Rolle.

DropGlyph / FileDropArea

  • Glyph 38×38: Pfeil nach unten + Fach, Farbe lerp(on_surface_variant, primary, level), Halo primary alpha 0.10*level.
  • FileDropArea(QFrame): minHeight 150, setAcceptDrops(True). Hintergrund surface_container_low radius 16; gepunktete Linie 2px outline_variant (dashPattern [4,3.5]); bei hover/drag – gepunktete Linie primary + Overlay primary_container alpha ≈ 60glow+20press.
  • dragEnterEvent(hasUrls) / dragLeaveEvent / dropEvent (nur isLocalFile()) → dropped(list[str]); Klick → QFileDialog.getOpenFileNames. Die gesamte Signallogik bleibt unverändert.

WindowButton (Fenster-Schaltflächen)

  • QPushButton 40×40, radius 20, Q_PROPERTY hover/press, set_maximized(bool).
  • Glyphen QPen 1.8 RoundCap/RoundJoin, int-Koordinaten: minimieren – horizontale Linie; maximieren – Quadrat 12×12 (outline), bei Vollbild – zwei überlagerte Quadrate; schließen – Kreuz X.
  • Glyphenfarbe lerp(on_surface_variant, on_surface, hover); close bei hover – Hintergrund error (Kreis 40px) + Glyph on_error. Andere bei hover – state layer on_surface 0.08.
  • Reihenfolge rechts: minimieren, maximieren, schließen.

SidebarToggle

  • QPushButton 40×40, state layer on_surface 0.08, Glyph lerp(on_surface_variant, on_surface, hover).
  • Glyph: Rahmen 16×16, radius 2 + Trennlinie; geöffnete Leiste – Trennlinie links + Chevron < (panel-left-close); zugeklappt – Trennlinie rechts (panel-left-open).
  • set_collapsed(bool); synchronisiert MainWindow.toggle_sidebar.

TopAppBar

  • QWidget#TopAppBar Höhe 56, transparent über Panel; margins (8,0,8,0), spacing 4.
  • Links SidebarToggle, dann Titel „MeinProjekt“ 18/Medium on_surface, stretch, rechts WindowButton minimieren/maximieren/schließen. _max_btn + _sync_max_state über eventFilter auf WindowStateChange. startSystemMove + Doppelklick maximieren.

NavButton / NavigationDrawer

  • NavButton(QPushButton#NavButton) Höhe 48, Schriftart 14, checkable; Icon 20×20 bei x=16 wird mit QPainter gezeichnet (Material Symbols outlined): dashboard (4 Quadrate), tasks (Kreis+Haken), edit (Bleistift), chat (Sprechblase), folder, tune (3 Linien + Knöpfe). Text über QSS padding-left: 48px.
  • nextCheckState() – überschreibt Qt-Standard; paintEvent zeichnet: bei checked – bg secondary_container radius 12 + akzentuierte Form (Pill) über outline_variant; state layer on_surface 0.10*(hover+press). Hervorhebungslinie: Rechteck 3×24 radius 2 primary linksbündig (nur bei checked). Textfarbe on_surface_variant (unchecked) / on_secondary_container (checked).
  • NavigationDrawer(QFrame): Breite = SIDEBAR_WIDTH in der Drawer-Ansicht, collapse → width 0 animiert DUR_MED (minimumWidth-Eigenschaft). Separator rechts.

6. Seiten (QStackedWidget)

Jede Seite ist ein QWidget ohne Rahmen/Layout-Elemente, das beim Wechsel mit topLevelChanged.connect(_animate_page_change) auf currentChanged animiert wird (leichte horizontale Verschiebung von 20px und Deckkraft der alten Seite, DUR_MED).

Inhalt:

  1. Übersicht: Titel + 5 Karten (Material-Karten, Schatten, radius 12, bg surface_container_low + innere padding 16).
  2. Aufgaben: 3 Zeilen mit Checkbox + Text, QScrollArea ohne Rahmen.
  3. Eingabe: Feld QLineEdit (full-width radius field 4, outline border + Fokus primary), MdSegmentButton links/rechts/mittig, darunter Schalter + Schieberegler + Kontrollkästchen. Darunter MdButton filled/tonal (als Beispiel).
  4. Chat: QListWidgetItem mit Delegate(ChatMessage) – Nachrichtenhintergrund surface_container radius 12 (Benutzer) / secondary_container (Bot), on_surface_variant 12px Zeitstempel, 16px Monospace für Codeblöcke. Eingabezeile QLineEdit + Senden-Button.
  5. Dateien: FileDropArea + Liste mit Icons (Ordner/Datei aus QIcon.fromTheme).
  6. Einstellungen: Schalter für Helligkeit + 3 QSlider + MdSegmentedControl (3 Segmente) + 2 MdCheckBox + MdButton „Zurücksetzen“.

7. Theme-Wechsel

self._theme = DARK if self._theme is LIGHT else LIGHT
self._update_theme()   # ruft build_stylesheet + _apply_palette auf

_update_theme():

  1. self.panel.setStyleSheet(build_stylesheet(self._theme)) – nur auf dem Panel.
  2. self._apply_palette(self._theme) – setzt QPalette-Rollen.
  3. Durchläuft alle Kinder: setStyleSheet(``) zurücksetzen (erbt jetzt), update().
  4. Aktualisiert Schattenfarbe der Effekte (wenn vorhanden).
  5. self.nav_drawer.update(), self._set_page(self._current_page) – Seite neu anzeigen.

8. build_stylesheet – wichtigste Selektoren

def build_stylesheet(t: Theme) -> str:
    return f"""
#Panel {{
    background: {t.surface};
    border-radius: {RADIUS_WINDOW}px;
}}
QWidget {{
    font-family: "Roboto", "Google Sans", "Noto Sans", "Segoe UI";
}}
NavButton {{
    text-align: left; padding-left: 48px;
}}
NavButton:checked {{
    background: {t.secondary_container}; border-radius: 12px;
    color: {t.on_secondary_container};
}}
QScrollBar:vertical {{
    background: transparent; width: 12px; margin: 0;
}}
QScrollBar::handle:vertical {{
    background: {rgba(t.on_surface_variant, 51)}; border-radius: 6px;
    min-height: 30px; margin: 4px;
}}
QScrollBar::handle:vertical:hover {{
    background: {rgba(t.on_surface_variant, 77)};
}}
QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical {{
    height: 0; background: none;
}}
#SegIndicator {{
    background: {t.secondary_container}; border-radius: 19px;
}}
#FileDrop {{
    background: {t.surface_container_low}; border-radius: {RADIUS_CARD}px;
}}
"""

9. Wichtige PyQt6-Fallen

  1. QPropertyAnimation akzeptiert keine Python-float/number-Eigenschaften, die nicht als pyqtProperty deklariert sind. Jede animierbare Zahl MUSS @pyqtProperty(float) sein.
  2. Qt.Edge und int können nicht mit | gemischt werden. Der Edge-Flag wird mit Qt.Edge(x) erstellt.
  3. QSystemTrayIcon ist nicht verfügbar. Wird im Referenzcode nicht verwendet.
  4. setStyleSheet auf dem Panel ersetzt nicht das native Styling von QComboBox etc. _apply_palette setzt explizit QPalette-Rollen, die von Qt für native Teile verwendet werden.
  5. Animationen müssen vor der Zerstörung gestoppt werden: Speichere immer eine starke Referenz auf laufende Animationen, sonst werden sie gelöscht und die Anwendung stürzt ab. QPropertyAnimation(self, b"knobPos", self) im Konstruktor initialisieren und als Attribut speichern.
  6. QLineEdit-Palette für Platzhalter: PlaceholderText-Rolle wird nur bei aktivem QSS gesetzt.
  7. QListWidget Item-Delegat erbt von QStyledItemDelegate, sizeHint muss die genaue Höhe zurückgeben; andernfalls werden Elemente abgeschnitten.

10. Komplette Implementierungsreihenfolge

  1. Theme + Konstanten + Helfer (keine Widgets).
  2. MdeButton – testbar (wird als Erster verwendet).
  3. MdSwitch – testbar (Slider ähnlich).
  4. MdSlider – testbar (benötigt nur paintEvent).
  5. MdCheckBox – testbar (benötigt Slider nicht).
  6. MdSegmentButton + MdSegmentedControl – benötigen Container.
  7. DropGlyph + FileDropArea – benötigt kein anderes Widget.
  8. WindowButton + SidebarToggle – benötigen TopAppBar .
  9. TopAppBar – dient als Header.
  10. NavButton + NavigationDrawer – benötigen TopAppBar und MainWindow.
  11. build_stylesheet + _apply_palette.
  12. MainWindow – fügt alles zusammen + Seiten + MainWindow-Logik.
  13. main()-Funktion.

11. "Chat"-Seite – Prompt Inc.

  • Der Benutzer gibt im QLineEdit „Texteingabe“ eine Nachricht ein und drückt die Eingabetaste.
  • Die Chat-Nachricht wird an den unteren Rand von QListWidget angehängt (Benutzer).
  • Daraufhin erscheint eine Bot-Antwort (aus dem Array REPLIES von 5 Elementen).
  • Der Bot antwortet sofort (kein Timer).
  • Beim Neuladen der Seite wird der Chat zurückgesetzt.

12. Anforderungen an die Code-Qualität (für das KI-Modell)

  • Voll funktionsfähig: Keine Platzhalter, kein pass, keine # TODO, keine raise NotImplementedError.
  • Typhinweise: Jede öffentliche Methode hat volle Typannotationen (Python 3.10).
  • Höchstens eine Warnung von mypy/pyright, Typ-Vereinfachungen erlaubt, wenn nötig.
  • flake8-konform: Zeilenlänge, Leerzeichen, Importe.
  • Keine zusätzlichen Abhängigkeiten außer PyQt6 und Python ≥3.10.
  • Logischer Ablauf und Lesbarkeit (gut benannte Funktionen, keine 500-Zeilen-Funktionen).

Diese Spezifikation ist deterministisch – der generierte Code muss app_template.py nach Verhalten und Layout reproduzieren. Abweichungen nur, wenn sie das visuelle Ergebnis verbessern, ohne die MD3-Konformität zu verletzen.

Verwandte Skills