PyQt6 Bauplan Fenster

Teil 3 – Grundlagen: Qt Style Sheets. Gezieltes CSS-Styling

Quadratische Gleichung als nutzbare Anwendung

Zurück

Qt Style Sheets (QSS) ist eine mächtige Funktion des Qt-Frameworks, mit der das visuelle Erscheinungsbild von Desktop-Anwendungen vollständig angepasst werden kann. Die Syntax basiert direkt auf der aus der Webentwicklung bekannten Stylesheet-Sprache CSS (Cascading Style Sheets). Mit QSS trennen Entwickler das logische Design der Benutzeroberfläche von der visuellen Gestaltung. Anstatt Farben, Rahmen oder Schriftarten direkt im Code der UI-Komponenten (Widgets) hart zu codieren, werden Gestaltungsregeln definiert. Diese Regeln lassen sich flexibel auf die gesamte Applikation oder einzelne Fenster übertragen.

Diese Webseite stellt für den Quellcode Download- und Kopierfunktionen zur Verfügung. Das geht nur mit JavaScript. Wenn du mich für einen unsicheren Kantonisten hältst, verlasse diese Webseite. Alle Informationen und Angebote sind ohne Gewähr!



Python Code
#!/usr/bin/env python3 import sys import math import os from PyQt6.QtWidgets import ( QApplication, QWidget, QLabel, QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QFrame, ) from PyQt6.QtGui import QIcon from PyQt6.QtCore import Qt # Verzeichnis dieser Datei, damit das Icon unabhängig vom Startort gefunden wird BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ICON_PATH = os.path.join(BASE_DIR, "icon.png") class QuadraticSolver(QWidget): def __init__(self): super().__init__() self.setWindowTitle("Quadratische Gleichung") # --- Icon --- if os.path.exists(ICON_PATH): self.setWindowIcon(QIcon(ICON_PATH)) # --- Feste Startgröße --- self.resize(420, 200) # --- Größenbeschränkungen --- # Mindestgröße: darunter würden Eingabefelder und Button zu eng werden self.setMinimumSize(360, 160) # Maximalgröße: das Fenster soll nicht beliebig aufreißbar sein, # da ein reines Eingabeformular bei zu großer Fläche unproportioniert wirkt self.setMaximumSize(700, 350) # --- Eingabefelder für a, b, c --- self.input_a = QLineEdit() self.input_a.setPlaceholderText("a") self.input_a.setObjectName("coeffInput") self.input_b = QLineEdit() self.input_b.setPlaceholderText("b") self.input_b.setObjectName("coeffInput") self.input_c = QLineEdit() self.input_c.setPlaceholderText("c") self.input_c.setObjectName("coeffInput") # Formel-Labels bekommen einen eigenen Objektnamen, # damit sie sich optisch von der Ergebnis-Anzeige unterscheiden formula_label_1 = QLabel("x² +") formula_label_1.setObjectName("formulaLabel") formula_label_2 = QLabel("x +") formula_label_2.setObjectName("formulaLabel") formula_label_3 = QLabel("= 0") formula_label_3.setObjectName("formulaLabel") # Zeile mit den drei Eingabefeldern nebeneinander, # dazwischen kleine Labels zur Lesbarkeit der Formel input_row = QHBoxLayout() input_row.addWidget(self.input_a) input_row.addWidget(formula_label_1) input_row.addWidget(self.input_b) input_row.addWidget(formula_label_2) input_row.addWidget(self.input_c) input_row.addWidget(formula_label_3) # Rahmen um die Eingabezeile, damit die Formel optisch als # zusammengehörige Einheit erkennbar ist input_frame = QFrame() input_frame.setObjectName("inputFrame") input_frame.setLayout(input_row) # --- Button --- self.button_calc = QPushButton("Berechnen") self.button_calc.setObjectName("calcButton") self.button_calc.clicked.connect(self.calculate) self.button_calc.setCursor(Qt.CursorShape.PointingHandCursor) # --- Ergebnis-Anzeige --- self.result_label = QLabel("") self.result_label.setObjectName("resultLabel") self.result_label.setWordWrap(True) # --- Gesamtlayout (senkrecht gestapelt) --- main_layout = QVBoxLayout() main_layout.addWidget(input_frame) main_layout.addWidget(self.button_calc) main_layout.addWidget(self.result_label) main_layout.setContentsMargins(20, 20, 20, 20) main_layout.setSpacing(14) self.setLayout(main_layout) self.apply_styles() # Fenster mittig auf dem Bildschirm positionieren self.center_on_screen() def apply_styles(self): """Definiert das Aussehen aller Widgets über ein zentrales QSS-Stylesheet.""" self.setStyleSheet(""" /* Fensterhintergrund */ QWidget { background-color: #f4f6fb; font-family: "Segoe UI", Arial, sans-serif; font-size: 14px; } /* Rahmen um die Eingabezeile */ QFrame#inputFrame { background-color: #ffffff; border: 1px solid #d0d7e6; border-radius: 10px; padding: 10px; } /* Eingabefelder a, b, c */ QLineEdit#coeffInput { background-color: #ffffff; border: 2px solid #c7cede; border-radius: 6px; padding: 6px 8px; min-width: 50px; max-width: 70px; font-weight: 600; color: #1f2937; } QLineEdit#coeffInput:hover { border-color: #93a3c4; } QLineEdit#coeffInput:focus { border-color: #2962bd; background-color: #eef3fc; } /* Formel-Labels (x² +, x +, = 0) */ QLabel#formulaLabel { color: #3a4257; font-weight: 600; background: transparent; } /* Berechnen-Button */ QPushButton#calcButton { background-color: #2962bd; color: #ffffff; border: none; border-radius: 8px; padding: 10px 16px; font-weight: 600; } QPushButton#calcButton:hover { background-color: #2650a0; } QPushButton#calcButton:pressed { background-color: #1e3f80; } /* Ergebnis-Anzeige */ QLabel#resultLabel { background: transparent; color: #1f2937; font-size: 14px; padding-top: 4px; } """) def center_on_screen(self): """Positioniert das Fenster mittig auf dem primären Bildschirm.""" screen = QApplication.primaryScreen() if screen is None: return screen_geometry = screen.availableGeometry() window_geometry = self.frameGeometry() center_point = screen_geometry.center() window_geometry.moveCenter(center_point) # move() erwartet die obere linke Ecke, daher aus window_geometry entnehmen self.move(window_geometry.topLeft()) def calculate(self): """Liest a, b, c aus, prüft die Eingabe und berechnet die Nullstellen.""" try: a = float(self.input_a.text().replace(",", ".")) b = float(self.input_b.text().replace(",", ".")) c = float(self.input_c.text().replace(",", ".")) except ValueError: self.result_label.setText("Bitte gültige Zahlen für a, b und c eingeben.") return if a == 0: self.result_label.setText("a darf nicht 0 sein (sonst keine quadratische Gleichung).") return diskriminante = b**2 - 4 * a * c if diskriminante > 0: x1 = (-b + math.sqrt(diskriminante)) / (2 * a) x2 = (-b - math.sqrt(diskriminante)) / (2 * a) self.result_label.setText(f"Zwei Lösungen: x1 = {x1:.4g}, x2 = {x2:.4g}") elif diskriminante == 0: x = -b / (2 * a) self.result_label.setText(f"Eine Lösung: x = {x:.4g}") else: realteil = -b / (2 * a) imagteil = math.sqrt(-diskriminante) / (2 * a) self.result_label.setText( f"Keine reelle Lösung: x1 = {realteil:.4g} + {imagteil:.4g}i, " f"x2 = {realteil:.4g} - {imagteil:.4g}i" ) def main(): app = QApplication(sys.argv) window = QuadraticSolver() window.show() sys.exit(app.exec()) if __name__ == "__main__": main()


Objektnamen statt globaler Selektoren:
Mit setObjectName("coeffInput"), [Zeile: 45, 49, 53], gebe ich Widgets einen eindeutigen Namen, den ich im Stylesheet Mit via QLineEdit#coeffInput { ... }, [Zeile: 124, 134, 137], gezielt anspreche – ähnlich einer CSS-ID. Das ist wichtig, weil ein Fenster oft mehrere QLineEdit- oder QLabel-Typen enthält, die unterschiedlich aussehen sollen (hier z. B. die Formel-Labels vs. das Ergebnis-Label).

QSS (Qt Style Sheets):
setStyleSheet(), [Zeile 107], nimmt eine CSS-ähnliche Syntax entgegen. Zentrale Konzepte im Code: Pseudo-Zustände wie :hover, :focus, :pressed – Qt übernimmt automatisch das Umschalten je nach Mausposition/Fokus, ganz ohne eigene Event-Handler. border-radius erzeugt abgerundete Ecken bei Feldern, Rahmen und Button. Ich style auf Klassenebene (QWidget { ... }) für Basiswerte wie Schriftart und Hintergrund, und überschreibe gezielt mit den #objectName-Selektoren, wo es abweichen soll. Das ist die gleiche Kaskadierungs-Logik wie in Web-CSS.

Der Rahmen um die Eingabezeile:
Statt die drei Felder + Labels direkt ins Hauptlayout zu hängen, packe ich sie jetzt in ein QFrame (input_frame), [Zeile 74-78]. Ein reines QHBoxLayout selbst ist nicht "stylebar" (Layouts haben kein eigenes Aussehen), ein QFrame aber schon – daher dieser kleine Container-Trick, um den weißen Kasten mit Rahmen um die Formel zu bekommen.

Kleine Zusatzverbesserungen:
setCursor(Qt.CursorShape.PointingHandCursor), [Zeile 84], beim Button signalisiert visuell "klickbar". setContentsMargins() [Zeile 96] und setSpacing() [Zeile 97] im Hauptlayout sorgen für Luft am Fensterrand und zwischen den Elementen, statt dass alles am Rand klebt. setWordWrap(True) [Zeile 89] beim Ergebnis-Label verhindert, dass lange Texte (z. B. die komplexe Lösung) über den Fensterrand hinauslaufen.

Warum apply_styles(), [Zeile 100, 105], als eigene Methode?
So bleibt der Konstruktor lesbar (Struktur vs. Optik getrennt), und du kannst später z. B. einen Dark-Mode als zweite Methode apply_dark_styles() ergänzen und je nach Bedarf umschalten.


PyQt6 Bauplan3
Zurück