Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Problems with signals and slots in separate threads



  • So I'm trying to make a fairly simple signal/slot setup with a thread sending the signal, and my main application picking up on it with a slot.
    I've been adviced not to subclass QThread, so I'm using a QThread object running an object of my own class.

    The abstract idea here is that I have a class "A" (main application), that has a slot "slot_a". My other class "B", has a signal "signal_b".
    In As constructor, a thread member is initialized, and an object of type B is moved into said thread. I then proceed to connect signal_b to slot_a. (Thread emits signal, main application receives it)
    This is where the problem occurs: The signal is indeed emitted, but the slot is never reached, unless I use Qt::DirectConnection, which I can't use, because the slot is supposed to update a couple of UI elements.

    Relevant code:
    A's constructor

    A::A(QWidget *parent)
    	: QMainWindow(parent), b_member(new B()), controllerThread(this)
    {
    	ui.setupUi(this);
    
    	b_member->moveToThread(&controllerThread);
    	QObject::connect(&controllerThread, &QThread::started, b_member, &B::slot_b); //this works just fine, "slot_b" is executed
    	QObject::connect(connectionHandler, &B::signal_b, this, &A::slot_a); // here, it only works with Qt:DirectConnection, which I DON'T want
            controllerThread->start(QThread::Priority::HighestPriority);
    }
    

    slot_a:

    void A::slot_a(const MyEnum check)
    {
            // this code is never reached. Breakpoint confirms that
    	if (check == MyEnum::SOMECHECK)
    	{
    		ui.label_someLabel->setVisible(false);
    	}
    	else if (check == MyEnum::SOMEOTHERCHECK)
    	{
    		ui.label_someLabel->setVisible(true);
    	}
    }
    // I need a QueuedConnection for this to work, apparently. Evidently does not work with DirectConnection at least. Causes runtime exception.
    

    signal_b: (defined in b.h)

    void signal_b(const MyEnum);
    

    signal_b is emitted from slot_b as such:

    void B::slot_b()
    {
        // this code is reached and executed
        emit signal_b(MyEnum::SOMECHECK);
    }
    

    Not sure if it's even relevant, or just a property of Qt and VS, but following the emition of singal_b just leads me to "qobject.cpp not found".

    I've spent far too many hours looking into this problem, and according to multiple other forum posts and stack overflow topics, I've done everything right.
    If anything's unclear, I'd be glad to clear it up. Thanks in advance!


  • Lifetime Qt Champion

    Hi @Henduww,

    the connection types are described here.

    Qt::DirectConnection 1 The slot is invoked immediately when the signal is emitted. The slot is executed in the signalling thread.

    This means, a function call takes place, therefore your breakpoint is hit and the runtime problems occur.

    Qt::QueuedConnection 2 The slot is invoked when control returns to the event loop of the receiver's thread. The slot is executed in the receiver's thread.

    Here the function call is put in a mailbox of the receiver and is executed when the event loop has time for it.

    That leads me to the question: do you have an event loop running in the receiver thread?

    but following the emition of singal_b just leads me to "qobject.cpp not found".

    What do you mean by that?

    Regards



  • Thanks for clearing those things up @aha_1980!
    I'm not sure how to implement an event loop, didn't realize I had to do that, but I'll look into it :)
    By the qobject.cpp not found I mean after following the emitting with breakpoints, I follow it into the moc file of my header, then I get led to "qobject.cpp" not found in Visual Studio. I don't think it's an error though, probably just VS that doesn't know how to step through the emition.



  • You should already have two event loops running, no need to create one yourself. When you do:

    controllerThread->start(QThread::Priority::HighestPriority);
    

    you start the controller thread event loop.

    I assume your widget "A" is running in the main thread (it has to?). When you do the typical

    return app.exec() 
    

    in main function you start the event loop on the main thread.

    You should now have two event loops running. What you need to look out for is what kind of arguments you are passing around in the signals/slots. In direct connections you can pass anything. If you want to pass anything over a queued connection you need to register the types with the qt meta system (most basic types such as ints etc are already registered). What are you sending? You shoul get a warning in your console if you try to pass anything that is unregistered.



  • Did you work it out?



  • @BjornW Thank you so much for clearing that up with the meta-types, that was indeed the problem!
    I've been on vacation the past week, so sorry for the delayed answer.
    The type I was passing was an enum class, which really wasn't necessary. I just changed it to a regular enum and made the signal/slots use int to make use of enums' implicit casting, works like a charm!


Log in to reply