Seite 1 von 1
[gelöst] Lokalen QEventLoop per Signal beenden
Verfasst: 6. Juni 2017 19:21
von bobcat
Ich möchte an einer Stelle meines Codes auf ein Signal warten. Bei StackOverflow gibt's dazu ein paar Vorschläge, einen lokalen QEventLoop zu verwenden:
https://stackoverflow.com/questions/355 ... -qt-signal
https://stackoverflow.com/questions/313 ... n-a-thread
Also habe ich folgendes ausprobiert (alles im gleichen Thread):
Code: Alles auswählen
MyDevice* device = new MyDevice();
QTimer timer;
timer.setSingleShot(true);
QEventLoop loop;
connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); // Ohne diesen connect bleibt der Code im loop hängen
connect(device, SIGNAL(released()), &loop, SLOT(quit()));
device->release();
timer.start(500);
loop.exec();
// Hier geht's weiter
someMoreCode();
Erreichen möchte ich folgendes:
Wenn von meinem device das Signal released() gesendet wird, dann soll der Slot quit() von loop aufgerufen werden. Dann geht's weiter mit someMoreCode().
Ich habe überprüft, dass das Signal released gesendet wird. Allerdings wird der Slot quit() anscheinend nicht aufgerufen und das Programm bleibt hängen.
Wenn ich stattdessen den Slot quit() über einen Timer aufrufe, dann geht's weiter.
Das würde ich gerne verstehen. Außerdem würde ich gerne wissen, was ich machen muss, damit ich ohne den Timer auskomme (schließlich will ich ja auf das Signal released() warten und nicht auf den Timer). Hat jemand eine Idee?
(Und ja, mir ist klar, dass das kein besonders guter Stil ist. Ich brauche es als Workaround an einer Stelle, da ich für einen Sonderfall nicht meine ganze Anwendung auf eine besser implementierte asynchrone Kommunikation umschreiben kann ...)
Re: Lokalen QEventLoop per Signal beenden
Verfasst: 6. Juni 2017 19:40
von Christian81
Woher das Signal kommt ist der QEventLoop (oder wen auch immer) egal. Deshalb würde ich sagen, dass entweder das connect() nicht funktioniert (da es z.B. das Signal released() nicht gibt) - connect() hat einen Rückgabewert - oder released() nicht aufgerufen wird. Ist aber nur Kaffeesatzleserei da zu wenig Code.
Re: Lokalen QEventLoop per Signal beenden
Verfasst: 6. Juni 2017 23:48
von odt
Mein Kafi-Satz:
Der quit wird in den loop gestellt, bevor er exec't, d.h. verworfen da der loop noch gar nicht läuft.
Code: Alles auswählen
connect(device, SIGNAL(released()), &loop, SLOT(quit()));
device->release(); -> emit released -> loop.quit();
loop.exec(); -> "neuer" loop startet
möglicherweise hilft "dirty work-a-round"
Code: Alles auswählen
connect(device, SIGNAL(released()), &loop, SLOT(quit()), [b]Qt::QueuedConnection[/b] );
da der Event dann erst mal in die Queue gelegt wird (ohne Gewähr, nicht getestet).
Wie wäre es mit einem Lamda ohne zusätzlichen EventLoop?
Code: Alles auswählen
connect(device, SIGNAL(released()), [](){
someMoreCode();
} );
Re: Lokalen QEventLoop per Signal beenden
Verfasst: 7. Juni 2017 16:04
von bobcat
In
wird ein release-command an den eigentlichen Treiber des Gerätes gesendet. Dieser liefert dann ein Signal (sigEnabled(false)) zurück, dass das Gerät ausgeschaltet wurde:
Code: Alles auswählen
MyDevice::MyDevice
{
timer.setSingleShot(true);
connect(&deviceDriver, SIGNAL(sigEnabled(bool)), SLOT(setEnabled(bool)));
connect(&timer, SIGNAL(timeout()), SLOT(sendReleased()));
...
}
void MyDevice::setEnabled(bool enabled)
{
if (enabled)
{ }
else
{
// 1) Versende ich released() über den timer, dann bricht mein lokaler event loop über quit() ab.
timer.start(10);
// 2) Versende ich released() alternativ direkt, dann bricht der event loop nicht ab.
emit released();
}
}
void MyDevice::sendReleased()
{
emit released();
}
Das Verhalten meines Codes hängt nun davon ab, wie device das Signal released() versendet: Mache ich es über einen Timer, dann wird das quit() meines lokalen Event-Loops aufgerufen (Version 1)). Sende ich direkt released() aus senEnabled(...), dann hängt meine Anwendung (Version 2)).
Habt ihr noch eine Idee?
Re: Lokalen QEventLoop per Signal beenden
Verfasst: 7. Juni 2017 17:49
von odt
Vermutlich habe ich mich bei meiner ersten Antwort zu kompliziert ausgedrückt (war schon spät)? Jedenfalls hast Du nicht geschrieben, ob es mit QueuedConnection wie erwartet funktioniert. Deine Varianten bestätigen meine Vermutung und darum habe ich es als "Beispiel" versucht umzusetzen:
Code: Alles auswählen
class Test : public QObject
{
Q_OBJECT
private:
QEventLoop loop;
public:
explicit Test();
public:
void test();
signals:
void signal1();
void signal2();
public slots:
void slot1();
void slot2();
};
Code: Alles auswählen
Test::Test()
{
connect( this, SIGNAL(signal1()), this, SLOT(slot1()) );
connect( this, SIGNAL(signal2()), this, SLOT(slot2()), Qt::QueuedConnection );
}
void Test::test()
{
qDebug() << "emit signal1";
emit signal1();
qDebug() << "emit signal2";
emit signal2();
qDebug() << "loop.exec (der Event-Loop startet)";
int r = loop.exec();
qDebug() << "loop quited (der Event-Loop hat sich beendet)" << r;
}
void Test::slot1()
{
qDebug() << "slot1 (das signal1 wird aufgerufen, bevor loop.exec gestartet ist, ein quit ist ohne effekte)";
loop.quit(); // bewirkt nichts
}
void Test::slot2()
{
qDebug() << "slot2 (eine 'QueuedConnection' wird 'verzögert', bis loop.exec (oder QApplication::processEvents) läuft, darum hat quit ein Effekt)";
loop.quit();
}
liefert:
Code: Alles auswählen
emit signal1
slot1 (das signal1 wird aufgerufen, bevor loop.exec gestartet ist, ein quit ist ohne effekte)
emit signal2
loop.exec (der Event-Loop startet)
slot2 (eine 'QueuedConnection' wird 'verzögert', bis loop.exec (oder QApplication::processEvents) läuft, darum hat quit ein Effekt)
loop quited (der Event-Loop hat sich beendet) 0
Re: Lokalen QEventLoop per Signal beenden
Verfasst: 7. Juni 2017 18:08
von bobcat
Ich hatte die Variante mit der QueuedConnection ausprobiert, allerdings auch ohne Erfolg, sorry, dass ich das nicht erwähnt hatte. Evtl. ist mir bei der Umsetzung noch ein Fehler unterlaufen, ich versuche es noch ein zweites Mal.
Allerdings habe ich auch ein qApp->processEvents() ausprobiert, ohne Erfolg. Aber vielleicht war hier der Fehler, dass ich das processEvents(...) auf den lokalen event loop hätte anwenden müssen.
Re: Lokalen QEventLoop per Signal beenden
Verfasst: 8. Juni 2017 10:18
von bobcat
@odt: Danke für's nochmalige Nachfragen! Ich habe einen weiteren Versuch gestartet, die Variante mit der Qt::QueuedConnection funktioniert, d.h., der lokale event loop bricht ab. Ich hatte beim ersten Versuch noch einen Fehler in meinem Code, den ich jetzt gefunden und beseitigt habe.