Connect QML signal to C++11 lambda slot (Qt 5)

后端 未结 3 1971
夕颜
夕颜 2020-12-18 20:44

Connecting a QML signal to a regular C++ slot is easy:

// QML
Rectangle { signal foo(); }

// C++ old-style
QObject::connect(some_qml_container, SIGNAL(foo()         


        
相关标签:
3条回答
  • 2020-12-18 21:18

    You can use a helper:

    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()));
       }
    };
    

    Then:

    LambdaHelper::connect(sender, SIGNAL(mySignal()), [] { ... });
    

    The sender owns the helper object and will clean it up upon its destruction.

    0 讨论(0)
  • 2020-12-18 21:25

    Instead of creating lambda functions on the fly to deal with different signals, you may want to consider using a QSignalMapper to intercept the signals and send them to a statically-defined slot with an argument dependent on the source. The behavior of the function would then depend entirely on the source of the original signal.

    The trade-off with QSignalMapper is that you gain information about the source of the signal, but you lose the original arguments. If you can't afford to lose the original arguments, or if you don't know the source of the signals (as is the case with QDBusConnection::connect() signals), then it doesn't really make sense to use a QSignalMapper.

    hyde's example would require a little more work, but would allow you to implement a better version of QSignalMapper where you can add information about the source signal to the arguments when connecting the signal to your slot function.

    QSignalMapper class reference: http://qt-project.org/doc/qt-5.0/qtcore/qsignalmapper.html
    Example: http://eli.thegreenplace.net/2011/07/09/passing-extra-arguments-to-qt-slots/

    Here is an example rippling a signal through a QSignalMapper instance connecting to a top ApplicationWindow instance with an objectName of "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;
    }
    
    0 讨论(0)
  • 2020-12-18 21:29

    Lambdas etc only work with new syntax. If you can't find a way to give QML signal as a pointer, then I think it is not directly possible.

    If so, you have a workaround: create a dummy signal-routing QObject subclass, which only has signals, one for every QML signal you need to route. Then connect QML signals to corresponding signals of an instance of this dummy class, using the old connect syntax.

    Now you have C++ signals you can use with the new syntax, and connect to lambdas.

    The class could also have a helper method, to automate connections from QML to signals of the class, which would utilize QMetaObject reflection mechanisms and a suitable signal naming scheme, using same principle as QMetaObject::connectSlotsByName uses. Alternatively you can just hard-code the QML-router signal connections but still hide them inside a method of the router class.

    Untested...

    0 讨论(0)
提交回复
热议问题