實現(xiàn)QT元類型和QT線程通信
實現(xiàn)QT元類型和QT線程通信是本文將要介紹的內容,不多說廢話,先來看內容。今天調試QT線程通信的程序時,突然發(fā)現(xiàn)如下消息:
其中PEOPLE只是我定義的枚舉類型即enum PEOPLE。然后在Qt的信號-槽函數(shù)的參數(shù)中使用了這個枚舉型,在發(fā)送信號時就出現(xiàn)了上述警告。上面警告的大概意思是信號隊列中無法使用PEOPLE類型,要使用qRegisterMetaType()注冊該類型后方可使用。
通常使用的connect,實際上最后一個參數(shù)使用的是Qt::AutoConnection類型:(友們,點擊之后,就會放大,不好意思,影響你視覺了)
Qt支持6種連接方式,其中3中最主要:
Qt::DirectConnection(直連方式)
當信號發(fā)出后,相應的槽函數(shù)將立即被調用。emit語句后的代碼將在所有槽函數(shù)執(zhí)行完畢后被執(zhí)行。(信號與槽函數(shù)關系類似于函數(shù)調用,同步執(zhí)行)
Qt::QueuedConnection(排隊方式)
當信號發(fā)出后,排隊到信號隊列中,需等到接收對象所屬線程的事件循環(huán)取得控制權時才取得該信號,調用相應的槽函數(shù)。emit語句后的代碼將在發(fā)出信號后立即被執(zhí)行,無需等待槽函數(shù)執(zhí)行完畢。(此時信號被塞到信號隊列里了,信號與槽函數(shù)關系類似于消息通信,異步執(zhí)行)
Qt::AutoConnection(自動方式)
Qt的默認連接方式,如果信號的發(fā)出和接收這個信號的對象同屬一個線程,那個工作方式與直連方式相同;否則工作方式與排隊方式相同。
我的項目中的確跨線程使用了PEOPLE為參數(shù)類型的信號,因此使用的應當是排隊方式的信號-槽機制,出現(xiàn)“隊列中無法使用PEOPLE類型”的警告信息就可以理解了。放狗搜了一圈,有篇文章提供了個這樣的解決方案:
- 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)));
這樣做的確能使警告信息消失,因為Qt官方文檔寫了:
- 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.
即使用排隊方式的信號-槽機制,Qt的元對象系統(tǒng)(meta-object system)必須知道信號傳遞的參數(shù)類型。這里手動改為直連方式,Qt的元對象系統(tǒng)就不必知道參數(shù)類型了,于是警告信息消失。但這樣做是不安全的,見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.
因此,咱還是老老實實地用qRegisterMetaType()注冊類型吧
我寫的線程通訊方法是采用信號槽機制,通常情況下,信號和槽機制可以同步操作,這就意味著在發(fā)射信號的時候,使用直接函數(shù)即可以立刻調用連接到一個信號上的多個槽。然而,當連接位于不同線程中的對象時,這一機制就會變得不同步起來,可以通過剛才介紹的,修改QObject::connect()的第5個可選參數(shù)而改變。
connect的第五個參數(shù)Qt::QueuedConnection表示槽函數(shù)由接受信號的線程所執(zhí)行,如果不加表示槽函數(shù)由發(fā)出信號的次線程執(zhí)行。當傳遞信號的參數(shù)類型不是QT的元類型時要先注冊,關于QT的元類型可以參看QT文檔。
QMetaType這個類里面列舉了所有的元類型。
以枚舉PEOPLE為例,注冊時首先Q_DECLARE_METATYPE(PEOPLE);
然后,int id=qRegisterMetaType<PEOPLE>("PEOPLE");
加上這兩句就注冊成功了。
#p#
貼個示例的代碼,次線程不斷更改一個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;
- }
- }
小結:實現(xiàn)QT元類型和QT線程通信的內容到這就介紹完了,希望本文能幫你解決問題。