20

将 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 调用的正确语法是什么?

4

3 回答 3

6

Lambda 等仅适用于新语法。如果您找不到将 QML 信号作为指针的方法,那么我认为这不是直接可能的。

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

现在你有了可以使用新语法的 C++ 信号,并连接到 lambdas。

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

未经测试...

于 2013-03-25T21:32:35.740 回答
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销毁时对其进行清理。

于 2017-07-21T18:10:37.213 回答
2

与其动态创建 lambda 函数来处理不同的信号,您可能需要考虑使用 aQSignalMapper来拦截信号并将它们发送到静态定义的插槽,其中的参数取决于源。函数的行为将完全取决于原始信号的来源。

权衡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-插槽/

下面是一个示例,通过连接到具有ofQSignalMapper的顶级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;
}
于 2013-06-07T07:31:19.377 回答