Qt 学习之路:深入 Qt5 信号槽新语法
2015-09-17 09:56
423 查看
在前面的章节(信号槽和自定义信号槽)中,我们详细介绍了有关 Qt 5 的信号槽新语法。由于这次改动很大,许多以前看起来不是问题的问题接踵而来,因此,我们用单独的一章重新介绍一些 Qt 5 的信号槽新语法。
基本用法
Qt 5 引入了信号槽的新语法:使用函数指针能够获得编译期的类型检查。使用我们在自定义信号槽中设计的Newspaper类,我们来看看其基本语法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | //!!! Qt5 #include <QObject> ////////// newspaper.h class Newspaper : public QObject { Q_OBJECT public: Newspaper(const QString & name) : m_name(name) { } void send() const { emit newPaper(m_name); } signals: void newPaper(const QString &name) const; private: QString m_name; }; ////////// reader.h #include <QObject> #include <QDebug> class Reader : public QObject { Q_OBJECT public: Reader() {} void receiveNewspaper(const QString & name) const { qDebug() << "Receives Newspaper: " << name; } }; ////////// main.cpp #include <QCoreApplication> #include "newspaper.h" #include "reader.h" int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); Newspaper newspaper("Newspaper A"); Reader reader; QObject::connect(&newspaper, &Newspaper::newPaper, &reader, &Reader::receiveNewspaper); newspaper.send(); return app.exec(); } |
main()函数中,我们使用
connect()函数将
newspaper对象的
newPaper()信号与
reader对象的
receiveNewspaper()槽函数联系起来。当
newspaper发出这个信号时,
reader相应的槽函数就会自动被调用。这里我们使用了取址操作符,取到
Newspaper::newPaper()信号的地址,同样类似的取到了
Reader::receiveNewspaper()函数地址。编译器能够利用这两个地址,在编译期对这个连接操作进行检查,如果有个任何错误(包括对象没有这个信号,或者信号参数不匹配等),编译时就会发现。
有重载的信号
如果信号有重载,比如我们向Newspaper类增加一个新的信号:
1 | void newPaper(const QString &name, const QDate &date); |
SIGNAL和
SLOT两个宏来连接信号槽。如果有一个带有两个参数的信号,像上面那种,那么,我们就可以使用下面的代码:
1 2 | QObject::connect(&newspaper, SIGNAL(newPaper(QString, QDate)), &reader, SLOT(receiveNewspaper(QString, QDate))); |
receiveNewspaper()函数的重载,以便支持两个参数的信号。在 Qt 4 中不存在我们所说的错误,因为 Qt 4 的信号槽连接是带有参数的。因此,Qt 能够自己判断究竟是哪一个信号对应了哪一个槽。
对此,我们也给出了一个解决方案,使用一个函数指针来指明到底是哪一个信号:
1 2 3 | void (Newspaper:: *newPaperNameDate)(const QString &, const QDate &) = &Newspaper::newPaper; QObject::connect(&newspaper, newPaperNameDate, &reader, &Reader::receiveNewspaper); |
newspaperNameDate声明一个带有
QString和
QDate两个参数,返回值是 void 的函数,将该函数作为信号,与
Reader::receiveNewspaper()槽连接起来。这样,我们就回避了之前编译器的错误。归根结底,这个错误是因为函数重载,编译器不知道要取哪一个函数的地址,而我们显式指明一个函数就可以了。
如果你觉得这种写法很难看,想像前面一样写成一行,当然也是由解决方法的:
1 2 3 4 | QObject::connect(&newspaper, (void (Newspaper:: *)(const QString &, const QDate &))&Newspaper::newPaper, &reader, &Reader::receiveNewspaper); |
1 2 3 4 | QObject::connect(&newspaper, static_cast<void (Newspaper:: *)(const QString &, const QDate &)>(&Newspaper::newPaper), &reader, &Reader::receiveNewspaper); |
(const QString &, const QDate &)两个参数修改成
(const QDate &, const QString &),C 风格的强制类型转换就会失败,并且这个错误只能在运行时发现。而第二种则是 C++ 推荐的风格,当参数类型改变时,编译器会检测到这个错误。
注意,这里我们只是强调了函数参数的问题。如果前面的对象都错了呢?比如,我们写的
newspaper对象并不是一个
Newspaper,而是
Newspaper2?此时,编译器会直接失败,因为
connect()函数会去寻找
sender->*signal,如果这两个参数不满足,则会直接报错。
带有默认参数的槽函数
Qt 允许信号和槽的参数数目不一致:槽函数的参数数目要比信号的参数少。这是因为,我们信号的参数实际是作为一种返回值。正如普通的函数调用一样,我们可以选择忽略函数返回值,但是不能使用一个并不存在的返回值。如果槽函数的参数数目比信号的多,在槽函数中就使用到这些参数的时候,实际这些参数并不存在(因为信号的参数比槽的少,因此并没有传过来),函数就会报错。这种情况往往有两个原因:一是槽的参数就是比信号的少,此时我们可以像前面那种写法直接连接。另外一个原因是,信号的参数带有默认值。比如1 | void QPushButton::clicked(bool checked = false) |
然而,有一种情况,槽函数的参数可以比信号的多,那就是槽函数的参数带有默认值。比如,我们的
Newspaper和
Reader有下面的代码:
1 2 3 4 5 | // Newspaper signals: void newPaper(const QString &name); // Reader void receiveNewspaper(const QString &name, const QDate &date = QDate::currentDate()); |
Reader::receiveNewspaper()的参数数目比
Newspaper::newPaper()多,但是由于
Reader::receiveNewspaper()后面一个参数带有默认值,所以该参数不是必须提供的。但是,如果你按照前面的写法,比如如下的代码:
1 2 3 4 | QObject::connect(&newspaper, static_cast<void (Newspaper:: *)(const QString &)>(&Newspaper::newPaper), &reader, static_cast<void (Reader:: *)(const QString &, const QDate & =QDate::currentDate())>(&Reader::receiveNewspaper)); |
1 | The slot requires more arguments than the signal provides. |
当然,此时你可以选择 Qt 4 的连接语法。如果你还是想使用 Qt 5 的新语法,目前的办法只有一个:Lambda 表达式。不要担心你的编译器不支持 Lambda 表达式,因为在你使用 Qt 5 的时候,能够支持 Qt 5 的编译器都是支持 Lambda 表达式的。于是,我们的代码就变成了:
1 2 3 | QObject::connect(&newspaper, static_cast<void (Newspaper:: *)(const QString &)>(&Newspaper::newPaper), [=](const QString &name) { /* Your code here. */ }); |
相关文章推荐
- QTP脚本不能录制解决办法
- 彻底卸载QTP11.5方法
- 破解QTP11.5 方法
- 认识QTP自动化测试工具
- Ubuntu下Qt安装全过程解答
- QTP自动化测试框(转自小强老师博客)
- Qt 学习之路 :信号槽
- QTP自动化测试陷阱
- QT对话框中show和exec的区别
- Qt 学习之路:Qt 简介
- Qt移植对USB鼠标键盘、触摸屏的支持
- Qt5.5 支持eglfs旋转(Support rotation for raster content in eglfs)
- Qt quoted Printable 解码
- Qt的内省机制剖析(转)
- Qt动态库的生成和使用
- ubuntu14.04下ITK4.8,VTK6.2和Qt4整合笔记
- QT美化界面的文章(真的很美)
- QT5开发及实例1 计算圆周面积 之新建项目文件
- Qt 学习之路:文件
- qt 总结