Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige
Lesedauer 14 Min.

Oberflächen für Python

Auch in rechenintensiven Anwendungen will man Daten eingeben und saubere Ausgaben erhalten. Qt5 als Benutzerschnittstelle realisiert beides in ansprechender Form.
© dotnetpro
Benutzerschnittstellen können mit vielen Bibliotheken erstellt werden. Eine ganze Reihe der bekannten Biblio­theken läuft unter Microsoft Windows: die MFC (Microsoft Foundation Classes), die aber schon ein bisschen älter ist. Windows Forms – auch nicht mehr ganz neu. Und natürlich WPF (Windows Presentation Foundation). Diese in der Windows-Welt weit verbreitete neuere Bibliothek ist modern und vielseitig anwendbar.Manches UI-Framework hat jedoch den Nachteil, dass es sich entweder nur mit einer bestimmten Programmiersprache anwenden oder nur auf einer bestimmten Plattform einsetzen lässt.In diesem Artikel soll die UI-Bibliothek Qt5 (lies englisch: cute [kju:t]) vorgestellt werden, die sich auf vielen Plattformen und mit diversen Programmiersprachen benutzen lässt. Im ersten Artikel [1] dieser Qt-Serie wurde die Programmiersprache C++ für die Erstellung der Benutzerschnittstellen eingesetzt. Im vorliegenden Artikel verwenden wir die ebenfalls weit verbreitete Sprache Python. Außerdem kommt hierbei auch wieder der Qt-Designer für das Fensterdesign zum Einsatz.

Installation

Wenn Sie die Beispiele, die wir in diesem Artikel vorstellen, nachvollziehen wollen, dann sollten Sie (wenn erforderlich) zunächst Python 3.6 auf dem Rechner installieren. Danach können Sie die Bibliothek PyQt5 herunterladen und mit dem Befehl
pip3 <span class="hljs-keyword">install</span> PyQt5 
im Kommandozeilenfenster installieren. Wenn noch nicht vorhanden, laden Sie nun auf der Webseite von Qt den Qt-Designer [2] herunter und installieren ihn. Und schon kann es losgehen!

Ein erstes Beispiel

Die Benutzerschnittstelle einer Python-Qt-Anwendung wird am besten mit dem Qt-Designer erstellt (Bild 1).
Das Startfensterdes Qt-Designers(Bild 1) © Autor
Die folgenden Bereiche sind im Qt-Designer benutzbar:
  • 1: Start-Dialog mit Fenstertyp-Auswahl
  • 2: Liste aller Widgets
  • 3: Objekt-Inspektor
  • 4: Fenster für die Einstellung von Eigenschaften
  • 5: Editor für Signale und Slots sowie Aktionen
Für das erste Beispiel erstellen Sie im Qt-Designer einen Dia­log without Buttons. Es sollen zwei Zahlen in Textboxen eingegeben und dann addiert oder subtrahiert werden.Im Fenster werden drei Labels und drei LineEdit-Controls wie in Bild 2 positioniert. Fügen Sie außerdem die beiden PushButton-Elemente für die Auslösung der Addition und der Subtraktion ein. Die Namen der Steuerelemente können nun gesetzt werden, indem Sie das jeweilige Control einmal anklicken und dann im Property-Fenster die Eigenschaft objectName auf die entsprechenden Namen aus Bild 2 einstellen. Die Labels benötigen in diesem Fall keinen Objektnamen. Der Text auf den Schaltflächen wird mit der Eigenschaft text gesetzt, ebenso wie die Texte der drei Labels.
Eine einfacheBenutzer­schnittstelle(Bild 2) © Autor
Die Benutzerschnittstelle des kleinen Rechners ist nun fertig und wird in der Datei Rechner.ui gespeichert. Als Nächstes benötigen Sie ein Kommandozeilen-Fenster. Wechseln Sie in das Verzeichnis, in dem die Datei Rechner.ui gespeichert wurde, und führen Sie folgenden Befehl aus:
<span class="hljs-selector-tag">pyuic5</span> <span class="hljs-selector-tag">Rechner</span><span class="hljs-selector-class">.ui</span> –<span class="hljs-selector-tag">o</span> <span class="hljs-selector-tag">Rechner_ui</span><span class="hljs-selector-class">.py</span> 
Die Datei Rechner.ui vom Qt-Designer enthält das erstellte User-Interface in XML-Code. Das Tool pyuic5 wandelt den XML-Code in Python-Code um. Die Datei Rechner_ui.py enthält nun also den erforderlichen Python-Code, um das Fenster für den kleinen Rechner mit seinen Steuerelementen zur Laufzeit aufzubauen.Jetzt wird noch eine weitere Python-Datei Rechner.py benötigt, die das Fenster initialisiert und darstellt. Listing 1 zeigt den dafür erforderlichen Code.
Listing 1: Der Code für die Darstellung des Fensters
# Einfacher Rechner &lt;br/&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; sys &lt;br/&gt;&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; PyQt5.QtWidgets &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; QDialog, QApplication &lt;br/&gt;&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; Rechner_ui &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; * &lt;br/&gt;&lt;br/&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; MyForm(QDialog): &lt;br/&gt;  # Initialisierung des Dialog-Fensters &lt;br/&gt;  def __init__(self): &lt;br/&gt;    super().__init__() &lt;br/&gt;    self.ui = Ui_Dialog() &lt;br/&gt;    self.ui.setupUi(self) &lt;br/&gt;    self.show() &lt;br/&gt;    &lt;br/&gt;###### main ######################## &lt;br/&gt;&lt;span class="hljs-keyword"&gt;if&lt;/span&gt; __name__ == &lt;span class="hljs-string"&gt;"__main__"&lt;/span&gt;: &lt;br/&gt;  # Erzeugung eines Application-Objekts &lt;br/&gt;  app = QApplication(sys.argv) &lt;br/&gt;  # Erzeugung des Fensters &lt;br/&gt;  win = MyForm() &lt;br/&gt;  # Darstellung des Fensters &lt;br/&gt;  win.show() &lt;br/&gt;  &lt;br/&gt;  # Ordnungsgemässe Beendigung der Anwendung &lt;br/&gt;  sys.exit(app.exec_()) &lt;br/&gt;###### End &lt;span class="hljs-keyword"&gt;of&lt;/span&gt; main #################   
Nach dem Import der benötigten Bibliotheken wird zunächst die Klasse MyForm implementiert, die von QDialog abgeleitet ist. Hier wird im Member self.ui die Klasse Ui_Dia­log instanziert, die sich in der eben erzeugten Datei Rechner_ui.py befindet.In der main-Funktion wird nun ein Application-Objekt erstellt und initialisiert. Danach wird das Fenster der Klasse MyForm erstellt und mit der show-Methode dargestellt. Der Aufruf von sys.exit() garantiert eine „ordentliche“ Beendigung der Anwendung mit der Auflösung aller benutzten Ressourcen. Die Anwendung selbst startet durch den Aufruf von app.exec_(). Dieser Aufruf blockiert, bis das Fenster geschlossen wird.Das Rechner-Programm lässt sich nun durch den Aufruf
<span class="hljs-keyword">python</span> Rechner.<span class="hljs-keyword">py</span> 
starten. Leider lässt sich mit dem Rechner noch nicht viel anfangen, da der Code für die Ausführung einer Addition oder Subtraktion noch nicht implementiert ist.Die beiden PushButton-Elemente müssen also noch mit den passenden Funktionen verbunden werden, welche die jeweilige Rechenoperation implementiert. Hierzu kommt der Signal/Slot-Mechanismus von Qt zum Einsatz, der bereits in [1] ausführlich erläutert wurde. Das Signal clicked des Addiere-PushButtons wird mit der aufzurufenden Methode self.add verbunden. Entsprechend wird das gleiche Signal des Subtrahiere-Buttons mit der Methode self.sub verbunden. Dieser Code wird im Konstruktor von MyForm implementiert. Die vollständige Implementierung der Klasse MyForm sehen Sie in Listing 2. Die neuen Codezeilen sind darin in blauer Schrift dargestellt.
Listing 2: Die vollständige Rechner-Klasse MyForm
class MyForm(QDialog): &lt;br/&gt;  # Initialisierung des Dialog-Fensters &lt;br/&gt;  def __init__(&lt;span class="hljs-keyword"&gt;self&lt;/span&gt;): &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;super&lt;/span&gt;().__init__() &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.ui = Ui_Dialog() &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.ui.setupUi(&lt;span class="hljs-keyword"&gt;self&lt;/span&gt;) &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.ui.pushButtonAdd.clicked.connect(&lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.add) &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.ui.pushButtonSub.clicked.connect(&lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.sub) &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.show() &lt;br/&gt;    &lt;br/&gt;  def add(&lt;span class="hljs-keyword"&gt;self&lt;/span&gt;): &lt;br/&gt;    zahl1 = &lt;span class="hljs-keyword"&gt;float&lt;/span&gt;(&lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.ui.lineEditZahl1.text()) &lt;br/&gt;    zahl2 = &lt;span class="hljs-keyword"&gt;float&lt;/span&gt;(&lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.ui.lineEditZahl2.text()) &lt;br/&gt;    result = zahl1 + zahl2; &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.ui.lineEditResult.setText(&lt;span class="hljs-keyword"&gt;str&lt;/span&gt;(result)) &lt;br/&gt;    &lt;br/&gt;  def sub(&lt;span class="hljs-keyword"&gt;self&lt;/span&gt;): &lt;br/&gt;    zahl1 = &lt;span class="hljs-keyword"&gt;float&lt;/span&gt;(&lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.ui.lineEditZahl1.text()) &lt;br/&gt;    zahl2 = &lt;span class="hljs-keyword"&gt;float&lt;/span&gt;(&lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.ui.lineEditZahl2.text()) &lt;br/&gt;    result = zahl1 - zahl2; &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;self&lt;/span&gt;.ui.lineEditResult.setText(&lt;span class="hljs-keyword"&gt;str&lt;/span&gt;(result))  
Im Konstruktor von MyForm werden die beiden Signale mit ihren Slots verbunden. Die Slot-Funktionen werden in der Klasse MyForm implementiert. Zunächst werden die Texte der beiden oberen LineEdit-Controls mit der text-Methode abgefragt und in Fließkomma-Variablen umgewandelt. Nun kann die Berechnung erfolgen. Das Ergebnis wird in einen String konvertiert und im lineEditResult-Control mittels der setText-Methode ausgegeben.Die main-Funktion der Anwendung bleibt in diesem Beispiel unverändert. An dieser Stelle sei noch erwähnt, dass sich die UI-Datei Rechner.ui mithilfe des Qt-Designers jederzeit ändern lässt. Danach ist jedoch das Konvertierungs-Tool pyuic5 wieder auszuführen, um den korrekten neuen Python-Code zu erzeugen. Natürlich ist es dann gegebenenfalls notwendig, auch die Fensterklasse MyForm in der Datei Rechner.py anzupassen.

Slots und Signale

Alle Steuerelemente von Qt kommunizieren mit dem Code der Anwendung mithilfe von Signalen, die an bestimmte Slots gebunden werden. Ein Button-Control kann zum Beispiel das Signal clicked auslösen, welches dann an eine Funktion gebunden wird. Die Ausführung des Codes dieser Funktion erfolgt genau dann, wenn auf den Button geklickt und dadurch das Signal ausgelöst wird.In Listing 2 wurden die Signale der PushButton-Controls direkt im Python-Code mit den entsprechenden Slots verbunden. Es gibt aber noch eine andere Methode im Qt-De­signer, um Signale mit Slots durch wenige Mausklicks mit­einander zu verbinden.Für das folgende Beispiel soll die einfache Benutzerschnittstelle aus Bild 3 zum Einsatz kommen. Das PushButton-Control wird mit dem Text Alles selektieren und das LineEdit-Control mit dem Text Hallo, Qt5! versehen. Die Namen der Steuerelemente sind in diesem Beispiel unerheblich und bleiben unverändert. Nun wird im Qt-Designer der Befehl Edit Signal/Slots im Menü Edit aufgerufen. In diesem Modus kann man verschiedene Steuerelemente grafisch miteinander verbinden. Dazu klickt man zuerst auf das PushButton-Control mit der linken Maustaste, zieht dann die Maus zum zweiten Control (hier: das LineEdit-Control) und lässt dann die Maustaste los. Es erscheint nun ein Dialog, in dem im linken Listenfeld das Signal clicked und im rechten Listenfeld der Slot selectAll ausgewählt wird. In diesem Fall soll beim Anklicken des Buttons der gesamte Text in der Textbox selektiert werden. Die Aktion stellt Qt-Designer grafisch durch eine Linienverbindung dar.
Die Benutzerschnittstellefür die Signal/Slot-Erstellung im Qt-Designer (Bild 3) © Autor
Wenn alle gewünschten Verbindungen eingerichtet sind, wird die [Esc]-Taste gedrückt, um den speziellen Signal/Slot-Modus zu beenden. Dieses Design wird in der Datei Designer.ui abgespeichert und mit dem Converter pyuic5 in eine Python-Klasse umgewandelt (pyuic5 Designer.ui –o Designer_ui.py). Wenn Sie einen Blick in die Datei Designer_ui.py werfen, finden Sie etwa in der Mitte auch den entsprechenden connect-Aufruf für die Steuerelemente pushButton und lineEdit.Listing 3 zeigt das Hauptprogramm in der Datei Designer.py. Es beinhaltet die Klasse MyForm, die initialisiert wird, nun aber keine connect-Aufrufe enthält. Die main-Funktion ist genauso wie im ersten Beispiel aufgebaut.
Listing 3: Die Datei Designer.py
# Signal &amp;amp; Slot mit dem Qt-Designer &lt;br/&gt;&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; sys &lt;br/&gt;&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; PyQt5.QtWidgets &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; QDialog, QApplication &lt;br/&gt;&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; Designer_ui &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; * &lt;br/&gt;&lt;br/&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; MyForm(QDialog): &lt;br/&gt;  # Initialisierung des Dialog-Fensters &lt;br/&gt;  def __init__(self): &lt;br/&gt;    super().__init__() &lt;br/&gt;    self.ui = Ui_Dialog() &lt;br/&gt;    self.ui.setupUi(self) &lt;br/&gt;    self.show() &lt;br/&gt;    &lt;br/&gt;###### main ######################## &lt;br/&gt;&lt;span class="hljs-keyword"&gt;if&lt;/span&gt; __name__ == &lt;span class="hljs-string"&gt;"__main__"&lt;/span&gt;: &lt;br/&gt;  app = QApplication(sys.argv) &lt;br/&gt;  win = MyForm() &lt;br/&gt;  win.show() &lt;br/&gt;  &lt;br/&gt;  sys.exit(app.exec_()) &lt;br/&gt;###### End &lt;span class="hljs-keyword"&gt;of&lt;/span&gt; main #################   

Dialoge mit Qt

In den Qt-Bibliotheken stehen Standarddialoge für die Farb-, Font- oder Dateiauswahl zur Verfügung. Außerdem gibt es das Control QInputDialog, das für einfache Eingaben verwendbar ist. Und natürlich kann man eigene Dialogfenster designen, um spezielle Eingaben für die Anwendungen zu ermöglichen.Das erste Beispiel stellt das einfach zu benutzende QInputDialog-Control vor. Es soll ein Eingabefenster erzeugt werden, das den Anwender abfragt, in welchem Land er sich aufhält. Es soll eine Liste von Ländern zur Auswahl vorgegeben sein. Mit dem Qt-Designer erstellt man zunächst eine einfache Benutzerschnittstelle mit einem Label, einem Line­Edit-Control und einem Button (Bild 4).
UI für die Auswahleines Landes(Bild 4) © Autor
Alle Steuerelemente behalten in diesem Beispiel ihre automatisch generierten Namen. Das UI wurde in der Datei InputDialog.ui abgespeichert und wird nun konvertiert (pyuic5 InputDialog.ui –o InputDialog_ui.py).In der Datei InputDialog.py wird die Fensterklasse MyForm implementiert (Listing 4). Das Signal clicked des Buttons wird mit dem Slot getCountryDialog verbunden. In dieser Funktion wird zunächst eine Liste der auswählbaren Länder erstellt und danach erfolgt der Aufruf des QInputDialogs mit der Methode getItem. Hier gibt es verschiedene Möglichkeiten. Durch die Benutzung der getInt-, getDouble- oder getText-Funktion lassen sich unterschiedliche Datentypen (Integer, Fließkomma oder Text) einlesen. Die Funktion get­Item stellt eine Combobox dar und man kann ein Element aus einer Liste von Strings auswählen und zurückgeben. Der numerische Wert beim Aufruf des Dialogs nach dem Parameter countries gibt an, welcher Eintrag in der Combobox als Default-Wert selektiert werden soll (hier: Deutschland). Eine Kopie des ausgewählten Landes landet schließlich im Line­Edit-Control des Hauptfensters.
Listing 4: Auswahl mit einem QInputDialog
&lt;span class="hljs-keyword"&gt;import&lt;/span&gt; sys &lt;br/&gt;&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; PyQt5.QtWidgets &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; QDialog, QInputDialog, QApplication &lt;br/&gt;&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; InputDialog_ui &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; * &lt;br/&gt;&lt;br/&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; MyForm(QDialog): &lt;br/&gt;  # Initialisierung des Dialog-Fensters &lt;br/&gt;  def __init__(self): &lt;br/&gt;    super().__init__() &lt;br/&gt;    self.ui = Ui_Dialog() &lt;br/&gt;    self.ui.setupUi(self) &lt;br/&gt;    # Im Slot wird der QInputDialog aufgerufen &lt;br/&gt;    self.ui.pushButton.clicked.connect(&lt;br/&gt;      self.getCountryDialog) &lt;br/&gt;    self.show() &lt;br/&gt;    &lt;br/&gt;  def getCountryDialog(self): &lt;br/&gt;    countries = (&lt;span class="hljs-string"&gt;"Frankreich"&lt;/span&gt;, &lt;span class="hljs-string"&gt;"Belgien"&lt;/span&gt;, &lt;br/&gt;      &lt;span class="hljs-string"&gt;"Deutschland"&lt;/span&gt;, &lt;span class="hljs-string"&gt;"Spanien"&lt;/span&gt;, &lt;span class="hljs-string"&gt;"Italien"&lt;/span&gt;, &lt;br/&gt;      &lt;span class="hljs-string"&gt;"Schweiz"&lt;/span&gt;) &lt;br/&gt;    countryName, ok = QInputDialog.getItem(&lt;br/&gt;      self, &lt;span class="hljs-string"&gt;"Eingabe des Landes"&lt;/span&gt;, &lt;span class="hljs-string"&gt;"Liste aller &lt;/span&gt;&lt;br/&gt;&lt;span class="hljs-string"&gt;      Länder:"&lt;/span&gt;, countries, &lt;span class="hljs-number"&gt;2&lt;/span&gt;, &lt;span class="hljs-literal"&gt;False&lt;/span&gt;) &lt;br/&gt;    # Auswahl &lt;span class="hljs-keyword"&gt;in&lt;/span&gt; Textbox kopieren &lt;br/&gt;    &lt;span class="hljs-keyword"&gt;if&lt;/span&gt; ok and countryName: &lt;br/&gt;      self.ui.lineEdit.setText(countryName) &lt;br/&gt;      &lt;br/&gt;###### main ######################## &lt;br/&gt;&lt;span class="hljs-keyword"&gt;if&lt;/span&gt; __name__ == &lt;span class="hljs-string"&gt;"__main__"&lt;/span&gt;: &lt;br/&gt;  # Erzeugung eines Application-Objekts &lt;br/&gt;  app = QApplication(sys.argv) &lt;br/&gt;  # Erzeugung des Fensters &lt;br/&gt;  win = MyForm() &lt;br/&gt;  # Darstellung des Fensters &lt;br/&gt;  win.show() &lt;br/&gt;  &lt;br/&gt;  # Ordnungsgemässe Beendigung der Anwendung &lt;br/&gt;  sys.exit(app.exec_()) &lt;br/&gt;###### End &lt;span class="hljs-keyword"&gt;of&lt;/span&gt; main #################   
Die main-Funktion der Anwendung behält ihr bereits bekanntes Aussehen. Bild 5 zeigt das Eingabedialogfenster der Demoanwendung.
Die Auswahl eines Landesmit dem QInputDialog-Fenster(Bild 5) © Autor
Im nächsten Beispiel sollen in einem Fenster ein Menü und die Standarddialoge für das Öffnen und Speichern von Da­teien verwendet werden.Mit dem Qt-Designer wird zunächst ein neues Projekt vom Typ Main Window erzeugt. Bei diesem Projekttyp wird das Hauptfenster mit einem Menü und einer Statuszeile versehen. Im Beispielprogramm sollen vier Menüpunkte implementiert werden:
  • Neu  [Strg+N]
  • Öffnen …  [Strg+F]
  • Speichern …  [Strg+S]
  • Beenden
Das Menü lässt sich mit dem Qt-Designer sehr schnell erstellen. Dort, wo der Text Type here steht, wird der Text &Datei eingegeben und mit der Eingabetaste beendet. Damit ist der Hauptmenüpunkt schon fertig. Nun kommen die eigentlichen Menüpunkte aus der oben aufgeführten Liste dran. Nach dem Erstellen des Befehls Neu wird einmal Add Separator ausgewählt, um eine horizontale Trennlinie in das Menü einzufügen. Nun werden die Menüpunkte für die beiden Befehle &Öffnen... und &Speichern angelegt. Danach kommt wieder ein Separator und schließlich wird der Befehl &Beenden erstellt. Das &-Zeichen definiert die jeweiligen Schnelltasten für eine Benutzung des Menüs über die Tastatur.Jeder Menübefehl ist eine sogenannte Action. Darum gibt es im Qt-Designer rechts unten (auf einem separaten Tab neben dem Signal/Slot Editor) den Action Editor. Dort lassen sich die neuen Menüpunkte genauer definieren. Ein Doppelklick auf einen Menüpunkt zeigt ein Dialogfenster mit den Informationen für den entsprechenden Befehl an (Bild 6).
Die Bearbeitungeine Menübefehls(Bild 6) © Autor
Zunächst sollte man nun prüfen, ob der angegebene Objektname in Ordnung ist. Hier muss man darauf achten, dass der Qt-Designer zum Beispiel Umlaute aus dem Objektnamen entfernt. Dadurch entstehen dann manchmal etwas unleserliche Namen, die korrigiert werden sollten. Weiterhin kann man hier den gewünschten Tooltipp-Text eingeben und eine Kurztaste (zum Beispiel [Strg+N]) definieren. Für die Definition der Kurztaste klickt man einfach in das Eingabefeld und gibt dann die gewünschte Tastenkombination über die Tastatur ein.Um das kleine Programm noch etwas sinnvoller zu gestalten, fügen wir in das Hauptfenster zusätzlich ein PlainTextEdit-Steuerelement ein, um einen ganz einfachen Texteditor zu bekommen. Dieses Steuerelement vergrößern wir so, dass es das gesamte Fenster ausfüllt.Nun wird die erstellte Benutzerschnittstelle in der Datei StdDialog.ui abgespeichert und mit pyuic5 zur Datei StdDialog_ui.py konvertiert.Listing 5 zeigt das benötigte Hauptprogramm, das in der Datei StdDialog.py implementiert wird.
Listing 5: Das Hauptprogramm für den einfachen Editor
import sys &lt;br/&gt;from PyQt5.QtWidgets import QMainWindow, QAction, QApplication, QFileDialog &lt;br/&gt;from StdDialog_ui import * &lt;br/&gt;&lt;br/&gt;class MyForm(QMainWindow): &lt;br/&gt;  # Initialisierung des Hauptfensters &lt;br/&gt;  def __init__(self): &lt;br/&gt;    super().__init__() &lt;br/&gt;    self.ui = Ui_MainWindow() &lt;br/&gt;    self.ui.setupUi(self) &lt;br/&gt;    # Slots für die Menüpunkte definieren &lt;br/&gt;    self.ui.action_Neu.triggered.connect(&lt;br/&gt;      self.actionDateiNeu) &lt;br/&gt;    self.ui.action_Oeffnen.triggered.connect(&lt;br/&gt;      self.actionDateiOeffnen) &lt;br/&gt;    self.ui.action_Speichern.triggered.connect(&lt;br/&gt;      self.actionDateiSpeichern) &lt;br/&gt;    self.ui.action_Beenden.triggered.connect(&lt;br/&gt;      self.actionDateiBeenden) &lt;br/&gt;    self.show() &lt;br/&gt;    &lt;br/&gt;  def actionDateiNeu(self): &lt;br/&gt;    # Inhalt Plain Text-Control löschen &lt;br/&gt;    self.ui.plainTextEdit.setPlainText("") &lt;br/&gt;    self.ui.statusbar.showMessage("Text gelöscht!") &lt;br/&gt;&lt;br/&gt;  def actionDateiOeffnen(self): &lt;br/&gt;    # Datei zum Öffnen abfragen &lt;br/&gt;    filename = QFileDialog.getOpenFileName(&lt;br/&gt;      self, "Datei öffnen", "", &lt;br/&gt;      "Alle Dateien (*.*)") &lt;br/&gt;    if filename[0]: &lt;br/&gt;      file = open(filename[0], "r") &lt;br/&gt;      data = file.read() &lt;br/&gt;      self.ui.plainTextEdit.setPlainText(data) &lt;br/&gt;      file.close() &lt;br/&gt;      self.ui.statusbar.showMessage("Datei geladen.") &lt;br/&gt;    else: &lt;br/&gt;      self.ui.statusbar.showMessage(&lt;br/&gt;        "Fehler beim Laden!") &lt;br/&gt;      &lt;br/&gt;  def actionDateiSpeichern(self): &lt;br/&gt;    # Datei zum Speichern abfragen &lt;br/&gt;    filename, _ = QFileDialog.getSaveFileName(&lt;br/&gt;        self, "Datei speichern", &lt;br/&gt;        file = open(filename, "w") &lt;br/&gt;      data = self.ui.plainTextEdit.toPlainText() &lt;br/&gt;      file.write(data) &lt;br/&gt;      file.close() &lt;br/&gt;      self.ui.statusbar.showMessage(&lt;br/&gt;        "Datei gespeichert.") &lt;br/&gt;    else: &lt;br/&gt;      self.ui.statusbar.showMessage(&lt;br/&gt;        "Fehler beim Speichern!") &lt;br/&gt;&lt;br/&gt;  def actionDateiBeenden(self): &lt;br/&gt;    quit() &lt;br/&gt;    &lt;br/&gt;###### main ######################## &lt;br/&gt;if __name__ == "__main__": &lt;br/&gt;  # Erzeugung eines Application-Objekts &lt;br/&gt;  app = QApplication(sys.argv) &lt;br/&gt;  # Erzeugung des Fensters &lt;br/&gt;  win = MyForm() &lt;br/&gt;  # Darstellung des Fensters &lt;br/&gt;  win.show() &lt;br/&gt;  &lt;br/&gt;  # Ordnungsgemässe Beendigung der Anwendung &lt;br/&gt;  sys.exit(app.exec_()) &lt;br/&gt;###### End of main #################   
Nach den erforderlichen import-Statements wird die Klasse MyForm implementiert, in diesem Beispiel aber abgeleitet von QMainWindow. In der vom pyuic5-Konverter erzeugten Datei befindet sich dieses Mal die Klasse Ui_MainWindow, die dann hier bei der Initialisierung von MyForm Verwendung findet. Im weiteren Verlauf des Konstruktors werden die Slots für die Menüpunkte definiert. Hier findet die Verbindung des triggered-Signals der Menüpunkte mit den gewünschten Slot-Funktionen in der MyForm-Klasse statt.Die Implementierung dieser Funktionen ist relativ einfach. Für den Neu-Befehl wird einfach der Inhalt des PlainTextEdit-Controls gelöscht, und zum Beenden des Programms dient der Aufruf der quit-Funktion.Der Öffnen-Befehl benutzt aus der Klasse QFileDialog die Funktion getOpenFileName, um die zu öffnende Datei abzufragen. Als Parameter werden der Fenstertitel, das Ausgangsverzeichnis und der Dateifilter (hier: *.*) angegeben. Als Rückgabe bekommt man einen oder mehrere Dateinamen in einem Array. Nun wird der erste Dateiname zum Einlesen der Daten geöffnet und der Text im Text-Control ausgegeben.Der Speichern-Befehl verwendet die Funktion getSave­FileName, um den Namen der Datei abzufragen, in der die Daten gespeichert werden sollen. Die Parameter sind die gleichen wie bei der Funktion getOpenFileName.Die main-Funktion des kleinen Editors wird aus einem anderen Beispiel unverändert übernommen.Nach dem Start des Programms StdDialog.py kann das Menü ausprobiert werden (Bild 7).
Das Menüdes Text-Editors(Bild 7) © Autor
Eigene, anwendungsspezifische Dialoge werden ebenfalls mit dem Qt-Designer erstellt, in einer eigenen ui-Datei gespeichert und mit pyuic5 konvertiert. Der modale Dialog wird dann ganz normal instanziert und mithilfe der exec_-Funk­tion aufgerufen:
def actionDialog(self): 
  dlg = .... # Dialog-Klasse 
  dlg.setWindowModality( 
    Qt.ApplicationModal) 
  dlg.exec_() 

Und jetzt ein wenig Layout

In den bisherigen Beispielprogrammen wurden die Steuerelemente einfach an einer bestimmten Stelle positioniert. Diese Vorgehensweise ist zwar sehr einfach, wenn das Fenster jedoch vergrößert oder verkleinert wird, passt sich die Anordnung der Controls nicht an die neue Fenstergröße an.Qt bietet verschiedene Möglichkeiten an, um Fenster zu erstellen, in denen sich die Positionen der Steuerelemente bei einer Größenänderung anpassen. Zunächst existieren bei allen Controls die Eigenschaften maximumSize und minimumSize, die im Property-Fenster gesetzt werden können. Außerdem gibt es folgende Layout-Steuerelemente:
  • Horizontal Layout
  • Vertical Layout
  • Grid Layout
  • Form Layout
Weiterhin stehen noch die beiden Abstandhalter HorizontalSpacer und VerticalSpacer zur Verfügung. Mit einer Kombination dieser Controls kann man gut funktionierende Fenster-Layouts aufbauen.Um die Layout-Möglichkeiten von Qt5 zu testen, soll im Folgenden ein Dialogfenster erstellt werden, das in seiner Größe anpassbar ist. Dazu wird zunächst ein Grund-Layout erstellt (Bild 8). In diesem werden die Steuerelemente einfach an ihrer ungefähren Posi­tion platziert. Das blaue „Zickzack“-Control am rechten Rand des Fensters ist ein VerticalSpacer-Element. Das große Steuerelement im Zentrum des Fensters ist ein ListBox-Control.
Das Grundlayoutim Dialogfenster(Bild 8) © Autor
Nun beginnt der eigentliche Layout-Prozess. Man selektiert zuerst mit der Maus (durch einmaliges Anklicken mit gedrückter [Strg]-Taste) das Label und die Textbox und ruft dann das Kontextmenü durch Anklicken mit der rechten Maustaste auf. Dort wird der Befehl Layout und der Unterbefehl Lay Out Horizontally aufgerufen. Die roten Randlinien markieren das Layout-Control im Fenster. Nun werden die beiden RadioButtons selektiert. Mit der rechten Maustaste ist aus dem Kontextmenü der Befehl Layout und der Unterbefehl Lay Out Vertically aufzurufen. Als Nächstes werden die drei Buttons und der VerticalSpacer auf der rechten Seite des Fensters selektiert. Als Layout wird hier Lay Out Vertically eingestellt. Das Gleiche geschieht mit den drei Controls auf der linken Seite: Man selektiert das horizontale Layout-Element mit Label und Textbox, das ListBox-Control und die GroupBox und stellt Lay Out Vertically ein. Im letzten Layout-Schritt klickt man nun mit der rechten Maustaste in das Dialogfenster (es ist kein Steuerelement selektiert) und setzt das Layout auf Lay Out Horizontally. Das fertige Layout wird in Bild 9 gezeigt.
Das fertige Layoutdes Dialogfensters(Bild 9) © Autor
Man kann das entwickelte Layout im Qt-Designer jederzeit testen. Hierzu ruft man einfach den Befehl Preview … im Menü Form auf. Sie können nun das Fenster vergrößern oder verkleinern und die Anordnung der Steuerelemente überprüfen.Nun ist es auch interessant, einmal einen kurzen Blick auf das Fenster Object Inspector am rechten Rand des Qt-Designers zu werfen. Hier kann man den Aufbau der Hierarchie der Steuerelemente im Fenster sehen (Bild 10). Die verwendeten Layout-Controls sind hier ebenfalls entsprechend aufgelistet. Diese Liste eignet sich auch sehr gut, wenn man gezielt einzelne Elemente auswählen möchte, da hier auch eine Mehrfach-Selektion möglich ist.
Der Aufbauder Steu­er­elemente-Hierarchie im Qt-Designer(Bild 10) © Autor
Weiterer Python-Code wird für dieses Beispielprogramm nicht benötigt. Das fertige Layout lässt sich in der Datei MyLayout.ui abspeichern und mit pyuic5 konvertieren.Die anderen Layout-Steuerelemente werden in analoger Weise benutzt. Beim Designen eines Layouts geht man in der Regel „von innen nach außen“ vor. Man selektiert einzelne Gruppen von Controls und arrangiert sie. Danach ordnet man diese Gruppen zueinander an. Dieser Prozess kann mehrfach wiederholt werden. Und zum Schluss definiert man das Layout für das Fenster.

Nun ein bisschen Bewegung

Als letztes Beispielprogramm soll in diesem Artikel ein hochspannendes Anima­tionsspiel erstellt werden. Im Qt-Designer wird wieder ein neues Projekt vom Typ Dialog without Buttons erzeugt. In der linken und in der rechten oberen Ecke wird jeweils ein PushButton-Control platziert. Der Button in der rechten Ecke wird mit dem Text Start versehen und bekommt den Objektnamen pushButtonStart. Der linke Button bleibt unverändert. Die erforderliche Benutzerschnittstelle ist fertig und wird in der Datei Animation.ui gespeichert und mit pyuic5 in die Datei Animation_ui.py konvertiert.Der Code für das Dialogfenster wird in der Datei Anima­tion.py implementiert und ist in Listing 6 zu sehen. Die Dialogklasse MyForm wird zunächst implementiert und wieder von QDialog abgeleitet. Im Konstruktor werden für die clicked-­Signale der beiden Buttons zwei Slots definiert.
Listing 6: Ein Super-Animationsspiel
import sys &lt;br/&gt;from PyQt5.QtWidgets import QDialog, QApplication, QMessageBox &lt;br/&gt;from PyQt5.QtCore import QRect, QPropertyAnimation &lt;br/&gt;from Animation_ui import * &lt;br/&gt;&lt;br/&gt;class MyForm(QDialog): &lt;br/&gt;  # Initialisierung des Dialog-Fensters &lt;br/&gt;  def __init__(self): &lt;br/&gt;    super().__init__() &lt;br/&gt;    self.ui = Ui_Dialog() &lt;br/&gt;    self.ui.setupUi(self) &lt;br/&gt;    # Im Slot wird die Animation gestartet &lt;br/&gt;    self.ui.pushButtonStart.clicked.connect(&lt;br/&gt;      self.startAnimation) &lt;br/&gt;    # Anzeige einer MessageBox, wenn getroffen &lt;br/&gt;    self.ui.pushButton.clicked.connect(self.hitButton) &lt;br/&gt;    self.show() &lt;br/&gt;    &lt;br/&gt;  def startAnimation(self): &lt;br/&gt;    # Definition aller Animations-Parameter &lt;br/&gt;    self.anim = QPropertyAnimation(&lt;br/&gt;      self.ui.pushButton, b"geometry") &lt;br/&gt;    self.anim.setDuration(2000) &lt;br/&gt;    self.anim.setStartValue(QRect(20, 20, 75, 23)) &lt;br/&gt;    self.anim.setEndValue(QRect(200, 200, 100, 50)) &lt;br/&gt;    self.anim.setLoopCount(5) &lt;br/&gt;    self.anim.start() &lt;br/&gt;&lt;br/&gt;  def hitButton(self): &lt;br/&gt;    QMessageBox.question(self, "Animation", &lt;br/&gt;      "Getroffen!!! Sehr gut!!!", &lt;br/&gt;      QMessageBox.Ok)  &lt;br/&gt;    &lt;br/&gt;###### main ######################## &lt;br/&gt;if __name__ == "__main__": &lt;br/&gt;  # Erzeugung eines Application-Objekts &lt;br/&gt;  app = QApplication(sys.argv) &lt;br/&gt;  # Erzeugung des Fensters &lt;br/&gt;  win = MyForm() &lt;br/&gt;  # Darstellung des Fensters &lt;br/&gt;  win.show() &lt;br/&gt;  &lt;br/&gt;  # Ordnungsgemässe Beendigung der Anwendung &lt;br/&gt;  sys.exit(app.exec_()) &lt;br/&gt;###### End of main #################   
Im Slot startAnimation wird die Bewegung des linken Buttons definiert. Dazu gehört zunächst einmal das Animations-Objekt selbst. Mit dem QPropertyAnimation-Objekt erfolgt die Angabe, welche Eigenschaft in welchem UI-Objekt zu animieren ist. Hier soll die Geometrie des Buttons manipuliert werden. Unter Geometrie versteht man hier die Position und die Größe des Buttons. Danach wird die Dauer der Bewegung in Millisekunden gesetzt. Schließlich wird die Änderung der Geometrie angegeben. Der Startwert (Funktion: setStartValue) ist in diesem Fall die ursprüngliche Größe und Position des linken PushButton-Objekts. Diese Zeile kann auch weggelassen werden und ist hier nur der Vollständigkeit halber an­gegeben. Der Endwert (Funk­tion: setEnd­Value) gibt die finale Position und Größe des Buttons an. Die angegebene Bewegung soll in diesem Beispiel fünfmal wiederholt werden. Dies wird mit der Funk­tion setLoopCount(5) angegeben. Nachdem alle Animationsparameter gesetzt sind, kann der Bewegungsablauf schließlich gestartet werden.Als Höhepunkt dieses fantastischen Beispiels wurde ein kleiner Reaktionstest eingebaut (Bild 11). Versuchen Sie, während der Animation den Button anzuklicken, und freuen Sie sich, wenn Sie getroffen haben. Hierzu wurde die zweite Schaltfläche einfach mit dem Slot hitButton verbunden, der nur eine QMessageBox mit einer Erfolgsmeldung ausgibt.
Ein Super-Animationsspielmit Qt5(Bild 11) © Autor

Zusammenfassung

Auch mit Python macht die Erstellung von Benutzerschnittstellen mit Qt5 viel Spaß. Der Qt-Designer kann eingesetzt werden, um die Steuerelemente im Fenster schnell zu platzieren. Die Programmierung gestaltet sich relativ einfach und ist gut zu erlernen. Wer schon Erfahrung mit Qt5 und C++ hat, kann dieses Wissen sehr leicht in die Python-Welt übertragen und anwenden.Zum Schluss möchte ich noch anmerken, dass in diesem Artikel nur ein sehr kleiner Teil von Qt5 vorgestellt wurde. Es gibt noch viele weitere Steuerelemente und auch zusätzliche Features, wie zum Beispiel Klassen für den Datenbank- oder Netzwerkzugriff, sowie allgemeine Klassen für die Stringverarbeitung oder die Benutzung grafischer Grundelemente.
Projektdateien herunterladen

Fussnoten

  1. Bernd Marquardt, Ein bisschen User Interface gefällig?, dotnetpro 9/2019, Seite 56 ff., http://www.dotnetpro.de/A1909QtCPP
  2. Qt-Designer, http://www.qt.io/download

Neueste Beiträge

DWX hakt nach: Wie gestaltet man intuitive User Experiences?
DWX hakt nach: Wie gestaltet man intuitive User Experiences? Intuitive Bedienbarkeit klingt gut – doch wie gelingt sie in der Praxis? UX-Expertin Vicky Pirker verrät auf der Developer Week, worauf es wirklich ankommt. Hier gibt sie vorab einen Einblick in ihre Session.
4 Minuten
27. Jun 2025
„Sieh die KI als Juniorentwickler“
CTO Christian Weyer fühlt sich jung wie schon lange nicht mehr. Woran das liegt und warum er keine Angst um seinen Job hat, erzählt er im dotnetpro-Interview.
15 Minuten
27. Jun 2025
IoT neu eingebunden - Integration und Verwaltung von IoT-Geräten mit Azure IoT Operations
Wie sich das neue Azure IoT Operations von bestehenden Azure-Diensten unterscheidet, welche Technologien dabei zum Einsatz kommen und wann sich der Umstieg lohnt.
16 Minuten
15. Jun 2025
Miscellaneous

Das könnte Dich auch interessieren

UIs für Linux - Bedienoberflächen entwickeln mithilfe von C#, .NET und Avalonia
Es gibt viele UI-Frameworks für .NET, doch nur sehr wenige davon unterstützen Linux. Avalonia schafft als etabliertes Open-Source-Projekt Abhilfe.
16 Minuten
16. Jun 2025
Mythos Motivation - Teamentwicklung
Entwickler bringen Arbeitsfreude und Engagement meist schon von Haus aus mit. Diesen inneren Antrieb zu erhalten sollte für Führungskräfte im Fokus stehen.
13 Minuten
19. Jan 2017
Bausteine guter Architektur - Entwurf und Entwicklung wartbarer Softwaresysteme, Teil 2
Code sauberer gestalten anhand von wenigen Patterns und Grundhaltungen.
6 Minuten
Anzeige
Anzeige
Anzeige
Anzeige
Anzeige