2013-03-25 19 views
14

Podłączanie sygnału QML do zwykłego C++ gniazdo jest łatwe:Połącz QML sygnał do C++ 11 gniazda lambda (Qt 5)

// QML 
Rectangle { signal foo(); } 

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

Jednak bez względu na to, co staram, nie wydaje się być w stanie podłączyć do gniazda funkcji lambda C++ 11.

// C++11 
QObject::connect(some_qml_container, SIGNAL(foo()), [=]() { /* response */ }); // fails... 
QObject::connect(some_qml_container, "foo()", [=]() { /* response */ }); // fails... 

Obie próby nie powiodą się z błędem sygnatury funkcji (nie QObject :: połączyć przeciążenie może zaakceptować te parametry). Jednak dokumentacja Qt 5 sugeruje, że powinno to być możliwe.

Niestety, Qt 5 przykłady zawsze podłączyć sygnał C++ do C++ gniazdo lambda:

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

składnia ta nie może pracować na sygnał QML jako QMLContainer :: podpis foo nie jest znany w czasie kompilacji (i deklarowanie QMLContainer :: foo ręcznie eliminuje cel używania QML w pierwszej kolejności.)

Czy staram się to zrobić? Jeśli tak, jaka jest prawidłowa składnia wywołania QObject :: connect?

Odpowiedz

6

Lambdas itp. Działają tylko z nową składnią. Jeśli nie możesz znaleźć sposobu na przekazanie sygnału QML jako wskaźnika, to myślę, że nie jest to bezpośrednio możliwe.

Jeśli tak, możesz obejść to: utwórz podrzędną podrzędność QObject, która ma tylko sygnały, po jednym dla każdego sygnału QML, który musisz skierować. Następnie podłącz sygnały QML do odpowiednich sygnałów instancji tej dummy class, używając starej składni connect.

Teraz masz sygnały C++, których możesz użyć z nową składnią i połączyć się z lambdami.

Klasa może również posiadać metodę pomocniczą, która automatyzuje połączenia z QML do sygnałów klasy, które wykorzystywałyby mechanizmy odbicia QMetaObject i odpowiedni schemat nazewnictwa sygnałów, wykorzystując tę ​​samą zasadę co zastosowania QMetaObject::connectSlotsByName. Alternatywnie możesz po prostu zakodować połączenia sygnałowe routera QML, ale nadal ukryć je w metodzie routera.

Nietestowane ...

+0

Dzięki za odpowiedź, daje mi to nowy kierunek poszukiwania odpowiedzi: czy możliwe jest uzyskanie wskaźnika C++ do sygnału QML? Jeśli tak, mogę powiązać funkcję std :: z sygnałem i lambda do gniazda. Niestety, tworzenie kopii lustrzanej każdego sygnału QML do obiektu C++ QObject jest (prawdopodobnie) gorszym projektem niż definiowanie samych szczelin w obiektach QObject (tj. Podejście staroświeckie). Chciałbym tylko całkowicie unikać QObjects, korzystając z nowych interfejsów Qt 5 (co może, ale nie musi być możliwe). –

+0

Cóż, połączenie sygnałów i sygnałów stanie się automatyczne, może oznaczać, że jest to tylko jedna linia kodu, coś w rodzaju linii po zadeklarowaniu przeglądarki QML: 'MyQMLSignalRouter qmlSignals (& myQmlView.rootObject());' a następnie użyj 'qmlSignals' w nowym -style połączenia połączeń. Sygnały QML nie istnieją jako funkcje C++, nie mogą (są dynamiczne, C++ jest statyczne), więc uzyskanie bezpośredniego wskaźnika do nich nie jest teoretycznie możliwe, o ile rozumiem. – hyde

+0

Mój sceptycyzm wobec tego podejścia polega na ścisłym powiązaniu między sygnałami QML i wprowadzonym przez niego C++, a także pojedynczym "superklasowym" podejściem (jedna klasa deklaruje wszystkie sygnały, wszędzie). To brzydko pachnie! Masz całkowitą rację, że sygnały QML nie są statycznie dostępne dla C++. Jednak może istnieć dynamiczne rozwiązanie: QQuickItem :: metaObject() -> indexOfSignal ("foo()") poprawnie zwraca indeks tego sygnału. AFAICT, instalator do otrzymania opakowania wymienialnego również istnieje, ale jest ukryty w przestrzeni nazw QtPrivate. Bummer. –

2

Zamiast tworzyć funkcje lambda w locie do czynienia z różnymi sygnałami, może warto rozważyć użycie QSignalMapper przechwycić sygnały i wysyłać je do statycznie zdefiniowanym przedziale z argumentem zależne od źródła. Zachowanie funkcji zależałoby wówczas całkowicie od źródła pierwotnego sygnału.

Kompromis z QSignalMapper polega na tym, że uzyskujesz informacje o źródle sygnału, ale tracisz oryginalne argumenty. Jeśli nie możesz sobie pozwolić na utratę oryginalne argumenty lub jeśli nie znają źródła sygnałów (jak ma to miejsce w przypadku QDBusConnection::connect() sygnałów), to naprawdę nie ma sensu używać QSignalMapper.

Przykład hyde'a wymagałby nieco więcej pracy, ale pozwoliłby na implementację lepszej wersji QSignalMapper, w której można dodać informacje o źródle sygnału do argumentów podczas podłączania sygnału do funkcji automatu.

QSignalMapper odniesienia klasy: http://qt-project.org/doc/qt-5.0/qtcore/qsignalmapper.html
przykład: http://eli.thegreenplace.net/2011/07/09/passing-extra-arguments-to-qt-slots/

Oto przykład falujący sygnał poprzez przykład QSignalMapper łączący z górną ApplicationWindow przykład ze związkiem objectName z "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; 
} 
2

Można użyj pomocnika:

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

Następnie:

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

sender właścicielem obiektu pomocnika i będzie go oczyścić po jego zniszczeniu.

+0

... a teraz masz memeak – Teimpz

+0

@Teimpz Wcale nie! –