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

Calling function from object of object in QML results in TypeError



  • I'm trying to connect a function of a C++ object of another C++ object to my QML UI. The first C++ object lives in a separate QThread. The structure is as follows:

    • main.qml
    • main.cpp
      • Base class, QThread
        • Hardware class

    Which means that the Hardware class is just an object created in the QThread Base.

    I would like to connect my QML UI to the functions of the Hardware class. So I set the context property:

    Base* base = new Base(argc, argv);
    engine.rootContext()->setContextProperty("base", base);
    

    I was not able to connect to the functions of the Hardware class, e.g.

    onClicked: base.hardware.resetSystem()
    

    fails with

    qrc:/SideBarForm.ui.qml:95: TypeError: Cannot call method 'resetSystem' of undefined
    

    But I can successfully call functions of the base class, e.g.

    onClicked: base.test()
    

    And calling the function from main.cpp works also fine:

    base->hardware->resetSystem();
    

    Furthermore, hardware is public in Base and resetSystem() is a public slot in Hardware:

    public:
      std::shared_ptr<Hardware> hardware;
    

    --

    public slots: 
      void resetSystem();
    

    What is the proper way of connecting signals and slots from "sub-objects" in QThreads to a QML interface?



  • @prex one simple way is to add a Q_INVOKABLE method (or public slot) to Base

    Q_INVOKABLE void resetHardwareSys(){
     hardware->resetSystem();
    }
    

    or you can expose your Hardware to QML adding a Q_PROPERTY for it and returning it as a QVariant (it is not enough to set it public to access it in QML )



  • @LeLev Thanks, this works now. So the first solution means going over two public slots. I'm wondering about how to handle signals from C++ to QML then. First using QObject::connect() and then the Connections QML type? I have lots of data from the Hardware class which I need to visualize in the UI.

    I was reading many posts about signals & slots not working between QML and QThreads. I'm a little bit confused since it seems to work here...



  • I like the Q_PROPERTY approach in general but I don't understand what you mean by

    @LeLev said in Calling function from object of object in QML results in TypeError:

    you can expose your Hardware to QML adding a Q_PROPERTY for it and returning it as a QVariant (it is not enough to set it public to access it in QML )

    At the moment I have different Q_PROPERTIES defined in my Hardware class, but how can I make them accessible by QML? Or do you mean adding a Q_PROPERTY for the whole Hardware class?



  • @prex said in Calling function from object of object in QML results in TypeError:

    you mean adding a Q_PROPERTY for the whole Hardware class?

    yes,
    for the getter method, don't return the hardware object directly, use QVariant::fromValue to return it as a QVariant



  • @LeLev I‘m not sure if I understand correctly. Should I do something like

    Q_PROPERTIES(Hardware hardware ...)
    

    in Base? But how to access this then? Can you please elaborate a little bit more on it?



  • @prex hi

    class Hardware : public QObject
    {
        Q_OBJECT
    public:
        explicit Hardware (QObject *parent = nullptr);
    public slots:
    
        void write(const QString &str){
            qDebug()<< str;
        }
    };
    
    class Example : public QObject
    {
        Q_OBJECT
        Q_PROPERTY( Hardware * hardware READ hardware NOTIFY hardwareChanged)
    
    public:
        explicit Example(QObject *parent = nullptr);
    
        Hardware * hardware (){
            return  m_hardware;
        }
    signals:
        void hardwareChanged();
    
    private:
        Hardware *m_hardware;
    };
    
     Example e;
        engine.rootContext()->setContextProperty("obj",&e);
    
    import QtQuick 2.12
    import QtQuick.Window 2.12
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
    
        Component.onCompleted: obj.hardware.write("hello")
    
    }
    
    


  • @LeLev Thank you very much for the code sample!


Log in to reply