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....
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).
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()));
Related
my Qt/QML program uses classes declared in C++ to structure information in QML and pass it as a bundle to C++ functions.
The structs are derived from QObject as required by QML. Example:
class ResultStorageOptionsStruct : public QObject {
Q_OBJECT
public:
explicit ResultStorageOptionsStruct(QObject * parent = nullptr) : QObject(parent) {}
~ResultStorageOptionsStruct() = default;
ResultStorageOptionsStruct(const ResultStorageOptionsStruct& blob) {
}
bool folderValid;
QString folderURL;
Q_PROPERTY(bool folderValid MEMBER folderValid)
Q_PROPERTY(QString folderURL MEMBER folderURL)
};
Q_DECLARE_METATYPE(ResultStorageOptionsStruct);
Using this method, passing information from QML to C++ works without a problem, but handling of that information on the C++ side is very bothersome due to the fact that you cannot copy/assign the class(derived of QObject).
I would like to copy/serialize/deserialize my classes for easy handling once they are on the c++ side. I do not need the features QObject gives me once we are on the c++ side as those classes are just containers for the information.
Is there some kind of trick or data structure to make this happen?
You can serialize and deserialize to a QIODevice, but this would be for file storage. To me, it sounds more like you want a QObject wrapper around a plain C++ object.
I would create a C++ class with the data that you need. Then create a QObject derived class with a (weak) reference to the C++ class that you wrap the original class in when passing it to QML.
class MyData
{
public:
int x;
};
class MyDataWrapped : QObject
{
Q_OBJECT
public:
MyDataWrapped(MyData *obj) : md(obj) {}
void x() const { return md->x; }
Q_PROPERTY(...)
private:
MyData *md; // You might want to make this a weak pointer
};
What is potentially tricky is if the data is changed from QML or C++, while the wrapper lives. How can QML tell that a property is changed if you change it from C++, and the other way around. Here, QObject and signals and slots come in handy. However, if the data is read-only while shared, I guess you should be fine.
I am reading other people's code and see this:
class UAVItem:public QObject,public QGraphicsItem
{
Q_OBJECT
Q_INTERFACES(QGraphicsItem)
...
But I didn't see they are using any sort of plug-in in this program.
Therefore, can I just remove the line:
Q_INTERFACES(QGraphicsItem)
?
If you have a class Derived which inherits from a class Base, which in turn inherits from QObject, and both Derived and Base contain the Q_OBJECT macro, then qobject_cast can be used to safely cast from a pointer (or reference) to Base, to a pointer (or reference) to Derived, similar to dynamic_cast in standard C++ but without RTTI.
If Base does not inherit from QObject, then qobject_cast can still be used in this way, but only if Base has a corresponding Q_DECLARE_INTERFACE macro and Derived contains Q_INTERFACES(Base).
In your case, Q_INTERFACES(QGraphicsItem) being present in UAVItem means that qobject_cast can be used to cast from a pointer (or reference) to QGraphicsItem to a pointer (or reference) to UAVItem, despite QGraphicsItem not inheriting from QObject.
From reference doc,
class ToolInterface
{
public:
virtual QString toolName() const = 0;
};
Q_DECLARE_INTERFACE(ToolInterface, "in.forwardbias.tool/1.0");
// Hammer in hammer.h (our Hammer plugin)
#include "toolinterface.h"
class Hammer : public QObject, public ToolInterface
{
Q_OBJECT
Q_INTERFACES(ToolInterface)
public:
QString toolName() const { return "hammer"; }
};
Q_EXPORT_PLUGIN2(hammer, Hammer);
When moc runs on the hammer.h code, it inspects Q_INTERFACES. It
generates code for a function called qt_metacall - void
*Hammer::qt_metacast(const char *iname). This goal of this 'casting' function is to return a pointer to an interface depending on iname.
moc also verifies whether the interface names that you have put inside
Q_INTERFACES have indeed been declared. It can do this by inspecting
the header files and looking for a Q_DECLARE_INTERFACE. In our case,
there is a Q_DECLARE_INTERFACE inside toolinterface.h.
I have the following case:
class A: public QObject
class B: public A, public QThread
Then the inheritance ambiguous happens because the QObject is inherited twice...
Is there a solution to this?
QThread inherits non-virtually from QObject. Therefore, there is no way of inheriting down an hierarchy from both QThread and QObject without creating an ambiguity. Virtual inheritance won't help here, since you are not dealing with any diamond inheritance pattern.
A fix is to alter your design, as #Gabor Angyal mentioned.
Related question: how can i inherit from both QWidget and QThread?
Multiple inheritance of QObject base classes does not work.
You can do something like this to solve this:
class A: public QObject
class B: public A
{
Q_OBJECT
public:
//...
QThread *threadController() { return &mThreadController; }
private:
//...
QThread mThreadController;
}
You could also write delegates for the signals and slots you need instead of exposing the whole QThread object, or just write higher level API for class B and leave its internal QThread completely hidden. Depends on what you are trying to do, really.
If you really need to subclass QThread, then just use that as member variable type instead.
A little tricky, but might work for you:
template<class T>
class A: public T
class B: public A<QThread>
And if you need to use A just by itself then:
A<QObject> *a = new A<QObject>;
This pattern is called Mixin.
UPDATE
Ok, I realised that the moc system would obviously not work with this solution. I support hyde's answer instead.
Yesterday I was asked to recreate a regular QT form using QML (which was my first attempt ever using QLM). Everything was going well until I tried using c++ methods in the QML. This is obviously not the original code, but the scenario looks something like this:
I have a super class deriving from QObject, with some properties, methods and even virtual methods:
class SuperClass : public QObject {
Q_OBJECT
Q_PROPERTY(QString someProperty READ someProperty WRITE setSomeProperty)
protected:
QString m_someProperty;
public:
QString someProperty(void){return m_someProperty;} //get method
void setSomeProperty(QString newValue){m_someProperty = newValue;} //set method
Q_INVOKABLE virtual QString printSomething(void) = 0;
}
And then I have a class deriving from the SuperClass (like a specialization) with some more specific properties and methods and of course the virtual methods implementations and stuff:
class DerivedClass : public SuperClass {
Q_PROPERTY(QString someSpecificProperty READ someSpecificProperty WRITE setSomeSpecificProperty)
private:
QString m_someSpecificProperty;
public:
QString specificProperty(void){return m_someSpecificProperty;} //get method
void someSpecificProperty(QString newValue){m_someSpecificProperty = newValue;} //set method
QString printSomething(void){return QString("Something!");} //SuperClass virtual method
Q_INVOKABLE QString printSomethingSpecific(void){return QString("Something Specific!");}
}
OK, this is it! Now assuming that DerivedClass is instantiated and added to the QML context properly under the name of "DrvClass" for example and that I have some QML control like a TextField which has a 'text:' property:
text: DrvClass.someProperty
using MasterClass' properties, it works just fine.
text: DrvClass.printSomething()
even using virtual methods from MasterClass' which are implemented in the derived class works fine. but...
text: DrvClass.someSpecificProperty
doesn't work and I get something like "Unable to assign [undefined] to QString"
text: DrvClass.printSomethingSpecific()
also doesn't work! "TypeError: Property 'printSomethingSpecific' of object SuperClass() is not a function" And the weird part is that it says that it's not a function from the SuperClass, being the instantiated class the Derived one!
I've looked for similar errors, but most of the time is from people who just forgot to include the Q_OBJECT macro... Mine's there for sure!
It seems that QML doesn't like much classes deriving from other classes that derive from QObjects :-/ Probably something to do with the meta-object compiler who only looks for invokable methods where it finds the Q_OBJECT macro and not on it's subclasses!
So what you guys think the solution for this might be?
I could just add the Q_OBJECT macro to the DerivedClasses instead of the SuperClass, but I really need the SuperClass to be a QObject because of signals and stuff! So is there some other macro I have to add to the DerivedClass for the moc to 'see' it?
Or is this just the fruit of inexperience and I'm doing a dumb mistake somewhere?
DerivedClass is missing Q_OBJECT macro (it is not inherited!).
Then simply run qmake again on your project & compile: it should work.
I wanted to create a QObservableCollection wrapper around a QList(using an internal QList for implementation and forwarding all calls while emiting some sort of CollectionsChanged signal for functions that change the collection), but I see that QList does not inherit from QObject.
I believe you need to inherit from QObject to emit Qt Signals. So I'd need to inherit from QObject for my QObeservableCollection.
But QList and QVector and the other Qt collections doen't inherit from QObject, so I imagine their must be some sort of downside or problem making a collection.
I see that QSignalSpy inherits from both QObject and QList<QList<QVariant>> so maybe they just didn't see a reason to inherit from QObject?
There is a very important technical reason: The moc cannot handle templates, which is pretty much a necessity for a generic container type.
QList is meant to be a value type (like std::vector) which uses implicit sharing while QObjects must be used as pointers and disallow copying
there are other classes which mirror this use such as QImage
Reason is simple containers are like values, you have assign operator, you can copy them clone and so on.
QObjects can't have such functionality, they are not copyable. Try imagine what should happen when you creating clone of object with connected slots and signals. It will lead to total mess. And what should happen with children of cloned object? Should be also cloned?
Another thing is templates usage. Class template which is a QObject is real problem for moc a tool.
It is certainly not true that you need to be a QObject to emit signals. All you need is there to be a QObject somewhere that emits the signals for you. If you want your class to be directly passable to QObject::connect, your class should provide a conversion operator to QObject* that returns the pointer to such a proxy object. This completely sidesteps the whole no-templates-with-moc brouhaha.
class FrobinatorObject : public QObject {
Q_OBJECT
Q_SIGNAL void frobSignal();
...
};
template <typename T> class Frobinator {
QScopedPointer<FrobinatorObject> m_proxy;
// Could be a QSharedPointer, depending on what semantics we want
...
public:
operator FrobinatorObject*() const { return m_proxy.data(); }
};
...
Frobinator<int> frob;
QObject::connect(frob, SIGNAL(frobSignal()), ...);
// or
QObject::connect(frob, &FrobinatorObject::frobSignal, ...);
Also note that while it's true that you can't have signals nor slots in a template-parametrized class, you can certainly have them in a base class that you then derive from. The base class can deal with type-deleted arguments. So:
// This won't work
template <typename T> class TemplateClass : public QObject {
Q_OBJECT
Q_SLOT void aSlot(const T *);
...
};
// But this certainly does work
class BaseClass : public QObject {
Q_OBJECT
Q_SLOT void aSlot(const void *);
...
}
template <typename T> class TemplateClass : public BaseClass {
void aMethod(const T * t) {
BaseClass::aSlot((const void*)&t);
}
...
}
The TemplateClass can also dynamically add slots of the correct type signature to the BaseClass. While that requires some understanding of Qt's internals, it can certainly be done for a class that's supposed to be a reusable, framework-style class.
While I can't look into the minds of the developers, I would say that there's simply no need. A QList is a simple container. It should hold elements, allow for their addition or removal, iteration over them, etc.
It does not need a parent or children. There's no immediate need for signals or slots. It's a matter of keeping things simple.
If you do indeed require additional functionality beyond what a QList provides, it's easy enough to implement. But as for a general case, I guess it's a reasonable and logical decision to not overcomplicate things.
The extra overhead in inheriting from QObject is unnecessary for the majority of uses. Containers should be as small and as fast as possible.
If you wanted to inherit from QList and provide that functionality for a class of your own, you can do that.