Subclass of subclass of qobject
-
Hi,
I want to inherite QObject class in another class. But in this new class I want to add some Q_PROPERTY-ies too and register both as type to qml.
Like that:
baseclass.h#ifndef BASECLASS_H #define BASECLASS_H #include <QObject> class baseclass : public QObject { protected: Q_OBJECT Q_PROPERTY(QString baseProperty READ baseProperty WRITE setBaseProperty NOTIFY basePropertyChanged) QString m_baseProperty; public: explicit baseclass(QObject *parent = 0) : QObject(parent) {} explicit baseclass(QString baseProperty, QObject *parent = 0) : QObject(parent), m_baseProperty(baseProperty) {} ~baseclass() {} QString baseProperty() const { return m_baseProperty; } signals: void basePropertyChanged(QString baseProperty); public slots: void setBaseProperty(QString baseProperty) { if (m_baseProperty == baseProperty) return; m_baseProperty = baseProperty; emit basePropertyChanged(baseProperty); } }; #endif // BASECLASS_H
inheritingclass.h
#ifndef INHERITINGCLASS_H #define INHERITINGCLASS_H #include "baseclass.h" class inheritingclass : public baseclass { Q_PROPERTY(QString newProperty READ newProperty WRITE setNewProperty NOTIFY newPropertyChanged) QString m_newProperty; public: explicit inheritingclass(QObject *parent = 0) : baseclass(parent) {} explicit inheritingclass(QString baseProperty, QString newProperty, QObject *parent = 0) : baseclass(baseProperty, parent), m_newProperty(newProperty) {} ~inheritingclass() {} QString newProperty() const { return m_newProperty; } signals: void newPropertyChanged(QString newProperty); public slots: void setNewProperty(QString newProperty) { if (m_newProperty == newProperty) return; m_newProperty = newProperty; emit basePropertyChanged(newProperty); } }; #endif // INHERITINGCLASS_H
main.cpp:
#include <QApplication> #include <QQmlApplicationEngine> #include <QtQml> #include "baseclass.h" #include "inheritingclass.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); qmlRegisterType<baseclass>("example.inheritance",1,0,"baseClass"); qmlRegisterType<inheritingclass>("example.inheritance",1,0,"inheritingClass"); baseclass BaseClass("baseProperty of base class"); inheritingclass InheritingClass ("baseProperty of inheriting class", "new Property of inheriting clas"); QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("BaseClass", &BaseClass); engine.rootContext()->setContextProperty("InheritingClass", &InheritingClass); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); }
main.qml:
import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Dialogs 1.2 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") menuBar: MenuBar { Menu { title: qsTr("File") MenuItem { text: qsTr("Exit") onTriggered: Qt.quit(); } } } MainForm { anchors.fill: parent baseProperty.text:BaseClass.baseProperty inherBaseProp.text: InheritingClass.baseProperty newProperty.text: InheritingClass.newProperty } }
MainForm.ui.qml
import QtQuick 2.5 import QtQuick.Controls 1.4 import QtQuick.Layouts 1.2 Item { id: item1 width: 640 height: 480 property alias newProperty: newProperty property alias inherBaseProp: inherBaseProp property alias baseProperty: baseProperty ColumnLayout { id: columnLayout1 anchors.fill: parent Text { id: baseProperty text: qsTr("this.id") Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter font.pixelSize: 12 } Text { id: inherBaseProp text: this.id Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter font.pixelSize: 12 } Text { id: newProperty text: this.id Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter font.pixelSize: 12 } } }
But there inherining class isn't registered as a new type, and newProperty of inheriting class isn't displayed. My question is:
How to register class that inherits from another custom qobject class as a type?
-
Hi! I think you forgot the
Q_OBJECT
macro ininheritingclass
. Does this really compile? -
@Alart
In addition to what @Wieland wrote,Q_OBJECT
is supposed to appear in the private section of the class, not as protected. So this:class baseclass : public QObject { protected: Q_OBJECT Q_PROPERTY(QString baseProperty READ baseProperty WRITE setBaseProperty NOTIFY basePropertyChanged) QString m_baseProperty; // ... };
Would go like this:
class baseclass : public QObject { Q_OBJECT Q_PROPERTY(QString baseProperty READ baseProperty WRITE setBaseProperty NOTIFY basePropertyChanged) protected: QString m_baseProperty; // ... };
-
It compiles obviously. I did "a lot of work" to provide that truncated but working example. Q_OBJECT macro is inherited from baseclass.
You can try yourself... I can provide example for earlier versions of Qt.
I can't add new Q_OBJECT macro, because inheritingclass does not inherit from QObject. If I add multiple inheritance it does not compile, because of ambiguous QObject.In fact Q_OBJECT macro can be put even in public section - it does not affect anything. Maybe that macro forces private somewhere and despite I make it protected base QObject is private?
Edit:
I previously worked around it by creating all properties (private or protected macros, protected variables) in base class and defining methods or whatever I needed in subclass. Then I used that in qml as base class even if it was subclass.
But I want to define Q_PROPERTY in subclass too if it's possible, because that's more neat. I don't need to control unnecessary properties when I use base class and all special informations are stored in one class, not scattered between base and inheriting class. -
@Alart
Hello,Q_OBJECT macro is inherited from baseclass.
And if that were the proper way of doing it a lot of people writing Qt have gone way out of their way for nothing including it in every class that derives from
QObject
, whether directly or indirectly.I can't add new Q_OBJECT macro, because inheritingclass does not inherit from QObject. If I add multiple inheritance it does not compile, because of ambiguous QObject.
Well, it does through
baseclass
, and it gives you ambiguity becauseQ_OBJECT
is not in the private section of your base class.I previously worked around it by creating all properties (private or protected macros, protected variables) in base class and defining methods or whatever I needed in subclass.
This would defeat the whole purpose of having inheritance, wouldn't it?
Then I used that in qml as base class even if it was subclass.
But I want to define Q_PROPERTY in subclass too if it's possibleWhat you want has nothing to do with QML, but is a matter of the Qt meta-object system, and you can define properties in your derived classes, if you do that properly.
Kind regards.
-
@Alart said:
I can't add new Q_OBJECT macro, because inheritingclass does not inherit from QObject. If I add multiple inheritance it does not compile, because of ambiguous QObject.
inheritingclass
inheritsbaseclass
which inheritsQObject
, so it is a QObject already. There's no multiple inheritance present or needed here. And yes, you needQ_OBJECT
macro in theinheritingclass
, because it declares signals and slots. If you don't specify theQ_OBJECT
macro you are limited to signals and slots declared inbaseclass
only, and you can't add new ones.
Q_OBJECT
macro is needed on every "level" of inheritance that adds any moc dependent features (signals, slots, properties etc.). -
OK, I tried around it already, because this seems like a proper way.
This is what I got, however it does not compile:#ifndef BASECLASS_H #define BASECLASS_H #include <QObject> class baseclass : public QObject { private: // if neccesary Q_OBJECT Q_PROPERTY(QString baseProperty READ baseProperty WRITE setBaseProperty NOTIFY basePropertyChanged) QString m_baseProperty; // this can be private or protected public: explicit baseclass(QObject *parent = 0) : QObject(parent) {} explicit baseclass(QString baseProperty, QObject *parent = 0) : QObject(parent), m_baseProperty(baseProperty) {} ~baseclass() {} QString baseProperty() const { return m_baseProperty; } signals: void basePropertyChanged(QString baseProperty); public slots: void setBaseProperty(QString baseProperty) { if (m_baseProperty == baseProperty) return; m_baseProperty = baseProperty; emit basePropertyChanged(baseProperty); } }; #endif // BASECLASS_H
#ifndef INHERITINGCLASS_H #define INHERITINGCLASS_H #include "baseclass.h" class inheritingclass : public baseclass { Q_OBJECT Q_PROPERTY(QString newProperty READ newProperty WRITE setNewProperty NOTIFY newPropertyChanged) QString m_newProperty; public: explicit inheritingclass(QObject *parent = 0) : baseclass(parent) {/*setParent(parent);*/} explicit inheritingclass(QString baseProperty, QString newProperty, QObject *parent = 0) : baseclass(baseProperty, parent), m_newProperty(newProperty)/*, m_baseProperty(baseProperty)*/ {/*setParent(parent);*/} ~inheritingclass() {} QString newProperty() const { return m_newProperty; } signals: void newPropertyChanged(QString newProperty); public slots: void setNewProperty(QString newProperty) { if (m_newProperty == newProperty) return; m_newProperty = newProperty; emit basePropertyChanged(newProperty); } }; #endif // INHERITINGCLASS_H
Now I'm getting:
/home/alart/QTworek/InheritanceQobject/inheritingclass.h:14: error: undefined reference to `vtable for inheritingclass'
The same in both constructors and destructors. I remember that problem, it had something to do with macros...I already tried clean and build.
-
This is a very different thing altogether. Now, try without inlining your constructors, and I think you'll be good to go. Like this:
class baseclass : public QObject { Q_OBJECT Q_PROPERTY(QString baseProperty READ baseProperty WRITE setBaseProperty NOTIFY basePropertyChanged) protected: QString m_baseProperty; // this can be private or protected public: // Define those in your cpp: explicit baseclass(QObject *parent = 0); //< Don't inline this! ~baseclass(); //< Don't inline this since it's virtual and virtual methods can't be inlined by the compiler anyway };
and also the same for
inheritingclass
. -
The
Q_OBJECT
macro can be put in public, protected or private section and it will do the same. It doesn't matter for moc but I highly recommend to always put it at the top of your class for the following reason:The
Q_OBJECT
macro is defined like this:#define Q_OBJECT \ public: \ //stuff... \ private: \ //other stuff...
So it's easy to make your other members private by mistake like this:
class Foo { public: Q_OBJECT int bar; //oh no! bar is private };
If you put the macro at the top it won't affect access declarations below:
class Foo { Q_OBJECT public: int bar; //yes! bar is public as intended };