首页 文章

将QML信号连接到C 11 lambda插槽(Qt 5)

提问于
浏览
15

将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 :: connect重载可以接受这些参数) . 但是,Qt 5文档暗示这应该是可能的 .

不幸的是,Qt 5示例总是将C信号连接到C lambda插槽:

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

这种语法不适用于QML信号,因为QMLContainer :: foo签名在编译时是未知的(并且手动声明QMLContainer :: foo会破坏首先使用QML的目的 . )

我正在尝试做什么?如果是这样,QObject :: connect调用的正确语法是什么?

3 回答

  • 3

    Lambdas等只能使用新语法 . 如果你找不到一种方法将QML信号作为指针,那么我认为这不是直接可能的 .

    如果是这样,你有一个解决方法:创建一个虚拟信号路由QObject子类,它只有信号,一个用于你需要路由的每个QML信号 . 然后使用旧的连接语法将QML信号连接到该虚拟类的实例的相应信号 .

    现在您可以使用新语法使用C信号,并连接到lambdas .

    该类还可以有一个辅助方法,以自动化从QML到类的信号的连接,这将使用QMetaObject反射机制和合适的信号命名方案,使用与QMetaObject::connectSlotsByName使用相同的原理 . 或者,您可以对QML路由器信号连接进行硬编码,但仍然将它们隐藏在路由器类的方法中 .

    未经测试...

  • 6

    你可以使用帮手:

    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 拥有辅助对象,并在销毁时将其清除 .

  • 2

    您可能需要考虑使用QSignalMapper拦截信号并将其发送到静态定义的槽,而不是动态创建lambda函数来处理不同的信号,并使用依赖于源的参数将它们发送到静态定义的槽 . 然后,函数的行为将完全取决于原始信号的来源 .

    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 实例的信号,其中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;
    }
    

相关问题