2010-08-24 16 views
18

Ich weiß, es gibt einige ähnliche Fragen zu den folgenden da draußen, aber ich konnte keine konkrete Antwort finden, die mir hilft. Also hier ist mein Problem:Blockiert warten auf ein asynchrones Qt-Signal

Ich arbeite an einer Anwendung, die einige GUI-Initialisierungen beim Start durchführt. Eines der Dinge, die ich tun muß, ist

Aufruf
NetworkConfigurationManager::updateConfigurations() 

Dies ist ein asynchroner Aufruf, der das updateCompleted() Signal aussendet, wenn es fertig ist. Das Problem ist, dass alle meine anderen GUI-Initialisierungen warten müssen bis die updateConfigurations() beendet ist.

Also, was ich könnte so etwas tun würde:

MyApp::MyApp(QWidget *parent) : .... 
{ 
    doSomeInits(); 
    //Now connect the signal we have to wait for 
    connect(configManager, SIGNAL(updateCompleted()), this, SLOT(networkConfigurationUpdated())); 
    configManager->updateConfigurations(); //call the async function 
} 

void MyApp::networkConfigurationUpdated() 
{ 
    doSomething(); 
    doRemainingInitsThatHadToWaitForConfigMgr(); 
} 

Um die Initialisierung aufgeteilt keinen guten Weg, um mich zu sein scheint. Ich denke, es macht den Code viel schwerer zu lesen - Inits sollten zusammen bleiben. Die andere Sache ist: Weil updateConfiguration()asynchron ist, wird der Benutzer in der Lage sein, die GUI zu benutzen, die ihm noch keine Information gibt, weil wir auf updateCompleted() warten.

Gibt es eine Möglichkeit, auf das Signal updateCompleted() zu warten, bevor die Anwendung fortgesetzt wird?

wie:

MyApp::MyApp(QWidget *parent) : .... 
{ 
    doSomeInits(); 
    //Now connect the signal we have to wait for 
    connect(configManager, SIGNAL(updateCompleted()), this, SLOT(doSomething())); 
    ???? //wait until doSomething() is done. 
    doRemainingInitsThatHadToWaitForConfigMgr(); 
} 

In einigen APIs gibt es blockieren Alternativen zu asynchronen Funktionen, aber nicht in diesem Fall.

Ich schätze jede Hilfe. Vielen Dank!

Antwort

19

Der Weg, dies zu tun ist, verschachtelte Ereignisschleifen zu verwenden. Sie erstellen einfach Ihren eigenen QEventLoop, verbinden das Signal, auf das Sie warten möchten, mit dem quit() Slot der Schleife und dann mit der Schleife exec(). Sobald das Signal aufgerufen wird, löst dies den QEventLoop-Slot quit() aus und verlässt somit die exec() der Schleife.

MyApp::MyApp(QWidget *parent) : .... 
{ 
    doSomeInits(); 
    { 
     QEventLoop loop; 
     loop.connect(configManager, SIGNAL(updateCompleted()), SLOT(quit())); 
     configManager->updateConfigurations(); 
     loop.exec(); 
    } 
    doReaminingInitsThatHadToWaitForConfigMgr(); 
} 
+0

Ja, das funktioniert, und es ist das Standardmuster für "Blockieren warten, ohne die Benutzeroberfläche zu blockieren". Ich würde es vermeiden, wenn möglich. Verschachtelte Ereignisschleifen können ernsthafte Kopfschmerzen verursachen, z. Der Benutzer macht zufällige unvorhergesehene Dinge während der Ausführung von loop.exec(), beendet die Anwendung usw. und belässt die Anwendung in einem unvorhergesehenen, inkonsistenten Zustand, nachdem exec() zurückkehrt. Oder: Ein (Benutzer-) Ereignis öffnet eine andere lokale Ereignisschleife, die auf ein Ereignis wartet, bei dem die erste Schleife beendet werden muss, wodurch quasi ein Deadlock verursacht wird. Deshalb empfehle ich, einen Steckplatz zu verwenden und dort fortzufahren, auch wenn das ausführlicher ist. –

+0

Zunächst einmal: Danke für Ihre Hilfe. Deine Antwort ist richtig, obwohl sie in meiner Lösung nicht funktioniert. Ich denke, ich habe eines der von Frank erwähnten Probleme, weil meine Schleife nicht beendet werden kann. Trotzdem habe ich im Nokia-Wiki (http://wiki.forum.nokia.com/index.php/TSQ001335_-_Asynchronous_operations_in_S60_and_Qt) einen Beweis zu Ihrer Antwort gefunden. Also danke euch allen! – cyphorious

+0

Laut Qt/Nokia sollten Sie QEventloop nicht als Ursache für eine außer Kontrolle geratene Rekursion verwenden (siehe http://developer.nokia.com/community/wiki/How_to_wait_synchronous_for_a_Signal_in_Qt). Dies sollte nur zum Testen sein. Oder irre ich diesen Artikel? – TSG

7

Arbeiten von chalup ‚s answer, wenn Sie für einen Benutzer wahrnehmbare Wartezeit gehen werden, Sie könnten einen progress bar stattdessen zeigen möchten (oder eine splash screen, vielleicht).

MyApp::MyApp(QWidget *parent) : .... 
{ 
    doSomeInits(); 
    { 
     QSpashScreen splash; 
     splash.connect(configManager, SIGNAL(updateCompleted()), SLOT(close())); 
     configManager->updateConfigurations(); 
     splash.exec(); 
    } 
    doReaminingInitsThatHadToWaitForConfigMgr(); 
} 
0

Eine andere Lösung könnte QSignalSpy::wait() sein. Diese Funktion wurde in Qt 5 eingeführt und macht genau das, was Sie wollen.

Das einzige Problem, das ich mit dieser Klasse sehe, ist, dass es im QtTest-Modul zur Verfügung gestellt wird. In meinem Fall finde ich es sehr nützlich beim Testen von Code, aber es ist möglicherweise nicht die beste Lösung für Produktionscode.

+0

Scheint innerlich dasselbe wie 'chalup''s Antwort zu sein. –

Verwandte Themen