Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. C++ / QML property problem
Forum Updated to NodeBB v4.3 + New Features

C++ / QML property problem

Scheduled Pinned Locked Moved Solved QML and Qt Quick
2 Posts 1 Posters 222 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • SPlattenS Offline
    SPlattenS Offline
    SPlatten
    wrote on last edited by SPlatten
    #1

    I thought I had this beat, I have an object, C++ prototype **staleData.h":

    class StaleData;
    typedef QString StaleDataMapKey;
    typedef QMap<StaleDataMapKey, StaleData*> StaleDataMap;
    
    class StaleData : public QObject {
        Q_OBJECT
        Q_PROPERTY(bool interface READ interfaceStatus WRITE setInterfaceStatus)
        Q_PROPERTY(double value READ dataValue WRITE setValue NOTIFY toolTip)
        Q_PROPERTY(double timeout READ timeoutValue WRITE setTimeout)
        Q_PROPERTY(double tolerance READ toleranceValue WRITE setTolerance)
    
    private:
        bool mblnDeviceSts, mblnInitd, mblnIsStale;
        double mdblEpoch, mdblTimeout, mdblTolerance, mdblValue;  
    
        static const double mscdblDefaultTimeout;
        static StaleDataMap msStaleData; //All configured stale data types
    
    public:
        explicit StaleData(QObject *parent_ = nullptr);
    
        static StaleDataMap* pGetAllstaledata()
            { return &StaleData::msStaleData; }
        static StaleData* pFindData(QString strUDT)
        {
            StaleDataMap::const_iterator citFound(StaleData::msStaleData.find(strUDT));
            if ( citFound != StaleData::msStaleData.constEnd() ) {
                return citFound.value();
            }
            return nullptr;
        }
    
        Q_INVOKABLE bool canbeStale(QString strUDT)
        {
            StaleData* pobjStaleData(StaleData::pFindData(strUDT));
            if ( pobjStaleData != nullptr ) {
                return true;
            }
            return false;
        }
        double& dataValue() { return mdblValue; }
        Q_INVOKABLE bool interfaceStatus() { return mblnDeviceSts; }
        Q_INVOKABLE void setInterfaceStatus(bool blnEN) { mblnDeviceSts = blnEN; }
        Q_INVOKABLE void setTimeout(double dblTimeout) { mdblTimeout = dblTimeout; }
        Q_INVOKABLE void setTimeout(QString strTimeout);
        Q_INVOKABLE void setTolerance(double dblTolerance) { mdblTolerance = dblTolerance; }
        Q_INVOKABLE void setUDT(QString strUDT);
        Q_INVOKABLE void setValue(double dblValue);
        Q_INVOKABLE bool stale();
        Q_INVOKABLE QString strNormal();
        Q_INVOKABLE QString strStale();
        double& timeoutValue()  { return mdblTimeout; }
        double& timestamp() { return mdblEpoch; }
        double& toleranceValue() { return mdblTolerance; }
    
    signals:
        void dataIsOk(const QString cstrColour);
        void dataIsStale(const QString cstrColour);
        void toolTip(QString strToolTip);
    
    public slots:
    };
    

    Implementation:

    #include <float.h>
    #include <math.h>
    
    #include <QDateTime>
    
    #include "staleData.H"
    
    //Static initialisation
    static const long sclngSecsInMin(60);
    static const long sclngMinsInHour(60);
    static const long sclngHoursInDay(24);
    static const long sclngMsInSec(1000);
    static const long sclngMsInMin(sclngSecsInMin * sclngMsInSec);
    static const long sclngMsInHour(sclngMinsInHour * sclngMsInMin);
    static const long sclngMsInDay(sclngHoursInDay * sclngMsInHour);
    static const char scszDelim[](", ");
    static const char scszStale[]("#777777");
    static const char scszNormal[]("#000000");
    
    const double StaleData::mscdblDefaultTimeout(8000.0);
    StaleDataMap StaleData::msStaleData;
    /**
     * @brief staledata::StaleData
     * @param parent_   Optional pointer to parent
     */
    StaleData::StaleData(QObject* parent_) : QObject(parent_) {
        mblnDeviceSts = mblnInitd = mblnIsStale = false;
        mdblTimeout = StaleData::mscdblDefaultTimeout;
        mdblEpoch = mdblTolerance = mdblValue = 0.0;
    }
    /**
     * @brief StaleData::setTimeout
     * @param strTimeout    Timeout as time string HH:MM:SS.zzz
     */
    void StaleData::setTimeout(QString strTimeout) {
        QTime tmTimeout(QTime::fromString(strTimeout, Qt::TextDate));
        double dblTimeout(QTime(0,0).msecsTo(tmTimeout));
        setTimeout(dblTimeout);
    }
    /**
     * @brief StaleData::setValue
     * @param dblValue  Value to use
     */
    void StaleData::setValue(double dblValue) {
        if ( mdblTolerance > 0.0 && mblnInitd == true ) {
            double dblDiff = mdblValue - dblValue;
            if ( fabs(dblDiff) < mdblTolerance ) {
                return;
            }
        }
        mblnInitd = true;
        mblnDeviceSts = true;
        mdblValue = dblValue;
        mdblEpoch = QDateTime::currentMSecsSinceEpoch();
        //Update stale age
        stale();
    }
    /**
     * @brief StaleData::setUDT
     * @param strUDT User Defined Tag to unqiuely identify data
     */
    void StaleData::setUDT(QString strUDT) {
        StaleData* pobjNew(StaleData::pFindData(strUDT));
        if ( pobjNew != nullptr ) {
        //Already defined, no action required
            return;
        }
        StaleData::msStaleData.insert(strUDT, new StaleData());
    }
    /**
     * @brief StaleData::stale
     * @return true if data is stale else false
     */
    bool StaleData::stale() {
        double dblAge(QDateTime::currentMSecsSinceEpoch());
        QString strToolTip;
        bool blnStale;
        if ( mdblEpoch > 0.0 ) {
            dblAge -= mdblEpoch;
        }
        blnStale = dblAge >= mdblTimeout;
    
        if ( blnStale == true ) {
            long lngRemaining(static_cast<long>(dblAge));
            long lngDays(lngRemaining / sclngMsInDay);
            lngRemaining -= (lngDays * sclngMsInDay);
            long lngHours(lngRemaining / sclngMsInHour);
            lngRemaining -= (lngHours * sclngMsInHour);
            long lngMins(lngRemaining / sclngMsInMin);
            lngRemaining -= (lngMins * sclngMsInMin);
            long lngSecs(lngRemaining / sclngMsInSec);
            lngRemaining -= (lngSecs * sclngMsInSec);
            long lngMS(lngRemaining);
    
            if ( lngDays > 0 ) {
                strToolTip.append(QString("%1 days").arg(lngDays));
            }
            if ( lngHours > 0 ) {
                if ( strToolTip.isEmpty() != true ) {
                    strToolTip.append(scszDelim);
                }
                strToolTip.append(QString("%1 hours").arg(lngHours));
            }
            if ( lngMins > 0 ) {
                if ( strToolTip.isEmpty() != true ) {
                    strToolTip.append(scszDelim);
                }
                strToolTip.append(QString("%1 minutes").arg(lngMins));
            }
            if ( strToolTip.isEmpty() != true ) {
                strToolTip.append(scszDelim);
            }
            strToolTip.append(QString("%1").arg(lngSecs));
    
            if ( lngMS > 0 ) {
                const QString cstrZeros("000");
                const int cintMSlength(cstrZeros.length());
                QString strMS(QString::number(lngMS));
                int intLength(strMS.length());
                if ( intLength < cintMSlength ) {
                    strMS = cstrZeros.mid(0, cintMSlength - intLength) + strMS;
                }
                strToolTip.append(QString(".%1").arg(strMS));
            }
            strToolTip.append(" seconds");
    
            if ( mblnDeviceSts != true ) {
                strToolTip.prepend("IF down: ");
            } else {
                strToolTip.prepend("Stale: ");
            }
        }
        emit toolTip(strToolTip);
    
        if ( mblnIsStale != blnStale ) {
        //Change in state
            if ( blnStale == true ) {
                emit dataIsStale(strStale());
            } else {
                emit dataIsOk(strNormal());
            }
        //Save state
            mblnIsStale = blnStale;
        }
        return blnStale;
    }
    /**
     * @brief StaleData::strNormal
     * @return RGB colour code for normal data representation
     */
    QString StaleData::strNormal() {
        return QString(scszNormal);
    }
    /**
     * @brief StaleData::strStale
     * @return RGB colour code for stale data representation
     */
    QString StaleData::strStale() {
        return QString(scszStale);
    }
    

    In the C++ main:

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlExtensionPlugin>
    
    #include <string>
    #include <qqml.h>
    
    #include "staleData.H"
    
    using namespace std;
    
    int main(int argc, char *argv[]) {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
        QQmlApplicationEngine engine;
    
        qmlRegisterType<StaleData> ("SimonsCPP", 1, 0, "StaleData");
    
        const QUrl url(QStringLiteral("qrc:/stage.qml"));
        setenv ("TZ", "UTC", 1);
    
        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();
    }
    

    DataModel.qml:

    import QtQuick 2.0
    import QtQuick.Controls 2.15
    import QtQuick.Controls.Styles 1.4
    import QtQuick.Layouts 1.3
    import QtQuick.Extras 1.4
    import QtQuick.Window 2.12
    import SimonsCPP 1.0
    
    Item {
        id: root
        property var fontPointSize: 16
        property string label
        property string labelColor: "#FF000000"
        property var percision: 4
        property real textWidth: 80
        property real timeout: 5000
        property string units
        property string unitsColor: "#FF000000"
        property var value: simonP.value
    
        function checkStaleStatus(strToolTip) {
            if ( typeof strToolTip != "string" ) {
                simonP.stale()
                return
            }
            rImage.ToolTip.text = strToolTip
            rImage.visible = (strToolTip.length > 0)
    
            if ( rImage.visible === true ) {
                rText.color = simonP.strStale()
            } else {
                rText.color = simonP.strNormal()
            }
        }
        function rad2Deg(rad_) {
          return rad_ * 180.0 /  Math.PI;
        }
        function setPrecision (val_, sigDigit_) {
            var precision = Math.abs (val_) >= 1000 ? sigDigit_ - 4 :
            Math.abs (val_) >= 100 ? sigDigit_ - 3 :
            Math.abs (val_) >= 10 ? sigDigit_ - 2 :
            2;
    
          if (precision < 0)
            precision = 0;
    
          return val_.toFixed (precision);
        }
        function setValue(newValue) {
            simonP.setValue(newValue)
            rText.text = setPrecision (newValue, percision)
        }
    
        StaleData {
            id: simonP
            onValueChanged: {
                if ( simonP.canbeStale(rLabel.text) !== true ) {
                    return;
                }
                checkStaleStatus(strToolTip)
            }
        }
    
        Label {
            id: rUnits
            anchors.top: root.top
            anchors.right: root.right
            text: {
                root.units
                    ? String(root.units)
                    : ""
            }
            verticalAlignment: Text.AlignTop
            horizontalAlignment: Text.AlignRight
            font.pointSize: root.fontPointSize
            color: root.unitsColor
            visible: root.units && root.units.length > 0
        }
    
        Text {
            id: rText
            anchors {
                right: root.right
                rightMargin: rUnits.visible ? 24 : 0
                top: root.top
            }
            verticalAlignment: Text.AlignTop
            horizontalAlignment: Text.AlignRight
            font.pointSize: 16//root.fontPointSize
            text: {
                root.value
                    ? String(setPrecision (rad2Deg (root.value), percision))
                    : "N/A"
            }
            width: root.textWidth
        }
    
        Label {
            id: rLabel
            text: root.label
            anchors.top: rText.bottom
            anchors.right: rText.right
            verticalAlignment: Text.AlignTop
            horizontalAlignment: Text.AlignRight
            font.pointSize: root.fontPointSize
            color: root.labelColor
        }
    
        Image {
            id: rImage
            anchors {
                right: rLabel.left
                verticalCenter: rLabel.verticalCenter
            }
            visible:  simonP.canbeStale(rLabel.text)
            source: "/stale.svg"
            ToolTip.visible: (rImage.visible && ToolTip.text.length) > 0 && ma.containsMouse
    
            MouseArea {
                id: ma
                anchors.fill: parent
                hoverEnabled: true
                onContainsMouseChanged: checkStaleStatus()
            }
        }
    
        Timer {
            interval: 250
            running: true
            repeat: true
            onTriggered: simonP.stale()
        }
    
        Component.onCompleted: {
            simonP.setUDT("DPT");
        }
    }
    

    Stale.qml:

    import QtQuick 2.0
    import QtQuick.Controls 2.15
    import QtQuick.Controls.Styles 1.4
    import QtQuick.Layouts 1.3
    import QtQuick.Extras 1.4
    import QtQuick.Window 2.12
    import SimonsCPP 1.0
    
    Window {
        id: root
        visible: true
        width: 640
        height: 480
        title: qsTr("Playground / Staging Arena")
    
        Button {
            id: btnDptData
            text: "DPT Click Me..."
            anchors {
                left: parent.left
                top: parent.top
            }
            onClicked: {
                dpt.setValue(parseFloat(dpt.value) + 0.01);
            }
        }
        Button {
            id: btnHdgData
            text: "HDG Click Me..."
            anchors {
                left: btnDptData.left
                top: btnDptData.bottom
                topMargin: 4
            }
            onClicked: {
                hdg.setValue(parseFloat(hdg.value) + 0.01);
            }
        }
        Button {
            id: btnDeviceFailure
            text: "Set Device Failure"
            enabled: true
            anchors {
                left: btnDptData.left
                top: btnHdgData.bottom
                topMargin: 4
            }
            onClicked: {
                dpt.interface = false;
                hdg.interface = false;
                btnDeviceFailure.enabled = false
            }
        }
        Button {
            id: btnClearDevFailure
            text: "Clr Device Failure"
            enabled: (btnDeviceFailure.enabled == true) ? false : true
            anchors {
                left: btnDptData.left
                top: btnDeviceFailure.bottom
                topMargin: 4
            }
            onClicked: {
                dpt.interface = true;
                hdg.interface = true;
                btnDeviceFailure.enabled = true
            }
        }
    
        DataModel {
            id: dpt
            label: "DPT"
            units: "M"
            anchors {
                right: parent.right
                top: parent.top
            }
            fontPointSize: 18
            textWidth: 256
        }
        DataModel {
            id: hdg
            label: "HDG"
            units: "\xB0"
            anchors {
                right: parent.right
                top: parent.top
                topMargin: 80
            }
            fontPointSize: dpt.fontPointSize
            textWidth: dpt.textWidth
        }
    }
    

    When I click the button btnDeviceFailure, I see in QML Debugger Console:

    qrc:/stage.qml:49: Error: Cannot assign to non-existent property "interface" qrc:/stage.qml: 49
    

    What haven't I done or what have I done wrong?

    Kind Regards,
    Sy

    1 Reply Last reply
    0
    • SPlattenS Offline
      SPlattenS Offline
      SPlatten
      wrote on last edited by
      #2

      Added to DataModel.qml:

      property alias api: simonP
      

      And implemented in stale.qml:

                  dpt.api.setInterfaceStatus(false);
                  hdg.api.setInterfaceStatus(false);
      

      Kind Regards,
      Sy

      1 Reply Last reply
      0

      • Login

      • Login or register to search.
      • First post
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved