實(shí)現(xiàn)QT元類型和QT線程通信
實(shí)現(xiàn)QT元類型和QT線程通信是本文將要介紹的內(nèi)容,不多說(shuō)廢話,先來(lái)看內(nèi)容。今天調(diào)試QT線程通信的程序時(shí),突然發(fā)現(xiàn)如下消息:
其中PEOPLE只是我定義的枚舉類型即enum PEOPLE。然后在Qt的信號(hào)-槽函數(shù)的參數(shù)中使用了這個(gè)枚舉型,在發(fā)送信號(hào)時(shí)就出現(xiàn)了上述警告。上面警告的大概意思是信號(hào)隊(duì)列中無(wú)法使用PEOPLE類型,要使用qRegisterMetaType()注冊(cè)該類型后方可使用。
通常使用的connect,實(shí)際上最后一個(gè)參數(shù)使用的是Qt::AutoConnection類型:(友們,點(diǎn)擊之后,就會(huì)放大,不好意思,影響你視覺(jué)了)
Qt支持6種連接方式,其中3中最主要:
Qt::DirectConnection(直連方式)
當(dāng)信號(hào)發(fā)出后,相應(yīng)的槽函數(shù)將立即被調(diào)用。emit語(yǔ)句后的代碼將在所有槽函數(shù)執(zhí)行完畢后被執(zhí)行。(信號(hào)與槽函數(shù)關(guān)系類似于函數(shù)調(diào)用,同步執(zhí)行)
Qt::QueuedConnection(排隊(duì)方式)
當(dāng)信號(hào)發(fā)出后,排隊(duì)到信號(hào)隊(duì)列中,需等到接收對(duì)象所屬線程的事件循環(huán)取得控制權(quán)時(shí)才取得該信號(hào),調(diào)用相應(yīng)的槽函數(shù)。emit語(yǔ)句后的代碼將在發(fā)出信號(hào)后立即被執(zhí)行,無(wú)需等待槽函數(shù)執(zhí)行完畢。(此時(shí)信號(hào)被塞到信號(hào)隊(duì)列里了,信號(hào)與槽函數(shù)關(guān)系類似于消息通信,異步執(zhí)行)
Qt::AutoConnection(自動(dòng)方式)
Qt的默認(rèn)連接方式,如果信號(hào)的發(fā)出和接收這個(gè)信號(hào)的對(duì)象同屬一個(gè)線程,那個(gè)工作方式與直連方式相同;否則工作方式與排隊(duì)方式相同。
我的項(xiàng)目中的確跨線程使用了PEOPLE為參數(shù)類型的信號(hào),因此使用的應(yīng)當(dāng)是排隊(duì)方式的信號(hào)-槽機(jī)制,出現(xiàn)“隊(duì)列中無(wú)法使用PEOPLE類型”的警告信息就可以理解了。放狗搜了一圈,有篇文章提供了個(gè)這樣的解決方案:
- connect(cm, SIGNAL(sendLog(QUuid, QByteArray, bool)),
- this,SLOT(sendRes(QUuid,QByteArray,bool)));
改為:
- connect(cm, SIGNAL(sendLog(QUuid, QByteArray, bool)),
- this,SLOT(sendRes(QUuid,QByteArray,bool)));
這樣做的確能使警告信息消失,因?yàn)镼t官方文檔寫(xiě)了:
- With queued connections, the parameters must be of types that are known to Qt's meta-object system,
- because Qt needs to copy the arguments to store them in an event behind the scenes.
即使用排隊(duì)方式的信號(hào)-槽機(jī)制,Qt的元對(duì)象系統(tǒng)(meta-object system)必須知道信號(hào)傳遞的參數(shù)類型。這里手動(dòng)改為直連方式,Qt的元對(duì)象系統(tǒng)就不必知道參數(shù)類型了,于是警告信息消失。但這樣做是不安全的,見(jiàn)Qt官方文檔:
- Be aware that using direct connections when the sender and receiver live in different threads is unsafe if
- an event loop is running in the receiver's thread, for the same reason that calling any function on an obje
- ct living in another thread is unsafe.
因此,咱還是老老實(shí)實(shí)地用qRegisterMetaType()注冊(cè)類型吧
我寫(xiě)的線程通訊方法是采用信號(hào)槽機(jī)制,通常情況下,信號(hào)和槽機(jī)制可以同步操作,這就意味著在發(fā)射信號(hào)的時(shí)候,使用直接函數(shù)即可以立刻調(diào)用連接到一個(gè)信號(hào)上的多個(gè)槽。然而,當(dāng)連接位于不同線程中的對(duì)象時(shí),這一機(jī)制就會(huì)變得不同步起來(lái),可以通過(guò)剛才介紹的,修改QObject::connect()的第5個(gè)可選參數(shù)而改變。
connect的第五個(gè)參數(shù)Qt::QueuedConnection表示槽函數(shù)由接受信號(hào)的線程所執(zhí)行,如果不加表示槽函數(shù)由發(fā)出信號(hào)的次線程執(zhí)行。當(dāng)傳遞信號(hào)的參數(shù)類型不是QT的元類型時(shí)要先注冊(cè),關(guān)于QT的元類型可以參看QT文檔。
QMetaType這個(gè)類里面列舉了所有的元類型。
以枚舉PEOPLE為例,注冊(cè)時(shí)首先Q_DECLARE_METATYPE(PEOPLE);
然后,int id=qRegisterMetaType<PEOPLE>("PEOPLE");
加上這兩句就注冊(cè)成功了。
#p#
貼個(gè)示例的代碼,次線程不斷更改一個(gè)PEOPLE{boy,girl}的信息傳給GUI主線程,主線程在GUI界面上顯示。
- mythread.h
- view plaincopy to clipboardprint?
- #ifndef MYTHREAD_H
- #define MYTHREAD_H
- #include <QThread>
- enum PEOPLE{boy,girl};
- class MyThread : public QThread
- {
- Q_OBJECT
- public:
- MyThread();
- ~MyThread();
- protected:
- void run();
- signals:
- void changeText(PEOPLE pe);
- };
- #endif // MYTHREAD_H
- #ifndef MYTHREAD_H
- #define MYTHREAD_H
- #include <QThread>
- enum PEOPLE{boy,girl};
- class MyThread : public QThread
- {
- Q_OBJECT
- public:
- MyThread();
- ~MyThread();
- protected:
- void run();
- signals:
- void changeText(PEOPLE pe);
- };
- #endif // MYTHREAD_H
- mainwindow.h
- view plaincopy to clipboardprint?
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
- #include "mythread.h"
- #include <QMainWindow>
- namespace Ui {
- class MainWindow;
- }
- class MainWindow : public QMainWindow {
- Q_OBJECT
- public:
- MainWindow(QWidget *parent = 0);
- ~MainWindow();
- private slots:
- void labelSetText(PEOPLE qstr);
- protected:
- void changeEvent(QEvent *e);
- private:
- Ui::MainWindow *ui;
- };
- #endif // MAINWINDOW_H
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
- #include "mythread.h"
- #include <QMainWindow>
- namespace Ui {
- class MainWindow;
- }
- class MainWindow : public QMainWindow {
- Q_OBJECT
- public:
- MainWindow(QWidget *parent = 0);
- ~MainWindow();
- private slots:
- void labelSetText(PEOPLE qstr);
- protected:
- void changeEvent(QEvent *e);
- private:
- Ui::MainWindow *ui;
- };
- #endif // MAINWINDOW_H
- mythread.cpp
- view plaincopy to clipboardprint?
- #include "mythread.h"
- MyThread::MyThread()
- : QThread()
- {
- }
- MyThread::~MyThread()
- {
- }
- void MyThread::run(){
- static int i=1;
- while(true)
- {
- if(i==1)emit changeText(boy);
- else emit changeText(girl);
- ii=i*(-1);
- QThread::sleep(1);
- }
- }
- #include "mythread.h"
- MyThread::MyThread()
- : QThread()
- {
- }
- MyThread::~MyThread()
- {
- }
- void MyThread::run(){
- static int i=1;
- while(true)
- {
- if(i==1)emit changeText(boy);
- else emit changeText(girl);
- ii=i*(-1);
- QThread::sleep(1);
- }
- }
- mainwindow.cpp
- view plaincopy to clipboardprint?
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- #include "mythread.h"
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- MyThread *mythread = new MyThread;
- int id=qRegisterMetaType<PEOPLE>("PEOPLE");
- connect(mythread,SIGNAL(changeText(PEOPLE)),this,SLOT(labelSetText(PEOPLE)),Qt::QueuedConnection);
- mythread->start();
- }
- MainWindow::~MainWindow()
- {
- delete ui;
- }
- void MainWindow::changeEvent(QEvent *e)
- {
- QMainWindow::changeEvent(e);
- switch (e->type()) {
- case QEvent::LanguageChange:
- ui->retranslateUi(this);
- break;
- default:
- break;
- }
- }
- void MainWindow::labelSetText(PEOPLE qstr){
- switch(qstr)
- {
- case boy:
- ui->label->setText("BOY");break;
- case girl:
- ui->label->setText("GIRL");break;
- }
- }
小結(jié):實(shí)現(xiàn)QT元類型和QT線程通信的內(nèi)容到這就介紹完了,希望本文能幫你解決問(wèn)題。