QT子线程与主线程的信号槽通信

最近用QT做一个服务器,众所周知,QT的主线程必须保持畅通,才能刷新UI。所以,网络通信端采用新开线程的方式。在涉及到使用子线程更新Ui上的控件时遇到了点儿麻烦。网上提供了很多同一线程不同类间采用信号槽通信的方式,但是并不完全适合线程间的信号槽通信,这主要体现在自定义消息的传递上。

首先我们看看一般的方式:

testthread.h 文件[

  1. #ifndef TESTTHREAD_H  
  2. #define TESTTHREAD_H  
  3.   
  4. #include <QThread>  
  5.   
  6. #include "msg.h"  
  7.   
  8. class TestThread : public QThread  
  9. {  
  10.     Q_OBJECT  
  11. public:  
  12.     explicit TestThread(QObject *parent = 0);  
  13.   
  14. protected:  
  15.     void run();  
  16.   
  17. signals:  
  18.     void TestSignal(int);  
  19.   
  20. private:  
  21.     Msg msg;  
  22. };  
  23.   
  24. #endif // TESTTHREAD_H  

testthread.cpp文件[

  1. #include "testthread.h"  
  2.   
  3. TestThread::TestThread(QObject *parent) :  
  4.     QThread(parent)  
  5. {  
  6. }  
  7.   
  8. void TestThread::run()  
  9. {  
  10.     //触发信号  
  11.     emit TestSignal(123);  
  12. }  

自己定义的类继承了QThread类,重写run函数,然后触发TestSignal信号。

mainwindow.h

  1. #ifndef MAINWINDOW_H  
  2. #define MAINWINDOW_H  
  3.   
  4. #include <QMainWindow>  
  5.   
  6. #include "testthread.h"  
  7.   
  8. namespace Ui {  
  9. class MainWindow;  
  10. }  
  11.   
  12. class MainWindow : public QMainWindow  
  13. {  
  14.     Q_OBJECT  
  15.   
  16. public:  
  17.     explicit MainWindow(QWidget *parent = 0);  
  18.     ~MainWindow();  
  19.   
  20. private slots:  
  21.     void DisplayMsg(int);  
  22.   
  23. private:  
  24.     Ui::MainWindow *ui;  
  25.     TestThread *t;  
  26. };  
  27.   
  28. #endif // MAINWINDOW_H  

mainwindow.cpp

  1. #include "mainwindow.h"  
  2. #include "ui_mainwindow.h"  
  3.   
  4. MainWindow::MainWindow(QWidget *parent) :  
  5.     QMainWindow(parent),  
  6.     ui(new Ui::MainWindow)  
  7. {  
  8.     ui->setupUi(this);  
  9.   
  10.     //进行connect前必须实例化  
  11.     t = new TestThread();     
  12.   
  13.     connect(t, SIGNAL(TestSignal(int)), this, SLOT(DisplayMsg(int)));  
  14.   
  15.     //执行子线程  
  16.     t->start();   
  17. }  
  18.   
  19. void MainWindow::DisplayMsg(int a)  
  20. {  
  21.     ui->textBrowser->append(QString::number(a));  
  22. }  
  23.   
  24. MainWindow::~MainWindow()  
  25. {  
  26.     delete ui;  
  27. }  

Mainwindow里面连接信号槽,并且将收到的int参数显示在界面上。

运行效果

下面我们对程序进行一些简单,修改,使得它传输我们的自定义消息。

testthread.h 文件

  1. #ifndef TESTTHREAD_H  
  2. #define TESTTHREAD_H  
  3.   
  4. #include <QThread>  
  5.   
  6. #include "msg.h"  
  7.   
  8. class TestThread : public QThread  
  9. {  
  10.     Q_OBJECT  
  11. public:  
  12.     explicit TestThread(QObject *parent = 0);  
  13.     Msg msg;  
  14.   
  15. protected:  
  16.     void run();  
  17.   
  18. signals:  
  19.     void TestSignal(Msg);   //Msg!!!  
  20. };  
  21.   
  22. #endif // TESTTHREAD_H  

testthread.h 文件
[cpp]

  1. #include "testthread.h"  
  2.   
  3. TestThread::TestThread(QObject *parent) :  
  4.     QThread(parent)  
  5. {  
  6. }  
  7.   
  8. void TestThread::run()  
  9. {  
  10.     msg.int_info = 999;  
  11.     msg.str_info = "Hello Main Thread!";  
  12.     //触发信号  
  13.     emit TestSignal(msg);  
  14. }  

mainwindow.h 文件

  1. #ifndef MAINWINDOW_H  
  2. #define MAINWINDOW_H  
  3.   
  4. #include <QMainWindow>  
  5.   
  6. #include "testthread.h"  
  7. #include "msg.h"  
  8.   
  9. namespace Ui {  
  10. class MainWindow;  
  11. }  
  12.   
  13. class MainWindow : public QMainWindow  
  14. {  
  15.     Q_OBJECT  
  16.   
  17. public:  
  18.     explicit MainWindow(QWidget *parent = 0);  
  19.     ~MainWindow();  
  20.   
  21. private slots:  
  22.     void DisplayMsg(Msg);   //Msg!!!  
  23.   
  24. private:  
  25.     Ui::MainWindow *ui;  
  26.     TestThread *t;  
  27. };  
  28.   
  29. #endif // MAINWINDOW_H  

mainwindow.cpp 文件

  1. #include "mainwindow.h"  
  2. #include "ui_mainwindow.h"  
  3.   
  4. MainWindow::MainWindow(QWidget *parent) :  
  5.     QMainWindow(parent),  
  6.     ui(new Ui::MainWindow)  
  7. {  
  8.     ui->setupUi(this);  
  9.   
  10.     //进行connect前必须实例化  
  11.     t = new TestThread();  
  12.   
  13.     //Msg!!!  
  14.     connect(t, SIGNAL(TestSignal(Msg)), this, SLOT(DisplayMsg(Msg)));  
  15.   
  16.     //执行子线程  
  17.     t->start();  
  18. }  
  19.   
  20. void MainWindow::DisplayMsg(Msg msg)  
  21. {  
  22.     ui->textBrowser->append(QString::number(msg.int_info));  
  23.     ui->textBrowser->append(msg.str_info);  
  24. }  
  25.   
  26. MainWindow::~MainWindow()  
  27. {  
  28.     delete ui;  
  29. }  

此时再进行编译,能够通过,但是Qt Creator会有提示

  1. QObject::connect: Cannot queue arguments of type 'Msg'  
  2. (Make sure 'Msg' is registered using qRegisterMetaType().)  

并且运行程序,不会有任何反应。

mainwindow.cpp文件 改动为

  1. ui->setupUi(this);  
  2.   
  3. qRegisterMetaType<Msg>("Msg");  

此时能够正常运行

说明:

在线程间使用信号槽进行通信时,需要注意必须使用元数据类型

Qt内生的元数据类型,如int double QString 等

如果要用自己定义的数据类型,需要在connect前将其注册为元数据类型。形式见代码。

分享

34.204.191.31