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

C++ PROPERTY changed() to QML



  • Hi all,

    I lost my way in C++ PROPERTY to connect to QML.
    What I did already.

    1. Created main.cpp. main.cpp calls backend_qml.
    2. Backend_qml calls HWButton.
    3. In the backend_qml there is a Q_PROPERTY which shares a page number to QML.
    4. If a Hardware button is pushed, the button changes the page number and the page needs to change in QML.
    5. (need to add this: send some more variables to the QML)
    #include <backend_qml.h>
    #include <system_control.h>
    
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QtQml>
    #include <QQmlContext>
    #include <QQmlEngine>
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
    
        backend_qml bqml;
        system_control cc_system;
    
        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty("backend_qml", &bqml);
    
        //qmlRegisterType<backend_qml>("PropertyBinding", 1, 0, "PropertyBinding");
    
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    
    
        if (engine.rootObjects().isEmpty())
            return -1;
    
        return app.exec();
    }
    

    When I run:

        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty("backend_qml", &bqml);
    

    I can change the page number, but I'm not able to get the signal in QML when the PROPERTY has CHANGED.

    When I run:

      qmlRegisterType<backend_qml>("PropertyBinding", 1, 0, "PropertyBinding");
    

    The application runs, but no data is transfered to QML. I think this is normal as I didn't make a instance of backend_qml.

    Below the backend_qml.h and backend_qml.cpp

    #ifndef BACKEND_QML_H
    #define BACKEND_QML_H
    
    #include <QObject>
    #include <QScopedPointer>
    #include <QDebug>
    #include "hwbutton.h"
    
    class backend_qml : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged)
    
    public:
        explicit backend_qml(QObject *parent = nullptr);
        int page() const {qDebug() << QString::number(m_page); return m_page;}
    
    signals:
        void pageChanged();
    
    
    public slots:
        void setPage(int page);
    
    private:
            int m_page;
            HWButton hwbutton;
    };
    
    #endif // BACKEND_QML_H
    
    
    #include "backend_qml.h"
    
    backend_qml::backend_qml(QObject *parent) : QObject(parent)
    {
        qDebug()<< "app is running";
    
        connect(&hwbutton,SIGNAL(buttonPushed(int)),this,SLOT(setPage(int)));
    
    }
    
    void backend_qml::setPage(int page)
    {
        qDebug() << "page changer called" ;
        m_page = page;
        emit pageChanged(); // trigger signal if page is changed
    }
    

    So the main goal is when setPage() is called, a function in qml is called together with the new pagenumber.

    I hope somebody is able to point me in the correct direct. I got really confussed.

    Thanks in advance,
    Kind regards,
    TMJJ

    ************** EDIT **************

    QML

    I have multiple qml files.

    In the main, I just have some global variables. here I also run my Main_Screen{}

    import QtQuick 2.6
    import QtQuick.Window 2.2
    
    
    Window {
        visible: true
        width: 800
        height: 480
        title: qsTr("Hello World")
        
        property string workspace_background_color   : "#FFFFFF"
        property string errorheader_background_color : "#FFFFFF"
        
        Main_screen{
        }
    }
    
    

    In the main_screenForm.ui.qml I have the following:

    import QtQuick 2.4
    import QtQuick.Layouts 1.3
    import QtQuick.Controls 1.4
    import "MaterialDesignIconGlyphs.js" as MaterialGlyphs
    
    Item {
        width: 800
        height: 480
    
        property alias workspace: workspace
    
        property alias pushbutton1: pushbutton1
        property alias pushbutton2: pushbutton2
        property alias pushbutton3: pushbutton3
        property alias pushbutton4: pushbutton4
        property alias pushbutton5: pushbutton5
    
        property alias mouse: mouse
    
        property alias button2_pushed: button2_pushed
        property alias button3_pushed: button3_pushed
        property alias button4_pushed: button4_pushed
        property alias button5_pushed: button5_pushed
    
        property alias workspace_stacked_view: workspace_stacked_view
    
        property string leftbar_pushed_button_indicator_color: "#FF6110"
    
        GridLayout {
            id: maingrid
            columnSpacing: 0
            rowSpacing: 0
            anchors.fill: parent
            anchors.margins: 0
    
            columns: 2
            rows: 2
    
            Rectangle {
                id: companyheader
    
                Layout.preferredHeight: 80
                Layout.preferredWidth: 100
                Layout.margins: 0
    
                color: "#014088"
    
                Image {
                    id: companylogo
                    anchors.fill: parent
                    fillMode: Image.PreserveAspectFit
                    source: "qrc:/resource/img/Company/Logo.png"
                }
    
                MouseArea {
                    id: pushbutton1
                    anchors.fill: parent
                }
            }
    
            Rectangle {
                id: errorheader
    
                Layout.preferredHeight: 80
                Layout.fillWidth: true
    
                color: "#FF6110"
    
                Text {
                    id: tetlol
                    x: 23
                    y: 19
                    width: 112
                    height: 42
                    text: backend_qml.page
                    font.pixelSize: 12
                }
            }
    ...
    
      StackView {
                    id: workspace_stacked_view
                    initialItem: homepage
                    anchors.fill: parent
                }
    ...
    

    Like this I'm able to print the page number inside my header. So this works fine.
    Now I also have a stackview. Here I want to change the page when a hardware button is pushed.
    So As explained above, I tried to use:

    qmlRegisterType<backend_qml>("PropertyBinding", 1, 0, "PropertyBinding");
    

    Then in my Main_screen.qml I did like this:

    import QtQuick 2.4
    import QtQml 2.2
    import PropertyBinding 1.0
    
    
    import "qrc:/pages/"                // import qml file from different resource folder
    
    Main_screenForm {
    
        button2_pushed.visible:true
        button3_pushed.visible:false
        button4_pushed.visible:false
        button5_pushed.visible:false
    
        mouse {
            onClicked: console.info("pushed mouse")
        }
    
        function pagechanger(n)
        {
            switch(n){
                case 1: button2_pushed.visible=true
                        button3_pushed.visible=false
                        button4_pushed.visible=false
                        button5_pushed.visible=false
                        workspace_stacked_view.push(homepage);break;
                case 2: button2_pushed.visible=false
                        button3_pushed.visible=true
                        button4_pushed.visible=false
                        button5_pushed.visible=false
                        workspace_stacked_view.push(settingpage);break;
                case 3: button2_pushed.visible=false
                        button3_pushed.visible=false
                        button4_pushed.visible=true
                        button5_pushed.visible=false
                        workspace_stacked_view.push(diagnosepage);break;
                case 4: button2_pushed.visible=false
                        button3_pushed.visible=false
                        button4_pushed.visible=false
                        button5_pushed.visible=true;break;
            }
        }
    
        pushbutton2{
            onClicked: {
                console.info("touch pushbutton2 clicked");
                pagechanger(1);
            }
        }
        pushbutton3{
            onClicked: {
                console.info("touch pushbutton3 clicked");
                pagechanger(2);
            }
        }
        pushbutton4{
            onClicked: {
                console.info("touch pushbutton4 clicked");
                pagechanger(3);
            }
        }
        pushbutton5{
            onClicked: {
                console.info("touch pushbutton5 clicked");
                pagechanger(4);
            }
        }
    
    
    
        Component{
            id:homepage
            HomePage{
            //anchors.fill:parent
            }
    
        }
    
        Component{
            id:settingpage
            SettingPage{
            //anchors.fill:parent
            }
        }
    
    
         Component{
            id:diagnosepage
            DiagnosePage{
            //anchors.fill:parent
          }
    
        }
         
        
         // HERE IN NEED TO CALL FUNCTION pagechanger!
         
    }
    
    /*##^## Designer {
        D{i:0;autoSize:true;height:480;width:640}
    }
     ##^##*/
    
    

    I hope it is still clear what I try to reach.

    Kind regards



  • Show us what you are doing in QML.



  • @fcarney

    Thanks for the quick response.
    Please see the edited version.

    Thanks,
    TMJJ



  • @TMJJ001 I think you are doing too complicate and not in the cleanest way. Here some hints:

    • C++ class names should be UpperCamelCase, to respect common Qt/QML coding rules
    • Instance names should be lowerCamelCase, again to respect common Qt/QML coding rules
    • try to think simple, if you fight against the framework, then you are doing something in the wrong way
    • I would also strongly recommend you to read the Qt documentation: Integrating QML and C++

    So, now how I would do it.
    The backend class:

    #ifndef BACKENDQML_H
    #define BACKENDQML_H
    
    #include <QObject>
    
    class BackendQml : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(int page READ page WRITE setPage NOTIFY pageChanged)
    
    public:
        explicit BackendQml(QObject *parent = nullptr): QObject(parent), m_page(0) {}
        int page() const { return m_page; }
    
    signals:
        void pageChanged(int page);
    
    
    public slots:
        void setPage(int page)
        {
            if(m_page != page)
            {
                 m_page = page;
                 emit pageChanged(page);
            }
        }
    
    private:
            int m_page;
    };
    
    #endif // BACKENDQML_H
    

    Expose the global variable to QML Engine:

        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty("backendQml", &bqml);
    

    Then in QML (Main_Screen):

    import QtQuick 2.6
    import QtQuick.Window 2.2
    
    
    Window {
        visible: true
        width: 800
        height: 480
        title: qsTr("Hello World")
        
        property string workspace_background_color   : "#FFFFFF"
        property string errorheader_background_color : "#FFFFFF"
        readonly property int currentPage: backendQml.page 
    
        onCurrentPageChanged {
            console.log("Current page is " + currentPage)
       }
       .. 
    }
    

    And when changing page, simply change you pagechanger() function to add backendQml.page=n, like this:

        function pagechanger(n)
        {
            backendQml.page=n
            switch(n){
            ...
            }
        }
    


  • @KroMignon

    Thanks for the extensive explanation!
    Now I understand what I was doing wrong.

    Kind regards