Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. Qt for Python
  4. Listview delegate update from two sources
Forum Updated to NodeBB v4.3 + New Features

Listview delegate update from two sources

Scheduled Pinned Locked Moved Solved Qt for Python
5 Posts 2 Posters 773 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.
  • L Offline
    L Offline
    LS-KS
    wrote on last edited by
    #1

    I have to create an application as some of my study-work.
    I've never used Qt framework seriously before.
    Part of this is an application which monitors some rfid readers.
    As you can see in the image, I successfully read the nodes from a Yaml-file when the app starts and managed to display the List.

    However: I shall implement that the nodes can be checked/unchecked either by hitting the "Select All" /"Select None" buttons or by using the individual checkboxes.

    When I do that, i can see that the listmodel and my datamodel is updated properly. But the listview is only updated once by the buttons.

    The print-statement in my data-implementation indicates, that the "selected" - property is never passed to the Qml after a button is clicked.
    I see this behaviour in every single listview/tableview of my application and really need to understand where my mistake is.
    I couldn't find similar problemd which helped by google.
    Can anybody help?

    I'm using Pyside 6.5.1 with QtQuick2.

    46e32a7d-92e5-49a4-bde0-7a8f125be713-image.png

    snippet from my QML mainwindow:

    import QtQuick 2.9
    import QtQuick.Controls 2.5
    import QtQuick.Controls.Material
    import QtQuick.Layouts 1.3
    import QtQuick.Window 2.2
    .
    .
    .
    
    ListView{
                id: listView
                anchors.fill: parent
                model: rfidModel
                delegate: RfidDelegate{
                    width: contentRect.width
                    idVal: model.idVal
                    selected: model.selected
                    tagTextA: model.tagId
                    tagTextB: model.productID
                    tagTextC: model.cupSize
                    nameText: model.name
                    readerIpAdress: model.ipAddr
                    readerPort: model.ipPort
                    endpointIpAdress: model.endPointipAddr
                    endpointPort: model.endPointipPort
                    endpointModbusAddress: model.endPointModbus
                    locked: true
                }
            }
    

    part of the delegate:

    property bool selected: false
    CheckBox{
                id: isSelected
                text: "selected"
                checked: selected
                onCheckedChanged: {
                    selected = isSelected.checked
                    rfidController.selectNode(idVal, selected)
                }
            }
    

    From my QAbstractLisModel - class implementation i will post only the data and setData methods:

    def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> typing.Any:
            """
            Returns Data from viewmodel.
    
            :param index: Used to index into the model
            :type index: QModelIndex
            :param role: Used to index into the model
            :type role: int
            :return: returns data from viewmodel at given index and role
            """
            
            if 0 <= index.row() < self.rowCount():
                node = self.rfidData[index.row()]
                field = self.roleNames().get(role)
                #print("field: "+ str(field), "role: " + str(role))
                if field:
                    return getattr(node, field.decode())
    
            
        def setData(self, index: QModelIndex, value: Any, role: int) -> bool:
            """
            Writes data to an index and returns true if success
    
            :param index: Index at which data shall be changed
                :type index: QModelIndex
                :param value: New value to be written at index
            :type value: int for any ID, string for products names and bool for pallet existence
            :param role: Rolename to be written to
            :return: returns False if writing was not successful. Otherwise, it returns the old value.
            """
            role = Qt. DisplayRole + role
            if index.row() >= self.rowCount() or not index.isValid():
                return False
    
            roleNames = self.roleNames()
            field = roleNames.get(role)
            print(str(field))
            if field:
                setattr(self.rfidData[index.row()], field.decode(), value)
                self.dataChanged.emit(index, index)
                return True
            print("field not found for role " + str(role))
            return False
    

    and finally from my controller class RfidController:

        @Slot()
        def selectAll(self):
            """
            Marks all RFID-Nodes as selected.
            """
            nodes = [node.idVal for node in self.rfidViewModel.rfidData]
            for node in nodes:
                self.selectNode(node, True)
    
    
        @Slot()
        def selectNone(self):
            """
            Marks all RFID-Nodes as selected.
            """
            nodes = [node.idVal for node in self.rfidViewModel.rfidData]
            for node in nodes:
                self.selectNode(node, False)
    
        @Slot(int, bool)
        def selectNode(self, id: int, selected: bool):
            """
            marks RFID-Node with id as selected.
            :returns: None
            """
            rows = self.rfidViewModel.rowCount()
            for i in range(rows):
                print("i: " + str(i))
                node = self.rfidViewModel.rfidData[i]
                if node.idVal == id:
                    oldVal = node.selected
                    index = self.rfidViewModel.index(i, 0)
                    self.rfidViewModel.setData(index, selected, 13)
                    newVal = node.selected
                    print(f"Data changed from {oldVal} to {newVal} in index {index.row()}")
    
    1 Reply Last reply
    0
    • SGaistS Offline
      SGaistS Offline
      SGaist
      Lifetime Qt Champion
      wrote on last edited by
      #2

      Hi,

      I might be wrong be you are emitting dataChanged without any of the roles your delegate uses hence there's nothing for it to update. An empty roles means that all roles should be reloaded however, the ones you use for your delegate are likely unknown and thus not checked.

      Interested in AI ? www.idiap.ch
      Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

      L 1 Reply Last reply
      0
      • SGaistS SGaist

        Hi,

        I might be wrong be you are emitting dataChanged without any of the roles your delegate uses hence there's nothing for it to update. An empty roles means that all roles should be reloaded however, the ones you use for your delegate are likely unknown and thus not checked.

        L Offline
        L Offline
        LS-KS
        wrote on last edited by LS-KS
        #3

        @SGaist
        Thank you for your reply!
        I already tried following:

        role = Qt. DisplayRole + role
        self.dataChanged.emit(index, index, role)
        
        self.dataChanged.emit(index, index, roleNames.keys())
        
        self.dataChanged.emit(index, index, [role])
        

        I also tried some combinations of Qt.Userrole but in that case the list view won't load any data.

        I'm asking myself if it is possible that the binding gets destroyed when the button is klicked. Because:

        --> List loads fine
        .--> Select All is clicked, ListView updates fine
        --> Select None: model updates, Listview doesn't
        --> Select All doesn't work either.

        Also: when the model is loaded with checked flags the behaviour is vice versa.
        I read something in the internet which indicates that it may happen. But found not enough to get into the error.

        L 1 Reply Last reply
        0
        • L LS-KS

          @SGaist
          Thank you for your reply!
          I already tried following:

          role = Qt. DisplayRole + role
          self.dataChanged.emit(index, index, role)
          
          self.dataChanged.emit(index, index, roleNames.keys())
          
          self.dataChanged.emit(index, index, [role])
          

          I also tried some combinations of Qt.Userrole but in that case the list view won't load any data.

          I'm asking myself if it is possible that the binding gets destroyed when the button is klicked. Because:

          --> List loads fine
          .--> Select All is clicked, ListView updates fine
          --> Select None: model updates, Listview doesn't
          --> Select All doesn't work either.

          Also: when the model is loaded with checked flags the behaviour is vice versa.
          I read something in the internet which indicates that it may happen. But found not enough to get into the error.

          L Offline
          L Offline
          LS-KS
          wrote on last edited by
          #4

          I found out, that the selected property had no function connected to the property changed.

          I implemented this so that the checkbox.checked is set depending on the property.

          property bool selected: false
              onSelectedChanged: {
                  console.log("new value: "+selected + " field: " + isSelected.checked)
                  isSelected.checked = selected
                  console.log("after setting: checkbox.checked: = " + isSelected.checked)
              }
          
          
          CheckBox{
                      id: isSelected
                      text: "selected"
                      checked: false
                      onCheckedChanged: {
                          selected = isSelected.checked
                          rfidController.selectNode(idVal, selected)
                      }
                  }
          
          
          

          Now i found out: when the delegate falls out of the cacheBuffer and has to be reloaded, the value is shoewed correctly.
          Interestingly, i can use the button "Select All" to mark all delegates without problems... but they would also only be showed unchecked if I scroll them out of the view.

          I set ListViews cacheBuffer to 0 which showed clearly this behaviour: everything gets updated when it's newly loaded except the first delegate.

          Why is that? I never had an issue with updating model and ListView in C++.

          L 1 Reply Last reply
          0
          • L LS-KS

            I found out, that the selected property had no function connected to the property changed.

            I implemented this so that the checkbox.checked is set depending on the property.

            property bool selected: false
                onSelectedChanged: {
                    console.log("new value: "+selected + " field: " + isSelected.checked)
                    isSelected.checked = selected
                    console.log("after setting: checkbox.checked: = " + isSelected.checked)
                }
            
            
            CheckBox{
                        id: isSelected
                        text: "selected"
                        checked: false
                        onCheckedChanged: {
                            selected = isSelected.checked
                            rfidController.selectNode(idVal, selected)
                        }
                    }
            
            
            

            Now i found out: when the delegate falls out of the cacheBuffer and has to be reloaded, the value is shoewed correctly.
            Interestingly, i can use the button "Select All" to mark all delegates without problems... but they would also only be showed unchecked if I scroll them out of the view.

            I set ListViews cacheBuffer to 0 which showed clearly this behaviour: everything gets updated when it's newly loaded except the first delegate.

            Why is that? I never had an issue with updating model and ListView in C++.

            L Offline
            L Offline
            LS-KS
            wrote on last edited by
            #5

            I found the solution:

            I replaced the delegate with a simple Text and it worked.
            Then I rmeoved all functions in the original delegate which were related to the properties and assigned the properties directly to the TextFields, CheckBoxes etc.
            Then it also worked but i got an binding-loop error.

            I resolved this error and now it works.

            I learned my lesson this time.

            1 Reply Last reply
            1
            • L LS-KS has marked this topic as solved on

            • Login

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