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

Change QML Screen from C++ thread



  • Hello,
    My project consist in a simple touchscreen that enables few switchs by touching a button in the screen. When the application starts, runs in parallel a thread to launch a TCP socket (CreateSocket) and keeps listening for incoming connections.
    When an external connection is linked I would like to show the same screen that is shown when you click on "Advanced" button (Advanced.qml).

    I red the following tutorials but I dont know how to define the signals/slots. How should I define the signals to activate the screen that I in local mode activate by clicking on the button "Advanced"
    http://doc.qt.io/qt-4.8/qtbinding.html
    https://wiki.qt.io/Receiving_signals_with_arguments_in_QML_from_C%2B%2B

    Can anybody help me? I would really aprreciate it! I am quite new by Qt!

    dataprovider.h

    Class DataProvider : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
        public:
           explicit DataProvider(QObject *parent = 0);
    
    void setValue(int newVal) {    // <--- do your stuff to update the value
    
        if (newVal != m_value) {
            m_value = newVal;
          //  qDebug() << "changing to" << newVal;
            emit valueChanged(newVal);     // <--- emit signal to notify QML!
        }
    }
    int value() const {
        return m_value;
    }
     Q_INVOKABLE void on(int position);
     Q_INVOKABLE void adv_on(int switch_id, int position, bool is_off);
    
     void* CreateSocket(DataProvider *dataP);
    
    signals:
        void valueChanged(int i);             // <--- actual signal used as notification in Q_PROPERTY
    
    private:
        int m_value;     // <--- member value which stores the actual value
    

    };

    main.cpp

    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication app(argc, argv);
    QQmlApplicationEngine engine; /*Every app needs an app engine*/
    
    DataProvider dt;              /*Class with properties to access from back-end to front-end*/
    engine.rootContext() -> setContextProperty("dataProvider", &dt);
    engine.addImportPath(":/imports");
    
    std::thread thread(&CreateSocket, &dt);
    
    engine.load(QUrl(QLatin1String("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;
    return app.exec();
    

    mainwindow.qml

    import QtQuick.Extras 1.4
    import QtQuick.Dialogs 1.2
    
    Rectangle {
        id: root
        visible: true
        width: 800
        height: 480
    }
      // Extra objects removed to be irrelevant
    Advanced {
        id: advanced
        anchors.left: parent.left
        anchors.leftMargin: 450
        anchors.top: parent.top
        anchors.topMargin: 408
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 26
        anchors.right: parent.right
        anchors.rightMargin: 277
    }
    

    Advanced.qml

    import QtQuick 2.4
    Item {
        id: advance
        width: 74
        height: 50
    
    Image {
        id: logo
        width: 74
        height: 50
        fillMode: Image.PreserveAspectFit
        source: "images/Next.png"
    
        MouseArea {
            anchors.bottomMargin: 0
            anchors.topMargin: 0
            anchors.leftMargin: 0
            anchors.rightMargin: 0
            anchors.fill: parent
            onClicked: {
                   pageLoader.source = "AdvancedPage.qml";
                       }
                 }
            }
    }

  • Moderators

    How should I define the signals to activate the screen that I in local mode activate by clicking on the button "Advanced"

    Can you rephrase this? I have no idea what you mean.

    In any case, you attach your DataProvider correctly. It is now available as dataProvider in your whole QML application. So whenever you want to use it, you can simply call it, like:

    Button {
      onClicked: dataProvider.value = 1234
    }
    


  • Hi sierdzio, thanks for your answer.

    I try to explain it better:
    The project consists on a switch driver with "local" and "remote" working modes.
    In local mode you push few QML buttons that call C++ funtions ("on" and "adv_on") to activate GPIO outputs of a Raspberry Pi.
    To work in remote mode, a TCP socket is running in a separate thread and listen for incoming connections. These remote clients send commands corresponding to button pressings in local mode, therefore I use the same C++ functions ("on" and "adv_on").

    Until now (local mode) I call C++ functions from QML (when I push the button with the mouse).
    What I want to do now (remote mode) is to interact QML objects from the TCP socket written in C++ code .

    I would like to activate the QML button "Advance" from the C++ socket, as it would have been pressed with the mouse.

    I hope to have explained better now.

    Thanks for your support.



  • @JaviRodriguez Have you already check the QML / C++ integration documentation?

    So following the example of userNameChanged()/onUserNameChanged from that article, I'd suggest that your C++ backend handling the remote connection and parsing the commands, emit a signal (i.e. remoteModeRequested) whenever you know a remote connection has requested remote mode, so the QML handler onRemoteModeRequested will load/show the proper QML view for remote mode.



  • @Pablo-J-Rogina
    Thank you for you answer. I red integration documentation you mention several times.
    This is exactly the point where I am stuck on: I emit the Signal but i do not how to Connect It to the "Advanced.qml" nor to activate the command 'pageLoader.source = "AdvancedPage.qml";' to load the wished qml Page.

    Could you please write a small example for me bases on muy code? I would really appreciate it.

    Thanks for your support.



  • @JaviRodriguez said in Change QML Screen from C++ thread:

    I emit the Signal but i do not how to Connect It to the "Advanced.qml"

    Could you show your QML code so far? (at least the part relevant to the signal handler)



  • @Pablo-J-Rogina
    Hi. Following I show some relevant code.

    main.cpp

     // I launch a new thread to run the socket
     DataProvider dt;        /*Class with properties to access from back-end to front-end*/
     std::thread thread(&CreateSocket, &dt);
    

    dataprovider.cpp

     void* CreateSocket(DataProvider *dataP){
      //... some irrelevant code
     new_socket = accept(serverSocket, (struct sockaddr *)&serverAddr, (socklen_t*)&addr_size);
             if (new_socket > 0)
             {
                 emit dataP->RemoteModeOn();             
             }}
    

    dataprovider.h

    class DataProvider : public QObject
    {
    Q_OBJECT
    signals:
    void RemoteModeOn (void);
     }
    

    What I want is to call "AdvancedPage.qml" object in the same way that is done by "Advanced.qml" when I click with the mouse. I dont know how to tell the function RemoteModeOn to activate onClicked property of advanced.qml

    advanced.qml

    import QtQuick 2.4
    Item {
      id: advance
    width: 74
    height: 50
    Image {
        id: logo
        width: 74
        height: 50
        fillMode: Image.PreserveAspectFit
        source: {"images/Next.png"
        dataProvider.QRemoteModeOn}
    
        MouseArea {
            anchors.bottomMargin: 0
            anchors.topMargin: 0
            anchors.leftMargin: 0
            anchors.rightMargin: 0
            anchors.fill: parent
            onClicked: {
                   pageLoader.source = "AdvancedPage.qml";
            }
        }
    }
    }
    

    I hope this extract of code is enough to understand what I am trying to do.
    Thank you very much for your support.



  • @JaviRodriguez I'd suggest to go through the documentation referred before again. Pay attention to the C++ signal and how that signal is handled in QML


Log in to reply