Die Qt Bibliothek beinhaltet zahlreiche Widgets um auf graphischer Oberflächen Informationen anzuzeigen. Dazu zählt unter anderem das Text Edit Widget. Dieses Element ist prinzipiell sehr gut dafür geeignet, den aktuellen Status eines länger andauernden Prozesses auszugeben. Texte können mit der append()
Funktion komfortabel an bestehende Ausgaben angehängt werden. Es besteht jedoch das Problem, dass im Verlauf der Ausführung einer Qt Widgets Anwendung Anzeigeelemente nicht regelmäßig aktualisiert werden. Werden zum Beispiel mehrere Rechenvorgänge durchgeführt, die jeweils nach mehreren Sekunden eine Textausgabe auf ein Text Edit Element schreiben, kommt es nicht zu einer sofortigen Anzeige dieser Ausgaben. Diese erfolgt erst, wenn alle Berechnungen abgeschlossen sind. Wir wollen uns in diesem Artikel ansehen, wie eine zeitnahe Statusausgabe mit QThread in Qt Widgets Applikationen durchgeführt werden kann (Link zur Beschreibung von QThread).
Im ersten Schritt legen wir mit Qt Creator ein neues Widgets Projekt mit der Hauptklasse MainWindow an. Zusätzlich erstellen wir eine Klasse namens Worker, die von QObject abgeleitet wird. Diese Klasse übernimmt Berechnungen, die in einem eigenen Thread ausgeführt werden. Der Start des Threads erfolgt aus der Hauptklasse. Nach Abschluss jeder Berechnung innerhalb des Worker Objektes erfolgt eine Rückmeldung an das aufrufende MainWindow Objekt. Diese Rückmeldung ist vom Typ QString. Sie wird bei Erhalt in einem Text Edit Widget ausgegeben. Realisiert wir die Umsetzung mit dem Signal Slot Konzept von Qt.
In unserem Projektverzeichnis in Qt Creator liegen nun unter anderem die Dateien mainwindow.h, mainwindow.cpp, worker.h und worker.cpp. Durch einen Doppelklick auf mainwindow.ui öffnet sich das Designfenster von Qt. Dort ziehen wir ein PushButton Widget und ein TextEdit Widget in die Programmoberfläche. Wir benennen diese beiden Elemente pushButtonClicked
und textEditProgress
. Abschließend zeigen wir für den PushButton die verfügbaren Slots an und wählen das Signal clicked()
.
Zur Verwendung von QThread binden wir in der Datei mainwindow.h die entsprechende Headerdatei ein.
1 |
#include <QThread> |
Wir können nun in der selben Datei innerhalb der Klasse MainWindow die Variable workerThread
vom Typ QThread anlegen.
1 2 |
// workerThread als Membervariable definieren QThread workerThread; |
Zum Empfang von Statusmeldungen aus dem Worker Thread deklarieren wir die Funktion handleResults()
als Public Slot. Diese Funktion erhält die Textnachricht, die vom Worker Objekt gesendet wird. Um die Ausführung des Workers anstoßen zu können, deklarieren wir des Weiteren die Funktion operate()
als Signal. operate()
übergibt eine int Variable, die zur Ermittlung der Ausführung der gewünschten Berechnung dient.
1 2 3 4 5 |
public slots: void handleResults(const QString & str); signals: void operate(const int &); |
Wir binden in mainwindow.cpp die Headerdatei der Worker Klasse ein.
1 |
#include "worker.h" |
Nun sind im Konstruktor von MainWindow mehrere Schritte notwendig. Wir legen ein neues Worker Objekt an, und schieben es in den Worker Thread. Wir verbinden das Ereignis der Beendigung des Threads (finished()
) mit der Methode deleteLater()
. Damit wird nach der Beendigung des Threads dieser zur Löschung vorgemerkt.
Als nächstes verbinden wir die Funktion operate()
aus der Hauptklasse mit der Funktion processData()
aus der Worker Klasse. Für den umgekehrten Signalverlauf verbinden wir zusätzlich sendSignal()
aus der Worker Klasse mit handleResults()
aus der Hauptklasse.
Abschließend starten wir die Ausführung des Threads mit start()
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// Neues Worker Objekt anlegen Worker *worker = new Worker; // Ausführung des Worker Objektes in neuen Thred schieben worker->moveToThread(&workerThread); // Worker Thread zu einem späteren Zeitpunkt löschen connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater); // operate Funktion aus MainWindow Klasse // mit processData aus Worker Klasse verbinden connect(this, &MainWindow::operate, worker, &Worker::processData); // sendSignal Funktion aus Worker Klasse // mit Funktion handleResults aus MainWindow verbinden connect(worker, &Worker::sendSignal, this, &MainWindow::handleResults); // Ausführung des workerThread starten workerThread.start(); |
Das Stoppen des Threads erfolgt im Destruktor der MainWindow Klasse. Wir führen die quit()
Funtion aus und warten anschließend auf die Beendigung von workerThread (wait()
).
1 2 3 |
// Worker Thread beenden workerThread.quit(); workerThread.wait(); |
In der Funktion handleResults()
werten wir den empfangenen String des Worker Objekts aus und geben diesen im Text Edit Widget aus.
1 2 |
// Status in textEditProgress ausgeben ui->textEditProgress->append(str); |
In der Funktion on_pushButtonCalculate_clicked()
stoßen wir Berechnungen mit operate()
an. Wir starten drei verschiedene Berechnungen und versuchen Abschließend einen ungültigen Wert zu übergeben. Gültige Werte werden im nächsten Abschnitt in der Worker Klasse definiert.
1 2 3 4 5 6 7 |
// Task Alpha, Beta und Gamma starten operate(1); operate(2); operate(3); // Ungültigen Wert übergeben operate(0); |
Die Beschreibung der Worker Klasse erfolgt auf der zweiten Seite.
Hinterlasse jetzt einen Kommentar