qt5中信号和槽的新语法
qt5中的连接
有下列几种方式可以连接到信号上
旧语法
qt5将继续支持旧的语法去连接,在QObject对象上定义信号和槽函数,及任何继承QObjec的对象(包含QWidget)。
connect(sender, SIGNAL (valueChanged(QString,QString)),receiver, SLOT (updateValue(QString)) );
新语法:连接到QObject成员
下面是一种新的方式来连接两个QObjects:
connect(sender, &Sender::valueChanged,receiver, &Receiver::updateValue );
它支持:
- 编译期间检查信号和槽是否存在,它们的类型,及Q_OBJECT是否丢失
- 参数能被typedef或不同命名空间指定。
- 如果有隐式转换的参数,会自动转换类型。比如QString到QVariant
- 它可以连接QObject的任何成员方法,不仅仅是定义的槽。
它不支持:
- 更复杂的语法?你需要指定你的对象类型、
- 非常复杂的语法,比如重载,参见后面。
- 在槽的中默认参数不在被支持。
新语法:连接到简单的函数
新语法甚至能连接到函数,不仅仅是QObjects:
connect(sender, &Sender::valueChanged, someFunction);
支持:
能和tr1::bind一起使用:
connect(sender, &Sender::valueChanged,
tr1::bind(receiver, &Receiver::updateValue, "senderValue", tr1::placeholder::_1));
能和c++ 11 lambda表达式一起使用:
connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
receiver->updateValue("senderValue", newValue);
});
不支持:
当receiver被销毁时,新语法不能自动断开信号和槽的连接。 因为它是没有跟QObject一起的伪函数。不管怎样,从5.2版本开始有一个重载函数,它添加一个上下文对象,当对象摧毁时,这个连接会破坏。这个上下文也被使用在线程关联性上: 这个lambda方法将会被调用在对象事件循环的线程中。
qt5中断开连接
如你可能预期的那样,在qt5中如何终止连接也会有一些新变化。
旧方式
你仍可以旧方式断开连接(使用SIGNAL, SLOT方式)。但仅限是
- 你使用旧方式连接,或者
- 如果你想使用通配符,从指定的信号中断开所有的槽
对称的函数指针
disconnect(sender, &Sender::valueChanged,
receiver, &Receiver::updateValue );
这只可以用在你使用同样方式的连接上,或者你也可以使用0作为通配。 在实际中,它也不适用于静态函数,仿函数,或lambda函数。
使用QMetaObject::Connection的新方式
QMetaObject::Connection m_connection;
//…
m_connection = QObject::connect(…);
//…
QObject::disconnect(m_connection);
这适用于所有场景下,包括lambda函数和伪函数。
更简单的异步
随着C++ 11,它可以保持代码内联
void doYourStuff(const QByteArray &page)
{
QTcpSocket *socket = new QTcpSocket;
socket->connectToHost("qt.io", 80);
QObject::connect(socket, &QTcpSocket::connected, [socket, page] () {
socket->write(QByteArray("GET " + page + ""));
});
QObject::connect(socket, &QTcpSocket::readyRead, [socket] () {
qDebug()<< "GOT DATA "<< socket->readAll();
});
QObject::connect(socket, &QTcpSocket::disconnected, [socket] () {
qDebug()<< "DISCONNECTED ";
socket->deleteLater();
});
QObject::connect(socket, static_cast<void (QTcpSocket::*)(QAbstractSocket::SocketError)>
(&QAbstractSocket::error), [socket](QAbstractSocket::SocketError) {
qDebug()<< "ERROR " << socket->errorString();
socket->deleteLater();
});
}
下面是一个不用重新进入事件循环的QDialog,保持代码属于:
void Doc::saveDocument() {
QFileDialog *dlg = new QFileDialog();
dlg->open();
QObject::connect(dlg, &QDialog::finished, [dlg, this](int result) {
if (result) {
QFile file(dlg->selectedFiles().first());
// …
}
dlg->deleteLater();
});
}
使用QHttpServer的另外例子。
错误报告
用GCC测试的、
幸运的是,IDE能简化函数的命名,比如Qt Creator。
忘记Q_OBJECT
#include <QtCore>
class Goo : public QObject {
Goo() {
connect(this, &Goo::someSignal, this, &QObject::deleteLater);
}
signals:
void someSignal();
};
qobject.h: In member function 'void QObject::qt_check_for_QOBJECT_macro(const T&&) const [with T = Goo]':
qobject.h:535:9: instantiated from 'static typename QtPrivate::QEnableIf<((int)
(QtPrivate::FunctionPointer<Func>::ArgumentCount) >= (int)
(QtPrivate::FunctionPointer<Func2>::ArgumentCount)), void*>::Type QObject::connect(const typename
QtPrivate::FunctionPointer<Func>::Object*, Func1, const typename QtPrivate::FunctionPointer<Func2>::Object*,
Func2, Qt::ConnectionType) [with Func1 = void (Goo::*)(), Func2 = void (QObject::*)(), typename
QtPrivate::QEnableIf<((int)(QtPrivate::FunctionPointer<Func>::ArgumentCount) >= (int)
(QtPrivate::FunctionPointer<Func2>::ArgumentCount)), void*>::Type = void*, typename
QtPrivate::FunctionPointer<Func>::Object = Goo, typename QtPrivate::FunctionPointer<Func2>::Object = QObject]'
main.cc:4:68: instantiated from here
qobject.h:353:5: error: void value not ignored as it ought to be
make: '''* [main.o] Error 1
拼写错误
#include <QtCore>
class Goo : public QObject {
Q_OBJECT
public:
Goo() {
connect(this, &Goo::someSignal, this, &Goo::someSlot1); //error
connect(this, &Goo::someSignal, this, &Goo::someSlot2); //works
}
signals:
void someSignal(QString);
public:
void someSlot1(int);
void someSlot2(QVariant);
};
qobject.h: In static member function 'static typename QtPrivate::QEnableIf<((int)
(QtPrivate::FunctionPointer<Func>::ArgumentCount) >= (int)
(QtPrivate::FunctionPointer<Func2>::ArgumentCount)), void*>::Type QObject::connect(const typename
QtPrivate::FunctionPointer<Func>::Object*, Func1, const typename QtPrivate::FunctionPointer<Func2>::Object*,
Func2, Qt::ConnectionType) [with Func1 = void (Goo::*)(QString), Func2 = void (Goo::*)(int), typename
QtPrivate::QEnableIf<((int)(QtPrivate::FunctionPointer<Func>::ArgumentCount) >= (int)
(QtPrivate::FunctionPointer<Func2>::ArgumentCount)), void*>::Type = void*, typename
QtPrivate::FunctionPointer<Func>::Object = Goo, typename QtPrivate::FunctionPointer<Func2>::Object = Goo]':
main.cc:6:62: instantiated from here
qobject.h:538:163: error: no type named 'IncompatibleSignalSlotArguments' in 'struct
QtPrivate::CheckCompatibleArguments<QtPrivate::List<QString, void>, QtPrivate::List<int, void>, true>'
qobject.h: In static member function 'static void QtPrivate::FunctionPointer<Ret (Obj::*)(Arg1)>::call
(QtPrivate::FunctionPointer<Ret (Obj::*)(Arg1)>::Function, Obj*, void*) [with Args = QtPrivate::List<QString, void>,
Obj = Goo, Ret = void, Arg1 = int, QtPrivate::FunctionPointer<Ret (Obj::*)(Arg1)>::Function = void (Goo::*)(int)]':
qobject.h:501:13: instantiated from 'void QObject::QSlotObject<Func, Args>::call(QObject*, void**) [with Func =
void (Goo::*)(int), Args = QtPrivate::List<QString, void>, QObject = QObject]'
main.cc:14:2: instantiated from here
qobject.h:109:13: error: cannot conver
开放式问题
槽中的默认参数
如果你有类似下面的代码:
class A : public QObject { Q_OBJECT
public slots:
void someSlot(int foo = 0);
};
旧的连接方式允许你连接这个槽到信号上,不用带参数。 但是我不能从模板代码中知道一个函数是否带有默认参数。因此这个功能是被禁用的。 这里有个实现方法是,如果槽函数中参数数量多于信号函数中的参数数量时,退回到旧方式去连接。 不管怎样,这是相当不一致的,因此旧语法不再执行类型类型检查和类型转换。 它已经从分支中移除,并被合并。
重载
如你在上面例子中看到的那样,连接到QAbstractSocket::error,它不是真正完美的方式,因为error有一个重载。取得一个重载函数的地址需要隐式转换。比如一个连接如下所示:
connect(mySpinBox, SIGNAL(valueChanged(int)), mySlider, SLOT(setValue(int));
它不能简单的转换到:
connect(mySpinBox, &QSpinBox::valueChanged, mySlider, &QSlider::setValue);
因为 QSpinBox有2个信号,名字都叫valueChanged()带有不同的参数。 新方式要用下列代码替代:
connect(mySpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), mySlider, &QSlider::setValue);
最好的方式是尽量不要重载信号和槽。
但是我们已经在过去的release版本中添加重载了,因为取得函数的地址不是我们支持的使用方式。 但是现在不破坏代码兼用性已经是不可能的。
断开连接
是否QMetaObject::Connection应该有一个disconnect()函数?
其他的难题是,如果我们使用新语法,在一些对象关闭时,不能自动断开连接。一个方式是在断开连接中添加对象的集合,或者一个新函数例如QMetaObject::Connection::require
auto c = connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
receiver->updateValue("senderValue", newValue);
} , QList<QObject> { receiver } ); // solution 1
c.require(receiver); // solution 2
方案2是否有效? 没有什么比得上QMetaObject::Connection::require()
回调
函数例如QHostInfo::lookupHost或QTimer::singleShot或QFileDialog::open 带有一个QObject接收者和 char* 的slot。 这在新方式中是不能用的。 如果你想用c++方式的回调,应该使用 std::function (or tr1)。但我们不能在我们的API中,使用STL类型,因此一个qt函数应该被完成当复制一个std::function时。 无论如何,这是和QObject连接是不相关的。
译自:https://wiki.qt.io/New_Signal_Slot_Syntax
qt5中信号和槽的新语法的更多相关文章
- Qt 5中信号和槽的新语法
QT 是一个跨平台的 C++ GUI 应用构架,它提供了丰富的窗口部件集,具有面向对象.易于扩展.真正的组件编程等特点,更为引人注目的是目前 Linux 上最为流行的 KDE 桌面环境就是建立在 QT ...
- Qt5 UI信号、槽自动连接的控件重名
Qt5 UI信号.槽自动连接的控件重名 来源 http://blog.csdn.net/goldenhawking/article/details/51865909 对Qt5稍有熟悉的童鞋都知道信号. ...
- QT_5_ Qt中信号和槽 + 自定义信号和槽 + lambda 表达式
1.Qt中信号和槽 1.1 需求:点击按钮关闭窗口 1.2 利用connect进行链接 1.3 参数1 信号发送者(指针) 参数2 发送的信号(信号地址) 参数3 信号的接受者(指针) 参数4 处理槽 ...
- QT+信号有参数与无参数的实现+QT4和QT5在信号和槽使用上的区别
在QT5中,信号有参数和无参数 #ifndef SUBWIDGET_H #define SUBWIDGET_H #include <QWidget> #include <QPushB ...
- Qt5 中对 C++11 一些新特性的封装
在 Qt5 中,提供更多 C++11 的特性支持,接下来我们将进行详细的说明. slots (槽) 的 Lambda 表达式 Lambda表达式 是 C++11 中的一个新语法,允许定义匿名函数.匿名 ...
- 【Qt开发】Qt5 中对 C++11 一些新特性的封装
C++11 是现在的 C++ 标准的名称,C++11 为 C++ 语言带来很多新特性. 而 Qt 4.8 是 Qt 首个在其 API 中开始使用一些新的 C++11 特性的版本,我之前写过一篇博文:C ...
- qt中信号与槽机制
一. 简介 就我个人来理解,信号槽机制与Windows下消息机制类似,消息机制是基于回调函数,Qt中用信号与槽来代替函数指针,使程序更安全简洁. 信号和槽机制是 Qt 的核心机制,可以让编程人员将互不 ...
- Qt5 UI信号、槽自动连接的控件重名大坑(UI生成的槽函数存在一个隐患,即控件重名。对很复杂的控件,不要在 designer 里做提升,而是等到程序启动后,再动态创建,可以避免很多问题)
对Qt5稍有熟悉的童鞋都知道信号.槽的自动连接机制.该机制使得qt designer 设计的UI中包含的控件,可以不通过显式connect,直接和cpp中的相应槽相关联.该机制的详细文章见 http: ...
- 自己用纯C++实现简单的QT中信号与槽机制
前天在我很久以前的一篇博文 (http://blog.csdn.net/liukang325/article/details/45742675) 中有人回复说看到我的博文很激动,希望我详细介绍一下信号 ...
随机推荐
- NodeJs之child_process
一.child_process child_process是NodeJs的重要模块.帮助我们创建多进程任务,更好的利用了计算机的多核性能. 当然也支持线程间的通信. 二.child_process的几 ...
- MIP开发常见问题解答
校验相关 1. MIP 页面的 <a>链接校验报错,MIP 是强制 target="_blank" 吗? 如果想直接跳转MIP页,可以用mip-link 组件:MIP ...
- [翻译]开发文档:android Bitmap的高效使用
内容概述 本文内容来自开发文档"Traning > Displaying Bitmaps Efficiently",包括大尺寸Bitmap的高效加载,图片的异步加载和数据缓存 ...
- TODO:即将开发的第一个小程序
TODO:即将开发的第一个小程序 微信小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验.个人理解小程序是寄宿在微信平台上的一个前端框架,具有跨平台功能, ...
- 移动先行之谁主沉浮? 带着你的Net飞奔吧!
移动系源码:https://github.com/dunitian/Windows10 移动系文档:https://github.com/dunitian/LoTDotNet/tree/master/ ...
- 使用AWS亚马逊云搭建Gmail转发服务(三)
title: 使用AWS亚马逊云搭建Gmail转发服务(三) author:青南 date: 2015-01-02 15:42:22 categories: [Python] tags: [log,G ...
- CoreCRM 开发实录——Travis-CI 实现 .NET Core 程度在 macOS 上的构建和测试 [无水干货]
上一篇文章我提到:为了使用"国货",我把 Linux 上的构建和测试委托给了 DaoCloud,而 Travis-CI 不能放着不用啊.还好,这货支持 macOS 系统.所以就把 ...
- 【开源】.Net 动态脚本引擎NScript
开源地址: https://git.oschina.net/chejiangyi/NScript 开源QQ群: .net 开源基础服务 238543768 .Net 动态脚本引擎 NScript ...
- 深入研究Visual studio 2017 RC新特性
在[Xamarin+Prism开发详解三:Visual studio 2017 RC初体验]中分享了Visual studio 2017RC的大致情况,同时也发现大家对新的Visual Studio很 ...
- P2V之后的磁盘扩容新思路
背景: 原先的物理机环境多是若干块物理磁盘经过RAID卡进行了RAID5之后的虚拟磁盘组,这样我们在操作系统内看到的也就是一块完整的磁盘.我们会在上面进行分区,然后格式化后以便使用. Figure 1 ...