最近用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
9. {
10. Q_OBJECT
11. public:
12. explicit
13.
14. protected:
15. void
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
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
9. class
10. }
11.
12. class MainWindow : public
13. {
14. Q_OBJECT
15.
16. public:
17. explicit
18. ~MainWindow();
19.
20. private
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. new
7. {
8. this);
9.
10. //进行connect前必须实例化
11. new
12.
13. int)), this, SLOT(DisplayMsg(int)));
14.
15. //执行子线程
16. t->start();
17. }
18.
19. void MainWindow::DisplayMsg(int
20. {
21. ui->textBrowser->append(QString::number(a));
22. }
23.
24. MainWindow::~MainWindow()
25. {
26. delete
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
9. {
10. Q_OBJECT
11. public:
12. explicit
13. Msg msg;
14.
15. protected:
16. void
17.
18. signals:
19. void TestSignal(Msg); //Msg!!!
20. };
21.
22. #endif // TESTTHREAD_H
testthread.h 文件
1. #include "testthread.h"
2.
3. TestThread::TestThread(QObject *parent) :
4. QThread(parent)
5. {
6. }
7.
8. void
9. {
10. msg.int_info = 999;
11. "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
10. class
11. }
12.
13. class MainWindow : public
14. {
15. Q_OBJECT
16.
17. public:
18. explicit
19. ~MainWindow();
20.
21. private
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. new
7. {
8. this);
9.
10. //进行connect前必须实例化
11. new
12.
13. //Msg!!!
14. this, SLOT(DisplayMsg(Msg)));
15.
16. //执行子线程
17. t->start();
18. }
19.
20. void
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
29. }
此时再进行编译,能够通过,但是Qt Creator会有提示
1. QObject::connect: Cannot queue arguments of type 'Msg'
2. (Make sure 'Msg' is registered using
并且运行程序,不会有任何反应。
mainwindow.cpp文件 改动为
1. ui->setupUi(this);
2.
3. qRegisterMetaType<Msg>("Msg");
此时能够正常运行

说明:
在线程间使用信号槽进行通信时,需要注意必须使用元数据类型
Qt内生的元数据类型,如int double QString 等
如果要用自己定义的数据类型,需要在connect前将其注册为元数据类型。形式见代码。
这个例子是主线程和子线程的,子线程与子线程之间通信呢?
connect 的 第一个参数 和第三个参数改成监听对象
connect(t, SIGNAL(TestSignal(Msg)), this, SLOT(DisplayMsg(Msg)));
以上的this就是主线程响应,把this改成要监听的另一个线程对象就好了(QT多么健壮 友好 强大)
前提是全部的线程都要在主线程里面实例化new
今天实现的一个结构提参数通过Singal传递的例子。。 那个实现是子线程与GUI子线程的参数进行传递 通过mainWindow来做中转。具体是把用户输入的数据进行复杂计算,计算交给线程做,GUI负责用户输入输出
线程头文件
ABFThread.h
public:
struct G_ABFTableSrcUnit
{
int a;
int b;
int c;
float d;
float e;
unsigned int f;
float Gg;
QString waveformTypel;
};
public slots:
void parameterPassing(abfThread::G_ABFTableSrcUnit); //线程自己调用自己的结构体。。。必须这么写不然主线程会报错的 错误是参数内容不一样
ABFThread.cpp
void abfThread::parameterPassing(abfThread::G_ABFTableSrcUnit)
{
}GUI线程
radarControl.h
#include "abfThread"
private:
Ui::radarControl *ui;
abfThread::G_ABFTableSrcUnit mst_abfSrcUnit;
signals:
void sendString(abfThread::G_ABFTableSrcUnit);radarControl.cpp
按下按钮就发射信号
void radarControl::on_pushButton_clicked()
{
emit sendString(mst_abfSrcUnit);
}
mainWindow.h
#include "abfThread.h"
#include "radarControl.h"mainWindow.cpp
radarInterface = new radarControl();
m_ABFThread = new QThread();
m_ABF = new abfThread();
m_ABF->moveToThread(m_ABFThread);
m_ABFThread->start();
qRegisterMetaType<abfThread::G_ABFTableSrcUnit>("abfThread::G_ABFTableSrcUnit");
connect(radarInterface,SIGNAL(sendString(abfThread::G_ABFTableSrcUnit)),m_ABF,SLOT(parameterPassing(abfThread::G_ABFTableSrcUnit)));
//除了注册结构体外 还要保证传递的参数写法要一样 这就是为什么 前面线程自己定义的结构体自己调用自己的原因了
















