Results 1 to 6 of 6

Thread: QTreeWidget selectedItems ordered parent-child

  1. #1
    Join Date
    May 2013
    Posts
    321
    Thanks
    9
    Thanked 8 Times in 8 Posts
    Qt products
    Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default QTreeWidget selectedItems ordered parent-child

    Hi,
    I use the selectedItems() function to get the actual selected items to remove them.
    The problem is if you have a tree like that :

    A
    -B
    --C

    You select A then B then C and then delete, since a for loop is used that delete A then B and C of the for loop are not valid and I got a crash.
    Qt Code:
    1. void CMainEditorWindow::Delete()
    2. {
    3. // Get the selected items.
    4. QList< QTreeWidgetItem* > SelectedItems = SceneOutlinerWidget->GetSelectedItems();
    5.  
    6. // Delete each actor.
    7. foreach( QTreeWidgetItem* Item, SelectedItems )
    8. {
    9. CSceneOutlinerItem* SceneOutlinerItem = static_cast< CSceneOutlinerItem* >( Item );
    10. MainEditorUndoStack->push( new CUndoCommandDeleteActor( SceneOutlinerItem->GetUniqueID() ) );
    11. }
    12. }
    To copy to clipboard, switch view to plain text mode 
    Is it possible to have selected items but remove item found in children of other or that needs to be implemented in a recursive function manually ?
    Thanks for the help

  2. #2
    Join Date
    May 2012
    Posts
    136
    Thanks
    2
    Thanked 27 Times in 24 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: QTreeWidget selectedItems ordered parent-child

    You can use a reverse for loop, start with deleting the last item first

  3. #3
    Join Date
    May 2013
    Posts
    321
    Thanks
    9
    Thanked 8 Times in 8 Posts
    Qt products
    Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: QTreeWidget selectedItems ordered parent-child

    That's not safe because if you select C then B then A delete will crash because reverse loop will start to delete A then B then C.

  4. #4
    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: QTreeWidget selectedItems ordered parent-child

    Make a separate method to delete each item. The method should take the item to be deleted, along with a reference to a QList of deleted items. The first time through the foreach() loop, this list will be empty. Inside the method, check to see if the current item is on the list. If it is, do nothing and return. If it isn't, check to see if it has children. For each child, call the deletion method again (recursively), passing the child item and list. After you run out of children, push the current item onto the list and delete the item.

    Qt Code:
    1. // Pseudocode
    2.  
    3. void CMainEditorWindow::DeleteItem( CSceneOutlinerItem * item, QList< CSceneOutlinerItem * > & deletedItems )
    4. {
    5. if ( deletedItems contains item )
    6. return;
    7.  
    8. if ( item has children )
    9. {
    10. foreach ( CSceneOutlinerItem * child, children of item )
    11. {
    12. DeleteItem( child, deletedItems );
    13. }
    14. }
    15.  
    16. // Now, delete the current item
    17. deletedItems.push_back( item );
    18. MainEditorUndoStack->push( new CUndoCommandDeleteActor( item->GetUniqueID() ) );
    19. }
    20.  
    21. void CMainEditorWindow::Delete()
    22. {
    23. // Get the selected items.
    24. QList< QTreeWidgetItem* > SelectedItems = SceneOutlinerWidget->GetSelectedItems();
    25.  
    26. QList< CSceneOutlinerItem * > deletedItems;
    27.  
    28. // Delete each actor.
    29. foreach( QTreeWidgetItem* Item, SelectedItems )
    30. {
    31. CSceneOutlinerItem* SceneOutlinerItem = static_cast< CSceneOutlinerItem* >( Item );
    32. DeleteItem( SceneOutlinerItem, deletedItems );
    33.  
    34. }
    35. }
    To copy to clipboard, switch view to plain text mode 

    By keeping the list of deleted items and checking it before you try to do anything, you guarantee that no matter what order the items appear on the selection list, you will delete each item's children first, and you won't try to delete anything twice.

    There are probably other ways to do this, but this is pretty straightforward. You can figure out which QTreeWidget / QTreeWidgetItem methods to use to travel down the tree to find the children of the current item.
    Last edited by d_stranz; 22nd November 2014 at 21:42.

  5. #5
    Join Date
    May 2013
    Posts
    321
    Thanks
    9
    Thanked 8 Times in 8 Posts
    Qt products
    Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: QTreeWidget selectedItems ordered parent-child

    Ok yea, that must be made yourself, looks like the only way.
    Since CUndoCommandDeleteActor stores the children of the deleted actor so the best way is to find recursively to keep only the parent if child and parent selected.

  6. #6
    Join Date
    May 2013
    Posts
    321
    Thanks
    9
    Thanked 8 Times in 8 Posts
    Qt products
    Qt5
    Platforms
    MacOS X Unix/X11 Windows

    Default Re: QTreeWidget selectedItems ordered parent-child

    Here the solution I wrote :
    Qt Code:
    1. bool RecursiveFindItem( QTreeWidgetItem* Item, QTreeWidgetItem* TestItem )
    2. {
    3. // Check if the item is the same.
    4. if( Item == TestItem )
    5. return true;
    6.  
    7. // For each child of the item.
    8. for( int i = 0; i < Item->childCount(); ++i )
    9. {
    10. // Check if we found the item.
    11. if( RecursiveFindItem( Item->child( i ), TestItem ) )
    12. return true;
    13. }
    14.  
    15. // Return not found.
    16. return false;
    17. }
    18.  
    19. void CSceneOutlinerWidget::DeleteSelectedActors()
    20. {
    21. // Find unique items.
    22. QList< QTreeWidgetItem* > UniqueItems;
    23. foreach( QTreeWidgetItem* Item, m_SceneOutlinerTree->selectedItems() )
    24. {
    25. // Find the selected item in the list.
    26. bool ItemFound = false;
    27. foreach( QTreeWidgetItem* UniqueItem, UniqueItems )
    28. {
    29. if( RecursiveFindItem( UniqueItem, Item ) )
    30. {
    31. ItemFound = true;
    32. break;
    33. }
    34. }
    35.  
    36. // Remove the item if found or add in the list.
    37. if( ItemFound )
    38. UniqueItems.removeOne( Item );
    39. else
    40. UniqueItems.append( Item );
    41. }
    42.  
    43. // Delete each unique item.
    44. foreach( QTreeWidgetItem* Item, UniqueItems )
    45. {
    46. CSceneOutlinerItem* SceneOutlinerItem = static_cast< CSceneOutlinerItem* >( Item );
    47. CMainEditorWindow::GetUndoStack()->push( new CUndoCommandDeleteActor( SceneOutlinerItem->GetUniqueID() ) );
    48. }
    49. }
    To copy to clipboard, switch view to plain text mode 
    That works but the I surely missed something because if I select A then B then C, only A is removed, if I select C then B then A I got 3 item to delete, normally only A is needed.

    EDIT : The final solution working :
    Qt Code:
    1. bool RecursiveFindItem( QTreeWidgetItem* Item, QTreeWidgetItem* TestItem )
    2. {
    3. // Check if the item is the same.
    4. if( Item == TestItem )
    5. return true;
    6.  
    7. // For each child of the item.
    8. for( int i = 0; i < Item->childCount(); ++i )
    9. {
    10. // Check if we found the item.
    11. if( RecursiveFindItem( Item->child( i ), TestItem ) )
    12. return true;
    13. }
    14.  
    15. // Return not found.
    16. return false;
    17. }
    18.  
    19. void RecursiveRemoveUniqueItems( QTreeWidgetItem* Item, QList< QTreeWidgetItem* >* UniqueItems )
    20. {
    21. // Remove the item if found in the unique list.
    22. foreach( QTreeWidgetItem* UniqueItem, *UniqueItems )
    23. {
    24. if( UniqueItem == Item )
    25. {
    26. UniqueItems->removeOne( UniqueItem );
    27. break;
    28. }
    29. }
    30.  
    31. // For each child of the item.
    32. for( int i = 0; i < Item->childCount(); ++i )
    33. RecursiveRemoveUniqueItems( Item->child( i ), UniqueItems );
    34. }
    35.  
    36. void CSceneOutlinerWidget::DeleteSelectedActors()
    37. {
    38. // Find unique items.
    39. QList< QTreeWidgetItem* > UniqueItems;
    40. foreach( QTreeWidgetItem* Item, m_SceneOutlinerTree->selectedItems() )
    41. {
    42. // Remove item and children if already in unique list.
    43. RecursiveRemoveUniqueItems( Item, &UniqueItems );
    44.  
    45. // Search the item.
    46. bool ItemFound = false;
    47. foreach( QTreeWidgetItem* UniqueItem, UniqueItems )
    48. {
    49. if( RecursiveFindItem( UniqueItem, Item ) )
    50. {
    51. ItemFound = true;
    52. break;
    53. }
    54. }
    55.  
    56. // Check if the item is found.
    57. if( ItemFound )
    58. continue;
    59.  
    60. // Add the item in the list.
    61. UniqueItems.append( Item );
    62. }
    63.  
    64. // Delete each unique item.
    65. foreach( QTreeWidgetItem* Item, UniqueItems )
    66. {
    67. CSceneOutlinerItem* SceneOutlinerItem = static_cast< CSceneOutlinerItem* >( Item );
    68. CMainEditorWindow::GetUndoStack()->push( new CUndoCommandDeleteActor( SceneOutlinerItem->GetUniqueID() ) );
    69. }
    70. }
    To copy to clipboard, switch view to plain text mode 
    Last edited by Alundra; 23rd November 2014 at 17:19.

Similar Threads

  1. Parent child relationship in Qt
    By TheIndependentAquarius in forum Newbie
    Replies: 18
    Last Post: 14th July 2011, 09:43
  2. Child-Parent Widget??
    By anupamgee in forum Qt Programming
    Replies: 1
    Last Post: 11th May 2009, 14:17
  3. Parent Child for window
    By febil in forum Qt Programming
    Replies: 6
    Last Post: 1st April 2009, 06:00
  4. resizeEvent from parent to child?
    By ChasW in forum Qt Programming
    Replies: 3
    Last Post: 11th February 2007, 19:21
  5. Parent-child-problems ;)
    By nupul in forum Qt Programming
    Replies: 11
    Last Post: 9th May 2006, 15:03

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.