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?


  • Moderators

    Hi! I think you forgot the Q_OBJECT macro in inheritingclass. Does this really compile?


  • Qt Champions 2016

    @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.


  • Qt Champions 2016

    @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 because Q_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 possible

    What 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.


  • Moderators

    @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 inherits baseclass which inherits QObject, so it is a QObject already. There's no multiple inheritance present or needed here. And yes, you need Q_OBJECT macro in the inheritingclass, because it declares signals and slots. If you don't specify the Q_OBJECT macro you are limited to signals and slots declared in baseclass 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.


  • Qt Champions 2016

    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.



  • OK, now it works.
    BTW. with protected macros in baseclass it works too. But anyway I will keep it private unless I read whole documentation.

    Thank you all.


  • Moderators

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

Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.