Grafische Benutzeroberflächen (engl. Graphical User Interface, GUI) kommen immer da zu Einsatz, wo eine dedizierte Benutzergruppe ein Programm benutzen. Im Gegensatz zu Webanwendungen benötigen sie keine Serverinfrastruktur, denn sie werden direkt auf dem Rechner des Anwenders ausgeführt.

Der Nachteil ist dann allerdings, dass du einen Updatemechanismus benötigst, um eine neue Programmversion auf alle Rechner zu übertragen.
GUI
Mit dem Fortschritt der Computerentwicklung forschte Douglas Engelbart in den 1950er und 1960er Jahren an der Mensch-Maschinen-Interaktion. Darauf baute Ivan Sutherland auf und entwickelte 1962 mit Sketchpad eine grafische Benutzeroberfläche, die mit einem „Lightpen“ bedient wurde.
Die Maus als Eingabegerät wurde dann von Engelbert vorgestellt. Der Prototyp bestand aus einem Holzkasten mit zwei Rädern.
Bei Xerox PARC stellte 1972 mit dem Xerox Alto den ersten Computer mit grafischer Benutzeroberfläche vor, der schon über die uns heute vertrauten Dinge wie Icons, Fenster und Menüs verfügte.
Auf dem Massenmarkt setzten sich die GUIs ab 1984 durch, als Apple den ersten Macintosh herausbrachte. 1985 folgte die erste Version von Windows und auch Heimcomputer wie der Commodore Amiga und der Atari ST wurden mit einem grafischen Desktop ausgeliefert.
Mein persönlicher erster Kontakt mit einem GUI war auf dem C64C, bei dem als alternatives OS GEOS auf einer eigenen Diskette beilag. Allerdings war die Bedienung per Joystick eher anstrengend.
Heutzutage begegnen uns GUIs überall; auf dem Smartphone, Fahrscheinautomaten, Bestellterminals. Neben GUIs im klassischen Sinn werden in den letzten Jahren immer häufiger Webanwendungen benutzt, die im Browser laufen.
PyQt6
Mit PyQt6 steht uns ein Framework zur Verfügung, relativ einfach GUI Oberflächen zu entwickeln. Dies will ich dir in diesem Tutorial Teil näher bringen.
Projekt einrichten
Zunächst legen wir uns eine Arbeitsumgebung an und installieren dahinein die benötigten Module.
virtualenv pyqt6
cd pyqt6
source bin/activate
pip install pyqt6
Auf meinem Debian Rechner musste ich noch mit
sudo apt install libxcb-cursor0 -y
ein zusätzliches Paket installieren, umPyQt6 vollständig zum Laufen zu bringen.
Qt und Widgets
Ein Widget ist ein Bedienelement im GUI. Es gibt ein PushButtonWidget für klickbare Buttons, ein LineEditWidget für die Texteingabe. Alles, was du für ein Userinterface benötigst, findest du als Widget in der PyQt6 Library.
QApplication
Die zentrale Klasse in PyQt6 ist QApplication,
Fenster erzeugen
Jetzt können wir direkt starten und bauen unser erstes Fenster. Am einfachsten ist dies mit der QT6Applikation. In ihr werden die Einstellungen verwaltet und die Event-Loop bearbeitet. Deshalb startet jede PyQt6 Anwendung mit einer Instanz von QApplication
app = QApplication(sys.argv)
Der Klasse kann eine Liste von Parametern übergeben werden. Dies können die Kommandozeilenparameter des Pythonprogramm sys.argv
oder auch einfach nur eine leere Liste []
sein.
einfaches Fenster.
Die QApplication allein zeigt aber auf dem Bildschirm noch nicht an. Sie wartet nur auf Ereignisse eines Widgets, um dies zu bearbeiten. Deshalb erzeugen wir mit
window = QWidget()
window.show()
Ein einfaches Widget, dass ein Fenster mit Rahmen und dessen Dekorationselementen („Schließen“,“Verkleinern“ und „Vergrößern“) aufspannt. Das Fenster öffnet sich nicht von allein., deshalb müssen wir dies mit der show()
Methode veranlassen.

Hier ist das komplette Programm. Es läuft so lang, bis du das [X] zum Schließen des Fensters drückst.
#! /usr/bin/python
#Datei: window.py
# Die benötigten Qt Widgets
from PyQt6.QtWidgets import QApplication, QWidget
# Für die Kommandozeilenparameter
import sys
# QTApplication instanziieren. Die Kommandozeilenparameter geben wir
# mit.
app = QApplication(sys.argv)
# Window Widget erzeugen
window = QWidget()
window.show() # Das Fenster muss immer manuell angzeigt werden.
# Wvent-Loop starten.
app.exec()
# So lang die Event-Loop läuft kommen wir hier nicht hin,
# sie kann durch den "Schließen" Button des Fensters unterbrochen werden.
PushButton
Als erstes Erfolgserlebnis ist das leere Fenster ganz nett, wird aber schnell uninteressant, da du nicht viel damit machen kannst. Daher ersetzen wir im nächsten Schritt QWidget
durch QPushButton
.
#! /usr/bin/python
#Datei: pushbutton.py
import sys
from PyQt6.QtWidgets import QApplication, QPushButton
app = QApplication(sys.argv)
window = QPushButton("Bitte klicken")
window.show()
app.exec()
Das Fenster ist dann genau so groß wie der PushButton, dessen Abmessung sich am enthaltenen Text orientiert.

Fenstergröße
Das ganze sieht unschön aus, da das Fenster den PushButton eng umschließt, daher werden wir jetzt eine eigene Klasse für das Fenster benutzen.
#! /usr/bin/python
# Datei: sizewindow.py
import sys
from PyQt6.QtCore import QSize, Qt
from PyQt6.QtWidgets import QApplication, QMainWindow, QPushButton
# abgeleitet von QMainWindow können wir unser GUI besser einstellen und
# z.B. die Dimensionen des Fensters ändern.
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Fenstergröße")
button = QPushButton("Bitte klicken")
self.setFixedSize(QSize(400, 300))
# der Button sitzt als zentrales Widget im Fenster.
self.setCentralWidget(button)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec()
Unsere Klasse MainWindow
leitet von QMainWindow
ab und erbt dadurch alle seine Werte und Methoden. Mit
self.setFixedSize(QSize(400, 300))
setzen wir die Ausdehnung des Fensters auf 400 * 300px. Nebenbei setzen wir mit self.setWindowTitle("Fenstergröße")
noch einen vernünftigen Titel in die Titelzeile des Fensterrahmens.

Auf Klick reagieren
Was nutzt uns ein Buttonklick, wenn das Programm nicht darauf reagiert. Das werden wir als nächstes angehen.
Jedes Widget verfügt über verschiedene Signale, die bei einer Interaktion angestoßen werden. Dort können wir eine eigene Methode
einklinken. Daher schreiben wir in unsere MainWindow Klasse eine Methode, die genau das tut
def handle_button_click(self):
print("Button geklickt")
Diese Methode gibt nur auf der Console aus, dass der Button geklickt wurde. Hier könnte aber auch ein weiteres Fenster geöffnet werden oder die Hälfte von Springfield vom Strom getrennt werden. 🙂
Wir können jetzt diese Methode in den PushButton einklinken, damit sie beim Klick auf den Button angestossen wird.
button.clicked.connect(self.handle_button_click)
Das Signal dazu heißt hier clicked
und an die connect
Methode übergeben wir den Zeiger auf unsere Handler Methode.

Texteingabe
Im nächsten Schritt wollen wir die Möglichkeit schaffen, einen Text mit einem QLineEditWidget einzugeben und bei jedem Tastendruck den Text in ein darunterliegendes QLabel zu setzen.
#! /usr/bin/python
#Datei: lineEdit.py
from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel, QLineEdit, QVBoxLayout, QWidget
import sys
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Eingabe mit LineEdit")
self.label = QLabel()
self.input = QLineEdit()
self.input.textChanged.connect(self.label.setText)
layout = QVBoxLayout()
layout.addWidget(self.input)
layout.addWidget(self.label)
container = QWidget()
container.setLayout(layout)
# Set the central widget of the Window.
self.setCentralWidget(container)
app = QApplication(sys.argv)
window = MainWindow()
window.show()
Neu ist hier das QVBoxLayout
, dies ist ein LayoutManager, der die Widgets, die zu ihm da zugefügt werden vertikal untereinander anordnet. Wir haben ein QLineEdit
und ein QLabel in ihm untereinander angeordnet. Beim Signal textChanged
lassen wir direkt die Methode setText
des Labels aufrufen.
Wenn ich jetzt einen Text in das QLineEdit eingebe, wird er sofort in das Label darunter gesetzt.

Layoutmanager
Über die Layoutmanager will ich noch ein paar Worte verlieren. Sie organisieren deine Widgets innerhalb des Fensters, jeder auf seine spezifische Weise.Es ist möglich, verschiedene Layoutmanager innerhalb eines Fensters verschachtelt zu benutzen. So kannst in QVBoxLayout ein QFormLayout für Formulardaten und ein QHBoxLayout für die Buttonleiste schachteln. Dadurch stehen die Eingabefelder und die Buttons untereinander, während mehrere PushButtons nebeneinander angeordnet werden. Mit ein bisschen Übung bekommst du ein Gefühl dafür, in welcher Situation du einen bestimmten Layoutmanager verwenden solltest.
Klasse | Darstellungsform | Beschreibung |
---|---|---|
QVBoxLayout | vertikal | Ordnet Widgets von oben nach unten in einer Spalte an |
QHBoxLayout | horizontal | Platziert Widgets nebeneinander in einer Reihe |
QGridLayout | Raster | Ordnet Widgets tabellenartig in einem Raster mit Zeilen und Spalten an. |
QFormLayout | Formular | Ideal für Formulare, da es Widgets in zwei Spalten (Beschriftung + Eingabefeld) organisiert |
QStackedLayout | gestapelt | Verwaltet mehrere Layouts übereinander, zeigt aber nur eines gleichzeitig an (z. B. für Tabs oder Seitenwechsel). |
Login-dialog
Zum Abschluss wollen wir noch etwas anwendungsbezogenes machen. Ein Fenster zum Login in ein System. dazu benötigen wir jeweils ein QLineEdit für den Usernamen und das Passwort. Sowie zwei Buttons für den Login und die Neuanmeldung. Ich verwende hier QGridLayout, um das zu demonstrieren. Außerdem möchte ich dein Augenmerk noch auf den „Passwort vergessen“ Button lenken, der sich mit CSS-Styles farbig von den anderen Buttons absetzt. Als Bonus zeige ich noch, wie du ein Bild im Fenster anzeigen kannst.
#! /usr/bin/python
#Datei: lineEdit.py
from PyQt6.QtWidgets import QApplication, QMainWindow, QLabel, QLineEdit, QPushButton, QGridLayout, QWidget
from PyQt6.QtGui import QPixmap
import sys
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Login")
layout = QGridLayout()
layout.setContentsMargins(20, 20, 20, 20)
layout.setSpacing(10)
self.user_logo_pixmap =QPixmap('./user.jpg')
self.user_logo_label = QLabel()
self.user_logo_label.setPixmap(self.user_logo_pixmap)
layout.addWidget(self.user_logo_label,1,1)
self.user_label = QLabel('Username:')
self.password_label = QLabel('Passwort:')
self.username_input = QLineEdit()
self.password_input = QLineEdit()
self.password_input.setEchoMode(QLineEdit.EchoMode.Password)
layout.addWidget(self.user_label,2,0)
layout.addWidget(self.username_input,2,1,1,2)
layout.addWidget(self.password_label,3,0)
layout.addWidget(self.password_input,3,1,1,2)
#Buttons
self.register_button = QPushButton("Register")
layout.addWidget(self.register_button, 4, 1)
self.login_button = QPushButton("Login")
self.login_button.clicked.connect(self.handle_login_button)
self.register_button.clicked.connect(self.handle_register_button)
layout.addWidget(self.login_button, 4, 2)
# Password vergessen
self.forgot_pw_button = QPushButton('Passwort vergessen')
self.forgot_pw_button.setStyleSheet('QPushButton {background-color: #A3C1DA; color: blue;}')
layout.addWidget(self.forgot_pw_button,5,2)
container = QWidget()
container.setLayout(layout)
# Set the central widget of the Window.
self.setCentralWidget(container)
def handle_register_button(self):
print('Register Button')
def handle_login_button(self):
print(f'Login mit {self.username_input.text()} and {self.password_input.text()}')
def handle_forgot_pw_button(self):
print('Forgot PW')

Den gesamten Code des Pythontutorials kannst du dir mit
git clone https://raspithekgit.srv64.de/raspithek/pythonkurs.git
auf deinen Rechner laden.