Results 1 to 12 of 12

Thread: filtering a TreeView from buttom to top

  1. #1
    Join Date
    Jan 2006
    Location
    germany
    Posts
    75
    Thanks
    9
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11

    Question filtering a TreeView from buttom to top

    What I want to do is rather simple:
    When filtering my QTreeView, I want to filter on the children first and hide the parent if - and only if - it has no more (visible) children.

    I subclassed QSortFilterProxyModel and got to the point where it filters correctly on the children and never hides the parent.
    The last is accomplished by(in filterAcceptsRow())
    Qt Code:
    1. /// if toplevel
    2. if (sourceParent == ((QStandardItemModel*)sourceModel())->indexFromItem(
    3. ((QStandardItemModel*)sourceModel())->invisibleRootItem()) )
    4. return true;
    To copy to clipboard, switch view to plain text mode 
    The Problem is I cant check if the item hasChildren() because at the point where an item is processed by filterAcceptsRow it never has any children (in the filtered Model).
    What I tried first was to check after the last child of a parent is processed whether its parent (in the filter model) has any children, and if not remove it.
    That doesnt work because filterAcceptsRow() has to be const. I tried all sorts of work-arounds with signals and slots but havent succeded.

    What should I do? It seems to me the desired behaviour is rather normal. At least it was default for filtering KListViews in KDE3.

    Thanks for your help!
    Quote Originally Posted by Bertolt Brecht
    What is the robbing of a bank compared to the founding of a bank?

  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: filtering a TreeView from buttom to top

    First of all you shouldn't cast the source model to QStandardItemModel...

    I think you'll need to compute the visibility of child items when deciding if a particular item is to be shown or not. You can cache the result somewhere (most probably using QModelIndex::internalPointer()), so that you can reuse it when making decisions about children.

  3. #3
    Join Date
    Jan 2006
    Location
    germany
    Posts
    75
    Thanks
    9
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11

    Default Re: filtering a TreeView from buttom to top

    Quote Originally Posted by wysota View Post
    First of all you shouldn't cast the source model to QStandardItemModel...

    I think you'll need to compute the visibility of child items when deciding if a particular item is to be shown or not.
    Isn't filterAcceptsRow() that place?
    You can cache the result somewhere (most probably using QModelIndex::internalPointer()), so that you can reuse it when making decisions about children.
    Well I can't write any data from filterAcceptsRow() because thats const

    Seems to me like the QSortFilterProxyModel is really incapable of doing what I want it to... Maybe I should just write my own subclassing QStandardItemModel, then I would have no porlbems using item-logic on the filterview's items and wouldn't have to cast the sourcemodel...
    Or would you recommend against that?

    Thanks for you help and sorry for the long time periods between answering (sadly I dont have mor time for developing free software).
    Quote Originally Posted by Bertolt Brecht
    What is the robbing of a bank compared to the founding of a bank?

  4. #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: filtering a TreeView from buttom to top

    Quote Originally Posted by soul_rebel View Post
    Isn't filterAcceptsRow() that place?
    Theoretically yes it is. The point is you have to do that when the proxy asks for visibility of the top most item. Of course you can do that elsewhere (like in a slot connected to dataChanged and a bunch of other signals), and simply return the decision in filterAcceptsRow(). That would solve your other problem.

    Well I can't write any data from filterAcceptsRow() because thats const
    Sure you can Either read about a "mutable" keyword or store data in an external object to fool the compiler.

    Seems to me like the QSortFilterProxyModel is really incapable of doing what I want it to... Maybe I should just write my own subclassing QStandardItemModel, then I would have no porlbems using item-logic on the filterview's items and wouldn't have to cast the sourcemodel...
    The "problem" is not with the filter, but with the architecture of the tree itself. When an item is shown there is no point in asking if its (grand)*children should be visible or not. That's why the view only asks about child items when it needs to.

  5. #5
    Join Date
    Jan 2006
    Location
    germany
    Posts
    75
    Thanks
    9
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11

    Default Re: filtering a TreeView from buttom to top

    Thanks for the help. I've read up on mutable and tried all sorts of dirty workarounds that couldnt do it....

    Now I am (back) to a more sensible approach I think: I set all top-level-items visible in filterAcceptsRow() and when rowsRemoved() is emitted I check whether a top-level remains empty to remove it then.

    Unfortunately that doesnt work either... here's the code: (the slot is connected to rowsRemoved(..):
    Qt Code:
    1. void rowsWereRemoved(const QModelIndex & parent, int start, int end)
    2. {
    3. if (parent != QModelIndex()) /// ! toplevel is being removed
    4. if (!hasChildren(parent))
    5. removeRow(parent.row(), parent.parent());
    6. }
    To copy to clipboard, switch view to plain text mode 
    It causes a segfault on filtering immediately leaving completely unusable dumps....
    So far all my attempts segfault as soon as a row is removed Is there something fundamentally flawed with my syntax?

    Thanks for your help!!
    Quote Originally Posted by Bertolt Brecht
    What is the robbing of a bank compared to the founding of a bank?

  6. #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: filtering a TreeView from buttom to top

    Calling removeRow() from within a proxy causes the call to be forwarded to the source model and I doubt this is what you want. Why don't you do what I suggested - calculate the visibility of the whole tree and cache it in the proxy so that all you do within filterAcceptsRow() and family is return the cached value.

  7. #7
    Join Date
    Jan 2006
    Location
    germany
    Posts
    75
    Thanks
    9
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11

    Default Re: filtering a TreeView from buttom to top

    Hmmm this is driving me nuts.... here are my attempts at "prefiltering"

    I have a member
    QMap <qint64, bool> visibleTable;
    that saves internalPointer<->visiblity;

    This is the extra function
    Qt Code:
    1. bool QBrowseViewFilterModel::myFilterAcceptsRow(int sourceRow,
    2. const QModelIndex &sourceParent)
    3. {
    4.  
    5. QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
    6. bool show = false;
    7.  
    8. if (sourceModel()->hasChildren(index0))
    9. {
    10. for (int i = 0; i < sourceModel()->rowCount(index0); ++i)
    11. if (myFilterAcceptsRow(i, index0))
    12. show = true; /// if any child is visible keep this visible
    13. } else
    14. {
    15. QModelIndex index1 = sourceModel()->index(sourceRow, 1, sourceParent);
    16. QModelIndex index2 = sourceModel()->index(sourceRow, 2, sourceParent);
    17.  
    18. show = true;
    19.  
    20. if (!sourceModel()->data(index0).toString().contains(filterRegExp()))
    21. show = false;
    22.  
    23. }
    24.  
    25. visibleTable.insert(index0.internalId(), show);
    26.  
    27. return show;
    28. }
    To copy to clipboard, switch view to plain text mode 
    the original filterAcceptsRow :
    Qt Code:
    1. bool QBrowseViewFilterModel::filterAcceptsRow(int sourceRow,
    2. const QModelIndex &sourceParent) const
    3. {
    4. QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent);
    5. return visibleTable[index0.internalId()];
    6. }
    To copy to clipboard, switch view to plain text mode 

    And I also overwrote invalidate and reset to actually start the recursion on the top-level-items:
    Qt Code:
    1. void QBrowseViewFilterModel::reset()
    2. {
    3. visibleTable.clear();
    4.  
    5. for (int i = 0; i < sourceModel()->rowCount(); ++i)
    6. myFilterAcceptsRow(i);
    7.  
    8. this->QSortFilterProxyModel::reset();
    9. }
    10.  
    11. void QBrowseViewFilterModel::invalidate()
    12. {
    13. visibleTable.clear();
    14.  
    15. for (int i = 0; i < sourceModel()->rowCount(); ++i)
    16. myFilterAcceptsRow(i);
    17.  
    18. this->QSortFilterProxyModel::invalidate();
    19. }
    To copy to clipboard, switch view to plain text mode 

    But now the table¹ is empty

    ... I hope I am not doing anything stupid there right now.... it is getting kind of late....

    Thanks for not giving up

    edit: ¹ I meant the view.
    Last edited by soul_rebel; 22nd April 2008 at 00:57.
    Quote Originally Posted by Bertolt Brecht
    What is the robbing of a bank compared to the founding of a bank?

  8. #8
    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: filtering a TreeView from buttom to top

    I'd do it differently. Connect to all signals telling you about changes in the source model. In a slot connected to the signal simply recompute the visibility of the changed subtree. Make sure your proxy is not used for sorting, so that it is purely a filtering proxy and you can be sure the layout of items won't change frequently. If you need sorting, apply another proxy over yours to provide the sorting layer. Your proxy shouldn't check if the source model has children but instead it should only determine visibility of its own items based on its own children (at least that's how I understand your use case). You should have a method that will be called recursively on subtrees which will compute visibility of a subtree and based on its result the visibility of the parent can be determined. It's not an easy task so it might prove easier if you do it outside your codebase on a clean and simple model with a simpler to make decision.

  9. #9
    Join Date
    Jan 2006
    Location
    germany
    Posts
    75
    Thanks
    9
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11

    Default Re: filtering a TreeView from buttom to top

    Man! After doing the craziest things you can do with qt, after fiddling around with beginRemoveRows, after like 20 different half-working approaches I can up with maybe the most simple solution, requiring no compiler-fooling, no extra data structures, no workarounds, no nothings:
    I simply start into a recursion while in filterAcceptsRow since that works on the source's items anyways!
    Here's the code, in case anyone ever goes through the same hell as me

    Qt Code:
    1. bool QBrowseViewFilterModel::filterAcceptsRow(int sourceRow,
    2. const QModelIndex &sourceParent) const
    3. {
    4. QModelIndex col0 = sourceModel()->index(sourceRow, 0, sourceParent);
    5.  
    6. /// do bottom to top filtering
    7. if (sourceModel()->hasChildren(col0))
    8. {
    9. for (int i=0; i < sourceModel()->rowCount(col0); ++i)
    10. if (filterAcceptsRow(i, col0))
    11. return true;
    12. return false;
    13. }
    14.  
    15. return sourceModel()->data(col0).toString().contains(filterRegExp()));
    16. }
    To copy to clipboard, switch view to plain text mode 

    Jeeze, I still cant believe it

    Thanks for reading my rambling and thanks for your time altogether!

    p.s.: I don't even need to do any dirty casting....
    Quote Originally Posted by Bertolt Brecht
    What is the robbing of a bank compared to the founding of a bank?

  10. #10
    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: filtering a TreeView from buttom to top

    Hmm.. isn't that what I suggested in the first place?

    I think you'll need to compute the visibility of child items when deciding if a particular item is to be shown or not. You can cache the result somewhere (most probably using QModelIndex::internalPointer()), so that you can reuse it when making decisions about children.
    Remember to cache the value or it will get reeeeeaaaaaaal slow when the depth of the tree increases.

  11. #11
    Join Date
    Jan 2006
    Location
    germany
    Posts
    75
    Thanks
    9
    Qt products
    Qt3 Qt4
    Platforms
    Unix/X11

    Default Re: filtering a TreeView from buttom to top

    Quote Originally Posted by wysota View Post
    Hmm.. isn't that what I suggested in the first place?
    Well, kind of
    From your latter posts I somehow got the impression it could not *just* be done in that function. (but that could have been my
    Remember to cache the value or it will get reeeeeaaaaaaal slow when the depth of the tree increases.
    Well, I thought about that. It sure would be the cleaner implementation but in my app the tree has exactly depth 2, always.
    So storing the visibilty-values in an extra list and iterating trough that everytime befor applying the filter won't really help much I think. And filtering is near instant right now(only avery small lagg), although there are nearly 20k items altogether in the list.
    Quote Originally Posted by Bertolt Brecht
    What is the robbing of a bank compared to the founding of a bank?

  12. #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: filtering a TreeView from buttom to top

    Quote Originally Posted by soul_rebel View Post
    Well, I thought about that. It sure would be the cleaner implementation but in my app the tree has exactly depth 2, always.
    Hmm... you could have said that earlier, you know... we could have solved the problem in 3 posts then

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.