博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
QT高级编程技巧(一)-- 编写高效的signal & slot通信代码
阅读量:5114 次
发布时间:2019-06-13

本文共 3195 字,大约阅读时间需要 10 分钟。

关于QT的线程通信,我们都会想到signal & slot机制。先回顾下利用signal & slot机制实现控件消息处理的方法。

控件消息处理

假设我们的主界面上有一个使用ui->btn指向的QPushButton对象,要实现该对象的clicked消息处理,可以在主界面对象MainWindow上添加一个slot方法onBtnClicked,并在其构造函数中使用connect方法与ui->btn的clicked消息进行绑定,如下:

MainWindow::MainWindow(QWidget* parent) :  QMainWindow(parent), ui(new ui::MainWindow){    ui->setupUi(this);        // 方法一,推荐使用    connect(ui->btn, &QPushButton::clicked,         this, &MainWindow::onBtnClicked);    // 方法二    //connect(ui->btn, SIGNAL(clicked),           this, SLOT(onBtnClicked));    …} void MainWindow::onBtnClicked(void) {
  doSomething(); }

事实上这种方法很繁杂。单纯为了处理一个按钮的消息我们就要新建一个slot方法,当界面控件多了的时候,会使得代码臃肿不堪(即使你可以设计成多个控件消息共享一个槽函数)。

在新版的支持C++11的QT中,我们可以使用Lambda函数优雅地解决这个问题。如下:

MainWindow::MainWindow(QWidget* parent) :    QMainWindow(parent), ui(new ui::MainWindow){    setupUi(this);    // 使用lambda函数实现slot方法    connect(ui->btn, &QPushButton::clicked, [&](){        doSomething();    });    …}

使用Lambda函数的另一个好处是,可以借助闭包的概念加速开发。比如我曾做过一个运动控制项目,界面上有32个之多的QCheckBox控件,如下:

需要响应每个QCheckBox控件的clicked消息,改变对应的IO口电平输出。我的做法是,对这些控件进行Tab排序,并将第一个控件命名为ui->ioChk,然后编码如下:

typedef QCheckBox   *F_QCB;#define F_QNEXT(w)  (w->nextInFocusChain()) … MainWindow::MainWindow(QWidget* parent) :   QMainWindow(parent), ui(new ui::MainWindow) {
  setupUi(ui);      …   F_QCB cb = ui->ioChk;   for (int i = 0; i < 32; ++i)   {
    connect(cb, &QCheckBox::clicked, [=](bool s){
      setIo(i, s); // 改变第i个端口的输出电平为s     });     cb = F_QNEXT(cb);   }   … }

要读懂上面的程序,你需要一些关于C++11标准的Lambda函数的知识,代码中使用值捕获方式捕获i变量值形成闭包函数,这些闭包函数被绑定到不同的QCheckBox的clicked消息中。

线程间通信

很长的时间里,我都以为QT的signal & slot机制只适用于单向异步通信。事实上,它可以设计为带返回的同步通信。

有个案例如下:工作线程在运行时需要同步提取界面上的参数信息(假设要提取界面上的QSpinBox的值),要同时保证界面的响应和工作线程的不间断运转。

一种可能的实现方法是,在工作线程上的某个对象定义一个request消息,绑定到MainWindow的response槽中,然后在MainWindow中定义一个answer消息,绑定到工作线程上的某个对象的onAnswer槽中。通信过程如下:

1. 工作线程需要界面参数值,发送request消息

2. 主线程接收到request消息,开始执行response槽方法

3. 在response函数中,获取界面控件的值,发送answer消息

4. 工作线程收到answer消息,调用onAnswer槽方法,恢复之前的运行流程

这种方法条理清晰,但编码实现过于繁琐。我的实现方法是,在工作线程实现一个形如request(QString req, QVariant& arg)的消息,然后以QT::BlockBlockingQueuedConnection方式连接至MainWindow的response槽方法中。编码如下:

WorkerThread声明:

#ifndef __WORKER_THREAD_H #define __WORKER_THREAD_H class WorkerThread : public QThread {
public:   …   // @Override   void run(); private:   // 用于通信   class InThreadObject;   InThreadObject *ito;   … } #endif

WorkerThread实现:

class WorkerThread::InThreadObject : public QObject {
  Q_OBJECT signals:   void request(QString req, QVariant& arg); } void WorkerThread::run() {
  ito = new InThreadObject;   …   // 注意下面的连接使用了QT::BlockingQueuedConnection选项   connect(ito,        &InThreadObject::request,        pMainWindow,        &MainWindow::response,        QT::BlockingQueuedConnection);   …   // 同步获取界面参数值   QVariant var;   ito->request("param.level", var);   // 这里会同步等待主线程执行response函数完   int param = var.toInt();   …… }

MainWindow相关实现:

void MainWindow::response(QString req, QVariant& ans) {
  if (req == "param.level")   {
    // 获取控件值并存放至ans引用变量中     ans.setValue(ui->levelSpin->value());   } }

本次经验分享完毕,关于一些具体的技术细节,如QT的消息与槽机制、C++11的Lambda函数还请读者另行学习。

 

本文链接:。

作者:wenris,联系:<wenris AT yeah.net>

后会有期哦 O(∩_∩)O

转载于:https://www.cnblogs.com/wenris/p/4447481.html

你可能感兴趣的文章
tp5之行为监听、钩子行为的绑定与侦听
查看>>
Java中算法的时间及空间复杂性
查看>>
Qt中Pro文件变量详细说明
查看>>
《JAVA程序设计》_第十周学习总结
查看>>
mysql命令使用3
查看>>
C C++ Java中的static
查看>>
(线段树)UESTC 360-Another LCIS
查看>>
Linux启动过程分析
查看>>
[NOI 2006] 最大获利
查看>>
[软件工程基础]2017.10.31 第四次 Scrum 会议
查看>>
线性基 复习总结
查看>>
Contest Hunter Adera6C 網絡升級 樹的直徑 樹形DP
查看>>
数据可视化(8)--D3数据的更新及动画
查看>>
DP 50 题
查看>>
每个程序员都应该了解的内存知识
查看>>
http请求在https中使用
查看>>
APP测试基本流程
查看>>
字符的编码
查看>>
堆表上的转发记录
查看>>
精心挑选的HTML5/CSS3应用及源码
查看>>