[gelöst] Lokalen QEventLoop per Signal beenden

Alles rund um die Programmierung mit Qt
Antworten
bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

[gelöst] Lokalen QEventLoop per Signal beenden

Beitrag 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 ...)
Zuletzt geändert von bobcat am 8. Juni 2017 10:19, insgesamt 1-mal geändert.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Re: Lokalen QEventLoop per Signal beenden

Beitrag 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.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
odt
Beiträge: 128
Registriert: 12. August 2010 11:49
Kontaktdaten:

Re: Lokalen QEventLoop per Signal beenden

Beitrag 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();
} );
ODT Informatik GmbH, Reto Tschofenig
bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Re: Lokalen QEventLoop per Signal beenden

Beitrag von bobcat »

In

Code: Alles auswählen

device->release()
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?
odt
Beiträge: 128
Registriert: 12. August 2010 11:49
Kontaktdaten:

Re: Lokalen QEventLoop per Signal beenden

Beitrag 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
ODT Informatik GmbH, Reto Tschofenig
bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Re: Lokalen QEventLoop per Signal beenden

Beitrag 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.
bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Re: Lokalen QEventLoop per Signal beenden

Beitrag 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.
Antworten