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 ListView and ListElement - initialization that is actually of use
Forum Updated to NodeBB v4.3 + New Features

QML ListView and ListElement - initialization that is actually of use

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
3 Posts 3 Posters 1.1k Views
  • 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.
  • V Offline
    V Offline
    VFCraig
    wrote on last edited by
    #1

    I have a ListView. It displays assorted information, including the current date and time. Or it would if QML would allow for a ListElement to be initialized with the values. And when I say, current date and time, I mean that it is intended to actively update. No, I do NOT want to have a timer update the value within the specific module because the value will be used other places and it would be inefficient to have multiple timers firing off for the same purpose.

    So, I have a data module, DataAccess. It has routines to calculate and format the current data and time and updates two strings to hold those values. I have hard coded these objects to the screen that also has the ListView as Text objects with the values bound to the text field. When I do this, they update as expected. But I need this to work IN the ListView. I need to bind this behavior to the specified ListElements. But binding() requires a function and the ListElement field is a string and an error is thrown that a function cannot be assigned to a string, even though all this function does is return the appropriate string.

    Is there a way to tell QML that the value returned is in fact a string? Is there a way to bind to that specific string? Because even DataAccess.currentDate, (a string) in the initialization of the ListElement throws an error about not being able to use a script to initialize the element.

    Yes, I'm stuck with 5.15. If 6 fixes a lot of this, that would be great, but of no use to me.

    A requisite code sample.

    From DataAccess.qml

    // The currentTIme is updated every second. The currentDate, if the date changes
    // The Timer is local to the DataAccess module.
    property string currentDate
    property string currentTime
    

    Tried this in the ListModel of where the dynamic values are needed.

            ListElement { theTitle: "UTC Date"; theRightString: DataAccess.currentDate }
            ListElement { theTitle: "UTC Time"; theRightString: DataAccess.currentTime }
    

    Tried this in the onVisibleChanged handler where it is needed.

            ciModel.setProperty(ConsoleInfo.CInfoEntries.CIUTCDate, "theRightString",
                                                    Qt.binding( function() { return DataAccess.currentDate }))
            ciModel.setProperty(ConsoleInfo.CInfoEntries.CIUTCTime, "theRightString",
                                                    Qt.binding( function() { return DataAccess.currentTime }))
    
    B 1 Reply Last reply
    0
    • F Offline
      F Offline
      freedbrt
      wrote on last edited by freedbrt
      #2

      Usually for this, you need to create c++ QAbstractListViewModel in which you can place pointers to QObject type, where you can declare Q_PROPERTY, and then qml ListView delegate will be refreshed automatically, when qproperty is changed. But if you want to do this in qml, with ListModel, there are few ways how to do this, here are some examples

      1. If you are sure that all elements have changed, or you do not care that all elements will be recreated, just call modelChanged, this will cause a complete recreating and redrawing of all content. Example:
          Timer {
              id: updateTimer
      
              interval: 1000
              repeat: true
              running: true
      
              onTriggered: {
                  for (let i = 0; i < listView.model.count; i++) {
                      listView.model.setProperty(i, "theTitle", new Date().toUTCString())
                  }
                  listView.modelChanged() // or modelUpdated with changeSet
              }
          }
      
          ListView {
              id: listView
      
              anchors.centerIn: parent
      
              width: 500
              height: 300
      
              model: ListModel {
                  ListElement {
                      theTitle: "UTC date"
                      theRightString: "string 1"
                  }
      
                  ListElement {
                      theTitle: "UTC date"
                      theRightString: "string 2"
                  }
              }
      
              delegate: Item {
                  width: parent.width
                  height: 30
      
                  Row {
                      spacing: 10
      
                      Text {
                          text: model.theTitle
                      }
                      Text {
                          text: model.theRightString
                      }
                  }
              }
          }
      
      1. You can bind your timer, or some others signals directly inside delegate. This is more "smart" way, because delegate won't be recreated. Example:
      import QtQuick 2.15
      import QtQuick.Window 2.15
      
      Window {
          id: root
      
          signal someUpdateSignal()
      
          width: 640
          height: 480
          visible: true
      
          Timer {
              id: updateTimer
      
              interval: 1000
              repeat: true
              running: true
      
              onTriggered: {
                  for (let i = 0; i < listView.model.count; i++) {
                      listView.model.setProperty(i, "theTitle", new Date().toUTCString())
                  }
                  root.someUpdateSignal()
              }
          }
      
          ListView {
              id: listView
      
              anchors.centerIn: parent
      
              width: 500
              height: 300
      
              model: ListModel {
                  ListElement {
                      theTitle: "UTC date"
                      theRightString: "string 1"
                  }
      
                  ListElement {
                      theTitle: "UTC date"
                      theRightString: "string 2"
                  }
              }
      
              delegate: Item {
                  width: parent.width
                  height: 30
      
                  Row {
                      spacing: 10
      
                      Text {
                          id: theTitleLabel
      
                          text: model.theTitle
      
                          // Or you can connect this signal with Connections, or create property
                          Component.onCompleted: root.someUpdateSignal.connect(() => {
                                                                                  theTitleLabel.text = model.theTitle
                                                                               })
                      }
                      Text {
                          id: theRightStringLabel
      
                          text: model.theRightString
      
                          Component.onCompleted: root.someUpdateSignal.connect(() => {
                                                                                  theRightStringLabel.text = model.theRightString
                                                                               })
                      }
                  }
              }
          }
      }
      
      1. Or you can change values directly in visible delegates. Example:
      import QtQuick 2.15
      import QtQuick.Window 2.15
      
      Window {   
          width: 640
          height: 480
          visible: true
      
          Timer {
              id: updateTimer
      
              interval: 1000
              repeat: true
              running: true
      
              onTriggered: {
                  for (let i = 0; i < listView.model.count; i++) { // Refresh the model
                      listView.model.setProperty(i, "theTitle", new Date().toUTCString())
                  }
      
                  for (let i = 0; i < listView.children.length; i++) { // Refresh delegate properties
                      const delegateItem = listView.children[i];
                      if (delegateItem.hasOwnProperty("titleProperty")) {
                          delegateItem.titleProperty = listView.model.get(delegateItem.index).theTitle
                      }
                  }
              }
          }
      
          ListView {
              id: listView
      
              anchors.centerIn: parent
      
              width: 500
              height: 300
      
              model: ListModel {
                  ListElement {
                      theTitle: "UTC date"
                      theRightString: "string 1"
                  }
      
                  ListElement {
                      theTitle: "UTC date"
                      theRightString: "string 2"
                  }
              }
      
              delegate: Item {
                  property string titleProperty: model.theTitle
                  property string rightProperty: model.theRightString
                  readonly property int index: model.index
      
                  width: parent.width
                  height: 30
      
                  Row {
                      spacing: 10
      
                      Text {
                          text: titleProperty
                      }
                      Text {
                          text: rightProperty
                      }
                  }
              }
          }
      }
      
      1 Reply Last reply
      0
      • V VFCraig

        I have a ListView. It displays assorted information, including the current date and time. Or it would if QML would allow for a ListElement to be initialized with the values. And when I say, current date and time, I mean that it is intended to actively update. No, I do NOT want to have a timer update the value within the specific module because the value will be used other places and it would be inefficient to have multiple timers firing off for the same purpose.

        So, I have a data module, DataAccess. It has routines to calculate and format the current data and time and updates two strings to hold those values. I have hard coded these objects to the screen that also has the ListView as Text objects with the values bound to the text field. When I do this, they update as expected. But I need this to work IN the ListView. I need to bind this behavior to the specified ListElements. But binding() requires a function and the ListElement field is a string and an error is thrown that a function cannot be assigned to a string, even though all this function does is return the appropriate string.

        Is there a way to tell QML that the value returned is in fact a string? Is there a way to bind to that specific string? Because even DataAccess.currentDate, (a string) in the initialization of the ListElement throws an error about not being able to use a script to initialize the element.

        Yes, I'm stuck with 5.15. If 6 fixes a lot of this, that would be great, but of no use to me.

        A requisite code sample.

        From DataAccess.qml

        // The currentTIme is updated every second. The currentDate, if the date changes
        // The Timer is local to the DataAccess module.
        property string currentDate
        property string currentTime
        

        Tried this in the ListModel of where the dynamic values are needed.

                ListElement { theTitle: "UTC Date"; theRightString: DataAccess.currentDate }
                ListElement { theTitle: "UTC Time"; theRightString: DataAccess.currentTime }
        

        Tried this in the onVisibleChanged handler where it is needed.

                ciModel.setProperty(ConsoleInfo.CInfoEntries.CIUTCDate, "theRightString",
                                                        Qt.binding( function() { return DataAccess.currentDate }))
                ciModel.setProperty(ConsoleInfo.CInfoEntries.CIUTCTime, "theRightString",
                                                        Qt.binding( function() { return DataAccess.currentTime }))
        
        B Offline
        B Offline
        Bob64
        wrote on last edited by
        #3

        @VFCraig If I have understood correctly, an aspect of each delegate component in your list view is to show the current date and time. As you yourself allude to, the "current time" is a system-wide thing and I can't really see any benefit in trying to do this via the model. I would suggest that a better way to think about is that your delegate makes use of a visual component that displays the current date and time and that this is independent from the parts of the delegate that genuinely depend on the model (i.e. the data that is specific to the current item in the list).

        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