2013-07-30 29 views
19

Jak zadeklarować sygnał Qt w abstrakcyjnej klasie/interfejsie, gdy klasa implementacji jest już wykreślona z QObject/QWidget?Deklaracja abstrakcyjnego sygnału w klasie interfejsu

class IEmitSomething 
{ 
    public: 
    // this should be the signal known to others 
    virtual void someThingHappened() = 0; 
} 

class ImplementEmitterOfSomething : public QWidget, public IEmitSomething 
{ 
    // signal implementation should be generated here 
    signals: void someThingHappended(); 
} 
+0

Możesz napisać tylko 'sygnały: void someThingHappened();'. To nie jest potrzebne Wdrożenie sygnału. – Ruu

+0

Wiem, że sygnał jest generowany podczas implementacji, ale jak obserwujący wie, że jest to sygnał (qt), jeśli znany jest tylko interfejs? – Beachwalker

+0

@Beachwalker Zastąpiłem chronione słowo kluczowe sygnałami w mojej odpowiedzi. To najedzie na Twoje pytanie. –

Odpowiedz

37

Jak dowiedział się w ostatnich dniach ... droga Qt robić to tak:

class IEmitSomething 
{ 
    public: 
    virtual ~IEmitSomething(){} // do not forget this 

    signals: // <- ignored by moc and only serves as documentation aid 
      // The code will work exactly the same if signals: is absent. 
    virtual void someThingHappened() = 0; 
} 

Q_DECLARE_INTERFACE(IEmitSomething, "IEmitSomething") // define this out of namespace scope 

class ImplementEmitterOfSomething : public QWidget, public IEmitSomething 
{ 
    Q_OBJECT 
    Q_INTERFACES(IEmitSomething) 

    signals: 
     void someThingHappended(); 
} 

Teraz można połączyć te sygnały interfejsów.

Jeśli nie masz dostępu do wykonania podczas łączenia się z sygnału Twój connect oświadczenie będzie wymagać dynamicznego oddanych do QObject:

IEmitSomething* es = ... // your implementation class 

connect(dynamic_cast<QObject*>(es), SIGNAL(someThingHappended()), ...); 

... iw ten sposób nie jesteś zmuszony wystawiać klasa implementacji dla subskrybentów i klientów. Tak!!!

+0

Gdzie o tym czytałeś? –

+0

Czas mija ... Nie pamiętam odniesienia, ale jeśli masz słowa kluczowe Q_INTERFACES i Q_DECLARE_INTERFACE, możesz to zrobić za to w Google. Problemem w czasie mojego posta było to, że ich nie znałem. Możesz przeczytać coś o http://doc.qt.io/qt-5/qtplugin.html#Q_DECLARE_INTERFACE i przeglądać informacje o tym interfejsie. – Beachwalker

+0

Każdy pomysł, dlaczego wirtualne void someThingHappended(); powoduje ostrzeżenie kompilatora, zobacz tutaj: http://stackoverflow.com/questions/28614607/virtual-override-for-signal-signals-cannot-be-declared-virtual –

14

Qt "sygnały" jest synonimem do "zabezpieczone". Ale pomaga MOC generować niezbędny kod. Tak więc, jeśli potrzebujesz interfejsu z pewnymi sygnałami - powinieneś zadeklarować je jako wirtualne abstrakcyjne metody chronione. Cały niezbędny kod zostanie wygenerowany przez MOC - możesz zobaczyć szczegóły, że "emituj somesignal" zostanie zastąpione wirtualnym wywołaniem metody chronionej o tej samej nazwie. Zauważ, że ciało metody aslo generowane przez Qt.

UPDATE: kod próbki:

MyInterfaces.h

#pragma once 

struct MyInterface1 
{ 
signals: 
    virtual void event1() = 0; 
}; 

struct MyInterface2 
{ 
signals: 
    virtual void event2() = 0; 
}; 

MyImpl.h

#ifndef MYIMPL_H 
#define MYIMPL_H 

#include <QObject> 
#include "MyInterfaces.h" 

class MyImpl 
    : public QObject 
    , public MyInterface1 
    , public MyInterface2 
{ 
    Q_OBJECT 

public: 
    MyImpl(QObject *parent); 
    ~MyImpl(); 

    void doWork(); 

signals: 
    void event1(); 
    void event2(); 
}; 

class MyListner 
    : public QObject 
{ 
    Q_OBJECT 

public: 
    MyListner(QObject *parent); 
    ~MyListner(); 

public slots: 
    void on1(); 
    void on2(); 
}; 

#endif // MYIMPL_H 

MyImpl.cpp

#include "MyImpl.h" 
#include <QDebug> 

MyImpl::MyImpl(QObject *parent) 
    : QObject(parent) 
{} 

MyImpl::~MyImpl() 
{} 

void MyImpl::doWork() 
{ 
    emit event1(); 
    emit event2(); 
} 

MyListner::MyListner(QObject *parent) 
{} 

MyListner::~MyListner() 
{} 

void MyListner::on1() 
{ 
    qDebug() << "on1"; 
} 

void MyListner::on2() 
{ 
    qDebug() << "on2"; 
} 

main.cpp

#include <QCoreApplication> 
#include "MyImpl.h" 

int main(int argc, char *argv[]) 
{ 
    QCoreApplication a(argc, argv); 

    MyImpl *invoker = new MyImpl(NULL); 
    MyListner *listner = new MyListner(NULL); 

    MyInterface1 *i1 = invoker; 
    MyInterface2 *i2 = invoker; 

    // i1, i2 - not QObjects, but we are sure, that they will be. 
    QObject::connect(dynamic_cast< QObject * >(i1), SIGNAL(event1()), listner, SLOT(on1())); 
    QObject::connect(dynamic_cast< QObject * >(i2), SIGNAL(event2()), listner, SLOT(on2())); 

    invoker->doWork(); 

    return a.exec(); 
} 
+0

thx, dodano wirtualny, ominąłem go właśnie w tym przykładzie – Beachwalker

+0

jeśli jesteś pewien, że i1 jest QObject, wtedy dynamic_cast nie jest potrzebny. static_cast działa dobrze w tym przypadku. –

+2

@FernandoPelliccioni nie, zupełnie się mylisz. Ponieważ interfejs nie dziedziczy 'QObject'. Tylko 'reinterpret_cast' może być użyty, ale nie działa - ponieważ istnieje dziedziczenie wielokrotne. –

3

Istnieją dwa problemy z deklarowania sygnały jak abstrakcyjnych metod w interfejsach:

  1. sygnał jest sygnałem z punktu widzenia Qt tylko gdy wdrażane w sposób szczególny - mianowicie, gdy realizacja jest generowany przez moc i jest zawarty w metadanych obiektu.

  2. Zazwyczaj źle jest emitować sygnały bezpośrednio z zewnątrz obiektu.

Jako następstwo, ponieważ interfejs jest abstrakcyjny, naprawdę nie trzeba zadeklarować swoje sygnały w ogóle - to niczemu nie służy inny niż do dokumentowania intencji, ponieważ:

  1. Jeśli sygnał jest zaimplementowany w klasie, która pochodzi z interfejsu, możesz użyć systemu metaobiektów, aby zweryfikować jego obecność.

  2. Nie należy jednak bezpośrednio wywoływać tych metod sygnału.

  3. Po dynamicznym przesłaniu interfejsu innego niż obiekt do QObject, nie ma już znaczenia, że ​​implementacja pochodziła z interfejsu.

Jedyne ważne powody pozostawione do prowadzenia tego rodzaju gimnastyki byłoby:

  1. Coax doxygen lub inny generator dokumentacji w dostarczaniu dokumentacji do kodu.

  2. Wymuś zastosowanie konkretnej klasy, aby zastosować metodę o tej samej nazwie. Nie gwarantuje to oczywiście, że jest to sygnał.