Qt 的内部进程通信机制

级 别: 初级

续欣 (xxin76@hotmail.com), 博士、大学讲师

2004 年 4 月 01 日

Qt 作为一种跨平台的基于 C++ 的 GUI 系统,能够提供给用户构造图形用户界面的强大功能。自从 1996 年 Qt 被 Trolltech 公司发布以来,该系统成为世界上很多成功的图形用户应用所使用的主要系统。更为重要的是,Linux 操作系统的桌面环境系统 KDE 也是基于 Qt 构造的。目前,Qt 已经提供了对包括 MS/Windows、Unix/X11 和嵌入式平台的支持,得到了越来越广泛的应用。

在 Qt 系统中,不仅有着构造完善的系统结构,而且为了满足用户对编写图形用户界面应用的种种需求,它还创建了许多新的系统机制,其中 Qt 所特有的内部进程通信机制尤其值得一提。 本文分析了基于 QT 的应用进程之间通信常用的三种机制:QCOP 协议,Signal-Slot 机制和 FIFO 机制。给出了各自的使用方法,并指出了各自的使用场合。

1、 QCOP协议

QCOP 是 Qt 内部的一种通信协议,这种协议用于不同的客户之间在同一地址空间内部或者不同的进程之间的通信。目前,这种机制还只在 Qt 的嵌入式版本中提供。

为实现这种通信机制,Qt 中包括了由 QObject 类继承而来的 QCopChannel 类,该类提供了诸如 send()、isRegistered() 等静态函数,它们可以在脱离对象的情况下使用。为了在 channel 中接收通信数据,用户需要构造一个 QCopChannel 的子类并提供 receive() 函数的重载函数,或者利用 connect() 函数与接收到的信号相联系。

值得一提的是,在 Qt 系统中,只提供了 QCOP 协议机制和用于接收消息的类,而如何发送消息则没有提供相应的类供用户使用。

在基于 Qt 的桌面系统 Qtopia(QPE)中,则提供了相应的发送类:QCopEnvelope。用户可以通过该类利用 channel 向其他进程发送消息。该类将通过 QCopChannel 发送 QCop 消息的过程进行了封装,用户只需要调用该类中的相关函数就可以方便地实现进程之间的通信过程。一方面,QCop 消息的发送要利用 QCopEnvelope 类,另一方面,接收消息则是通过与一个 QCopChannel 相关联。

在发送消息时,将利用如下的协议机制:

QCopEnvelope e(channelname, messagename);

对于需要携带参数的消息,必须使用"<<()"运算符将参数添加到envelope中。

e << parameter1 << parameter2 << ...;

对于不带参数的消息,只需要利用:

QCopEnvelope e(channelname, messagename);

在Qtopia中,所有的channels名都以"QPE/"开始,而messagename则是一个函数的标识符。

在接收消息时,通常只需要利用在应用程序中预先定义好的QPE/Application/{appname}管道,当然,也可以根据需 要自己定义管道,并将其与一个slot函数相关联:

myChannel = new QCopChannel( "QPE/FooBar", this );connect( myChannel, SIGNAL(received(const QCString &, const QByteArray &)),          this, SLOT(fooBarMessage( const QCString &, const QByteArray &)) );

下面将具体的通信过程举例如下:

在需要接收消息的类(如Window1)中定义管道:

QCopChannel *doChannel = new QCopChannel("QPE/Do", this);  connect(doChannel, SIGNAL(received(const QCString &, const QByteArray &)),        this, SLOT(doMessage(const QCString &, const QByteArray &)));

同时,需要在该类中定义相应的消息处理函数doMessage,

void Window1::doMessage(const QCString &msg, const QByteArray &args){    QDataStream stream(args, IO_ReadOnly);    if(msg == "Message1(QString)")    {     QString text;     stream >> text;     button->setText(text); } else if(msg == "Message2()")  {     close();  }}

其中的Message1(QString)和Message2(QString)都是用户自己定义的消息,该函数中分别对这些消息进行了相应的处理。在该 例中当收到带有参数的Message1消息时,将该字符串参数stream显示在按钮button上;当收到Message2消息时,将执行关闭 Window1窗口的动作,当然用户可以根据需要自行编写相应的处理过程。

另一方面,在类Class2中需要发出消息的函数function中利用QCopEnvelope发送消息:

void Class2::function(){   QCopEnvelope e("QPE/Do", "Message1(QString)");    e << param; }

这里发出了Message1消息,并将需要携带的参数param发送到管道中。

通过这样的过程,用户可以很方便地实现不同对象、不同进程之间通信过程,而且可以根据需要在通信过程中任意传递参数。


回页首

2、 信号-槽(Signal-Slot)机制

在Qt中,有一种用于对象之间的通信:信号-槽机制,这种机制是Qt的核心机制,也是它区别于其他GUI工具的最主要的特征。在大多数GUI工具中,通常 为可能触发的每种行为定义一个回调函数,这个回调函数是一个指向函数的指针。在Qt中,信号-槽机制取代了这种繁杂的函数指针,能够实现同样的功能。信号 -槽机制可以携带任意类型、任意数量的参数,而且完全是安全的,不会引起系统的崩溃。

所有由QObject类继承而来的类,或者是它的一个子类,都可以包括信号-槽机制。信号通常是当对象改变他们的状态时发出的,这就是 一个对象在需要与其他对象通信时所需要做的一切,它并不知道是否有其他对象在另一端接收该信号。从这个意义上来说,这种机制实现了真正的信息封装,确保了 对象可以被当作一个独立的软件构件来使用。

而槽可以被用于接收信号,它们通常是类中的成员函数。一个槽并不知晓是否有一个信号与自己相联系,同样,包含有槽函数的对象也对通信机 制一无所知,它们也可以作为一个独立的软件构件。

用户可以按照需要将许多信号与一个单独的槽函数相联系,一个信号也可以按需要被联系到很多不同的槽函数。甚至还可以将一个信号直接与另 一个信号相联系,这样当第一个信号被发出时立刻发出第二个信号。

这样,信号-槽相结合就产生了一种功能强大的编程机制。

例如:

button = new QAction(tr("button"), QIconSet(QPixmap("button.png")), 0, 0, this);connect(button, SIGNAL(activated()), this, SLOT(slotButton()));

程序中定义了一个按钮,并利用connect()函数将该按钮button的activated()信号与slotButton()函 数相关联,当用户触发按钮时,就会执行相应的槽函数。当然,这里的信号是QAction类中预先定义好的信号,用户在使用该机制时,可以根据需要自行定义 信号,同时在适当的时候利用emit语句发出该信号。另外,在信号和相应的槽函数之间还可以传递任意参数,如:

emit signal(parameter);

回页首

3、 FIFO机制

当然,除了 Qt 内部所特有的通信机制之外,一般操作系统中常用的进程间通信机制同样可以用于 Qt 系统内部不同进程之间的通信。如消息队列、共享内存、信号量、管道等机制,其中有些机制,如信号量,在 Qt 中重新进行了封装;有些机制则可以直接调用操作系统的系统调用来实现。这里,有名管道是一种简单实用的通信机制,用户在对Qt内部机制

不甚了解的情况下,同样可以使用这种方法实现对象进程之间的通信。下面就对利用这种机制实现Qt内部进程之间的通信过程进行介绍。

首先,需要创建 FIFO,这个过程类似于创建文件,在系统中可以利用 mkfifo 命令来创建,这样就可以用 open 函数打开它,同时,一般的文件 I/O函数(close、read、write)都可以用于 FIFO。

在基于 Qt 的应用中,有很多应用采用了一种客户机-服务器模式,这时就可以利用 FIFO 在客户机和服务器之间传递数据。例如,有一个服务器,它负责接收底层程序发来的消息,同时,它与很多客户机有关,服务器需要将收到的不同消息发送到不同的 客户机,而每个客户机也有请求需要发给服务器,进而发给底层程序。

下面是服务器端的程序示例:(架设已有客户端进程为读而打开/dev/fifoclient1和/dev/fifoclient1)

fd = open("/dev/fifoserver", O_NONBLOCK|O_RDONLY);   file = fdopen(fd, "r"); ret = fgets(buf, MAX_LINE, file );  if(buf[0] == '0')  {       QFile fd_file("/dev/fifoclient1");      QString temp(buf);    if(fd_file.open(IO_WriteOnly|IO_Append)) {          QTextStream t(&fd_file);      t<< temp;             fd_file.close();  }  else if(buf[0] == '1')  {      QFile fd_file("/dev/fifoclient2");      QString temp(buf);    if(fd_file.open(IO_WriteOnly|IO_Append)) {          QTextStream t(&fd_file);      t<< temp;             fd_file.close();  }……

在该程序中,服务器接收底层发来的信息(这里假设也是由 FIFO 管道传来),然后根据收到的信息内容,如第一个字节的内容,将信息发到不同客户端的管道中,实现对信息的正确分发。

客户端程序示例如下:(假设服务器端已经为读而打开 /dev/fifo 管道)

QFile out_file("/dev/fifo"); if(out_file.open(IO_WriteOnly|IO_Append)) {       QTextStream t(&out_file);  t << text << "\n";  }

当任意一个客户端需要向服务器发送消息时,就可以通过 /dev/fifo 这个公共的管道发出。

通过这种方式,同样可以实现GUI内部不同进程或应用之间的通信过程,但是,当客户端数量较多时,这种方法就显示出了一定的局限性,整 个通信过程布局变得过于繁杂,管道越来越多使得出错的可能性也越来越大。因此,利用 FIFO 实现 Qt 中上述客户端和服务器端的通信过程,更适用于客户端应用较少时。

参考资料

  • Qt 官方文档: http://doc.trolltech.com/3.2/index.html
  • Qt 和 Qtopia 源代码:QCopChannel, QCopEnvelope,QAction.
  • Advanced Programming in the UNIX Environment, W. Richard Stevens.

转载于:https://my.oschina.net/alphajay/blog/6733

2010年08月18日相关推荐

  1. 神经网络贷款风险评估(base on keras and python ) 原创 2017年08月18日 14:35:17 标签: python / 神经网络 / keras 300 用我

    神经网络贷款风险评估(base on keras and python ) 原创 2017年08月18日 14:35:17 标签: python / 神经网络 / keras / 300 编辑 删除 ...

  2. 微软北京.NET俱乐部免费活动(2010年7月18日)–Visual Studio 2010 敏捷开发与云计算Azure...

    感谢您对VS2010一直以来的关注和支持.6月26日微软.NET俱乐部邀请到了多位MVP,针对Visual Studio 2010敏捷开发.Web前端开发与广大北京的.NET俱乐部会员进行互动交流,应 ...

  3. 华为,工资岗位揭秘 2010年08月02日

    华为员工尺度岗位工资明细及剖析 华为员工对应的标准岗位工资 13-C:5500, B:6500, A:7500 14-C:7500, B:9000, A:10500 15-C:10500,B:1250 ...

  4. 9月18日 第5届 FLASH开发者交流会 暨FLASH CAMP 2010

    more : http://www.wedoswf.com/events/1 主讲者和专题介绍 Adobe 平台技术 主讲人:Gary Zhu -   Adobe 平台技术总监 Gary 多年工作于 ...

  5. 活动目录迁移之03奔向08 --10月18日2008系列讲座预告篇

    现场讲座: 在上一次 <卍解,开放基础架构的力量> --9月26日讲座中着重讲解并演示了windows server 2008 core 模式下如何部署域控制器. blog: 具体操作步骤 ...

  6. 【06月18日】A股滚动市净率PB历史新低排名

    2010年01月01日 到 2019年06月18日 之间,滚动市净率历史新低排名. 上市三年以上的公司,2019年06月18日市净率在30以下的公司. 来源:A股滚动市净率(PB)历史新低排名. 1 ...

  7. 【08月20日】A股滚动市净率PB历史新低排名

    2010年01月01日 到 2019年08月20日 之间,滚动市净率历史新低排名. 上市三年以上的公司,2019年08月20日市净率在30以下的公司. 来源:A股滚动市净率(PB)历史新低排名. 1 ...

  8. 【08月01日】A股滚动市净率PB历史新低排名

    2010年01月01日 到 2019年08月01日 之间,滚动市净率历史新低排名. 上市三年以上的公司,2019年08月01日市净率在30以下的公司. 来源:A股滚动市净率(PB)历史新低排名. 1 ...

  9. 【08月02日】A股滚动市盈率PE历史新低排名

    2010年01月01日 到 2019年08月02日 之间,滚动市盈率历史新低排名. 上市三年以上的公司,2019年08月02日市盈率在300以下的公司. 1 - XD栖霞建(SH600533) - 历 ...

  10. 【08月28日】A股滚动市盈率PE历史新低排名

    2010年01月01日 到 2019年08月28日 之间,滚动市盈率历史新低排名. 上市三年以上的公司,2019年08月28日市盈率在300以下的公司. 1 - 文山电力(SH600995) - 历史 ...

最新文章

  1. PHP Log时时查看小工具
  2. PE知识复习之PE的导出表
  3. 【收藏】Spark之RDD的mapPartitions算子
  4. DataList控件分页
  5. 自己用java实现飞鸽传书 2 - 实现文件传输
  6. 天猫整站SSM-分页-herf(做个人学习笔记整理用)
  7. 【HDU - 1698】 Just a Hook(线段树模板 区间覆盖更新(laz标记) + 区间和查询 )
  8. c语言abc变def,C语言的permutation求解
  9. mysql for update死锁_Mysql 数据库死锁过程分析(select for update)
  10. kafka性能优化入门
  11. c语言实例100_pic单片机,PIC单片机C语言编程实例
  12. 注册测绘师执业制度实施的现状分析
  13. MapGIS67及Map2Shp安装教程
  14. 【APP推荐】手机制作微电影
  15. ssm基于微信小程序的恋上诗词设计与实现毕业设计源码011431
  16. 【UI设计】移动设备的界面设计尺寸
  17. 浪潮之颠二_读书笔记
  18. 揭示世界本质的「机器科学家」,比深度神经网络还强?
  19. 函数的单调性和曲线的凹凸性
  20. 如何用 ARKit 将太阳系装进 iPhone(二)

热门文章

  1. Linux下查看和停止所有java进程
  2. Android中常用适配器理解及使用
  3. c# async await异步方法
  4. uni_app UView 2.0 Upload 上传 header 如何配置
  5. PHP调用微信发放现金红包接口
  6. tableMerge 表格列自动合并
  7. Layui默认表单校验规则
  8. Git 如何拉取 github 上的代码
  9. Python获取图片的大小/尺寸
  10. 全网首发:ScrollBarUI混淆后效果错误的分析