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. To Bind or Not to Bind with Model Data. Am I Overcomplicating This?
Forum Updated to NodeBB v4.3 + New Features

To Bind or Not to Bind with Model Data. Am I Overcomplicating This?

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
3 Posts 2 Posters 275 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.
  • W Offline
    W Offline
    WatermelonJesus
    wrote on last edited by WatermelonJesus
    #1

    I have a simple model and within it a simple text property "name". I would like to bind this to a QML TextField:

    // C++, Within the model with instance myModel
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
    
    // QML
    TextField {
        text = myModel.name
    }
    

    This is fine for reading, but when the user goes to edit the text field this does not change the model. In other words, WRITE is not called as one would expect. My first and biggest question is why on earth not? Isn't this the point of a binding? Why even have a WRITE attribute if not.

    The first thing you naturally try is to manually set the text:

    // C++, Within the model with instance myModel
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
    
    // QML (Error Binding Loop)
    TextField {
        text = myModel.name
        onDisplayTextChanged: myModel.name = text
    }
    

    Which results in a binding loop warning. So reading around online, I find that people suggest that instead of binding you have specific sets for specific events. A common one is on object completion:

    // C++, Within the model with instance myModel
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
    
    // QML
    TextField {
        id: textFieldDescription
        Component.onCompleted: text = myModel.name
        onDisplayTextChanged: myModel.name = text
    }
    

    This is fine for simpler model/view relationships (though my original question remains; this seems unnecessary). But what if you have other events that can change name outside of what you see here? You have limited yourself by breaking the binding. So the suggestion I typically see is to do this:

    // C++, Within the model with instance myModel
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
    
    // QML
    MyModel {
        id: myModel
        onNameChanged: textFieldName.text = name
    }
    TextField {
        id: textFieldName
        onDisplayTextChanged: myModel.name = text
    }
    

    So question two (which is technically 3 questions) Am I overcomplicating this? Is this really how we are supposed to do it? There isn't a way to just use a binding that says "user edits = writes with no binding loop"? It seems overcomplicated to me.

    Further, what if we want to do something more complicated with delegates?

    // C++: Within a NameCheck class which holds only these two members
    Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(bool checkStatus READ getCheckStatus WRITE setCheckStatus NOTIFY checkStatusChanged)
    
    // C++: Within the model class with instance myModel
    Q_PROPERTY(QList<NameCheck*> allPeople READ getAllPeople WRITE setAllPeople NOTIFY allPeopleChanged)
    
    // QML:
    MyModel {
        id: myModel
        // I want something like: onCheckStatusChanged 
        //    set check status of checkDelegate index n.  
    }
    ListView {
        id: listView
        model: myModel.allPeople
        delegate: CheckDelegate {
            id: checkDelegate
            text: modelData.name
            onCheckStateChanged: modelData.checkStatus = checked
        }
    }
    

    So this gets pretty weird, when I would really just like to have a simple binding. Final question is how do I pull off this checkdelegate situation in a clean elegant way?

    Thanks for reading.

    1 Reply Last reply
    0
    • W Offline
      W Offline
      WatermelonJesus
      wrote on last edited by
      #2

      So once again, I'm going to answer my own question. What is it about formalizing your problem that helps you solve it? The simple elegant way to accomplish this is below:

      // C++, Within the model with instance myModel
      Q_PROPERTY(QString name READ getName WRITE setName NOTIFY nameChanged)
      
      void MyModel::setName(QString name) {
          if (name != m_name) {
              m_name = std::move(name);
              emit nameChanged();
          }
      }
      
      // QML
      TextField {
          text = myModel.name
          onDisplayTextChanged: myModel.name = text
      }
      

      The key to this is protecting the call to emit with the if check. When a user changes the text the WRITE method is called, m_name is changed, and the NOTIFY emit fires which causes the binding to call WRITE once again, but now name and m_name are equal so emit is not called and the loop stops here. You get all the benefits of having your binding still with text = myModel.name but you can also manually set it.

      I really think this information needs to be in the documentation somewhere. It's such an incredibly common problem, and I searched for a while without finding this.

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

        In Qt Creator, create your Q_PROPERTY statement first. Then right click and choose Refactor->Generate missing Q_PROPERTY members. This will create the default function members that do what you learned.

        Another option is MEMBER:

        Q_PROPERTY(type name MEMBER memberName)
        

        This creates functions for reading and writing the property. They are opaque to you so may not be what you want. I cannot remember if MEMBER requires a NOTIFY or not. I think it does not.

        Reference

        C++ is a perfectly valid school of magic.

        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