2013-07-30 3 views
19

Comment déclarer un signal Qt dans une classe/interface abstraite lorsque la classe d'implémentation est déjà dérivée de QObject/QWidget?Déclarez le signal abstrait dans la classe d'interface

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

Vous pouvez écrire uniquement 'signals: void quelquechoseHappened();'. Ce n'est pas nécessaire Mise en œuvre du signal. – Ruu

+0

Je sais que le signal est généré dans l'implémentation mais comment l'observateur sait-il qu'il s'agit d'un signal (qt) s'il n'y a que l'interface connue? – Beachwalker

+0

@Beachwalker J'ai remplacé le mot-clé protégé par des signaux dans ma réponse. Il planera ta question. –

Répondre

37

Comme je l'ai appris dans les derniers jours ... la façon de le faire Qt est comme ceci:

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

Maintenant, vous pouvez vous connecter à ces signaux d'interface.

Si vous n'avez pas accès à la mise en œuvre lors de la connexion au signal de votre déclaration de connexion nécessite une distribution dynamique QObject:

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

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

... et cette façon, vous n'êtes pas obligé d'exposer la classe d'implémentation aux abonnés et aux clients. Ouais!!!

+0

Où avez-vous lu à ce sujet? –

+0

Le temps passe ... Je ne me souviens pas de la référence, mais si vous avez les mots-clés Q_INTERFACES et Q_DECLARE_INTERFACE, vous pouvez google pour cela. Le problème à l'époque de mon poste était que je ne les connaissais pas. Vous pouvez lire quelque chose ici http://doc.qt.io/qt-5/qtplugin.html#Q_DECLARE_INTERFACE et parcourir les infos sur cette interface. – Beachwalker

+0

N'importe quelle idée pourquoi virtual void someThingHappended(); résultats dans un avertissement de compilateur, voir ici: http://stackoverflow.com/questions/28614607/virtual-override-for-signal-signals-cannot-be-declared-virtual –

14

Dans Qt, "signaux" est synonyme de "protégé". Mais cela aide le MOC à générer le code nécessaire. Donc, si vous avez besoin d'une interface avec certains signaux, vous devez les déclarer comme des méthodes protégées abstraites virtuelles. Tout le code nécessaire sera généré par MOC - vous pouvez voir les détails, que "emit somesignal" sera remplacé par l'appel virtuel de la méthode protégée avec le même nom. Notez, que le corps de avec la méthode aussi générée par Qt.

UPDATE: Exemple de code:

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, ajouté virtuel, je l'ai manqué juste dans cet exemple – Beachwalker

+0

si vous êtes sûr que i1 est un QObject, alors dynamic_cast n'est pas nécessaire. static_cast fonctionne bien dans ce cas. –

+2

@FernandoPelliccioni non, vous avez complètement tort. Car l'interface n'hérite pas de 'QObject'. Seul 'reinterpret_cast' peut être utilisé, mais cela ne fonctionne pas - parce qu'il y a multiplication de l'héritage. –

3

Il y a deux problèmes avec la déclaration des signaux comme des méthodes abstraites dans les interfaces:

  1. Un signal est un signal du point de vue de Qt ne fois mis en œuvre d'une manière particulière - à savoir quand est généré la mise en œuvre par moc, et est inclus dans les métadonnées de l'objet.

  2. Il est généralement de mauvaise conception d'émettre des signaux directement de l'extérieur d'un objet.

En corollaire, l'interface est abstraite, vous ne vraiment pas besoin de déclarer ses signaux du tout - il ne sert à rien d'autre que pour documenter l'intention, depuis:

  1. Si un signal est implémenté dans une classe dérivée de l'interface, vous pouvez utiliser le système metaobject pour vérifier sa présence.

  2. Vous n'êtes pas censé appeler directement ces méthodes de signal de toute façon.

  3. Une fois que vous avez transtypé dynamiquement l'interface non-objet en QObject, cela n'a plus d'importance que l'implémentation dérive de l'interface.

Les seules raisons valables pour faire de telles laissées gymnastique seraient:

  1. Coax doxygen ou un autre générateur de documentation en fournissant de la documentation pour votre code.

  2. Force la classe concrète à avoir une implémentation d'une méthode avec le même nom. Cela ne garantit évidemment pas qu'il s'agit en fait d'un signal.

Questions connexes