本文介绍两种在Qt子线程(非UI线程)中更新UI组件的常用方法。
1. 使用信号槽 这是一种非常常规的方式,通过自定义信号、槽,连接该信号和槽,在子线程中发送信号,在槽中更新 UI。
定义信号和槽:
1 2 3 4 signals: void updateUi (int v) ; private slots: void onUpdateUi (int v) ;
在子线程中发送信号:
1 2 3 4 5 6 7 8 9 10 connect (this , &UpdateUIInSubThread::updateUi, this , &UpdateUIInSubThread::onUpdateUi, Qt::AutoConnection);std::thread t = std::thread ([this ]() { for (int i = 0 ; i < 10000 ; i++) { emit updateUi (i); std::this_thread::sleep_for (std::chrono::milliseconds (50 )); } }); t.detach ();
在槽函数中更新 UI:
1 2 3 4 void UpdateUIInSubThread::onUpdateUi (int v) { ui.label->setText (QString::number (v)); }
这种方式需要单独额外定义信号和槽,使用起来比较繁琐。
2. 使用invokeMethod QMetaObject::invokeMethod 函数的原型如下:
1 template <typename Functor, typename FunctorReturnType> bool QMetaObject::invokeMethod (QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr )
该函数可以在context的事件循环中执行function函数。
1 2 3 4 5 6 7 8 9 10 11 12 std::thread t = std::thread ([this ]() { for (int i = 0 ; i < 10000 ; i++) { if (QMetaObject::invokeMethod (this , [i, this ]() { ui.label->setText (QString::number (i)); })) { qDebug () << "Update UI success" ; } std::this_thread::sleep_for (std::chrono::milliseconds (50 )); } }); t.detach ();
由于在子线程中更新 UI,因此信号和槽肯定使用的是 QueuedConnection 的连接方式,所以无法将FunctorReturnType返回给调用者,否则会出现如下错误:
1 QMetaObject::invokeMethod: Unable to invoke methods with return values in queued connections
当然上述示例中也可以不使用 lambda 表达式,直接调用槽函数:
1 2 3 4 5 6 7 8 std::thread t = std::thread ([this ]() { for (int i = 0 ; i < 10000 ; i++) { QMetaObject::invokeMethod (this , "onUpdateUi" , Qt::AutoConnection, Q_ARG (int , i)); std::this_thread::sleep_for (std::chrono::milliseconds (50 )); } }); t.detach ();