如何在Qt主线程中正确执行GUI操作?

Tom*_*ica 5 c++ qt multithreading

我有一个包含两个线程的简单程序:

  1. 由Qt操作的主GUI线程 QApplication::exec
  2. TCP网络线程由 boost::asio::io_service

TCP事件(例如连接或接收数据)会导致GUI发生更改。通常,它们setText在QLabel上并隐藏各种小部件。当前,我正在TCP客户端线程中执行这些操作,这似乎很不安全。

如何正确地将事件发布到Qt主线程?我正在寻找的Qt变体boost::asio::io_service::strand::post,将事件发布到boost::asio::io_service事件循环。

CJC*_*ink 6

如果你不想让你的 TCP 类成为 QObject,另一个选择是使用QMetaObject::invokeMethod()函数。

然后要求是您的目标类必须是 QObject 并且您必须调用在目标上定义的插槽。

假设您的 QObject 定义如下:

class MyQObject : public QObject {
    Q_OBJECT
public: 
    MyObject() : QObject(nullptr) {}
public slots:
    void mySlotName(const QString& message) { ... }
};
Run Code Online (Sandbox Code Playgroud)

然后您可以从您的 TCP 类调用该插槽。

#include <QMetaObject>

void TCPClass::onSomeEvent() {
    MyQObject *myQObject = m_object;
    myMessage = QString("TCP event received.");
    QMetaObject::invokeMethod(myQObject
                               , "mySlotName"
                               , Qt::AutoConnection // Can also use any other except DirectConnection
                               , Q_ARG(QString, myMessage)); // And some more args if needed
}
Run Code Online (Sandbox Code Playgroud)

如果您Qt::DirectConnection用于调用,则插槽将在 TCP 线程中执行并且它可能/将崩溃。

编辑:由于invokeMethod函数是静态的,您可以从任何类调用它,并且该类不需要是 QObject。


Adr*_* Jr 5

如果您的对象继承自 QObject,只需发出一个信号并将其连接(使用标志 Qt::QueuedConnection)到主线程中的插槽。信号和槽是线程安全的,应该优先使用。

如果它不是 QObject,那么您可以创建一个 lambda 函数(使用 GUI 代码)并使用单发 QTimer 将它排在主线程中并在回调中执行它。这是我正在使用的代码:

#include <functional>

void dispatchToMainThread(std::function<void()> callback)
{
    // any thread
    QTimer* timer = new QTimer();
    timer->moveToThread(qApp->thread());
    timer->setSingleShot(true);
    QObject::connect(timer, &QTimer::timeout, [=]()
    {
        // main thread
        callback();
        timer->deleteLater();
    });
    QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
}

...
// in a thread...

dispatchToMainThread( [&, pos, rot]{
    setPos(pos);
    setRotation(rot);
});
Run Code Online (Sandbox Code Playgroud)

原始信用https://riptutorial.com/qt/example/21783/using-qtimer-to-run-code-on-main-thread

请小心,因为如果您删除对象,您的应用程序可能会崩溃。两个选项是:

  • 调用 qApp->processEvents(); 在删除以刷新队列之前;
  • 也使用 dispatchToMainThread 对删除进行排队;