Was ist der richtige Weg, um Widgets anzuzeigen, ohne QApplication :: exec () aufzurufen?
Zu Testzwecken möchte ich ein Widget erstellen und anzeigen. Im Moment muss das Widget nur korrekt gerendert werden, aber in Zukunft möchte ich es möglicherweise erweitern, damit ich verschiedene Ereignisse simuliere, um zu sehen, wie sich das Widget verhält.
Aus verschiedenen Quellen scheint Folgendes zu funktionieren:
QApplication app;
QPushButton button("Hello");
button.show();
// Might also be necessary:
QApplication::processEvents();
Aber für mich wird das Widget nicht richtig gerendert. Es wird ein Fenster erstellt, in dem das Widget angezeigt wird. Es ist jedoch vollständig schwarz.
Ich kann das Widget zum korrekten Rendern bringen, indem ich die folgenden Zeilen hinzufüge:
std::this_thread::sleep_for(std::chrono::milliseconds(10));
QApplication::processEvents();
10 Millisekunden sind ungefähr die kleinste Zeit, die erforderlich ist, damit das Widget korrekt gerendert wird.
Weiß jemand, wie man dies ohne Zeitverzögerung zum Laufen bringt, oder weiß man, warum die Verzögerung notwendig ist?
Antworten
Zum Testen der Qt-GUI-Anwendung müssen mindestens die QApplication
Instanz- und Ereignisschleife verarbeitet werden. Der schnellste Weg ist, einfach ein QTEST_MAIN
Makro zu verwenden. Diese Antwort erklärt auf nette Weise, was es genau macht. Um jedoch mehr Flexibilität zu haben (z. B. um GTest, GMock zu verwenden), können Sie auch einfach eine QAplication
Instanz in Ihrem Testhaupt erstellen (kein Aufruf erforderlich exec
).
Um die Ereignisse verarbeiten zu können, sollten Sie QTest :: qWait aufrufen . Dadurch werden Ihre Ereignisse für die angegebene Zeit verarbeitet. Es ist eine gute Praxis, qWaitFor
ein Prädikat zu akzeptieren - auf diese Weise vermeiden Sie Rennbedingungen in Ihren Tests.
Wenn Sie in einem bestimmten Szenario erwarten, dass ein Signal ausgegeben wird, können Sie auch ähnliche Funktionen von QSignalSpy :: wait verwenden .
Kleines Beispiel, wenn wir warten möchten, bis einige Parameter von einem Element an ein anderes übergeben werden:
QSignalSpy spy(&widget1, &Widget1::settingsChanged);
widget2->applySettings();
ASSERT_TRUE(spy.wait(5000));
// do some further tests based on the content of passed settings
Warum soll die Anwendung nicht ausgeführt werden? Das Anzeigen eines Widgets ist nicht "statisch". Sie "zeichnen" das Widget nicht auf dem Bildschirm, sondern haben eine Anwendung, die auf verschiedene Ereignisse wartet und Zeichenereignisse vom Fenstermanager empfängt. Die Anwendung kann das Widget nur zeichnen, wenn der Fenstermanager es anfordert.
Der Grund, warum Ihr zweiter Code funktioniert, besteht darin, dass Sie ausreichend lange warten, bis der Fenstermanager die Anforderung "Zeichnen" unter Ihren Bedingungen gesendet hat . Dies garantiert nicht, dass es immer funktioniert.
Wenn Sie die Anzeige des Widgets garantieren möchten, müssen Sie eine Schleife starten und warten, bis Sie mindestens ein Ziehungsereignis erhalten haben, aber selbst das ist nicht kinderleicht.
Wie von Vincent Fourmond fachmännisch beschrieben, sind Widgets keine einmalige Angelegenheit. Die GUI ist nicht blockierend und muss dazu in einer Ereignisschleife ausgeführt werden .
Die exec()
Methode startet diese Ereignisschleife, die Sie durch Abfragen nachgeahmt haben. Es ist zwar möglich, die Ereignisschleife von Qt mit anderen Ereignisschleifen zu kombinieren, ich würde Ihnen jedoch eine einfachere Lösung empfehlen:
Fahren Sie mit Ihrem Programm innerhalb der Ereignisschleife fort, indem Sie beim Start eine Methode aufrufen. Hier finden Sie eine hervorragende Antwort dazu:https://stackoverflow.com/a/8877968/21974
Wie Sie bereits beim Testen von Einheiten erwähnt haben, gibt es auch ein Signal, mit dem Sie am Ende des Lebenszyklus (bevor Widgets zerstört werden) Überprüfungen durchführen können : QApplication::aboutToQuit
. Dies geschieht, wenn das letzte Fenster geschlossen wird (programmgesteuert oder vom Benutzer).