Qt信号与槽机制
一、信号和槽机制
信号和槽用于两个对象之间的通信,信号和槽机制是Qt的核心特征,也是Qt不同于其他开发框架的最突出的特征。在GUI编程中,当改变了一个部件时,总希望其他部件也能了解到该变化。更一般来说,我们希望任何对象都可以和其他对象进行通信。例如,如果用户点击了关闭按钮,我们希望可以执行窗口的close()函数来关闭窗口。为了实现对象间的通信,一些工具包中使用了回调(callback)机制,而在Qt中,使用了信号和槽来进行对象间的通信。当一个特殊的事情发生时便可以发射一个信号,比如按钮被单击;而槽就是一个函数,它在信号发射后被调用,来响应这个信号。在Qt的部件类中已经定义了一些信号和槽,但是更多的做法是子类化这个部件,然后添加自己的信号和槽来实现想要的功能。
信号槽是Qt中特有的概念。它使得程序员将不同的object绑定起来,而object对象间并不需要对相互了解。 Slots也是普通的c++方法,它们可以是virtual;可以被重载;可以使private、protected、public,可以像其它c++方法一样有各种类型的参数。它们唯一的区别就是signal与slot是相互链接起来的,slot函数在每次signal函数被emit时会被调用。信号和槽进行关联,使用的是QObject类的connect()函数,这个函数的原型如下:
bool QObject::connect ( const QObject *sender, const char * signal, const QObject * receiver, const char * method,Qt::ConnectionType type = Qt::AutoConnection )
标准的常用connect函数如下: connect(sender, SIGNAL(signal), receiver, SLOT(slot)。 sender和receiver是QObject对象的指针,signal和slot函数是不带参数的函数名,实际上SIGNAL()和SLOT()宏将他们的参数(signal和slot函数)转换成相应的字符串。 通过以下实例,我们可以看到信号槽的具体使用情况:
1)一个信号函数可以连接多个槽函数。当一个信号发出时,其所连接的槽函数会以不定顺序的情况下依次调用。
connect(slider,SIGNAL(valueChanged(int)),spinBox,SLOT(setValue(int)));
connect(slider,SIGNAL(valueChanged(int)),this,SLOT(updateStatusBarIndicator(int)));
2)多个信号函数可以连接一个槽函数。当任何一个信号函数emit时,槽函数都会被调用。
connect(lcd,SIGNAL(overflow()),this,SLOT(handleMathError()));
connect(calculator,SIGNAL(divisionByZero()),this,SLOT(handleMathError()));
3)一个信号函数可以连接另一个信号函数 , 第一个信号发出后,第二个信号也同时发出。除此之外,信号与信号连接上和信号和槽连接相同。
4)信号槽的连接也可以被断开, 这个函数很少使用。一个对象实例删除后,Qt自动删除这个对象的所有连接。
disconnect(lcd,SIGNAL(overflow()),this,SLOT(handleMathError()));
5)信号槽在连接时,她们的参数必须有相同的类型和相同的顺序。但是如果信号的参数比槽的参数多,那么多余的参数将会被忽略掉
connect(ftp, SIGNAL(rawCommandReply(int,const QString &)),this,SLOT(check
Error (int)));
在此例的QString将会被忽略。在信号槽的名字中不能包含参数名,否则Qt会发出警告.
6)信号是没有函数体定义的,如果不慎进行了定义,则编译器会提示“multiple definition of ”错误,应注意。
二、使用信号和槽
1、声明一个信号要使用signals关键字,signals前面不可加public、private和protected进行修饰,因为只有定义该信号的类及其子类才可以发射该信号; 声明一个槽需要使用slots关键字,一个槽可以是private、public或者protected类型的,槽也可以被声明为虚函数,这与普通的成员函数是一样的,也可以像调用一个普通函数一样来调用槽。槽的最大特点就是可以和信号关联。
2、信号只用声明,不需要也不能对它进行定义实现。还要注意,信号没有返回值,只能是void类型的。因为只有QObject类及其子类派生的类才能使用信号和槽机制,比如MyDialog类继承自QDialog类,QDialog类又继承自QWidget类,QWidget类是QObject类的子类,所以这里可以使用信号和槽。不过,使用信号和槽,还必须在类声明的最开始处添加Q_OBJECT宏,在这个程序中,类的声明是自动生成的,已经添加了这个宏。
3、带参数的宏定义和函数指针不能用于信号和槽的参数,信号和槽也不能有缺省参数。
信号
当某个信号对其客户或所有者发生的内部状态发生改动,信号被一个对象发射。只有定义过这个信号的类及其派生类能够发射这个信号。当一个信号被发射时,和其相关联的槽将被即时执行,就象一个正常的函数调用相同。信号-槽机制完全独立于所有GUI事件循环。只有当所有的槽返回以后发射函数(emit)才返回。如果存在多个槽和某个信号相关联,那么,当这个信号被发射时,这些槽将会一个接一个地执行,不过他们执行的顺序将会是随机的、不确定的,我们不能人为地指定哪个先执行、哪个后执行。
信号的声明是在头文件中进行的,QT的signals关键字指出进入了信号声明区,随后即可声明自己的信号。例如,下面定义了三个信号:
signals:
void mySignal();
void mySignal(int x);
void mySignalParam(int x,int y);
在上面的定义中,signals是QT的关键字,而非C/C++的。接下来的一行void mySignal() 定义了信号mySignal,这个信号没有携带参数;接下来的一行void mySignal(int x)定义了重名信号mySignal,不过他携带一个整形参数,这有点类似于C++中的虚函数。从形式上讲信号的声明和普通的C++函数是相同的,不过信号却没有函数体定义,另外,信号的返回类型都是void,不要指望能从信号返回什么有用信息。
信号由moc自动产生,他们不应该在.cpp文件中实现。
槽函数
槽是普通的C++成员函数,能被正常调用,他们唯一的特别性就是非常多信号能和其相关联。当和其关联的信号被发射时,这个槽就会被调用。槽能有参数,但槽的参数不能有缺省值。 既然槽是普通的成员函数,因此和其他的函数相同,他们也有存取权限。槽的存取权限决定了谁能够和其相关联。同普通的C++成员函数相同,槽函数也分为三种类型,即public slots、private slots和protected slots。
public slots:
在这个区内声明的槽意味着所有对象的信号均可与之相连接。这对于组件编程非常有用,你能创建彼此互不了解的对象,将他们的信号和槽进行连接以便信息能够正确的传递。
protected slots:
在这个区内声明的槽意味着当前类及其子类的信号可以与之相连接。这适用于那些槽,他们是类实现的一部分,不过其界面接口却面向外部。
private slots:
在这个区内声明的槽意味着只有当前类自己的信号才能与之相连接。这适用于联系非常紧密的类。
槽也能够声明为虚函数,这也是非常有用的。
槽的声明也是在头文件中进行的。例如,下面声明了三个槽:
public slots:
void mySlot();
void mySlot(int x);
void mySignalParam(int
x,int y);
三、信号与槽自动关联
信号和槽还有一种自动关联方式,例如前面程序中在设计模式直接生成的按钮的单击信号的槽,就是使用的这种方式:on_pushButton_clicked(),它由“on”、部件的objectName和信号三部分组成,中间用下划线隔开。这样组织的名称的槽就可以直接和信号关联,而不用再使用connect()函数。不过使用这种方式还要进行其他设置,而前面之所以可以直接使用,是因为程序中默认已经进行了设置。下面来看一个简单的例子。
可以看到,如果要使用信号和槽的自动关联,就必须在connectSlotsByName()函数之前进行部件的定义,而且还要指定部件的objectName。鉴于这些约束,虽然自动关联形式上很简单,但是实际编写代码时却很少使用。而且,在定义一个部件时,很希望明确的使用connect()函数来对其进行信号和槽的关联,这样当别人看到这个部件定义时,就可以知道和它相关的信号和槽的关联了。而使用自动关联,却没有这么明了。
四、信号与槽机制的局限性
信号和槽机制是比较灵活的,但有些局限性我们必须了解,这样在实际的使用过程中做到有的放矢,避免产生一些错误。下面就介绍一下这方面的情况。
1.信号和槽的效率是非常高的,不过同真正的回调函数比较起来,由于增加了灵活性,因此在速度上还是有所损失,当然这种损失相对来说是比较小的,通过在一台 i586-133的机器上测试是10微秒(运行Linux),可见这种机制所提供的简洁性、灵活性还是值得的。但如果我们要追求高效率的话,比如在实时系统中就要尽可能的少用这种机制。
2.信号和槽机制和普通函数的调用相同,如果使用不当的话,在程式执行时也有可能产生死循环。因此,在定义槽函数时一定要注意避免间接形成无限循环,即在槽中再次发射所接收到的同样信号。例如,在前面给出的例子中如果在mySlot()槽函数中加上语句emit mySignal()即可形成死循环。
3.如果一个信号和多个槽相联系的话,那么,当这个信号被发射时,和之相关的槽被激活的顺序将是随机的。
4. 带参数的宏定义不能用在signal和slot的参数中。
既然moc工具不扩展#define,因此,在signals和slots中携带参数的宏就不能正确地工作,如果不带参数是能的。例如,下面的例子中将带有参数的宏SIGNEDNESS(a)作为信号的参数是不合语法的:
#ifdef ultrix
#define SIGNEDNESS(a) unsigned a
#else
#define SIGNEDNESS(a) a
#endif
class Whatever : public QObject
{
[…]
signals:
void someSignal( SIGNEDNESS(a) );
[…]
};
5. 构造函数不能用在signals或slots声明区域内。
的确,将一个构造函数放在signals或slots区内有点不可理解,无论怎么,不能将他们放在private
slots、protected slots或public slots区内。下面的用法是不合语法需求的:
class SomeClass : public QObject
{
Q_OBJECT
public slots:
SomeClass( QObject *parent, const char *name )
: QObject( parent, name ) {} // 在槽声明区内声明构造函数不合语法
[…]
};
6. 函数指针不能作为信号或槽的参数。
例如,下面的例子中将void
(*applyFunction)(QList*, void*)作为参数是不合语法的:
class someClass : public QObject
{
Q_OBJECT
[…]
public slots:
void apply(void (*applyFunction)(QList*, void*), char*); // 不合语法
};
你能采用下面的方法绕过这个限制:
typedef void (*ApplyFunctionType)(QList*, void*);
class someClass : public QObject
{
Q_OBJECT
[…]
public slots:
void apply( ApplyFunctionType, char *);
};
7.信号和槽不能有缺省参数。
既然signal->slot绑定是发生在运行时刻,那么,从概念上讲使用缺省参数是困难的。下面的用法是不合理的:
class SomeClass : public QObject
{
Q_OBJECT
public slots:
void someSlot(int x=100); // 将x的缺省值定义成100,在槽函数声明中使用是错误的
};
8.信号和槽也不能携带模板类参数。
如果将信号、槽声明为模板类参数的话,即使moc工具不报告错误,也不可能得到预期的结果。例如,下面的例子中当信号发射时,槽函数不会被正确调用:
[…]
public slots:
void MyWidget::setLocation (pair location);
[…]
public signals:
void MyObject::moved (pair location);
不过,你能使用typedef语句来绕过这个限制。如下所示:
typedef pair IntPair;
[…]
public slots:
void MyWidget::setLocation (IntPair location);
[…]
public signals:
void MyObject::moved (IntPair location);
这样使用的话,你就能得到正确的结果。
9.嵌套的类不能位于信号或槽区域内,也不能有信号或槽。
例如,下面的例子中,在class B中声明槽b()是不合语法的,在信号区内声明槽b()也是不合语法的。
class A
{
Q_OBJECT
public:
class B
{
public slots: // 在嵌套类中声明槽不合语法
void b();
[….]
};
signals:
class B
{
// 在信号区内声明嵌套类不合语法
void b();
[….]
}:
};
10.友元声明不能位于信号或槽声明区内。相反,他们应该在普通C++的private、protected或public区内进行声明。下面的例子是不合语法规范的:
class someClass : public QObject
{
Q_OBJECT
[…]
signals: //信号定义区
friend class ClassTemplate; // 此处定义不合语法
};
五、总结
- 需要继承自QObject或其子类;
- 在类声明的最开始处添加Q_OBJECT宏;
- 槽中的参数的类型要和信号的参数的类型相对应,且不能比信号的参数多;
- 信号只用声明,没有定义,且返回值为void类型。
- 在Qt实际编程时,最好确保工程中每个信号都至少有一个槽函数与之相连接,即不存在无用信号;并且确保工程中每个槽函数都在cpp中实现。
- 信号和槽机制是类型安全的,相关联的信号和槽的参数必须匹配;
- 信号和槽是松耦合的,信号发送者不知道也不需要知道接受者的信息;
- 信号和槽可以使用任意类型的任意数量的参数。
Qt信号与槽机制的更多相关文章
- 关于Qt信号与槽机制的传递方向性研究(结论其实是错误的,但是可以看看分析过程)
最近由于项目的需求,一直在研究Qt.信号与槽机制是Qt的一大特色,该机制允许两者间传递参数,依次来实现对象间的通信.这个参数会分别存在于信号的参数列表和槽函数的参数列表中.需要注意的是,若将槽函数绑定 ...
- Qt信号和槽机制
概述 信号和槽机制是QT的核心机制,要精通QT编程就必须对信号和槽有所了解.信号和槽是一种高级接口,应用于对象之间的通信,他是QT的核心特性,也是QT差别于其他工具包的重要地方.信号和槽是QT自行定义 ...
- qt中信号与槽机制
一. 简介 就我个人来理解,信号槽机制与Windows下消息机制类似,消息机制是基于回调函数,Qt中用信号与槽来代替函数指针,使程序更安全简洁. 信号和槽机制是 Qt 的核心机制,可以让编程人员将互不 ...
- Qt入门之信号与槽机制
一. 简介 就我个人来理解,信号槽机制与Windows下消息机制类似,消息机制是基于回调函数,Qt中用信号与槽来代替函数指针,使程序更安全简洁. 信号和槽机制是 Qt 的核心机制,可以让编程人员将互不 ...
- QT的信号与槽机制介绍
信号与槽作为QT的核心机制在QT编程中有着广泛的应用,本文介绍了信号与槽的一些基本概念.元对象工具以及在实际使用过程中应注意的一些问题. QT是一个跨平台的C++ GUI应用构架,它提供了丰富的窗 ...
- Qt源码分析之信号和槽机制
Qt的信号和槽机制是Qt的一大特点,实际上这是和MFC中的消息映射机制相似的东西,要完成的事情也差不多,就是发送一个消息然后让其它窗口响应,当然,这里的消息是广义的说法,简单点说就是如何在一个类的一个 ...
- Qt 的信号与槽机制介绍(10个要注意的问题)
QT 是一个跨平台的 C++ GUI 应用构架,它提供了丰富的窗口部件集,具有面向对象.易于扩展.真正的组件编程等特点,更为引人注目的是目前 Linux 上最为流行的 KDE 桌面环境就是建立在 QT ...
- 【qt】QT 的信号与槽机制
QT 是一个跨平台的 C++ GUI 应用构架,它提供了丰富的窗口部件集,具有面向对象.易于扩展.真正的组件编程等特点,更为引人注目的是目前 Linux 上最为流行的 KDE 桌面环境就是建立在 QT ...
- 【QT学习】信号和槽机制
QT中发射信号使用 emit 来完成. 类要使用信号和槽机制,必须从QObject或者QObject子类继承.必须在类的定义中加上 Q_OBJECT 宏. 信号和槽连接时,参数类型必须相同. 关键字大 ...
随机推荐
- python是一门解释性语言吗?
其实这只能算说对了一半,准确来说是编译跟解释性语言.python跟java.C# 一样都是会预编译一部分代码(简称做了优化) 都知道java编译要先在cmd里敲 javac hello.world 是 ...
- FPGA烧完程序之后,检测不到网口的
原因:未给phy芯片添加复位 解决方法:在程序顶部添加一个输出信号output e_reset,使其值一直为高. output e_reset, 'b1;
- Windows 显示环境变量
echo %% D:\>echo %python3% C:\Users\zy\AppData\Local\Programs\Python\Python36 D:\> 我的环境变量如下:
- apache和IIS共存,服务器对外统一使用80端口
apache和IIS共用80端口为了PHP与ASP各自的执行效率,要在服务器上安装iis与Apache,但是无法同时使用80端口,否则其中必定有一个启动不了.让它们共存的并且访问网站不需要加端口号,解 ...
- 利用反射获取Model值
public ActionResult Base(UserModel Model) { Model.Tel = string.Format("{0}-{1}", Model.Are ...
- 一步一步学习JNI
本文来自网易云社区 作者:孙有军 前言 本篇的主要目的就是JNI开发入门,使大家对JNI开发流程有一个大致的了解,后续再进行深入学习. JNI不是Android特有的,JNI是Java Native ...
- Spring学习记录-Java 11运行eureka-server报javax.xml.bind.JAXBContext not present错
在pom.xml加入依赖就行 <dependency> <groupId>org.glassfish.jaxb</groupId> <artifactId&g ...
- C# 简单工厂
如下: public static IList<T> Create<T>(Type type) { if (type == typeof(List<T>)) { r ...
- 抓取Oracle数据快照
进入到oracle安装目录下的admin(找到这个目录)开启cmd键入sqlplus system/mima@实例名>@awrrpt.sql Would you like an HTML rep ...
- egrep及扩展正则
模式:pattern 正则: grep:基本正则,查找速度慢 Extended grep:扩展正则 fgrep:fast grep,不支持正则,直接查找字符串,执行速度快 基本正则: . :任意单个字 ...