Parduz
Parduz

Reputation: 594

QT5 QML-C++ binding and interaction: what am i doing wrong?

I am trying to get how to show C++ variables on a QML window, and also how to hande C++ properties or call C++ functions from there. While i'm trying to follow various tutorials (most of them linked in other questions at this site) i can't get it work....

The "big plan" is that i would like to have a class UI_Updater which will expose to the QML all the data i want to show, and have the "real" app working underline (it will gather data using a socket or a serial).

Right now, all i want is to show 3 integers, changing them with a timer and/or by a couple of menu items. This is all my code and the errors i get:

ui_updater.h

#ifndef UI_UPDATER_H
#define UI_UPDATER_H

#include <QObject>
#include <QtGui>

class UI_Updater : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int countEjT
               READ getcountEjT
               WRITE setcountEjT
               NOTIFY countEjTChanged
    )

    Q_PROPERTY(int countEjF
               READ getcountEjF
               WRITE setcountEjF
               NOTIFY countEjFChanged
    )

    Q_PROPERTY(int countEjFNC
               READ getcountEjFNC
               WRITE setcountEjFNC
               NOTIFY countEjFNCChanged
    )

public:
    explicit UI_Updater(QObject *parent = 0);

    int getcountEjT();
    int getcountEjF();
    int getcountEjFNC();
    void setcountEjT(int NewVal);
    void setcountEjF(int NewVal);
    void setcountEjFNC(int NewVal);

    Q_INVOKABLE void increment(int i);

signals:
    void countEjTChanged();
    void countEjFChanged();
    void countEjFNCChanged();

public slots:
    void testSlot();

private:
    int _countEjT;
    int _countEjF;
    int _countEjFNC;

    QTimer *myTimer;

    void OnTimeout();
};

#endif // UI_UPDATER_H

ui_updater.cpp

#include "ui_updater.h"

UI_Updater::UI_Updater(QObject *parent) :
    QObject(parent)
{
    myTimer = new QTimer(this);
    myTimer->start(1000);
    connect(myTimer, SIGNAL(timeout()), this, SLOT(testSlot()));

    _countEjT = _countEjF = _countEjFNC = 0;
    emit countEjTChanged();
    emit countEjFChanged();
    emit countEjFNCChanged();

}

void UI_Updater::setcountEjT(int NewVal)
{
    _countEjT = NewVal;
    emit countEjTChanged();
}

void UI_Updater::setcountEjF(int NewVal)
{
    _countEjF = NewVal;
    emit countEjFChanged();
}

void UI_Updater::setcountEjFNC(int NewVal)
{
    _countEjFNC = NewVal;
    emit countEjFNCChanged();
}


int UI_Updater::getcountEjT()
{
    return _countEjT;
}

int UI_Updater::getcountEjF()
{
    return _countEjF;
}

int UI_Updater::getcountEjFNC()
{
    return _countEjFNC;
}


void UI_Updater::OnTimeout()
{
    increment(0);
}

void UI_Updater::increment(int i)
{
    if (i==0) {
        ++_countEjT;
    }else{
        _countEjT+=i;
    }
    emit countEjTChanged();

    if (_countEjT%2 == 0) {
        ++_countEjF;
        emit countEjFChanged();
    }
    if (_countEjF%4 == 0) {
        ++_countEjFNC;
        emit countEjFNCChanged();
    }

}

void UI_Updater::testSlot()
{
    increment(2);
}

main.cpp

#include <QtGui/QGuiApplication>
#include <QQmlApplicationEngine>

#include <QtQml>
#include <QtQuick/QQuickView>   // Necessario per QQuickWindow

#include "ui_updater.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);


    qmlRegisterType<UI_Updater>("Phase.UI_Updater", 1, 0, "UI_Updater");
    QQmlApplicationEngine engine(QUrl("qrc:/qml/MainForm.qml"));

    QObject *topLevel = engine.rootObjects().value(0);
    QQuickWindow *window = qobject_cast<QQuickWindow *>(topLevel);
    if ( !window ) {
        qWarning("Error: Your root item has to be a Window.");
        return -1;
    }
    window->show(); // Evita di dover settare visible: true nel file qml
    return app.exec();
}

The QML windows is meant to have multiple pages. I'll copy the code only of the relevant one:

MainForm.qml

import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Window 2.1
import Phase.UI_Updater 1.0

ApplicationWindow {
    id: screen
    width: 480
    height: 272
    //toolBar: {

    //}
    property int partition: height / 3

    title: qsTr("Main Screen")

    menuBar: MenuBar {
        Menu {
            title: qsTr("File")
            MenuItem {
                text: qsTr("Exit")
                onTriggered: Qt.quit();
            }
        }
        Menu {
            title: qsTr("Pages")
            MenuItem {
                text: qsTr("Working")
                onTriggered: currentPage = "pagWorking";
            }
            MenuItem {
                text: qsTr("Graphics")
                onTriggered: currentPage = "pagGraphics";
            }
            MenuItem {
                text: qsTr("Setup")
                onTriggered: currentPage = "pagSetup";
            }
            // These items should increment the variables....
            MenuItem {
                text: qsTr("Add One")
                onTriggered: UI_Updater.countEjT = UI_Updater.countEjT + 1;
            }
            MenuItem {
                text: qsTr("Increment")
                onTriggered: UI_Updater.increment(1);
            }
        }
    }

    // GESTIONE PAGINE ---------------------------------------------------------
    property variant pagesList: [
        "pagWorking",
        "pagSetup",
        "pagGraphics"
    ];
    property string currentPage: "pagWorking";

    Repeater {
        model: pagesList;
        delegate: Loader {
            id: pageLoader
            x: 0
            y: 0
            anchors.rightMargin: 0
            anchors.bottomMargin: 0
            anchors.leftMargin: 0
            anchors.topMargin: 0
            active: false;
            asynchronous: true;
            anchors.fill: parent;
            visible: (currentPage === modelData);
            source: "%1.qml".arg(modelData);
            onVisibleChanged: { loadIfNotLoaded(); }
            Component.onCompleted: { loadIfNotLoaded(); }

            function loadIfNotLoaded() {
                // Carica la pagina la prima volta che ce n'è bisogno
                if (visible && !active) {
                    active = true;
                }
            }
        }   // Fine Loader
    }

    UI_Updater {
        countEjT: 0;
        countEjF: 0;
        countEjFNC: 0;
    }

    // Fine GESTIONE PAGINE ------------------------------------------------
}

pagWorking.qml

import QtQuick 2.0
import QtQuick.Controls 1.0
import Phase.UI_Updater 1.0

Rectangle {
    id: pagBackground
    width: 400
    height: 250
    gradient: Gradient {
        GradientStop {
            position: 0
            color: "#010036"
        }

        GradientStop {
            position: 1
            color: "#08006b"
        }
    }

    Label {
        id: lPagTitle
        x: 0
        width: parent.width
        height: 20
        color: "#0e909c"
        text: "Working"
        font.bold: true
        font.pointSize: 12
        verticalAlignment: Text.AlignVCenter
        horizontalAlignment: Text.AlignHCenter
        anchors.top: parent.top
        anchors.topMargin: 0
    }

    GroupBox {
        id: group_box1
        x: 133
        width: 120
        height: 100
        anchors.top: parent.top
        anchors.topMargin: 20
        anchors.horizontalCenter: parent.horizontalCenter

        Text {
            id: tTachimetro
            x: 162
            y: 50
            width: 100
            height: 30
            color: "#d0d0ff"
            text: qsTr("000.0°")
            anchors.verticalCenterOffset: 5
            anchors.verticalCenter: parent.verticalCenter
            font.bold: true
            style: Text.Normal
            verticalAlignment: Text.AlignVCenter
            font.pixelSize: 25
            anchors.horizontalCenter: parent.horizontalCenter
            horizontalAlignment: Text.AlignHCenter
        }

        Text {
            id: labTach
            x: 149
            y: 30
            width: 100
            height: 20
            color: "#a0a0ff"
            text: qsTr("Position")
            anchors.verticalCenterOffset: -20
            anchors.verticalCenter: parent.verticalCenter
            anchors.horizontalCenterOffset: 0
            verticalAlignment: Text.AlignVCenter
            font.pixelSize: 15
            anchors.horizontalCenter: parent.horizontalCenter
            horizontalAlignment: Text.AlignHCenter
        }
    }

    GroupBox {
        id: group_box2
        x: 204
        width: 200
        height: 74
        anchors.horizontalCenterOffset: 100
        anchors.horizontalCenter: parent.horizontalCenter
        anchors.top: parent.top
        anchors.topMargin: 120

        Text {
            id: tET
            x: 147
            y: 0
            width: 110
            height: 20
            color: "#ff8000"
            text: UI_Updater.countEjT.toString();
            anchors.right: parent.right
            anchors.rightMargin: 0
            anchors.top: parent.top
            anchors.topMargin: 0
            font.bold: true
            style: Text.Normal
            verticalAlignment: Text.AlignVCenter
            font.pixelSize: 16
            horizontalAlignment: Text.AlignRight
        }

        Text {
            id: labET
            x: 11
            width: 70
            height: 20
            color: "#a0a0ff"
            text: qsTr("Ej T")
            anchors.top: parent.top
            anchors.topMargin: 0
            anchors.right: parent.right
            anchors.rightMargin: 110
            verticalAlignment: Text.AlignVCenter
            font.pixelSize: 16
            horizontalAlignment: Text.AlignRight
        }


        Text {
            id: tEF
            x: 130
            y: 21
            width: 110
            height: 20
            color: "#ff8000"
            text: UI_Updater.countEjF.toString();
            anchors.top: parent.top
            font.bold: true
            font.pixelSize: 16
            verticalAlignment: Text.AlignVCenter
            style: Text.Normal
            anchors.rightMargin: 0
            anchors.right: parent.right
            anchors.topMargin: 21
            horizontalAlignment: Text.AlignRight
        }
        Text {
            id: labEF
            x: 60
            y: 21
            width: 70
            height: 20
            color: "#a0a0ff"
            text: qsTr("Ej F")
            anchors.top: parent.top
            font.pixelSize: 16
            verticalAlignment: Text.AlignVCenter
            anchors.rightMargin: 110
            anchors.right: parent.right
            anchors.topMargin: 21
            horizontalAlignment: Text.AlignRight
        }

        Text {
            id: tENCF
            x: 130
            y: 40
            width: 110
            height: 20
            color: "#ff8000"
            text: UI_Updater.countEjFNC.toString();
            anchors.top: parent.top
            font.bold: true
            anchors.rightMargin: 0
            style: Text.Outline
            verticalAlignment: Text.AlignVCenter
            font.pixelSize: 16
            anchors.right: parent.right
            anchors.topMargin: 42
            horizontalAlignment: Text.AlignRight
        }

        Text {
            id: labENCF
            x: 60
            y: 40
            width: 70
            height: 20
            color: "#a0a0ff"
            text: qsTr("Ej FNC")
            anchors.top: parent.top
            anchors.rightMargin: 110
            verticalAlignment: Text.AlignVCenter
            font.pixelSize: 16
            anchors.right: parent.right
            anchors.topMargin: 42
            horizontalAlignment: Text.AlignRight
        }
    }

    property bool shown: false;

    state: "NASCOSTO";

    onVisibleChanged:  {
        if (visible === false)
            pagBackground.state = "NASCOSTO"
        else if (visible === true)
            pagBackground.state = "VISIBILE"
    }
    states: [
        State {
            name: "VISIBILE"
            PropertyChanges { target: pagBackground; opacity: 1 }
        }
        ,
        State {
            name: "NASCOSTO"
            PropertyChanges { target: pagBackground; opacity: 0 }
        }
    ]
    //! [states]

    //! [transitions]
        transitions: [
            Transition {
                to: "NASCOSTO"
                NumberAnimation { properties: "opacity"; duration: 1500; easing.type: Easing.OutExpo }
            }
            ,
            Transition {
                to: "VISIBILE"
                NumberAnimation { properties: "opacity"; duration: 1500; easing.type: Easing.OutExpo }
            }

        ]
    //! [transitions]
}

Now, it builds, but when i run i get:

TypeError: Cannot call method 'toString' to undefned

and clicking on the menu item "Increment i get

TypeError: object [object Object] has no method 'increment'

Now seems to me that my object UI_Updater was'nt really istanciated.... even if the editor sees it.

What should i do?

Thanks

Upvotes: 0

Views: 1050

Answers (1)

epsilon
epsilon

Reputation: 2969

The error message seem quite clear, you are not invoking increment on an UI_Updater instance but on UI_Updater type.

Try to put an id on your UI_Updater, then you can call methods declare by your UI_Updater C++ class. Here a little update in your MainForm.qml :

import QtQuick 2.1
import QtQuick.Controls 1.0
import QtQuick.Window 2.1
import Phase.UI_Updater 1.0

ApplicationWindow {
    id: screen
    width: 480
    height: 272
    //toolBar: {

    //}
    property int partition: height / 3

    title: qsTr("Main Screen")

    menuBar: MenuBar {
        Menu {
            title: qsTr("File")
            MenuItem {
                text: qsTr("Exit")
                onTriggered: Qt.quit();
            }
        }
        Menu {
            title: qsTr("Pages")
            MenuItem {
                text: qsTr("Working")
                onTriggered: currentPage = "pagWorking";
            }
            MenuItem {
                text: qsTr("Graphics")
                onTriggered: currentPage = "pagGraphics";
            }
            MenuItem {
                text: qsTr("Setup")
                onTriggered: currentPage = "pagSetup";
            }
            // These items should increment the variables....
            MenuItem {
                text: qsTr("Add One")
                onTriggered: updater.countEjT = updater.countEjT + 1;
            }
            MenuItem {
                text: qsTr("Increment")
                onTriggered: updater.increment(1);
            }
        }
    }

    // GESTIONE PAGINE ---------------------------------------------------------
    property variant pagesList: [
        "pagWorking",
        "pagSetup",
        "pagGraphics"
    ];
    property string currentPage: "pagWorking";

    Repeater {
        model: pagesList;
        delegate: Loader {
            id: pageLoader
            x: 0
            y: 0
            anchors.rightMargin: 0
            anchors.bottomMargin: 0
            anchors.leftMargin: 0
            anchors.topMargin: 0
            active: false;
            asynchronous: true;
            anchors.fill: parent;
            visible: (currentPage === modelData);
            source: "%1.qml".arg(modelData);
            onVisibleChanged: { loadIfNotLoaded(); }
            Component.onCompleted: { loadIfNotLoaded(); }

            function loadIfNotLoaded() {
                // Carica la pagina la prima volta che ce n'è bisogno
                if (visible && !active) {
                    active = true;
                }
            }
        }   // Fine Loader
    }

    UI_Updater {
        id: updater
        countEjT: 0;
        countEjF: 0;
        countEjFNC: 0;
    }

    // Fine GESTIONE PAGINE ------------------------------------------------
}

Upvotes: 1

Related Questions