QMetaObject::invokeMethod crash



  • Hi !

    I'm trying to call a QWidget slot from a worker thread with QMetaObject::invokeMethod but the application crash on invokeMethod.

    @
    void functionRunningOnAWorkerThread()
    {
    QString arg = "Hello World";
    QWidget* w = 0;

    //I call a factory to create a QLabel object from the GUI thread (Blocking call)
    w = viewFactory()->createView("label");

    //At this point w is on the screen and I would like to set its content. So I call "invokeMethod"
    QMetaObject::invokeMethod(w,"setText", Qt::QueuedConnection,Q_ARG(QString,arg)); // CRASH
    }
    @

    I have done similar things before but only with non QWidget objects. I don't know if it is possible to do it with QWidget objects.


  • Moderators

    QWidgets can only be handled by the GUI thread. Send a signal to your main thread with the text you want to set and handle widget manipulation there.

    Try with Qt::BlockingQueuedConnection, too :)


  • Moderators

    Edit: nvm :)



  • That's what I do in my function even if the function itself runs on a worker thread.

    QMetaObject::invokeMethod is supposed to execute the corresponding slot from the GUI thread



  • Have you looked where it crashed exactly?
    Do you have a call stack?



  • [quote author="Tom C" date="1371635338"]That's what I do in my function even if the function itself runs on a worker thread.

    QMetaObject::invokeMethod is supposed to execute the corresponding slot from the GUI thread[/quote]

    This invokes a slot that directly manipulates the widget (i.e. GUI stuff).
    @
    QMetaObject::invokeMethod(w,"setText", Qt::QueuedConnection,Q_ARG(QString,arg));
    @

    What you have to do is invoke a custom slot you have to implement for example in a wrapper or subclass for your widget. And in this slot you can then call the setText() slot.
    @
    QMetaObject::invokeMethod(myWidgetPointer,"myCustomChangeTextSlot", Qt::QueuedConnection,Q_ARG(QString,arg));

    MyWidget::myCustomChangeTextSlot(QString a_text)
    {
    setText(a_text); // in case of subclass
    m_widget->setText(a_text); // in case of wrapper class
    }
    @


  • Moderators

    i don't see any advantage/difference in your solution KA51O??



  • The advantage is that his doesn't work because you cannot manipulate GUI stuff like widgets from outside of the GUI thread and mine works because the manipulating call is issued from within the GUI thread. Just as sierdzio already pointed out.

    Atleast thats what I thought the problem was all about?


  • Moderators

    since he used a queued-connection i will be executed in the thread of the widget (gui thread) and not from his worker-thread... or do i miss something?!

    i would also like to see the call stack of the crash please. And are you sure that w is a valid pointer?



  • well obviously not otherwise why would it crash.
    Edit: I'm not so sure about this myself, but I think the GUI manipulating call is not allowed to be triggered from a none GUI thread. The QueuedConnection in this case does not change the fact that its triggered from outside.


  • Moderators

    [quote author="KA51O" date="1371643135"]well obviously not otherwise why would it crash.[/quote]
    thats excatly the question ... to me it's not that obvious that this is the reason. Even without haven seen the call stack.



  • if you would do this in the setText() method you would see that sender and receiver are in different threads.
    @
    currentThread =this->thread();
    senderThread = QObject::sender()->thread();
    if(currentThread != senderThread)
    {
    //complain by either failing an assert or printing a warning or something
    }
    @
    And I think that's what's done somewhere inside to check that GUI objects are not manipulated from outside of the GUI thread.



  • For example this check is done in QCoreApplication. I think there's something similar for GUI objects.
    @
    void QCoreApplicationPrivate::checkReceiverThread(QObject *receiver)
    {
    QThread *currentThread = QThread::currentThread();
    QThread *thr = receiver->thread();
    Q_ASSERT_X(currentThread == thr || !thr,
    "QCoreApplication::sendEvent",
    QString::fromLatin1("Cannot send events to objects owned by a different thread. "
    "Current thread %1. Receiver '%2' (of type '%3') was created in thread %4")
    .arg(QString::number((quintptr) currentThread, 16))
    .arg(receiver->objectName())
    .arg(QLatin1String(receiver->metaObject()->className()))
    .arg(QString::number((quintptr) thr, 16))
    .toLocal8Bit().data());
    Q_UNUSED(currentThread);
    Q_UNUSED(thr);
    }
    @



  • QueuedConnection is used to emit across thread boundaries.
    This will not crash due to thread boundaries and invoking with this parameter doies the same as emitting a signal in a thread and connecting it by autoconnect to an object in another thread.



  • [quote author="Gerolf" date="1371644612"]QueuedConnection is used to emit across thread boundaries.
    This will not crash due to thread boundaries and invoking with this parameter doies the same as emitting a signal in a thread and connecting it by autoconnect to an object in another thread.[/quote]

    I know but the problem is not with QueuedConnection but with the fact that the slot that the signal is connected to is a slot that results in a GUI object being manipultated (e.g. QLabel::setText()).

    What I mean is that this is ok.
    @
    //object of this class lifes in main thread
    class MyClass : public QObject
    {
    Q_OBJECT
    public slots:
    void setText(QString a_text);
    private:
    QString m_text;
    };

    void MyClass:setText(QString a_text)
    {
    m_text = a_text;
    }

    MyClass * pointerToMyClassObject = new MyClass ();

    //somewhere outside of main thread
    QMetaObject::invokeMethod(pointerToMyClassObject,"setText", Qt::QueuedConnection,Q_ARG(QString,arg));
    @

    But this is not ok
    @
    // living in the context of the main thread
    QLabel* pointerToQLabelObject = new QLabel();

    //somewhere outside of main thread
    QMetaObject::invokeMethod(pointerToQLabelObject,"setText", Qt::QueuedConnection,Q_ARG(QString,arg));
    @


  • Moderators

    [quote author="Tom C" date="1371634772"]
    @
    //I call a factory to create a QLabel object from the GUI thread (Blocking call)
    w = viewFactory()->createView("label");
    @
    [/quote]Hi Tom, are you sure the QLabel is created in the GUI thread? Can you post the code for createView()?

    [quote author="KA51O" date="1371645076"]...the problem is not with QueuedConnection but with the fact that the slot that the signal is connected to is a slot that results in a GUI object being manipultated (e.g. QLabel::setText()).[/quote]Hi KA51O, please see http://qt-project.org/forums/viewthread/29030/ for my response to this.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.