Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Invoke slot of child of child of QQmlPropertyMap?



  • [This is a duplicate of StackOverflow question I asked, but I am hoping it find a different audience with other solutions.]

    BTW, this is for a Qt 5.15 application.

    I am working on a design where I have a class (call it MyBaseClass) that inherits from QQmlPropertyMap. It implements some additional behaviors for my system which are then used by other classes that inerit from MyBaseClass (e.g. ChildClass).

    From QML, (QtQuick), I wish to invoke a slot (mySlot()) of ChildClass. When I try, I get the following issues in the Application Output (mentioning MyBaseClass) even though I am only using ChildClass.

    qrc:/main.qml:17: TypeError: Property 'mySlot' of object MyBaseClass(0x55f88ef86b50) is not a function
    qrc:/main.qml:24: TypeError: Property 'mySlot' of object MyBaseClass(0x55f88ef86b50) is not a function
    

    I also tried implementing sending signals from QML, but I get a similar issue trying to bind to the QML emitted signals. (I am not adding that example.)

    My "gut" says that the problem is I am not doing things the "Qt Way," but I can't figure out what it is.
    [I like to say "If you have to do a lot of work to make something work, you aren't doing it the 'Qt Way.'"]

    Does anyone know the Qt Way to do this? I almost at doing C++ Templates, but I'm not sure I can extend the functionality I want to add to MyBaseClass that way.

    I'm also not totally surprised noting there are some interesting ways you have to initialize a member inheriting directly from QQmlPropertyMap.

    Here is the minimum example code of the problem to demonstrate the problem. (I'm leaving out the .pro file since it is trivial to make.)

    MyBaseClass.h

    #include <QQmlPropertyMap>
    
    class MyBaseClass : public QQmlPropertyMap
    {
        Q_OBJECT
    public:
        explicit MyBaseClass(QObject *parent = nullptr)
            : QQmlPropertyMap(this, parent)
        {}
    
    };
    

    ChildClass.h

    #include "MyBaseClass.h"
    #include <QObject>
    #include <QDebug>
    
    class ChildClass : public MyBaseClass
    {
        Q_OBJECT
    public:
        ChildClass(QObject *parent = nullptr)
            : MyBaseClass(parent) {}
    
    public slots:
        void mySlot() {
            qDebug() << "This is mySlot";
        };
    };
    

    main.qml

    import QtQuick 2.15
    import QtQuick.Window 2.15
    
    
    Window {
        id: theMainWindow
        width: 640
        height: 480
        visible: true
        title: qsTr("Hello World")
    
        property QtObject   cData: childData;
    
        onCDataChanged: {
            childData.mySlot();
        }
    
        Timer { // wait a bit before calling
            repeat: false
            interval: 2000
            running: true
            onTriggered: childData.mySlot();
        }
    }
    

    main.cpp

    int main(int argc, char *argv[])
    {
    #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    #endif
    
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
    
        auto childData = new ChildClass();
    
        engine.rootContext()->setContextProperties({
            QQmlContext::PropertyPair{"childData",  QVariant::fromValue(childData)},
        });
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
            &app, [url](QObject *obj, const QUrl &objUrl) {
                if (!obj && url == objUrl)
                    QCoreApplication::exit(-1);
            }, Qt::QueuedConnection);
        engine.load(url);
    
        return app.exec();
    }
    


  • The answers at this time in the StackOverflow thread here are correct.
    The instantiation of template constructor for QQmlPropertyMap needs to know the derived type to register it. The template is being instantiated for MyBaseClass, and therefore the metaobject system recognizes that as the most derived type of ChildClass.

    Is the base class that inherits from QQmlPropertyMap, plus the derived class that inherits from the base necessary? Can the code be restructured to either directly inherit QQmlPropertyMap in ChildClass, or compose and expose the MyBaseClass instance as a property of ChildClass?



  • @jeremy_k I'm now looking at a template based solution as suggested on the SO question (https://stackoverflow.com/a/65401996/447438). The ideas has been in my head for a couple of days, and the post pushed me to look more deeply into it.