16

I am new to QT and I am doing some learning.

I would like to trigger a slot that modify a GUI widget from a C++ thread(Currently a Qthread).

Unfortunatly I get a: ASSERTION failed at: Q_ASSERT(qApp && qApp->thread() == QThread::currentThread());

here is some code:

(MAIN + Thread class)

   class mythread : public QThread
    {
    public:
        mythread(mywindow* win){this->w = win;};
        mywindow* w;
        void run()
        {
            w->ui.textEdit->append("Hello");        //<--ASSERT FAIL
            //I have also try to call a slots within mywindow which also fail.
        };
    };

    int main(int argc, char *argv[])
    {
        QApplication* a = new QApplication(argc, argv);
        mywindow* w = new mywindow();

        w->show();
        mythread* thr = new mythread(w);
        thr->start();

        return a->exec();
    }

Window:

class mywindow : public QMainWindow
{
    Q_OBJECT

public:
    mywindow (QWidget *parent = 0, Qt::WFlags flags = 0);
    ~mywindow ();
    Ui::mywindow ui;

private:



public slots:
    void newLog(QString &log);
};

So I am curious on how to update the gui part by code in a different thread.

Thanks for helping

4 Answers4

22

stribika got it almost right:

QMetaObject::invokeMethod( textEdit, "append", Qt::QueuedConnection,
                           Q_ARG( QString, myString ) );

cjhuitt's right, though: You usually want to declare a signal on the thread and connect it to the append() slot, to get object lifetime management for free (well, for the price of a minor interface change). On a sidenote, the additional argument:

               Qt::QueuedConnection ); // <-- This option is important!

from cjhuitt's answer isn't necessary anymore (it was, in Qt <= 4.1), since connect() defaults to Qt::AutoConnection which now (Qt >= 4.2) does the right thing and switches between queued and direct connection mode based on QThread::currentThread() and the thread affinity of the receiver QObject at emit time (instead of sender and receiver affinity at connect time).

Marc Mutz - mmutz
  • 24,485
  • 12
  • 80
  • 90
  • 2
    I did not know that the connection was now at emit time... does this still work with thread objects, which "live" in the thread where it was created, not the thread that is spawned when QThread::run is called? (We just had a debate about this a couple of weeks ago at work, and decided that it was safer to specify the QueuedConnection option in those cases.) – Caleb Huitt - cjhuitt Jul 21 '09 at 15:36
  • 1
    I've used this to emit signals from Threads that aren't Qthreads at all. Works great for sending events _to_ Qt's main loop. Also, never had to specify QueuedConnection with Qt 4.3 and up. – Macke Oct 14 '09 at 20:23
9

In addition to stribika's answer, I often find it easier to use a signal/slot connection. You can specify that it should be a queued connection when you connect it, to avoid problems with the thread's signals being in the context of its owning object.

class mythread : public QThread
{
signals:
    void appendText( QString );
public:

    mythread(mywindow* win){this->w = win;};
    mywindow* w;
    void run()
    {
        emit ( appendText( "Hello" ) );
    };
};

int main(int argc, char *argv[])
{
    QApplication* a = new QApplication(argc, argv);
    mywindow* w = new mywindow();

    w->show();
    mythread* thr = new mythread(w);
    (void)connect( thr, SIGNAL( appendText( QString ) ),
                   w->ui.textEdit, SLOT( append( QString ) ),
                   Qt::QueuedConnection ); // <-- This option is important!
    thr->start();

    return a->exec();
}
Antony Hatchkins
  • 31,947
  • 10
  • 111
  • 111
Caleb Huitt - cjhuitt
  • 14,785
  • 3
  • 42
  • 49
  • 1
    How would this be achieved if from a non-Qt non-UI thread? (i.e. code without any Qt dependency; not a QThread subclass) – DavidJ May 03 '18 at 14:35
  • 1
    @DavidJ You could look at other answers, but my feeling in that case is that you are considerably far outside Qt's signal-slot use cases, and may run into difficulties if you keep thinking of it as a slot. However, all slots are also functions that can be called as normal (pending access to the function), so other cross-thread techniques to indicate it should be called should work. – Caleb Huitt - cjhuitt May 07 '18 at 20:54
8

You need to use QMetaObject::invokeMethod. For example:

void MyThread::run() {
    QMetaObject::invokeMethod(label, SLOT(setText(const QString &)), Q_ARG(QString, "Hello"));
}

(The above code comes from here: http://www.qtforum.org/article/26801/qt4-threads-and-widgets.html)

stribika
  • 3,146
  • 2
  • 23
  • 21
  • @MarcMutz-mmutz could you please also explain what is wrong with this code? I suspect it is the SLOT macro, but might be that you had some other point ;-) – FourtyTwo Aug 11 '17 at 10:56
  • 3
    `invokeMethod` just takes the function name (``"setText"``), not the result of `SLOT`. – Marc Mutz - mmutz Sep 07 '17 at 11:47
2

I don't think you are allowed to call directly things that results in paint events from any other threads than the main thread. That will result in a crash.

I think you can use the event loop to call things asynchronously so that the main gui thread picks up and then does the updating from the main thread, which is what cjhuitt suggests.