最近用QT做一个服务器,众所周知,QT的主线程必须保持畅通,才能刷新UI。所以,网络通信端采用新开线程的方式。在涉及到使用子线程更新Ui上的控件时遇到了点儿麻烦。网上提供了很多同一线程不同类间采用信号槽通信的方式,但是并不完全适合线程间的信号槽通信,这主要体现在自定义消息的传递上。
首先我们看看一般的方式:
testthread.h 文件
- #ifndef TESTTHREAD_H
- #define TESTTHREAD_H
- #include <QThread>
- #include "msg.h"
- class TestThread : public QThread
- {
- Q_OBJECT
- public:
- explicit TestThread(QObject *parent = 0);
- protected:
- void run();
- signals:
- void TestSignal(int);
- private:
- Msg msg;
- };
- #endif // TESTTHREAD_H
testthread.cpp文件
- #include "testthread.h"
- TestThread::TestThread(QObject *parent) :
- QThread(parent)
- {
- }
- void TestThread::run()
- {
- //触发信号
- emit TestSignal(123);
- }
自己定义的类继承了QThread类,重写run函数,然后触发TestSignal信号。
mainwindow.h
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
- #include <QMainWindow>
- #include "testthread.h"
- namespace Ui {
- class MainWindow;
- }
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
- public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
- private slots:
- void DisplayMsg(int);
- private:
- Ui::MainWindow *ui;
- TestThread *t;
- };
- #endif // MAINWINDOW_H
mainwindow.cpp
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- //进行connect前必须实例化
- t = new TestThread();
- connect(t, SIGNAL(TestSignal(int)), this, SLOT(DisplayMsg(int)));
- //执行子线程
- t->start();
- }
- void MainWindow::DisplayMsg(int a)
- {
- ui->textBrowser->append(QString::number(a));
- }
- MainWindow::~MainWindow()
- {
- delete ui;
- }
Mainwindow里面连接信号槽,并且将收到的int参数显示在界面上。
运行效果
下面我们对程序进行一些简单,修改,使得它传输我们的自定义消息。
testthread.h 文件
- #ifndef TESTTHREAD_H
- #define TESTTHREAD_H
- #include <QThread>
- #include "msg.h"
- class TestThread : public QThread
- {
- Q_OBJECT
- public:
- explicit TestThread(QObject *parent = 0);
- Msg msg;
- protected:
- void run();
- signals:
- void TestSignal(Msg); //Msg!!!
- };
- #endif // TESTTHREAD_H
testthread.h 文件
- #include "testthread.h"
- TestThread::TestThread(QObject *parent) :
- QThread(parent)
- {
- }
- void TestThread::run()
- {
- msg.int_info = 999;
- msg.str_info = "Hello Main Thread!";
- //触发信号
- emit TestSignal(msg);
- }
mainwindow.h 文件
- #ifndef MAINWINDOW_H
- #define MAINWINDOW_H
- #include <QMainWindow>
- #include "testthread.h"
- #include "msg.h"
- namespace Ui {
- class MainWindow;
- }
- class MainWindow : public QMainWindow
- {
- Q_OBJECT
- public:
- explicit MainWindow(QWidget *parent = 0);
- ~MainWindow();
- private slots:
- void DisplayMsg(Msg); //Msg!!!
- private:
- Ui::MainWindow *ui;
- TestThread *t;
- };
- #endif // MAINWINDOW_H
mainwindow.cpp 文件
- #include "mainwindow.h"
- #include "ui_mainwindow.h"
- MainWindow::MainWindow(QWidget *parent) :
- QMainWindow(parent),
- ui(new Ui::MainWindow)
- {
- ui->setupUi(this);
- //进行connect前必须实例化
- t = new TestThread();
- //Msg!!!
- connect(t, SIGNAL(TestSignal(Msg)), this, SLOT(DisplayMsg(Msg)));
- //执行子线程
- t->start();
- }
- void MainWindow::DisplayMsg(Msg msg)
- {
- ui->textBrowser->append(QString::number(msg.int_info));
- ui->textBrowser->append(msg.str_info);
- }
- MainWindow::~MainWindow()
- {
- delete ui;
- }
此时再进行编译,能够通过,但是Qt Creator会有提示
- QObject::connect: Cannot queue arguments of type 'Msg'
- (Make sure 'Msg' is registered using qRegisterMetaType().)
并且运行程序,不会有任何反应。
mainwindow.cpp文件 改动为
- ui->setupUi(this);
- qRegisterMetaType<Msg>("Msg");
此时能够正常运行
说明:
在线程间使用信号槽进行通信时,需要注意必须使用元数据类型
Qt内生的元数据类型,如int double QString 等
如果要用自己定义的数据类型,需要在connect前将其注册为元数据类型。形式见代码。