[Qt教程] 第44篇 进阶(四)信号和槽

[Qt教程] 第44篇
进阶(四)信号和槽

楼主

 发表于 2013-9-12 16:48:59 | 查看:
298| 回复: 0


信号和槽

版权声明


该文章原创于Qter开源社区

导语


在前面的内容中已经多次用到过信号和槽了,这一节我们将详细讲解信号和槽的机制和使用方式。大家可以在帮助中查看Signals& Slots关键字。

环境:Windows Xp + Qt 4.8.5+QtCreator2.8.0

目录


一、信号和槽机制

二、信号和槽的自动关联

三、信号和槽的高级应用

正文


一、信号和槽机制

信号和槽用于两个对象之间的通信,信号和槽机制是Qt的核心特征,也是Qt不同于其他开发框架的最突出的特征。在GUI编程中,当改变了一个部件时,总希望其他部件也能了解到该变化。更一般来说,我们希望任何对象都可以和其他对象进行通信。例如,如果用户点击了关闭按钮,我们希望可以执行窗口的close()函数来关闭窗口。为了实现对象间的通信,一些工具包中使用了回调(callback)机制,而在Qt中,使用了信号和槽来进行对象间的通信。当一个特殊的事情发生时便可以发射一个信号,比如按钮被单击;而槽就是一个函数,它在信号发射后被调用,来响应这个信号。在Qt的部件类中已经定义了一些信号和槽,但是更多的做法是子类化这个部件,然后添加自己的信号和槽来实现想要的功能。

       在前面使用过的信号和槽的关联,都是一个信号对应一个槽。其实,一个信号可以关联到多个槽上,多个信号也可以关联到同一个槽上,甚至,一个信号还可以关联到另一个信号上,如下图所示。如果存在多个槽与某个信号相关联,那么,当这个信号被发射时,这些槽将会一个接一个地执行,但是它们执行的顺序是随机的,无法指定它们的执行顺序。

 

下面通过一个简单的例子来进一步讲解信号和槽的相关知识。这个例子实现的效果是:在主界面中创建一个对话框,在这个对话框中可以输入数值,当按下确定按钮时关闭对话框并且将输入的数值通过信号发射出去,而在主界面中接收该信号并且显示数值。

新建Qt Gui应用,项目名称为“mySignalSlot”,基类选择QWidget,然后类名保持“Widget”不变。项目建立完成后,向项目中添加新文件,模板选择Qt分类中的“Qt设计师界面类”,界面模板选择“Dialog without Buttons”,类名为“MyDialog”。完成后首先在mydialog.h文件中添加代码来声明一个信号:

signals:

    void dlgReturn(int); 
                // 自定义的信号

       声明一个信号要使用signals关键字,在signals前面不能使用public、private和protected等限定符,因为只有定义该信号的类及其子类才可以发射该信号。而且信号只用声明,不需要也不能对它进行定义实现。还要注意,信号没有返回值,只能是void类型的。因为只有QObject类及其子类派生的类才能使用信号和槽机制,这里的MyDialog类继承自QDialog类,QDialog类又继承自QWidget类,QWidget类是QObject类的子类,所以这里可以使用信号和槽。不过,使用信号和槽,还必须在类声明的最开始处添加Q_OBJECT宏,在这个程序中,类的声明是自动生成的,已经添加了这个宏。

在mydialog.ui对应的界面中添加一个Spin Box部件和一个Push Button部件,将pushButton的显示文本改为“确定”。然后转到pushButton的单击信号clicked()槽,更改如下:

void MyDialog::on_pushButton_clicked() 
 // 确定按钮

{

   int value = ui->spinBox->value(); 
  // 获取输入的数值

   emit dlgReturn(value); 
             // 发射信号

   close();                             // 关闭对话框

}

       当单击确定按钮时,便获取spinBox部件中的数值,然后使用自定义的信号将其作为参数发射出去。发射一个信号要使用emit关键字,例如程序中发射了dlgReturn()信号。

       然后到widget.h文件中添加自定义槽的声明:

private slots:

    void showValue(int
value);

       声明一个槽需要使用slots关键字。一个槽可以是private、public或者protected类型的,槽也可以被声明为虚函数,这与普通的成员函数是一样的,也可以像调用一个普通函数一样来调用槽。槽的最大特点就是可以和信号关联。

下面打开widget.ui文件,向界面上拖入一个Label部件,然后更改其文本为“获取的值是:”。然后进入widget.cpp文件中添加头文件#include "mydialog.h",再在构造函数中添加代码:

MyDialog *dlg = new MyDialog(this);

// 将对话框中的自定义信号与主界面中的自定义槽进行关联

connect(dlg,SIGNAL(dlgReturn(int)),this,SLOT(showValue(int)));

dlg->show();

       这里创建了一个MyDialog,并且使用Widget作为父部件。然后将MyDialog类的dlgReturn()信号与Widget类的showValue()槽进行关联。信号和槽进行关联,使用的是QObject类的connect()函数,这个函数的原型如下:

bool QObject::connect ( const QObject *sender, const char * signal, const QObject * receiver, const char * method,Qt::ConnectionType type = Qt::AutoConnection )

它的第一个参数为发送信号的对象,例如这里的dlg;第二个参数是要发送的信号,这里是SIGNAL(dlgReturn(int));第三个参数是接收信号的对象,这里是this,表明是本部件,即Widget,当这个参数为this时,也可以将这个参数省略掉,因为connect()函数还有另外一个重载形式,该参数默认为this;第四个参数是要执行的槽,这里是SLOT(showValue(int))。对于信号和槽,必须使用SIGNAL()和SLOT()宏,它们可以将其参数转化为const
char* 类型。connect()函数的返回值为bool类型,当关联成功时返回true。还要注意,在调用这个函数时信号和槽的参数只能有类型,不能有变量,例如写成SLOT(showValue(int value))是不对的。对于信号和槽的参数问题,基本原则是信号中的参数类型要和槽中的参数类型相对应,而且信号中的参数可以多于槽中的参数,但是不能反过来,如果信号中有多余的参数,那么它们将被忽略。下面介绍一下connect()函数的最后一个参数,它表明了关联的方式,其默认值是Qt::AutoConnection,这里还有其他几个选择,在编程中一般使用默认值,例如这里,在MyDialog类中使用emit发射了信号之后,就会执行槽,只有等槽执行完了以后,才会执行emit语句后面的代码。大家也可以将这个参数改为Qt::QueuedConnection,这样在执行完emit语句后便会立即执行其后面的代码,而不管槽是否已经执行。当不再使用这个关联时,还可以使用disconnect()函数来断开关联。

下面是自定义槽的实现,在这里只是简单的将参数传递来的数值显示在了标签上。因为这里使用了中文,所以大家记着在main.cpp文件中添加相关代码。

void Widget::showValue(int
value)         // 自定义槽

{

   ui->label->setText(tr("获取的值是:%1").arg(value));

}

       现在大家可以运行一下程序查看效果。如下图所示。

这个程序中自定义了信号和槽,可以看到它们的使用是很简单的,只需要对它们进行关联,然后在适当的时候发射信号就行。下面列举一下使用信号和槽应该注意的几点:

  • 需要继承自QObject或其子类;
  • 在类声明的最开始处添加Q_OBJECT宏;
  • 槽中的参数的类型要和信号的参数的类型相对应,且不能比信号的参数多;
  • 信号只用声明,没有定义,且返回值为void类型。

二、信号和槽的自动关联

信号和槽还有一种自动关联方式,例如前面程序中在设计模式直接生成的按钮的单击信号的槽,就是使用的这种方式:on_pushButton_clicked(),它由“on”、部件的objectName和信号三部分组成,中间用下划线隔开。这样组织的名称的槽就可以直接和信号关联,而不用再使用connect()函数。不过使用这种方式还要进行其他设置,而前面之所以可以直接使用,是因为程序中默认已经进行了设置。下面来看一个简单的例子。

       新建Qt Gui应用,项目名称为“mySignalSlot2”,基类选择QWidget,然后类名保持“Widget”不变。完成后先在widget.h文件中进行函数声明:

private slots:

    void on_myButton_clicked();

这里自定义了一个槽,它使用自动关联。然后在widget.cpp文件中添加头文件#include <QPushButton>,再将构造函数的内容更改如下:

Widget::Widget(QWidget *parent) :

   QWidget(parent),

   ui(new Ui::Widget)

{

   QPushButton *button = new QPushButton(this); // 创建按钮

   button->setObjectName("myButton"); 
         // 指定按钮的对象名

   ui->setupUi(this); 
                    //要在定义了部件以后再调用这个函数

}

       因为在setupUi()函数中调用了connectSlotsByName()函数,所以要使用自动关联的部件的定义都要放在setupUi()函数之前,而且还必须使用setObjectName()函数指定它们的objectName,只有这样才能正常使用自动关联。下面是槽的定义:

void Widget::on_myButton_clicked() 
        // 使用自动关联

{

   close();

}

       这里进行了关闭部件的操作。对于槽的函数名,中间要使用前面指定的objectName,这里是“myButton”。现在运行一下程序,单击按钮,发现可以正常关闭窗口。

       可以看到,如果要使用信号和槽的自动关联,就必须在connectSlotsByName()函数之前进行部件的定义,而且还要指定部件的objectName,并且自动关联中必须使用Qt中已经定义的信号,而不能是自定义的信号。鉴于这些约束,虽然自动关联形式上很简单,但是实际编写代码时却很少使用。而且,在定义一个部件时,很希望明确的使用connect()函数来对其进行信号和槽的关联,这样当别人看到这个部件定义时,就可以知道和它相关的信号和槽的关联了。而使用自动关联,却没有这么明了。

三、信号和槽的高级应用

       有时我们希望获得信号发送者的信息,在Qt中提供了QObject::sender()函数来返回发送该信号的对象的指针。但是如果有多个信号关联到了同一个槽上,而在该槽中需要对每一个信号进行不同的处理,使用上面的方法就很麻烦了。对于这种情况,便可以使用QSignalMapper类。QSignalMapper可以被叫做信号映射器,它可以实现对多个相同部件的相同信号进行映射,为其添加字符串或者数值参数,然后再发射出去。对于这个类的使用,大家可以参考《Qt及Qt
Quick开发实战精解》
的1.3.3小节,那里有这个类的实际应用。还有就是Qt的演示程序中的Tools分类下的Input
Panel示例程序中也使用了这个类,大家也可以参考一下这个程序。在这里便不再详细讲述这个类的使用了。

       在本节的最后,来看一下信号和槽机制的特色和优越性:

  • 信号和槽机制是类型安全的,相关联的信号和槽的参数必须匹配;
  • 信号和槽是松耦合的,信号发送者不知道也不需要知道接受者的信息;
  • 信号和槽可以使用任意类型的任意数量的参数。

结语


虽然信号和槽机制提供了高度的灵活性,但就其性能而言,还是慢于回调机制的。当然,这点性能差异通常在一个应用程序中是很难体现出来的。



涉及到的代码:  mySignalSlot.rar (2.84
KB, 下载次数: 1)  mySignalSlot2.rar (1.55
KB, 下载次数: 0) 
时间: 2024-10-07 08:53:54

[Qt教程] 第44篇 进阶(四)信号和槽的相关文章

[Qt教程] 第48篇 进阶(八) 3D绘图简介

[Qt教程] 第48篇 进阶(八) 3D绘图简介 楼主  发表于 2013-10-7 09:44:37 | 查看: 184| 回复: 0 3D绘图简介 版权声明 该文章原创于作者yafeilinux,转载请注明出处! 导语 OpenGL是一个跨平台的用来渲染3D图形的标准API.在Qt中提供了QtOpenGL模块,从而很轻松地实现了在Qt应用程序中使用OpenGL,这主要是在QGLWidget类中完成的.因为3D绘图涉及到了专业方面的内容,我们下面只是讲解最简单的使用,向大家演示在Qt中如何显示

[Qt教程] 第49篇 进阶(九) 多媒体应用简介

[Qt教程] 第49篇 进阶(九) 多媒体应用简介 楼主  发表于 2013-10-7 09:50:36 | 查看: 267| 回复: 2 多媒体应用简介 版权声明 该文章原创于作者yafeilinux,转载请注明出处! 导语      Qt对于音频视频的播放和控制等多媒体应用提供了强大的支持.要想使计算机发出响声,最简单的方法是调用QApplication::beep()静态函数:而对于简单的声音播放,可以使用 QSound类:对于简单的动画播放,可以使用QMovie类:要想对音频视频实现更多

[Qt教程] 第43篇 进阶(三)对象树与拥有权

[Qt教程] 第43篇 进阶(三)对象树与拥有权 楼主  发表于 2013-9-12 16:39:33 | 查看: 255| 回复: 1 对象树与拥有权 版权声明 该文章原创于Qter开源社区 导语 学习完前面的内容,大家对应用Qt编程应该已经有了一个大概的印象.后面的内容我们将介绍Qt中的一些核心机制,它们是构成Qt的基础,包括对象模型.信号和槽.对象树与拥有权等.在前面使用Qt编程时,大家对一些内容可能存在疑惑,学习完下面的知识,可以帮助大家更好的使用Qt进行编程. 环境:Windows X

[Qt教程] 第46篇 进阶(六) 国际化

[Qt教程] 第46篇 进阶(六) 国际化 楼主  发表于 2013-10-7 09:23:45 | 查看: 109| 回复: 0 国际化 版权声明 该文章原创于Qter开源社区 导语 在第2篇中讲述如何显示中文时,曾提到使用QTextCodec和tr()的方式直接显示中文,其实这只是一种临时的方法,方便我们快速完成程序,显示效果.当真正要发布一个程序时,最好的方式是在程序中使用英文字符串,而后使用国际化工具进行翻译. 国际化的英文表述为Internationalization,通常简写为I18

[Qt教程] 第45篇 进阶(五)Qt样式表

[Qt教程] 第45篇 进阶(五)Qt样式表 楼主  发表于 2013-9-13 15:43:55 | 查看: 329| 回复: 1 Qt样式表 版权声明 该文章原创于Qter开源社区 导语 一个完善的应用程序不仅应该有实用的功能,还要有一个漂亮的外观,这样才能使应用程序更加友善,更加吸引用户.作为一个跨平台的UI开发框架,Qt提供了强大而灵活的界面外观设计机制. Qt样式表是一个可以自定义部件外观的十分强大的机制.Qt样式表的概念.术语和语法都受到了HTML的层叠样式表(Cascading S

[Qt教程] 第47篇 进阶(七) 定制Qt帮助系统

[Qt教程] 第47篇 进阶(七) 定制Qt帮助系统 楼主  发表于 2013-10-7 09:32:39 | 查看: 93| 回复: 0 定制Qt帮助系统 版权声明 该文章原创于Qter开源社区(www.qter.org),作者yafeilinux,转载请注明出处! 导语        一个完善的应用程序应该提供尽可能丰富的帮助信息.在Qt中可以使用工具提示.状态提示以及"What's This"等简单的帮助提示,也可以使用QtAssistant来提供强大的在线帮助.如果要进行详细的

[Qt教程] 第24篇 数据库(四)SQL查询模型QSqlQueryModel

[Qt教程] 第24篇 数据库(四)SQL查询模型QSqlQueryModel 楼主  发表于 2013-5-21 14:33:47 | 查看: 869| 回复: 1 SQL查询模型QSqlQueryModel 版权声明 该文章原创于作者yafeilinux,转载请注明出处! 导语 在上一篇的最后我们讲到,Qt中使用了自己的机制来避免使用SQL语句,为我们提供了更简单的数据库操作及数据显示模型,分别是只读的QSqlQueryModel,操作单表的QSqlTableModel和以及可以支持外键的Q

[Qt教程] 第34篇 网络(四)FTP(二)

[Qt教程] 第34篇 网络(四)FTP(二) 楼主  发表于 2013-9-4 15:04:38 | 查看: 315| 回复: 9 FTP(二) 版权声明 该文章原创于作者yafeilinux,转载请注明出处! 导语 前面讲述了一个最简单的FTP客户端程序的编写,这一节我们将这个程序进行扩展,使其可以浏览并能下载服务器上的所有文件. 环境:Windows Xp + Qt 4.8.5+QtCreator 2.8.0 目录 一.修改界面 二.功能实现 正文 一.修改界面 我们删除了TextBrow

[Qt教程] 第30篇 XML(四)使用流读写XML

[Qt教程] 第30篇 XML(四)使用流读写XML 楼主  发表于 2013-5-22 13:03:33 | 查看: 611| 回复: 0 使用流读写XML 版权声明 该文章原创于作者yafeilinux,转载请注明出处! 导语        从Qt 4.3开始引入了两个新的类来读取和写入XML文档:QXmlStreamReader和QXmlStreamWriter. QXmlStreamReader类提供了一个快速的解析器通过一个简单的流API来读取格式良好的XML文档,它是作为Qt的SAX