本文介绍两种在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 ();