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. Duplicate items in ListView
Forum Updated to NodeBB v4.3 + New Features

Duplicate items in ListView

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
7 Posts 4 Posters 976 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.
  • K Offline
    K Offline
    KonstantinosTsakalis
    wrote on last edited by
    #1

    Hello,

    I have a ListView with a delegate which is controlled by a two-layer model in C++. By two layer I mean a base model and an intermediate QSortFilterProxyModel. I am using Qt.6.2 .

    The filter model implements the lessThan() and filterAcceptsRow() functions.

    In the QML side, the delegates that become visible depend on the result of filterAcceptsRow() which implementation is:

    bool CAlarmSortProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
    {
        const QModelIndex myIndex = sourceModel()->index(sourceRow, 0, sourceParent);
        orderHasChanged();
        return myIndex.data(CAlarmModel::StateRole).toBool();
    }
    

    I am aware of the Note in the documentation about not storing the state in a delegate (https://doc.qt.io/qt-6.2/qml-qtquick-listview.html#delegate-prop) which I think is not what I am doing here, right? As the StateRole is evaluated on the model level in the C++ side and not on the actual delegate object.

    The usage of this list is to display some alarms that are being activated or deactivated based on a boolean variable per alarm - True means the delegate must become visible in the ListView, so must be accepted in the proxy model, and False means become invisible. This is saved in the model items under the StateRole.

    My problem is that - some times - the list displays duplicate delegates for the same alarms. What is in fact happening, is that these delegate objects are actually overlayed on top or below the "correct" delegates, and will never be destroyed unless I restart the application.

    Also important, is that the list has two logical states. One is that the list is collapsed, and then only the higher in order delegate is shown. If this first delegate is extended, then the rest of the activated alarms become visible too. And each of them has an extendable/collapsable rectangle which is activated by clicking on it.

    I mention this because the duplication problem appears only when the delegate is extended and at the same time multiple other delegates may be created or destroyed.

    So, it feels like whenever there are fast changes in the accepted delegates of the proxy model something gets messed up.

    Images of the issue below:

    dc013edb-a4ce-4428-8855-ec9223109ba7-image.png

    1e08271b-0a41-4ede-a249-1cd393a47720-image.png

    I have tried to reproduce the issue by explicitly changing the alarms' state as fast as possible but I do not get this error. The issue arises when the application is used on an actual embedded device - the alarms' states are set from another processor. However, this should be of no difference for my application as it only "sees" a True/False for each alarm's state. It is very difficult to reproduce this error at runtime, it seems to be so random.

    I found a similar description on this issue https://forum.qt.io/topic/2953/qml-state-delegates but it's not really the same problem.

    I read that delegates that go out of the view get destroyed. Can this be that - sometimes - the delegate is not actually destroyed? And then, when the alarm is activated again, the proxymodel creates a new instance of the same alarm delegate, and thus leaving the older one in the view forever? Does this even make sense?

    Thank you for your help.

    1 Reply Last reply
    0
    • K Offline
      K Offline
      KonstantinosTsakalis
      wrote on last edited by
      #2

      [UPDATE]

      I have managed to reproduce the issue.

      My initial thought of non-destroyed delegates is indeed valid. I have managed to reproduce the issue by setting the ListView's property delayRemove to True. Doing so the view will never destroy the created delegates, and each time a new delegate is instantiated a duplicate will be displayed. See image below:

      ad3ea04c-4bec-416d-9fd6-51e5eda29ac6-image.png

      Now the first delegate of PA01 is an "old" instantiation of the delegate and the second is the newly created.

      So, is it valid to consider that when this issue appears on runtime, the construction/destruction changes so fast that the View does not destroy the object? Although it's still not clear to me why this happens anyway. I think this reported bug is also related https://bugreports.qt.io/browse/QTBUG-45500?focusedCommentId=343909&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel

      As a workaround,

      1. I want to detect if/when such "dangling" delegates occur and destroy them manually. I have tried to identify this with the following ways but I can't get a clear answer on whether a dangling delegate exists in the view or not:
      ListView.onRemove: {
                          // if the following would work I could identify this here and manually delete the object, but the parent is always reported as non-null, as this is the visual parent which is always non-zero. How can I get the object parent of a delegate?
                          console.log("Parent is null: " + (parent == null ? "True" : "False"))
                          }
                      
                      ListView.onAdd: {
                          for (var i = 0; i < alarms.children.length; i++)
                              {
                                  if (alarms.children[i].parent == null)
                                  {
                                      // same comment as above
                                      console.log("Data child of the list found with null parent!")
                                  }
                              }
                      }
      

      or

      1. I read that the delegates are actually removed when the view calls polish() or updatePolish() on the renderer. But these methods are private inside Qt. Is there a way to call these from QML? This way I could force the view to polish whenever a onAdd occurs.

      2. I can possibly use the attached property ListView.reuseItems: true and instead of creating a new delegate object each time the model changes, reuse the existing items. But does this guarantee that the problem will never occur again?

      I would appreciate some feedback on the above options since they can potentially be a solution.

      Thank you.

      K 2 Replies Last reply
      0
      • K KonstantinosTsakalis

        [UPDATE]

        I have managed to reproduce the issue.

        My initial thought of non-destroyed delegates is indeed valid. I have managed to reproduce the issue by setting the ListView's property delayRemove to True. Doing so the view will never destroy the created delegates, and each time a new delegate is instantiated a duplicate will be displayed. See image below:

        ad3ea04c-4bec-416d-9fd6-51e5eda29ac6-image.png

        Now the first delegate of PA01 is an "old" instantiation of the delegate and the second is the newly created.

        So, is it valid to consider that when this issue appears on runtime, the construction/destruction changes so fast that the View does not destroy the object? Although it's still not clear to me why this happens anyway. I think this reported bug is also related https://bugreports.qt.io/browse/QTBUG-45500?focusedCommentId=343909&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel

        As a workaround,

        1. I want to detect if/when such "dangling" delegates occur and destroy them manually. I have tried to identify this with the following ways but I can't get a clear answer on whether a dangling delegate exists in the view or not:
        ListView.onRemove: {
                            // if the following would work I could identify this here and manually delete the object, but the parent is always reported as non-null, as this is the visual parent which is always non-zero. How can I get the object parent of a delegate?
                            console.log("Parent is null: " + (parent == null ? "True" : "False"))
                            }
                        
                        ListView.onAdd: {
                            for (var i = 0; i < alarms.children.length; i++)
                                {
                                    if (alarms.children[i].parent == null)
                                    {
                                        // same comment as above
                                        console.log("Data child of the list found with null parent!")
                                    }
                                }
                        }
        

        or

        1. I read that the delegates are actually removed when the view calls polish() or updatePolish() on the renderer. But these methods are private inside Qt. Is there a way to call these from QML? This way I could force the view to polish whenever a onAdd occurs.

        2. I can possibly use the attached property ListView.reuseItems: true and instead of creating a new delegate object each time the model changes, reuse the existing items. But does this guarantee that the problem will never occur again?

        I would appreciate some feedback on the above options since they can potentially be a solution.

        Thank you.

        K Offline
        K Offline
        KonstantinosTsakalis
        wrote on last edited by
        #3

        @KonstantinosTsakalis

        Actually option 2 is not really a solution because when a new delegate is created (after the problem is already present) the view does update, but the duplicate delegate still remains. So, even if I manage to enforce a view update it will not have any effect on the problem.

        1 Reply Last reply
        0
        • K KonstantinosTsakalis

          [UPDATE]

          I have managed to reproduce the issue.

          My initial thought of non-destroyed delegates is indeed valid. I have managed to reproduce the issue by setting the ListView's property delayRemove to True. Doing so the view will never destroy the created delegates, and each time a new delegate is instantiated a duplicate will be displayed. See image below:

          ad3ea04c-4bec-416d-9fd6-51e5eda29ac6-image.png

          Now the first delegate of PA01 is an "old" instantiation of the delegate and the second is the newly created.

          So, is it valid to consider that when this issue appears on runtime, the construction/destruction changes so fast that the View does not destroy the object? Although it's still not clear to me why this happens anyway. I think this reported bug is also related https://bugreports.qt.io/browse/QTBUG-45500?focusedCommentId=343909&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel

          As a workaround,

          1. I want to detect if/when such "dangling" delegates occur and destroy them manually. I have tried to identify this with the following ways but I can't get a clear answer on whether a dangling delegate exists in the view or not:
          ListView.onRemove: {
                              // if the following would work I could identify this here and manually delete the object, but the parent is always reported as non-null, as this is the visual parent which is always non-zero. How can I get the object parent of a delegate?
                              console.log("Parent is null: " + (parent == null ? "True" : "False"))
                              }
                          
                          ListView.onAdd: {
                              for (var i = 0; i < alarms.children.length; i++)
                                  {
                                      if (alarms.children[i].parent == null)
                                      {
                                          // same comment as above
                                          console.log("Data child of the list found with null parent!")
                                      }
                                  }
                          }
          

          or

          1. I read that the delegates are actually removed when the view calls polish() or updatePolish() on the renderer. But these methods are private inside Qt. Is there a way to call these from QML? This way I could force the view to polish whenever a onAdd occurs.

          2. I can possibly use the attached property ListView.reuseItems: true and instead of creating a new delegate object each time the model changes, reuse the existing items. But does this guarantee that the problem will never occur again?

          I would appreciate some feedback on the above options since they can potentially be a solution.

          Thank you.

          K Offline
          K Offline
          KonstantinosTsakalis
          wrote on last edited by
          #4

          @KonstantinosTsakalis

          For option 2 this is the way to trigger it through QML https://doc.qt.io/qt-5/qml-qtquick-listview.html#forceLayout-method .

          W 1 Reply Last reply
          0
          • D Offline
            D Offline
            Davy
            wrote on last edited by Davy
            #5

            I ran into the same issue. Apparently delegates that are pooled for reuse but not directly reused, are still displayed. I managed to solve it using this in the delegates:

            ListView.onReused: visible = true
            ListView.onPooled: visible = false```
            1 Reply Last reply
            0
            • GrecKoG Offline
              GrecKoG Offline
              GrecKo
              Qt Champions 2018
              wrote on last edited by
              #6

              They are still visible but they shouldn't be displayed anymore. If not it warrants a bug report

              1 Reply Last reply
              0
              • K KonstantinosTsakalis

                @KonstantinosTsakalis

                For option 2 this is the way to trigger it through QML https://doc.qt.io/qt-5/qml-qtquick-listview.html#forceLayout-method .

                W Offline
                W Offline
                WCompton
                wrote on last edited by
                #7
                This post is deleted!
                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