qt线程间 信号槽的方式数据通信

简述:

1> Qt线程间共享数据主要有两种方式:

1)使用共享内存。即使用一个两个线程都能够共享的变量(如全局变量),这样两个线程都能够访问和修改该变量,从而达到共享数据的目的。

2)使用singal/slot机制,把数据从一个线程传递到另外一个线程。

第一种方法在各个编程语言都普遍使用,而第二种方法是QT的特有的,本文主要介绍第二种。

2 > 槽参数类型

1) 在线程间使用信号槽进行通信时,槽参数必须使用元数据类型的参数;

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

3) 如果要用自定义的数据类型,需要在connect之前将其注册(qRegisterMetaType)为元数据类型。

4) 线程间用“信号与槽”传递引用参数的话,要加const,因为const文字常量存在常量区中,生命周期与程序一样的长。这样可以避免slot调用的时候参数的运行期已过而使引用无效。

connect(m_thread, SIGNAL(MsgSignal(const QString&)),
              this, SLOT(OnMsgSignal(
const QString&)));

3 > Q_DECLARE_METATYPE与qRegisterMetaType 
Q_DECLARE_METATYPE

如果要使自定义类型或其他非QMetaType内置类型在QVaiant中使用,必须使用该宏。

该类型必须有公有的 构造、析构、复制构造函数。

qRegisterMetaType 

必须使用该函数的两种情况:

如果非QMetaType内置类型要在Qt的属性系统中使用。

如果非QMetaType内置类型要在queued 信号与槽中使用。

两者的关系:

Q_DECLARE_METATYPE展开后是一个特化后的类QMetaTypeId<TYPE>

qRegisterMetaType将某类型注册到MetaType系统中。

QMetaTypeId<TYPE>的类中成员包含对qRegisterMetaType的调用。

1、传递int参数(主线程与子线程)


testthread.h 文件


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


testthread.cpp文件


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

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

mainwindow.h


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


mainwindow.cpp


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

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



2、传递自定义参数(主线程与子线程)

对以上程序进行简单的修改,使它传递自定义消息。

testthread.h 文件


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

testthread.cpp文件


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

mainwindow.h


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


mainwindow.cpp


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

此时再进行编译,编译通过,但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. qRegisterMetaType<Msg>( "Msg");

此时便可正常运行:



3、传递自定义参数(子线程与子线程)


原理同上,然后把connect函数中的第三参数this(主线程)改成要监听的另一个线程对象就好了(QT多么健壮、友好、强大)。

connect(t, SIGNAL(TestSignal(Msg)), this, SLOT(DisplayMsg(Msg)));  
  
  

前提是全部的线程都要在主线程里面实例化(new)。


4、传递自定义结构体参数(子线程与子线程)


实现子线程与GUI子线程的参数进行传递。

线程

头文件 ABFThread.h


  
  
  1. public:
  2. struct G_ABFTableSrcUnit
  3. {
  4. int a;
  5. int b;
  6. int c;
  7. float d;
  8. float e;
  9. unsigned int f;
  10. float Gg;
  11. QString waveformTypel;
  12. };
  13. public slots:
  14. void parameterPassing(abfThread::G_ABFTableSrcUnit); //线程自己调用自己的结构体。。。必须这么写不然主线程会报错的 错误是参数内容不一样

ABFThread.cpp


  
  
  1. void abfThread::parameterPassing(abfThread::G_ABFTableSrcUnit)
  2. {
  3. }

GUI线程 

radarControl.h


  
  
  1. #include "abfThread"
  2. private:
  3. Ui::radarControl *ui;
  4. abfThread::G_ABFTableSrcUnit mst_abfSrcUnit;
  5. signals:
  6. void sendString(abfThread::G_ABFTableSrcUnit);
radarControl.cpp
按下按钮就发射信号


  
  
  1. void radarControl::on_pushButton_clicked()
  2. {
  3. emit sendString(mst_abfSrcUnit);
  4. }
mainwindow.h

  
  
  1. #include "abfThread.h"
  2. #include "radarControl.h"
mainwindow.cpp

  
  
  1. radarInterface = new radarControl();
  2. m_ABFThread = new QThread();
  3. m_ABF = new abfThread();
  4. m_ABF->moveToThread(m_ABFThread);
  5. m_ABFThread->start();
  6. qRegisterMetaType<abfThread::G_ABFTableSrcUnit>( "abfThread::G_ABFTableSrcUnit");
  7. connect(radarInterface,SIGNAL(sendString(abfThread::G_ABFTableSrcUnit)),m_ABF,SLOT(parameterPassing(abfThread::G_ABFTableSrcUnit)));
  8. //除了注册结构体外 还要保证传递的参数写法要一样 这就是为什么 前面线程自己定义的结构体自己调用自己的原因了


小结:

1 > Qt的信号槽函数只默认支持Qt的类型和C++提供的内建的基本类型,比如int double float等,根本不支持C++的std::string std::vector 自定义的struct类型。所以需要用Qt提供的Q_DECLARE_METATYPE和qRegisterMetaType来声明和注册自定义的类型和C++的其他类型。
2 > 多线程间的信号槽传递,在connect的时候需要以Qt::QueuedConnection的方式,不然以Qt::DirectConnection的方式接收者UI线程会很长时间收不到后台线程发出的信号,或者信号直接丢失都是有可能的


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值