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. QML Property behaviour
Forum Updated to NodeBB v4.3 + New Features

QML Property behaviour

Scheduled Pinned Locked Moved Solved QML and Qt Quick
13 Posts 5 Posters 1.9k 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.
  • ODБOïO Offline
    ODБOïO Offline
    ODБOï
    wrote on last edited by
    #4

    hi @Mr.-Dollbohrer

    i guess you are breaking the binding when you change the value from QML code.
    You can create a c++ method inside your counter class, and use that method to change the value from your QML code

    // counter.qml
    Window {
        visible: true
        width: 640
        height: 480
        Text {
            id:lab
            anchors.centerIn: parent
            text: counter.cpt
        }
        Button{
            text: "set value to -666"
          //  onClicked: {lab.text = -666} 
            onClicked: {counter.setQmlValue(-666)} // c++ method
        }
    }
    //  Counter.h
    #ifndef COUNTER_H
    #define COUNTER_H
    #include <QObject>
    #include <QTimer>
    class Counter : public QObject
    {
        Q_OBJECT
    
        Q_PROPERTY(int cpt READ cpt WRITE setCpt NOTIFY cptChanged)
    
    public:
        explicit Counter(QObject *parent = nullptr);
        Q_INVOKABLE void setQmlValue(const int &qmlVal){    
            setCpt(qmlVal);
        }
    
    int cpt() const
    {
        return m_cpt;
    }
    void setCpt(int cpt)
    {
        if (m_cpt == cpt)
            return;
        m_cpt = cpt;
        emit cptChanged(m_cpt);
    }
    signals:
        void cptChanged(int);
    private :
        int m_cpt=0;
        QTimer *countTimer;
        void incrementCount(){setCpt(m_cpt+1);}
    };
    #endif // COUNTER_H
    #include "counter.h"
    //Counter.cpp
    Counter::Counter(QObject *parent) : QObject(parent)
    {
        countTimer = new QTimer(this);
        QObject::connect(countTimer,&QTimer::timeout,this,Counter::incrementCount);
        countTimer->start(1000);
    }
    //main.cpp
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    #include "counter.h"
    
    int main(int argc, char *argv[])
    
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
        QQmlApplicationEngine engine;
    
        Counter cpt;
        engine.rootContext()->setContextProperty("counter",&cpt);
    
        engine.load(QUrl(QStringLiteral("qrc:/counter.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
        return app.exec();
    }
    
    
    1 Reply Last reply
    6
    • M Offline
      M Offline
      Mr. Dollbohrer
      wrote on last edited by
      #5

      Helly everyone and thanks a lot for your answers.

      I understand that the behaviour of my code is not following a concept/design so the problem must lie in my code. Here is a simple example that follows the design of my project and illustrates the loss of binding from c++.

      If you start the project, c++ modifies the value every second. But once you click on the qml button, there will never be any update from c++ anymore.

      //object.h
      #ifndef OBJECT_H
      #define OBJECT_H
      
      #include <QObject>
      
      class object : public QObject
      {
          Q_OBJECT
      
          Q_PROPERTY(double value READ getValue WRITE setValue NOTIFY valueChanged)
      
      private:
          double value;
      
      public:
          object(double);
          double getValue();
          void setValue(double);
      
      signals:
          void valueChanged();
      
      };
      
      #endif // OBJECT_H
      
      //object.cpp
      #include "object.h"
      
      object::object(double init)
      {
          value = init;
      }
      
      double object::getValue(){
          return value;
      }
      
      void object::setValue(double newValue){
          value = newValue;
          emit valueChanged();
      }
      
      //backend.h
      #ifndef BACKEND_H
      #define BACKEND_H
      
      #include <QObject>
      #include "object.h"
      
      class backend : public QObject
      {
      
          Q_OBJECT
      
      public:
          backend();
          QList<QObject*> objectList;
      
      public slots:
          void modifyValue();
      
      private:
          double anotherValue;
      
      };
      
      #endif // BACKEND_H
      
      //backend.cpp
      #include <QTimer>
      #include "backend.h"
      
      backend::backend()
      {
          anotherValue = 1.23;
      
          objectList.append(new object(anotherValue));
          objectList.append(new object(4.56));
          objectList.append(new object(7.89));
      
          QTimer *timer = new QTimer(this);
          QObject::connect(timer, SIGNAL(timeout()), this, SLOT(modifyValue()));
          timer->start(1000);
      }
      
      void backend::modifyValue()
      {
          anotherValue += 1;
          qobject_cast<object *>(objectList.at(0))->setValue(anotherValue);
      }
      
      //main.cpp
      #include <QGuiApplication>
      #include <QQmlApplicationEngine>
      #include <QQmlContext>
      #include <backend.h>
      
      int main(int argc, char *argv[])
      {
          QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
      
          QGuiApplication app(argc, argv);
      
          backend myBackend;
      
          QQmlApplicationEngine engine;
      
          QQmlContext *ctxt = engine.rootContext();
              ctxt->setContextProperty("objectModel", QVariant::fromValue(myBackend.objectList));
      
          engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
          if (engine.rootObjects().isEmpty())
              return -1;
      
          return app.exec();
      }
      
      //main.qml
      import QtQuick 2.12
      import QtQuick.Window 2.12
      
      Window {
          visible: true
          width: 640
          height: 480
          title: qsTr("Hello World")
      
          Component {
              id: objectDelegate
              Item {
                  height: 100
                  width: 300
      
                  ObjectModifier {
                      id: objectModifier
                      myValue: model.modelData.value
                  }
      
              }
          }
      
          ListView {
              id: objectView
              anchors.fill: parent
              model: objectModel
              delegate: objectDelegate
              clip: true
          }
      }
      
      //ObjectModifier.qml
      import QtQuick 2.0
      import QtQuick.Layouts 1.12
      
      Item {
      
          property real myValue: 0.0
      
          anchors.fill: parent
      
          ColumnLayout{
              spacing: 2
      
              Rectangle {
                  height: 50
                  width: 250
                  border.width: 3
                  radius: 5
                  Text{
                      anchors.fill: parent
                      text: "Value: "+myValue
                      horizontalAlignment: "AlignHCenter"
                      verticalAlignment: "AlignVCenter"
                      color: "black"
      
                  }
              }
      
              Rectangle {
                  height: 50
                  width: 250
                  color: "gray"
                  Text{
                      anchors.fill: parent
                      text: "Modify object"
                      horizontalAlignment: "AlignHCenter"
                      verticalAlignment: "AlignVCenter"
                      color: "white"
      
                  }
                  MouseArea {
                      id: theClickBox
                      anchors.fill: parent
                      onClicked: {
                          myValue = myValue+0.01
                      }
                  }
              }
          }
      
      }
      
      1 Reply Last reply
      0
      • fcarneyF Offline
        fcarneyF Offline
        fcarney
        wrote on last edited by
        #6

        @Mr.-Dollbohrer said in QML Property behaviour:

        If you start the project, c++ modifies the value every second. But once you click on the qml button, there will never be any update from c++ anymore.

        This is because you are breaking your binding.
        Here you define your binding:

                    ObjectModifier {
                        id: objectModifier
                        myValue: model.modelData.value
                    }
        

        Here you break it:

                        onClicked: {
                            myValue = myValue+0.01
                        }
        

        You are binding a new value to myValue.

        Your approach is off to begin with. Use the built in roles (search role to find info about index and modelData) that are exposed to delegates of a ListView. They are index and modelData. You could use modelData in place of myValue and it would work. However your model is suspect.

        ctxt->setContextProperty("objectModel", QVariant::fromValue(myBackend.objectList));
        

        The docs say it returns a QVariant containing a copy of the data. I am not sure this is what you want. Especially if you intend to work with this data in the backend. If you want to expose a list properly then you need to use QAbstractListModel and derive a proper list class from that.

        Here is more info on doing that. The cool part of building the list model from the abstract class is you get to define the role names yourself. You can make them a lot more relevant to your task.

        C++ is a perfectly valid school of magic.

        1 Reply Last reply
        1
        • fcarneyF Offline
          fcarneyF Offline
          fcarney
          wrote on last edited by
          #7

          Also, here is an example using default roles:
          main.cpp

          #include <QGuiApplication>
          #include <QQmlApplicationEngine>
          #include <QQmlContext>
          #include <QVector>
          
          int main(int argc, char *argv[])
          {
              QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
          
              QGuiApplication app(argc, argv);
          
              QQmlApplicationEngine engine;
          
              // simple list data
              QStringList slist;
              slist << "one";
              slist << "two";
              slist << "three";
              slist << "four";
              slist << "five";
          
              QQmlContext *ctxt = engine.rootContext();
              ctxt->setContextProperty("objectModel", slist);
          
              const QUrl url(QStringLiteral("qrc:/main.qml"));
              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();
          }
          

          main.qml:

          import QtQuick 2.12
          import QtQuick.Window 2.12
          
          Window {
              visible: true
              width: 640
              height: 480
              title: qsTr("QML List Roles")
          
              ListView {
                  id: objectView
                  anchors.fill: parent
                  model: objectModel
                  delegate: objectDelegate
                  clip: true
              }
          
              Component {
                  id: objectDelegate
                  Item {
                      height: 100
                      width: 300
          
                      ObjectModifier {
                          id: objectModifier
                      }
                  }
              }
          }
          

          Your ObjectModifier.qml updated with modelData usage:

          import QtQuick 2.0
          import QtQuick.Layouts 1.12
          
          Item {
          
              anchors.fill: parent
          
              ColumnLayout {
                  spacing: 2
          
                  Rectangle {
                      height: 50
                      width: 250
                      border.width: 3
                      radius: 5
                      Text{
                          anchors.fill: parent
                          text: "Value: "+modelData
                          horizontalAlignment: "AlignHCenter"
                          verticalAlignment: "AlignVCenter"
                          color: "black"
          
                      }
                  }
          
                  Rectangle {
                      height: 50
                      width: 250
                      color: "gray"
                      Text{
                          anchors.fill: parent
                          text: "Modify object"
                          horizontalAlignment: "AlignHCenter"
                          verticalAlignment: "AlignVCenter"
                          color: "white"
          
                      }
                      MouseArea {
                          id: theClickBox
                          anchors.fill: parent
                          onClicked: {
                              modelData = modelData+"x"
                          }
                      }
                  }
              }
          }
          

          C++ is a perfectly valid school of magic.

          M 1 Reply Last reply
          1
          • fcarneyF fcarney

            Also, here is an example using default roles:
            main.cpp

            #include <QGuiApplication>
            #include <QQmlApplicationEngine>
            #include <QQmlContext>
            #include <QVector>
            
            int main(int argc, char *argv[])
            {
                QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
            
                QGuiApplication app(argc, argv);
            
                QQmlApplicationEngine engine;
            
                // simple list data
                QStringList slist;
                slist << "one";
                slist << "two";
                slist << "three";
                slist << "four";
                slist << "five";
            
                QQmlContext *ctxt = engine.rootContext();
                ctxt->setContextProperty("objectModel", slist);
            
                const QUrl url(QStringLiteral("qrc:/main.qml"));
                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();
            }
            

            main.qml:

            import QtQuick 2.12
            import QtQuick.Window 2.12
            
            Window {
                visible: true
                width: 640
                height: 480
                title: qsTr("QML List Roles")
            
                ListView {
                    id: objectView
                    anchors.fill: parent
                    model: objectModel
                    delegate: objectDelegate
                    clip: true
                }
            
                Component {
                    id: objectDelegate
                    Item {
                        height: 100
                        width: 300
            
                        ObjectModifier {
                            id: objectModifier
                        }
                    }
                }
            }
            

            Your ObjectModifier.qml updated with modelData usage:

            import QtQuick 2.0
            import QtQuick.Layouts 1.12
            
            Item {
            
                anchors.fill: parent
            
                ColumnLayout {
                    spacing: 2
            
                    Rectangle {
                        height: 50
                        width: 250
                        border.width: 3
                        radius: 5
                        Text{
                            anchors.fill: parent
                            text: "Value: "+modelData
                            horizontalAlignment: "AlignHCenter"
                            verticalAlignment: "AlignVCenter"
                            color: "black"
            
                        }
                    }
            
                    Rectangle {
                        height: 50
                        width: 250
                        color: "gray"
                        Text{
                            anchors.fill: parent
                            text: "Modify object"
                            horizontalAlignment: "AlignHCenter"
                            verticalAlignment: "AlignVCenter"
                            color: "white"
            
                        }
                        MouseArea {
                            id: theClickBox
                            anchors.fill: parent
                            onClicked: {
                                modelData = modelData+"x"
                            }
                        }
                    }
                }
            }
            
            M Offline
            M Offline
            Mr. Dollbohrer
            wrote on last edited by Mr. Dollbohrer
            #8

            @fcarney

            Thanks so much for all that great help. I am basically going through the examples copying code and trying modify it with a lot of trial and error so that I sometimes dont really know why stuff work or doesnt work.

            Thanks to your explanations I now have a clue how binding works (only one binding is allowed, what makes sense now that im thinking about it) and that it is possible to directly write to the modeldata.

            Also Ill try to dive deeper into how to expose lists to qml. My "solution" was just another part I found in an example somewhere without really knowing what other options I have and what the advantages and disadvantages are.

            Again thanks for your time and all the great hints and help!!!

            1 Reply Last reply
            0
            • fcarneyF Offline
              fcarneyF Offline
              fcarney
              wrote on last edited by
              #9

              @Mr-Dollbohrer

              I am trying to see if there is a way to do a generic templated way to expose common QML QVector types to QML from C++. But for some reason my list values do not get updated properly even though the C++ values are getting changed and the signal is getting called.
              main.cpp:

              #include <QGuiApplication>
              #include <QQmlApplicationEngine>
              #include <QQmlContext>
              #include <QVector>
              #include <QAbstractListModel>
              #include <QDebug>
              
              template<class T>
              class VExposer : public QAbstractListModel
              {
              public:
                  explicit VExposer(QObject *parent = nullptr):QAbstractListModel(parent){}
              
                  int rowCount(const QModelIndex &parent = QModelIndex()) const override {
                      Q_UNUSED(parent);
                      return m_vector.count();
                  }
              
                  QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
                      int row = index.row();
              
                      if(row < 0 || row >= m_vector.count())
                          return QVariant();
              
                      if(role == Qt::DisplayRole)
                          return m_vector[row];
                      else
                          return QVariant();
                  }
              
                  bool setData(const QModelIndex &index, const QVariant &value, int role) override {
                      if (!hasIndex(index.row(), index.column(), index.parent()) || !value.isValid())
                          return false;
              
                      qInfo() << index.row() << value.toString();
              
                      T &item = m_vector[index.row()];
                      //if (role == Qt::DisplayRole){
                      if (role == Qt::EditRole){
                          item = value.toDouble();
                          qInfo() << m_vector[index.row()];
                      }
                      else
                          return false;
              
                      emit dataChanged(index, index, { role } );
              
                      return true ;
              
                  }
              
                  // hacky, don't do this for real app, create a bunch of functions for getting/setting values
                  QVector<T>& getVector(){return m_vector;}
              
              private:
                  QVector<T> m_vector;
              };
              
              void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles){
                  qInfo() << "dataChanged signal gets called!" << topLeft.row() << bottomRight.row();
              }
              
              int main(int argc, char *argv[])
              {
                  QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
              
                  QGuiApplication app(argc, argv);
              
                  QQmlApplicationEngine engine;    
              
                  VExposer<qreal> vex(&app);
                  auto& v_vex = vex.getVector();
                  v_vex << 1.0;
                  v_vex << 2.0;
                  v_vex << 3.0;
                  v_vex << 4.0;
                  v_vex << 5.0;
              
                  //qInfo() << vex.rowCount();
                  // proves signals work and get called
                  QObject::connect(&vex, &VExposer<qreal>::dataChanged, dataChanged);
              
                  QQmlContext *ctxt = engine.rootContext();
              
                  // make model available
                  ctxt->setContextProperty("objectModel", &vex);
              
                  const QUrl url(QStringLiteral("qrc:/main.qml"));
                  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();
              }
              //#include "main.moc"
              

              main.qml:

              import QtQuick 2.12
              import QtQuick.Window 2.12
              import QtQuick.Layouts 1.12
              
              Window {
                  visible: true
                  width: 640
                  height: 480
                  title: qsTr("QML List Roles")
              
                  ListView {
                      id: objectView
                      anchors.fill: parent
                      model: objectModel
                      delegate: objectDelegate
                      clip: true
                  }
              
                  Component {
                      id: objectDelegate
                      Item {
              
                          height: 100
                          width: 300
              
                          Item {
              
                              anchors.fill: parent
              
                              ColumnLayout {
                                  spacing: 2
              
                                  Rectangle {
                                      height: 50
                                      width: 250
                                      border.width: 3
                                      radius: 5
                                      Text{
                                          anchors.fill: parent
                                          text: "Value: %1".arg(display)
                                          horizontalAlignment: "AlignHCenter"
                                          verticalAlignment: "AlignVCenter"
                                          color: "black"
              
                                      }
                                  }
              
                                  Rectangle {
                                      height: 50
                                      width: 250
                                      color: "gray"
                                      Text{
                                          anchors.fill: parent
                                          text: "Modify object"
                                          horizontalAlignment: "AlignHCenter"
                                          verticalAlignment: "AlignVCenter"
                                          color: "white"
              
                                      }
                                      MouseArea {
                                          id: theClickBox
                                          anchors.fill: parent
                                          onClicked: {
                                              //display = display+0.01 // updates display
                                              edit = display+0.01 // changes value in variable, but does not update display
                                          }
                                      }
                                  }
                              }
                          }
                      }
                  }
              }
              

              I know I cannot template a class that has the Q_OBJECT macro in it. But you can template a class that derives from a QObject class. So I figured QAbstractListModel would be a good candidate. Everything works except the signal does not seem to update the ListView in QML properly.

              Does anyone know if I am missing something important here? I would love for this template to work as it would save time writing boiler plate code to expose vectors as models.

              C++ is a perfectly valid school of magic.

              1 Reply Last reply
              0
              • fcarneyF Offline
                fcarneyF Offline
                fcarney
                wrote on last edited by fcarney
                #10

                Dang it. I just realized value.toDouble() ruins the ability to template this... Darn, it was worth a try.
                Maybe not, I can check types. It will just take more work.

                C++ is a perfectly valid school of magic.

                1 Reply Last reply
                0
                • GrecKoG Offline
                  GrecKoG Offline
                  GrecKo
                  Qt Champions 2018
                  wrote on last edited by
                  #11

                  If you want a generic QAbstractItemModel from a list of objects deriving QObject, there is : QQmlObjectListModel from http://gitlab.unique-conception.org/qt-qml-tricks/qt-qml-models (some slides about it here : https://docs.google.com/presentation/d/13pkRav2Fks_AKTXfKtGTyBuc_RmkZGiDRQZTr4usQFk/pub?start=false&loop=false&delayms=3000 )

                  1 Reply Last reply
                  0
                  • fcarneyF Offline
                    fcarneyF Offline
                    fcarney
                    wrote on last edited by
                    #12

                    @GrecKo said in QML Property behaviour:

                    If you want a generic QAbstractItemModel from a list of objects deriving QObject

                    I saw that, but I was hoping to make a generic one for just types like double, int, QString. I will create a new post as I think I have one most of the way there, but for some reason it won't update the list values even though the signal is getting called.

                    C++ is a perfectly valid school of magic.

                    1 Reply Last reply
                    0
                    • GrecKoG Offline
                      GrecKoG Offline
                      GrecKo
                      Qt Champions 2018
                      wrote on last edited by
                      #13

                      Ah yes, I misread.
                      Indeed, it should be doable with QVariant::value<T>() and QVariant::fromValue(const T &value)

                      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