QML信号连接到C ++ 11拉姆达槽(Qt的5)(Connect QML signal to C+

2019-07-23 08:10发布

一个QML信号连接到一个常规的C ++时隙是容易的:

// 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 ::连接过载可以接受这些参数)。 然而,Qt的5文档意味着这应该是可能的。

不幸的是,Qt的5实施例始终连接一个C ++信号到一个C ++拉姆达槽:

// C++11
QObject::connect(some_qml_container, &QMLContainer::foo, [=]() { /* response */ }); // works!

此语法不能为QML信号工作,因为QMLContainer :: foo的签名不是在编译时已知的(并宣布QMLContainer :: foo的手工击败首先使用QML的目的。)

正是我试图做可能吗? 如果是这样,什么是正确的语法为的QObject ::连接电话吗?

Answer 1:

Lambda表达式等只能用新的语法工作。 如果你不能找到一种方式让QML信号为指针,那么我认为这是不能直接成为可能。

如果是这样,你有一个解决办法:建立一个虚拟路由信号QObject的子类,信号需要路由其中只有信号,一个用于每个QML。 然后QML信号连接到对应的该虚设类的一个实例的信号,使用旧连接的语法。

现在你有C ++的信号可以用新的语法使用,并连接到lambda表达式。

该类还可以具有一个辅助方法,从QML连接自动化的类的信号,这将利用QMetaObject反射机制和适当的信号的命名方案,使用相同的原理QMetaObject :: connectSlotsByName使用。 或者你可以硬编码的QML路由器信号连接,但仍隐藏路由器类的方法里面。

未经测试...



Answer 2:

你可以使用一个帮手:

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拥有的辅助对象,并将清理后其销毁。



Answer 3:

相反,在飞行应对不同的信号,从而造成lambda函数,你可能要考虑使用QSignalMapper拦截信号,并将其发送到静态定义插槽与相关来源的论据。 然后该函数的行为将完全取决于原始信号的源上。

权衡与QSignalMapper是您获得更多的信号源信息,但你失去了原有的参数。 如果你不能承受失去原有的参数,或者如果你不知道信号源(如与时QDBusConnection::connect()信号),那么它并没有真正意义的使用QSignalMapper.

海德的例子就是需要多一点的工作,但将让你实现一个更好的版本的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实例与objectName"app_window"

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://stackoverflow.com/questions/28465862
    static_cast<void (QSignalMapper::*)(int)>(&QSignalMapper::mapped),
    [](int /*ignored in this case*/) {
      FooSingleton::Inst().Bar();
    });
  break;
}


文章来源: Connect QML signal to C++11 lambda slot (Qt 5)