Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Qt signals and slots with multiple inheritance

I have a class (MyClass) that inherits most of its functionality from a Qt built-in object (QGraphicsTextItem). QGraphicsTextItem inherits indirectly from QObject. MyClass also implements an interface, MyInterface.

class MyClass : public QGraphicsTextItem, public MyInterface

I need to be able to use connect and disconnect on MyInterface*. But it appears that connect and disconnect only work on QObject* instances. Since Qt does not support multiple inheritance from QObject-derived classes, I cannot derive MyInterface from QObject. (Nor would that make much sense for an interface anyway.)

There is a discussion of the problem online, but IMO the proposed solution is fairly useless in the common case (accessing an object through its interface), because you cannot connect the signals and slots from MyInterface* but must cast it to the derived-type. Since MyClass is one of many MyInterface-derived classes, this would necessitate "code-smelly" if-this-cast-to-this-else-if-that-cast-to-that statements and defeats the purpose of the interface.

Is there a good solution to this limitation?

UPDATE: I noticed that if I dynamic_cast a MyInterface* to QObject* (because I know all MyInterface-derived classes also inherit eventually from QObject, it seems to work. That is:

MyInterface *my_interface_instance = GetInstance();
connect(dynamic_cast<QObject*>(my_interface_instance), SIGNAL(MyInterfaceSignal()), this, SLOT(TempSlot()));

But this really seems like I am asking for undefined behavior....

like image 707
Dave Mateer Avatar asked Jul 15 '10 20:07

Dave Mateer


2 Answers

You found the answer yourself: the dynamic_cast works as you would expect. It is not undefined behavior. If the instance of MyInterface you got is not a QObject, the cast will return null and you can guard yourself against that (which won't happen, since you said all instances of the interface are also QObjects). Remember, however, that you need RTTI turned on for it to work.

I would also offer a few other suggestions:

  • Use the Q_INTERFACES feature (it's not only for plug-ins). Then you'd work in terms of QObject and query for MyInterface using qobject_cast when it is really needed. I don't know your problem in detail, but since you know that all MyInterface instances are also QObjects, this seems to be the most sensible approach.

  • Add a QObject* asQObject() abstract method to MyInterface and implement it as { return this; } in all subclasses.

  • Having a QGraphicsTextItem (composition) instead of being one (inheritance).

like image 133
andref Avatar answered Oct 16 '22 09:10

andref


You can declare MyInterface that takes a QObject in its constructor:

class MyInterface {
public:
                MyInterface(QObject * object);
    QObject *   object() { return m_object; }
    ...
private:
    QObject *   m_object;
};

MyInterface::MyInterface(QObject * object) :
    m_object(object)
{
    ...
}

Then in MyClass constructor:

MyClass::MyClass() :
MyInterface(this)
{
    ...
}

And you can connect the signal:

MyInterface *my_interface_instance = GetInstance();
connect(my_interface_instance->object(), SIGNAL(MyInterfaceSignal()), this, SLOT(TempSlot()));
like image 29
Stephen Chu Avatar answered Oct 16 '22 08:10

Stephen Chu