我有一个由两个线程组成的简单程序:
QApplication::exec
boost::asio::io_service
TCP事件(例如连接或接收数据)会导致GUI发生更改。大多数情况下,这些都是QLabel上的setText
并隐藏了各种小部件。目前,我正在TCP客户端线程中执行这些操作,这似乎非常不安全。
如何将事件正确发布到Qt主线程?我正在寻找boost::asio::io_service::strand::post
的Qt变体,它将事件发布到boost::asio::io_service
事件循环。
答案 0 :(得分:3)
如果您不想使您的TCP类成为QObject,则另一个选择是使用QMetaObject::invokeMethod()函数。
然后要求您的目标类必须是QObject,并且您必须调用目标上定义的插槽。
假设您的QObject定义如下:
class MyQObject : public QObject {
Q_OBJECT
public:
MyObject() : QObject(nullptr) {}
public slots:
void mySlotName(const QString& message) { ... }
};
然后您可以从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
}
如果使用Qt::DirectConnection
进行调用,则插槽将在TCP线程中执行,并且可能/将崩溃。
编辑:由于invokeMethod
函数是静态的,您可以从任何类调用它,并且该类不需要是QObject。
答案 1 :(得分:0)
如果您的对象继承自QObject,则只需发出一个信号并将其连接到主线程中的插槽即可。信号和插槽是线程安全的,应优先使用。
如果它不是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);
});
对https://riptutorial.com/qt/example/21783/using-qtimer-to-run-code-on-main-thread的原始赠送金额