Search code examples
c++qtqwidgetqthread

Cannot send posted events for objects in another thread - Qt


I have created a QThread class to run a function that is in another class, but this another class has a pointer to a QWidget (QwtPlot), and I am receiving this message in the application output:

QCoreApplication::sendPostedEvents: Cannot send posted events for objects in another thread

I already read in another topics that QThreads doesn't work with QWidgets (the UI widgets must be in the main thread), but the output in my application seems to be correct.

Can anyone explain to me why this message appears? And what can happen if I let the code as is?

Note: sorry, I can't post the code.

Thanks in advance


Solution

  • I already read in another topics that QThreads doesn't work with QWidgets [...] but the output in my application seems to be correct.

    It's not correct, otherwise you wouldn't ask, right?

    A QWidget must be in the main thread. And most likely it is. But you're invoking its methods from another thread, and the methods you invoke are not thread safe. Don't do that. There are other ways of invoking methods safely across threads. Use them instead. For example, assuming that you wish to call QWidget::resize, you could use postToThread from this answer:

    QWidget* widget;
    QSize size;
    Q_ASSERT_X(widget->thread() == qApp->thread(), "widget",
               "The widget must live in the main thread.");
    postToThread([=]{ widget->resize(size); }, widget);
    

    If you want to be more verbose, or have to maintain a Qt 4 code base, you could do this instead:

    class TSWidgetAdapter : public QObject {
      Q_OBJECT
      QWidget * widget() const { return qobject_cast<QWidget*>(parent()); }
      Q_SLOT void resize_d(const QSize & size) { widget()->resize(size); }
    public:
      explicit TSWidgetAdapter(QWidget * parent) : QObject(parent) {
        Q_ASSERT_X(parent->thread() == qApp->thread(), "TSWidgetAdapter()",
                  "The widget must live in the main thread.");
        connect(this, SIGNAL(resize(QSize)), this, SLOT(resize_d(QSize)));
      }
      Q_SIGNAL void resize(const QSize & size);
    };
    
    QWidget* widget;
    QSize size;
    TSWidgetAdapter widget_ts(widget);
    widget_ts.resize(size);
    

    The _d slots are called in the thread of the widget. That's the beauty of automatic connections: you can call the signal in any thread, and the slot will be called in the thread of the target object only. Since the adapter is a child of the widget, it is in the widget's thread - that's enforced by Qt.