连接QML信号C++ 11拉姆达插槽(QT 5)
一个QML信号连接到一个普通的C++插槽很简单:连接QML信号C++ 11拉姆达插槽(QT 5)
// QML
Rectangle { signal foo(); }
// C++ old-style
QObject::connect(some_qml_container, SIGNAL(foo()), some_qobject, SLOT(fooSlot()); // works!
但是,不管我怎么努力,我似乎无法能够连接到C++ 11 lambda功能插槽。
// C++11
QObject::connect(some_qml_container, SIGNAL(foo()), [=]() { /* response */ }); // fails...
QObject::connect(some_qml_container, "foo()", [=]() { /* response */ }); // fails...
这两次尝试都失败并出现函数签名错误(没有QObject :: connect overload可以接受这些参数)。但是,Qt 5文档意味着这应该是可能的。
不幸的是,Qt的5例的C++信号总是连接到C++拉姆达槽:
// C++11
QObject::connect(some_qml_container, &QMLContainer::foo, [=]() { /* response */ }); // works!
此语法不能为QML信号工作,因为QMLContainer :: foo的签名不是在编译时已知(并且手动声明QMLContainer :: foo会首先破坏使用QML的目的。)
正在尝试做什么?如果是这样,QObject :: connect调用的正确语法是什么?
Lambdas等只适用于新的语法。如果您找不到一种将QML信号作为指针的方法,那么我认为这不是直接可能的。
如果是这样,你有一个解决方法:创建一个虚拟信号路由QObject子类,它只有信号,每个QML信号需要路由一个。然后使用旧连接语法将QML信号连接到此虚拟类实例的相应信号。
现在,您可以使用新语法的C++信号,并连接到lambda表达式。
该类还可以有一个辅助方法,用于自动执行从QML到类信号的连接,该类将使用QMetaObject反射机制和合适的信号命名方案,使用与QMetaObject::connectSlotsByName相同的原理。或者,您可以对QML路由器信号连接进行硬编码,但仍将其隐藏在路由器类的方法中。
未经测试...
而是在飞行应对不同的信号,从而造成lambda函数,你可能要考虑使用QSignalMapper
拦截信号,并将其发送到静态定义插槽与争论取决于来源。函数的行为将完全取决于原始信号的来源。
与QSignalMapper
的权衡取舍是您获得有关信号源的信息,但您失去了原始参数。如果你不能承受失去原有的参数,或者如果你不知道信号源(如与QDBusConnection::connect()
信号的情况下),那么它并没有真正意义的使用QSignalMapper.
hyde的例子需要多一点的工作,但是可以让你实现更好的版本QSignalMapper
,你可以在将信号连接到插槽功能时将源信号的信息添加到参数中。
QSignalMapper
类参考:http://qt-project.org/doc/qt-5.0/qtcore/qsignalmapper.html
实施例:http://eli.thegreenplace.net/2011/07/09/passing-extra-arguments-to-qt-slots/
这里是一个例子荡漾通过QSignalMapper
实例连接到顶部ApplicationWindow
实例与"app_window"
的objectName
一个信号:
for (auto app_window: engine.rootObjects()) {
if ("app_window" != app_window->objectName()) {
continue;
}
auto signal_mapper = new QSignalMapper(&app);
QObject::connect(
app_window,
SIGNAL(pressureTesterSetup()),
signal_mapper,
SLOT(map()));
signal_mapper->setMapping(app_window, -1);
QObject::connect(
signal_mapper,
// for next arg casting incantation, see http://*.com/questions/28465862
static_cast<void (QSignalMapper::*)(int)>(&QSignalMapper::mapped),
[](int /*ignored in this case*/) {
FooSingleton::Inst().Bar();
});
break;
}
你可以使用帮手:
class LambdaHelper : public QObject {
Q_OBJECT
std::function<void()> m_fun;
public:
LambdaHelper(std::function<void()> && fun, QObject * parent = {}) :
QObject(parent),
m_fun(std::move(fun)) {}
Q_SLOT void call() { m_fun(); }
static QMetaObject::Connection connect(
QObject * sender, const char * signal, std::function<void()> && fun)
{
if (!sender) return {};
return connect(sender, signal,
new LambdaHelper(std::move(fun), sender), SLOT(call()));
}
};
然后:
LambdaHelper::connect(sender, SIGNAL(mySignal()), [] { ... });
的sender
拥有辅助对象,并将清理后其销毁。
...现在你有一个memleak – Teimpz 2017-10-04 16:12:44
@Teimpz一点都不! – 2017-10-18 16:30:51
感谢您的回答,这给了我一个新的方向来寻找答案:是否有可能获得C++指针到QML信号?如果是这样,我可以将一个std ::函数绑定到该信号,并将一个lambda绑定到该插槽。 不幸的是,将每个QML信号镜像到一个C++ QObject中(可以说)是一种比定义QObject中的插槽更糟糕的设计(即旧式的方法)。我想要做的就是完全避免使用QObject,并利用新的Qt 5接口(可能或不可能)。 – 2013-03-26 08:49:46
那么,自动发生信号 - 信号连接可能意味着它只是一个额外的代码行,类似于在声明QML查看器之后的这一行:'MyQMLSignalRouter qmlSignals(&myQmlView.rootObject());'然后在新中使用'qmlSignals'式连接调用。 QML信号并不作为C++函数存在,它们不能(它们是动态的,C++是静态的),所以直接指向它们的方法指针在理论上是不可能的,据我所知。 – hyde 2013-03-26 09:04:18
我对此方法的怀疑在于它引入的QML信号与C++代码之间的紧密耦合,以及单一的“超类”方法(一个类声明所有信号,无处不在)。真难闻! 你是完全正确的,QML信号静态不可用于C++。然而,动态解决方案可能存在: QQuickItem :: metaObject() - > indexOfSignal(“foo()”) 正确返回该信号的索引。 AFAICT,用于获取可调用封装的管道也存在,但隐藏在QtPrivate命名空间中。游民。 – 2013-03-26 10:07:17