Results 1 to 15 of 15

Thread: Proxy model, index mapping.

  1. #1
    Join Date
    Feb 2007
    Posts
    5
    Thanks
    2
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Question Proxy model, index mapping.

    Hi,

    I have a tree model:

    A
    |_AA
    | |_id1 a b c d e f
    | |_id2 e r t y i j
    | |_id3 t f g h s w
    | |_id4 l o m n h k
    | |_id5 a b c d e f
    | |_id6 a b c d e f
    | |_id7 a b c d e f
    | |_id8 a b c d e f

    and I'd like to display it in the tree view as the following arrangement:

    A
    |_AA
    | |_id1 id2 id3 id4
    | |_id5 id6 id7 id8

    Mapping algorithm would be:
    if index has children then pass index otherwise map row to row/4, column to column%4 and don't display indices with column > 0.

    I've reimplemented mapToSource, mapFromSource, filterAcceptsColumn:

    Qt Code:
    1. bool MyProxyModel::filterAcceptsColumn(int sourceColumn, const QModelIndex &sourceParent) const
    2. {
    3. return ( sourceParent.isValid() ||
    4. sourceParent.child(0,0).isValid() ||
    5. ( !sourceParent.child(0,0).isValid() && sourceColumn==0 )
    6. );
    7. }
    8.  
    9. QModelIndex MyProxyModel::mapFromSource( const QModelIndex & sourceIndex ) const
    10. {
    11. if(sourceIndex.isValid()) {
    12. if( sourceIndex.child(0,0).isValid() ) {
    13. return sourceIndex;
    14. } else {
    15. if( sourceIndex.column() == 0 ) {
    16. return createIndex(sourceIndex.row()/4, sourceIndex.row()%4);
    17. } else {
    18. return QModelIndex();
    19. }
    20. }
    21. } else {
    22. return QModelIndex();
    23. }
    24. }
    25.  
    26.  
    27. QModelIndex MyProxyModel::mapToSource ( const QModelIndex & proxyIndex ) const
    28. {
    29. if(proxyIndex.isValid()) {
    30. QStandardItem *item = (QStandardItem *) proxyIndex.internalPointer();
    31. if( item!=0 && item->hasChildren() ) {
    32. return proxyIndex;
    33. } else {
    34. return sourceModel()->index(
    35. proxyIndex.row()*4 + proxyIndex.column(),
    36. 0,
    37. proxyIndex.parent()
    38. );
    39. }
    40. } else {
    41. return QModelIndex();
    42. }
    43. }
    To copy to clipboard, switch view to plain text mode 

    I'm crashing while clicking on AA and trying to expand it.
    ( QTreeViewPrivate::layout(int i); line 3130 Q_Assert(i>-1) )

    I'd appreciate ideas on how to approach this issue.

    Cheers,
    Wojtek

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Proxy model, index mapping.

    You can't reimplement mapFromSource() and mapToSource() for QSortFilterProxyModel because it needs its own implementation of those methods. Subclass QAbstractProxyModel, do the mapping there and then apply your proxy on a filter proxy model or the other way round.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  3. The following user says thank you to wysota for this useful post:

    wojtekw (16th December 2009)

  4. #3
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Proxy model, index mapping.

    Quote Originally Posted by wysota View Post
    You can't reimplement mapFromSource() and mapToSource() for QSortFilterProxyModel because it needs its own implementation of those methods. Subclass QAbstractProxyModel, do the mapping there and then apply your proxy on a filter proxy model or the other way round.
    So you're saying that if you want to support both sorting -and- source/proxy mapping, you need two layers of proxy model? One to handle the mapping and another handle sorting?

    In my case I have a model with many virtual columns, only a few of which need to be shown in any one table view. In each view, the order of the columns is different. In one view I'd like to support sorting.

    I derived from QSortFilterProxyModel and reimplemented mapToSource / mapFromSource and filterAcceptsColumn. All worked fine until I turned on sorting and clicked a column header. As you state, it blew up because the internal pointer in the proxy was invalid.

    OK, so I need a plain vanilla QSortFilterProxyModel with my filterAcceptsColumn method. The table uses this one. Then I need a QAbstractProxyModel that implements my mapping, and I set this as the source model for the table's proxy. Finally, I set my base model as the source model for the mapping proxy.

    Everything should cascade up and down through the proxy layers between the base model and the view, right?

    Then, what happens with the table's item selection model? Does it need to use the middle proxy instead of the base model as its source?

    Wish this were better documented somewhere. The Qt docs and examples are just too basic.
    Last edited by d_stranz; 21st November 2009 at 22:05. Reason: updated contents

  5. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Proxy model, index mapping.

    Quote Originally Posted by d_stranz View Post
    So you're saying that if you want to support both sorting -and- source/proxy mapping, you need two layers of proxy model? One to handle the mapping and another handle sorting?
    Sorting is also mapping and you can only have one mapping at a time, so yes - you need two layers.

    Everything should cascade up and down through the proxy layers between the base model and the view, right?
    Yes, that's correct.

    Then, what happens with the table's item selection model? Does it need to use the middle proxy instead of the base model as its source?
    The selection model always operates on the model set directly on the view.

    Wish this were better documented somewhere. The Qt docs and examples are just too basic.
    That's what we're here for.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  6. The following user says thank you to wysota for this useful post:

    d_stranz (22nd November 2009)

  7. #5
    Join Date
    Jan 2008
    Location
    Alameda, CA, USA
    Posts
    5,230
    Thanks
    302
    Thanked 864 Times in 851 Posts
    Qt products
    Qt5
    Platforms
    Windows

    Default Re: Proxy model, index mapping.

    OK, thanks. I implemented the three layers of models, and it's almost working.

    The Qt documentation for QAbstractProxyModel is wrong - it implies that to derive a custom proxy, you need only implement the mapTo/FromSource methods. There's a bunch more pure virtual methods in QAbstractItemModel that also need to be implemented.

    I've probably done some of that wrong, because I don't know whether mapping needs to be done (in the index() method, for example) or not, because I don't know who is calling them and in what direction the information is flowing.

    Thanks again for the help.

  8. #6
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Proxy model, index mapping.

    Quote Originally Posted by d_stranz View Post
    The Qt documentation for QAbstractProxyModel is wrong - it implies that to derive a custom proxy, you need only implement the mapTo/FromSource methods. There's a bunch more pure virtual methods in QAbstractItemModel that also need to be implemented.
    Technically speaking it's not wrong. As classes are inheriting other classes, you'd have to understand the sentence from the docs as "to subclass QAbstractProxyModel you have to implement x and y apart from things you have to implement from QAbstractItemModel".

    I've probably done some of that wrong, because I don't know whether mapping needs to be done (in the index() method, for example) or not, because I don't know who is calling them and in what direction the information is flowing.
    The proxy has to return all data (apart from mapToSource()) based on itself. So index() has to return index in the proxy space. A crude implementation of index() for flat models would be:

    Qt Code:
    1. QModelIndex MyProxy::index(int row, int column, const QModelIndex &parent = QModelIndex()) const {
    2. if(parent.isValid() || row<0 || column <0 || row>=rowCount() || column>=columnCount())
    3. return QModelIndex();
    4. return createIndex(row, column, 0);
    5. }
    To copy to clipboard, switch view to plain text mode 

    A less crude implementation would probably have something more useful than "0" as the last parameter to createIndex().
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  9. #7
    Join Date
    Feb 2007
    Posts
    5
    Thanks
    2
    Qt products
    Qt4
    Platforms
    Unix/X11 Windows

    Default Re: Proxy model, index mapping.

    You were right, splitting mapping and filtering made it working.
    Big thanks for the help.
    Wojtek

  10. #8
    Join Date
    Jul 2013
    Posts
    4
    Thanks
    1
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Proxy model, index mapping.

    This thread is rather old but ....
    I have the similar problem, a tree model subclassed from QStandardItemModel. I try to get familiar with the QAbstractProxyModel by subclassing it. At the first moment I just want to let it work in by-pass mode, all items should reach the view unchanged. But even that does not work, it crashed when I try to expand a node.
    I reimplement the methods: index(), mapToSource(), mapFromSource() and parent() but somehow I dont really understand how the createIndex() method works in conjunction with parent() and index()
    Can you give me an approach to this issue?

    Thanks
    Seroti

  11. #9
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Proxy model, index mapping.

    The only way to make generic tree models work through a proxy is to subclass either QIdentityProxyModel or QSortFilterProxyModel. In a general case going by subclassing QAbstractProxyModel will not work because there is no way to reliably reimplement parent().
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  12. #10
    Join Date
    Jul 2013
    Posts
    4
    Thanks
    1
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Proxy model, index mapping.

    @d_stranz: can you give me an example how you implemented the mapping foe your problem by subclassing the QAbstractProxyModel. It would help me alot to understand how to reimplement the required methods. Thanks
    Last edited by Seroti; 18th August 2013 at 21:57.

  13. #11
    Join Date
    Jul 2013
    Posts
    4
    Thanks
    1
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Proxy model, index mapping.

    @d_stranz: can you give me an example how you implemented the mapping foe your problem by subclassing the QAbstractProxyModel. It would help me alot to understand how to reimplement the required methods. Thanks


    Added after 1 41 minutes:


    The only way to make generic tree models work through a proxy is to subclass either QIdentityProxyModel or QSortFilterProxyModel. In a general case going by subclassing QAbstractProxyModel will not work because there is no way to reliably reimplement parent().
    But the QSortFilterProxyModel is subclassed from QAbstractProxyModel and it also works with a generic three model. If you dont filter anything, you get the original data from your source model, isn't it ? Can you explain your statement further? I really want to understand, especially why

    As you stated in the previous replies, I cannot use QSortFilterProxyModel to do the mapping because it uses its own I cannot reimplement the maptoSource and mapFromSource methods, so for a non-generic tree model I have to subclass QAbstractProxyModel to do the mapping.

    My problem in the following is rather simple for experienced Qt persons I think . But I do not really know how to start, especially the parent() & index() method
    A tree model subclassed by QStandardItemModel.

    Name--------------|Value-----|--SpecialValue
    A --------------------------------------
    |- B------------------------------------
    ---|-par1----------|---111----|
    -------|par1.1-----|---1.1-----|
    ----|-par2---------|---222-----|
    ----|-parSpe-------|---XXX----|
    ----|-par3---------|---333-----|
    -|-C---------------|------------
    ---|-D-------------|------------
    ----|--E-----------|-----------
    -------|-par4 -----|--444-----|
    -------|-par5 -----|--555-----|
    ----------|-par5.1-|--555.111-|
    -------|-par6 -----|-----------|
    -------|-parSpe---|--ZZZ-----|
    -------|-par7 -----|--777-----|


    I want to map the value XYZ of the "parSpe" item to the column "SpecialValue" of its parent, e.g of item "B" or "E" in this case. The item "parSpe" itself will not be displayed.

    Name--------------|Value-----|--SpecialValue
    A --------------------------------------
    |- B---------------|----------|----XXX---
    ---|-par1----------|---111----|-------------
    -------|par1.1-----|---1.1-----|--------------
    ----|-par2---------|---222-----|--------------
    ----|-par3---------|---333-----|--------------
    -|-C---------------|-------------------------
    ---|-D-------------|-------------------------
    ----|--E-----------|-----------|-----ZZZ
    -------|-par4 -----|--444-----|--------------
    -------|-par5 -----|--555-----|--------------
    ----------|-par5.1-|--555.111-|--------------
    -------|-par6 -----|-----------|--------------
    -------|-par7 -----|--777-----|--------------



    The upper level item A B C D E dont have any further items on column "Value" and "SpecialValue" per default. The column "SpecialValue" is thus a vitual column.
    The hierahchy level of the items "parSpe" is not fix but the item "parSpe" itself can be detected by data(QtUserRole +x).
    The row number of item "parSpe" is not fix, so I intend to code its row number during mapFromSource() (or after building the source tree) with setData(), so that I have a chance to map it back. Is it ok?

    Is it a correct approach to subclass QAbstractProxyModel to solve this problem?

    Can someone give me an example how to proceed? especially the parent() & index() method

    Thanks in advance

  14. #12
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Proxy model, index mapping.

    Quote Originally Posted by Seroti View Post
    But the QSortFilterProxyModel is subclassed from QAbstractProxyModel and it also works with a generic three model.
    That's why I said you could subclass that or QIdentityProxyModel. Qt internal classes have access to some APIs that external classes don't.

    As you stated in the previous replies, I cannot use QSortFilterProxyModel to do the mapping because it uses its own I cannot reimplement the maptoSource and mapFromSource methods, so for a non-generic tree model I have to subclass QAbstractProxyModel to do the mapping.
    At some point you have to call createIndex() on the source model and that method is protected. QIdentityProxyModel works around it by being made a friend of QAbstractItemModel. Since you can't add friends to existing classes, your own class cannot repeat this pattern. The only thing you can do is to make a proxy model operate on your own subclass of QAbstractItemModel that will explicitly make createIndex() public (or expose some other equivalent method) which breaks one of the fundamental rules of Qt's model-view pattern that only the model itself can create valid indexes for its items.

    The bottom line is that instead of putting your effort into subclassing QAbstractProxyModel, better subclass a class more suitable for your goal.

    Is it a correct approach to subclass QAbstractProxyModel to solve this problem?
    I don't understand why you want to use a proxy here. Why not implement a custom model whatsoever instead of using QStandardItemModel with a proxy?
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  15. The following user says thank you to wysota for this useful post:

    Seroti (19th August 2013)

  16. #13
    Join Date
    Jul 2013
    Posts
    4
    Thanks
    1
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Proxy model, index mapping.

    The reason why I intended to do the index mapping by subclassing QAbstractProxyModel is that I avoid to touch the existing tree model, which is not implemented by myself.
    I cannot use QIdentityProxyModel either because the overall project uses Qt of lower version.
    According to your advices and due to the circumstances I have no choice but to touch the existing tree model at the end .

    In a general case going by subclassing QAbstractProxyModel will not work because there is no way to reliably reimplement parent().
    While we are at it, can you explain why ? I'm just interested

    Thanks a lot for your help & explanation

  17. #14
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: Proxy model, index mapping.

    Quote Originally Posted by Seroti View Post
    According to your advices and due to the circumstances I have no choice but to touch the existing tree model at the end .
    I didn't say that. Maybe if we had more information about the model, we might suggest a better approach. However based on the vague ascii art you gave us it is not possible to suggest anything better.

    While we are at it, can you explain why ? I'm just interested
    To create an index you need three values -- the index row, the index column and the index internal value (id or pointer). The only thing that differs between an index with row 0 and column 0 from its parent (or any other index that has rows and columns set to 0) is the internal value. QSortFilterProxyModel keeps an internal mapping of all nodes (twice -- once in each direction) between models to be able to return that value which then allows to find the index in the source model. QIdentityProxyModel in turn returns the internal value of the original model's index. But for that it needs to call mapToSource() with the index in the proxy. Since it doesn't have any information that lets it find the proper index in the base model, it just creates an index (on behalf of the source model) with the same row and column and the internal value and calls parent() on that, maps it back to itself and calls createIndex() using that value. Something along the lines of:

    Qt Code:
    1. QModelIndex Proxy::parent(const QModelIndex &proxyIndex) const {
    2. QModelIndex srcParentIndex = mapToSource(proxyIndex).parent();
    3. QModelIndex parentIndex = mapFromSource(srcParentIndex);
    4. return createIndex(parentIndex.row(), parentIndex.column(), parentIndex.internalId());
    5. }
    6.  
    7. QModelIndex Proxy::mapToSource(const QModelIndex &proxyIndex) const {
    8. return sourceModel()->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalId()); // createIndex() is protected!
    9. }
    To copy to clipboard, switch view to plain text mode 

    You can work around this with a really nasty hack:

    Qt Code:
    1. class MyHackedModel : public QAbstractItemModel {
    2. public:
    3. QModelIndex myHackedCreateIndex(int row, int column, qint64 internalId) const { return createIndex(row, column, internalId); }
    4. };
    5.  
    6. QModelIndex Proxy::mapToSource(const QModelIndex &proxyIndex) const {
    7. const MyHackedModel *hackedModel = static_cast<const MyHackedModel*>(sourceModel()); // whaam! h4x0r!
    8. return hackedModel->myHackedCreateIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalId());
    9. }
    To copy to clipboard, switch view to plain text mode 

    ... but you know, it's a nasty... hack And if someone asks -- you don't know it from me.
    Last edited by wysota; 19th August 2013 at 21:14.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  18. #15

    Default Re: Proxy model, index mapping.

    tried a little bit about mapToSource() and mapToProxy() for QSortFilterProxyModel.
    But somehow I decided to make a copy of data and move the data there.

Similar Threads

  1. How to filter duplicates from a proxy model.
    By kaushal_gaurav in forum Qt Programming
    Replies: 6
    Last Post: 14th October 2016, 10:21
  2. Replies: 1
    Last Post: 18th November 2009, 23:21
  3. Replies: 3
    Last Post: 31st March 2008, 21:23
  4. Custom proxy model issue
    By Khal Drogo in forum Qt Programming
    Replies: 13
    Last Post: 30th November 2007, 12:41
  5. Model and Proxy
    By larry104 in forum Qt Programming
    Replies: 1
    Last Post: 4th August 2006, 21:05

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.