QT高级编程技巧(一)-- 编写高效的signal & slot通信代码

关于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函数还请读者另行学习。

 

本文链接:http://www.cnblogs.com/wenris/p/4447481.html

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

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

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

第1章 混合桌面/internet应用程序 1.1 internet相关窗口部件 1.2 webkit的使用 第2章 声音和视频 2.1 qsound和qmovie的使用 2.2 phonon多媒体框架 第3章 模型/视图表格模型 3.1 qt的模型/视图架构 3.2 用于表格的qstandarditemmodel 3.3 创建自定义表格模型 第4章 模型/视图树模型 4.1 用于树qstandarditemmodel的用法 4.2 创建自定义树模型 第5章 模型/视图委托 5.1 与数据类型相关的编辑器 5.2 与数据类型相关的委托 5.3 与模型相关的委托 第6章 模型/视图中的视图 6.1 qabstractitemview子类 .6.2 与模型相关的可视化视图 第7章 用qtconcurrent实现线程处理 7.1 在线程中执行函数 7.2 线程中的过滤和映射 第8章 用qthread实现线程处理 8.1 独立项的处理 8.2 共享项的处理 第9章 创建富文本编辑器 9.1 qtextdocument简介 9.2 创建自定义的文本编辑器 9.3 一个单行的富文本编辑器 9.4 编辑多行的富文本 第10章 创建富文本文档 10.1 高质量地输出qtextdocument文件 10.2 创建qtextdocument 10.3 输出和打印文档 10.4 绘制页面 第11章 创建图形/视图窗口 11.1 图形/视图架构 11.2 图形/视图窗口部件和布局 11.3 图形项简介 第12章 创建图形/视图场景 12.1 场景、项和动作 12.2 增强qgraphicsview的功能 12.3 创建可停靠的工具箱窗口部件 12.4 创建自定义图形项 第13章 动画和状态机框架 13.1 动画框架简介 13.2 状态机框架简介 13.3 动画和状态机的结合 结束语 精选书目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值