Results 1 to 5 of 5

Thread: My subclass of QAbstractProxyModel does not handle remove/insert items correctly

  1. #1
    Join Date
    Apr 2013
    Posts
    4
    Qt products
    Qt4
    Platforms
    MacOS X Windows

    Default My subclass of QAbstractProxyModel does not handle remove/insert items correctly

    Hello

    I have some issues with updating a QTreeView correctly when the model, connected to the view through one or two proxy models, inserts or removes items.

    The overall purpose is to have a view on the model that can switch between showing the model as a tree structure and as a flat structure (showing all items, leaves and internal nodes, in a table). It should also be able to sort both views. The model itself has a tree structure (which should not actually be converted into a flat structure).

    My implementation uses the following classes:
    • Model, which is derived from QAbstractItemModel and provides a tree-structured model
    • Viewer, which is derived from QTreeView and looks onto the model
    • ViewerProxyModel, which is derived from QSortFilterProxyModel
    • TreeToTableProxyModel, which translates the tree structure of the model to a flat structure


    The Viewer always looks onto an instance of ViewerProxyModel, which in turn either directly looks onto an instance of Model, or, if a flat hierarchy is desired, first onto an instance of TreeToTableProxyModel, which finally looks onto an instance of Model.

    Now, my problem is that in the case where the TreeToTableProxyModel is used, the Viewer does not update properly if items are inserted into or removed from the Model instance. To handle these updates, I implemented the following methods:

    Qt Code:
    1. void TreeToTableProxyModel::sourceRowsAboutToBeInserted(const QModelIndex& aParent, int aStart, int aEnd)
    2. {
    3. if (sourceModel())
    4. {
    5. QModelIndex modelStartIndex = sourceModel()->index(aStart, 0, aParent);
    6. QModelIndex modelEndIndex = sourceModel()->index(aEnd, 0, aParent);
    7. QModelIndex proxyStartIndex = mapFromSource(modelStartIndex);
    8. QModelIndex proxyEndIndex = mapFromSource(modelEndIndex);
    9.  
    10. beginInsertRows(proxyStartIndex.parent(), proxyStartIndex.row(), proxyEndIndex.row());
    11. //beginResetModel();
    12. }
    13. }
    14.  
    15. void TreeToTableProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex& aParent, int aStart, int aEnd)
    16. {
    17. if (sourceModel())
    18. {
    19. QModelIndex modelStartIndex = sourceModel()->index(aStart, 0, aParent);
    20. QModelIndex modelEndIndex = sourceModel()->index(aEnd, 0, aParent);
    21. QModelIndex proxyStartIndex = mapFromSource(modelStartIndex);
    22. QModelIndex proxyEndIndex = mapFromSource(modelEndIndex);
    23.  
    24. beginRemoveRows(proxyStartIndex.parent(), proxyStartIndex.row(), proxyEndIndex.row());
    25. //beginResetModel();
    26. }
    27. }
    28.  
    29. void TreeToTableProxyModel::sourceRowsInserted(const QModelIndex& aParent, int aStart, int aEnd)
    30. {
    31. if (sourceModel())
    32. {
    33. //emit layoutAboutToBeChanged();
    34. mMapFromSource.clear();
    35. mMapToSource.clear();
    36. createMap(...);
    37. //emit layoutChanged();
    38.  
    39. endInsertRows();
    40. //endResetModel();
    41. }
    42. }
    43.  
    44. void TreeToTableProxyModel::sourceRowsRemoved(const QModelIndex& aParent, int aStart, int aEnd)
    45. {
    46. if (sourceModel())
    47. {
    48. //emit layoutAboutToBeChanged();
    49. mMapFromSource.clear();
    50. mMapToSource.clear();
    51. createMap(...);
    52. //emit layoutChanged();
    53.  
    54. endRemoveRows();
    55. //endResetModel();
    56. }
    57. }
    To copy to clipboard, switch view to plain text mode 

    which are connected to the appropriate signals of the Model instance:

    Qt Code:
    1. connect(sourceModel(), SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int)), this, SLOT(sourceRowsAboutToBeInserted(const QModelIndex&, int, int)));
    To copy to clipboard, switch view to plain text mode 

    etc. in the setSourceModel() method of TreeToTableProxyModel.

    My problem is that I need to recompute the mapping of the TreeToTableProxyModel if items are inserted or removed. But when recomputing the mapping, I should probably emit layoutAboutToBeChanged() and layoutChanged(). This has to be done between beginInsertRows() and endInsertRows() or beginRemoveRows() and endRemoveRows(), which probably interferes with each other. Anyway, this solution does not update the Viewer properly. I did also test the variant without beginInsertRows()... and layoutChanged()... and directly using beginResetModel() and endResetModel(). This works almost fine. The only problem here (apart from the fact that it may be an overkill to reset the whole model) is that the sorting is not completely restored. If I first sort by column A and then by column B, items with the same value in column B remain sorted according to A. This secondary sorting is lost once the model has been reset.

    I suppose I could use the beginReset() and endReset() variant and manually sort again by the correct columns. But this does not seem like the proper solution. How should I implement this?

  2. #2
    Join Date
    Feb 2011
    Location
    Bangalore
    Posts
    207
    Thanks
    20
    Thanked 28 Times in 27 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: My subclass of QAbstractProxyModel does not handle remove/insert items correctly

    QAbstractProxyModel work with QPersistentModelIndex to create internal maps of items. I am assuming your TreeToTableProxyModel is QAbstractProxyModel inherited and not QSortFilterProxyModel.
    Given your problem why don't you make your original model class run in two modes. Tree mode and flat mode. Define your index() and parent() functions according to the mode set. While changing the mode do a model reset.

  3. #3
    Join Date
    Apr 2013
    Posts
    4
    Qt products
    Qt4
    Platforms
    MacOS X Windows

    Default Re: My subclass of QAbstractProxyModel does not handle remove/insert items correctly

    @pkj: Thank you. Yes, TreeToTableProxyModel is derived from QAbstractProxyModel. Sorry, I forgot this piece of information.

    Since the original model is already quite big, I would rather not expand it by even more functionality. But you are right, that relinking models may not be very elegant. Instead of your solution, I may make TreeToTableProxyModel subclass QIdentityProxyModel and implement a switch between flat and tree mode there.

    However, when I replace the QAbstractProxyModel by QIdentityProxyModel in TreeToTableProxyModel, I still get the same kind of error: the Viewer is not correctly updated, at least the secondary sorting gets lost. The basic question I have is quite general: when writing my own proxy model, how am I supposed to handle insertions and removals of items in the original model? The problem is that upon insertion/removal I need to recompute (at least a part of) the mapping, which in turn invalidates the layout of the items, which influences the sorting that the Viewer (or the ViewerProxyModel) is supposed to maintain.

  4. #4
    Join Date
    Feb 2011
    Location
    Bangalore
    Posts
    207
    Thanks
    20
    Thanked 28 Times in 27 Posts
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: My subclass of QAbstractProxyModel does not handle remove/insert items correctly

    The basic question I have is quite general: when writing my own proxy model, how am I supposed to handle insertions and removals of items in the original model? The problem is that upon insertion/removal I need to recompute (at least a part of) the mapping, which in turn invalidates the layout of the items, which influences the sorting that the Viewer (or the ViewerProxyModel) is supposed to maintain.
    If you are inheriting from QSortFilterProxyModel or QIdentityProxyModel and you don't reimplement, parent(), index() functions, you don't need to do anything(notice you can't do that anyway!!). Those classes maintains the mapping through QPersistentModelIndexes. Open the code of the classes to see for yourself how the mappings are preserved to come up with something similar. What you really want is to rewrite index() and parent() functions since you need to flatten the tree up. Here, the functionality provided by these classes won't help as they essentially just change either the display by data() function or hide a few items. That is their intended use.

    If you insist on having a new model, maintain a hash of treeItemObject and qpersistentmodelindex. Use it to arrive at index, parent and data functions. You can see the code QSortFilterProxyModel to get your ideas straight. And your new model cannot be a subclass of QAbstractProxyModel but only another QAbstractItemModel since you have to rewrite the index() and parent() functions.

  5. #5
    Join Date
    Apr 2013
    Posts
    4
    Qt products
    Qt4
    Platforms
    MacOS X Windows

    Default Re: My subclass of QAbstractProxyModel does not handle remove/insert items correctly

    Well, I have already implemented those methods in TreeToTableProxyModel (setSourceModel(), mapFromSource(), mapToSource(), columnCount(), data(), index(), parent() and rowCount()). I get a flat view on the model, just as I want it. Therefore, as far as I can tell, the mapping works fine. My problem is how to consistently update the mapping -- and how to send the correct signals in the correct order such that also the QSortFilterProxyModel and the Viewer update correctly --, once rows are inserted to or removed from the original model.

Similar Threads

  1. example of QPlainTextEdit subclass to handle large text files
    By nekkro-kvlt in forum Qt Programming
    Replies: 2
    Last Post: 17th October 2012, 14:30
  2. Replies: 3
    Last Post: 30th January 2011, 20:32
  3. How to subclass QAbstractProxyModel
    By skycrestway in forum Qt Programming
    Replies: 2
    Last Post: 22nd October 2010, 23:35
  4. How to correctly subclass classes that use implicit sharing
    By Gh0str1d3r in forum Qt Programming
    Replies: 1
    Last Post: 16th August 2010, 09:42
  5. QListWidget cannot handle too many items
    By mr.hichem in forum Qt Programming
    Replies: 1
    Last Post: 2nd November 2009, 23:00

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.