20

QML信号を通常のC++スロットに接続するのは簡単です。

// QML
Rectangle { signal foo(); }

// C++ old-style
QObject::connect(some_qml_container, SIGNAL(foo()), some_qobject, SLOT(fooSlot()); // works!

ただし、何を試しても、C++11ラムダ関数スロットに接続できないようです。

// 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++ラムダスロットに接続します。

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

QMLContainer :: fooシグネチャはコンパイル時に認識されないため、この構文はQMLシグナルでは機能しません(QMLContainer :: fooを手動で宣言すると、そもそもQMLを使用する目的が無効になります)。

私がやろうとしていることは可能ですか?もしそうなら、QObject :: connect呼び出しの正しい構文は何ですか?

4

3 に答える 3

6

ラムダなどは新しい構文でのみ機能します。QMLシグナルをポインタとして与える方法が見つからない場合、それは直接不可能だと思います。

その場合は、回避策があります。ルーティングする必要のあるQML信号ごとに1つずつ、信号のみを持つダミーの信号ルーティングQObjectサブクラスを作成します。次に、古い接続構文を使用して、QML信号をこのダミークラスのインスタンスの対応する信号に接続します。

これで、新しい構文で使用できるC ++シグナルができ、ラムダに接続できます。

クラスには、QMLからクラスのシグナルへの接続を自動化するヘルパーメソッドを含めることもできます。このメソッドは、 QMetaObject :: connectSlotsByNameが使用するのと同じ原理を使用して、QMetaObjectリフレクションメカニズムと適切なシグナル命名スキームを利用します。または、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

さまざまなシグナルを処理するためにラムダ関数をオンザフライで作成する代わりに、を使用し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接続しているインスタンスを介して信号を波打つ例です。ApplicationWindowobjectName"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 に答える