Results 1 to 14 of 14

Thread: How to reset model without collapsing tree?

  1. #1
    Join Date
    Jun 2014
    Posts
    98
    Thanks
    43
    Thanked 4 Times in 4 Posts
    Platforms
    Windows

    Default How to reset model without collapsing tree?

    I have a QTreeView of a QStandardItemModel. I have a button that allows the user to move a row up in the tree among its siblings, and I do this using QStandardItem.takeRow() and QStandardItem.insertRow().

    In pseudocode, I have:
    Qt Code:
    1. model.beginResetModel()
    2.  
    3. #perform insert and take operations here
    4.  
    5. model.endResetModel()
    6. view.expandAll()
    To copy to clipboard, switch view to plain text mode 
    My concern is that I am doing something wrong because I need to put in the expandAll() for things to work: otherwise when I reset the model the view collapses the entire tree. Is there a way to reset my model without having to re-expand my tree?
    Last edited by neuronet; 20th February 2015 at 18:45.

  2. #2
    Join Date
    Mar 2009
    Location
    Brisbane, Australia
    Posts
    7,729
    Thanks
    13
    Thanked 1,610 Times in 1,537 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows
    Wiki edits
    17

    Default Re: How to reset model without collapsing tree?

    Removing a row and inserting a row should update the view correctly. The model should not reset.

  3. #3
    Join Date
    Jun 2014
    Posts
    98
    Thanks
    43
    Thanked 4 Times in 4 Posts
    Platforms
    Windows

    Default Re: How to reset model without collapsing tree?

    If I don't reset, subsequent selection behavior does not work properly, unfortunately (I programmatically keep the content that was moved selected, and without resetting the model this selection behavior is fubar). So I need it unfortunately. Well, I need *something*. Here is my 'moveRowUp' method:

    Qt Code:
    1. def moveRowUp(self, selectedIndexes):
    2. nameIndex = selectedIndexes[self.columnIndices["nameIndex"]]
    3. sourceRowNum = nameIndex.row()
    4. if sourceRowNum == 0:
    5. pass
    6. else:
    7. targetRowNum = sourceRowNum - 1
    8. self.todoModel.beginResetModel()
    9. indexParent = nameIndex.parent()
    10. if indexParent.isValid():
    11. itemParent = self.todoModel.itemFromIndex(indexParent)
    12. sourceRowItems = itemParent.takeRow(sourceRowNum)
    13. targetRowItems = itemParent.takeRow(targetRowNum)
    14. itemParent.insertRow(targetRowNum, sourceRowItems)
    15. itemParent.insertRow(sourceRowNum, targetRowItems) #targetRowItems)
    16. else:
    17. sourceRowItems = self.todoModel.takeRow(sourceRowNum)
    18. targetRowItems = self.todoModel.takeRow(targetRowNum)
    19. self.todoModel.insertRow(targetRowNum, sourceRowItems)
    20. self.todoModel.insertRow(sourceRowNum, targetRowItems) #targetRowItems)
    21.  
    22. self.todoModel.endResetModel()
    23. self.todoView.expandAll()
    24.  
    25. selectIndex = self.todoModel.index(targetRowNum, 0, indexParent)
    26. rowSelection = QtGui.QItemSelection(selectIndex, selectIndex)
    27. self.todoView.selectionModel().select(rowSelection, QtGui.QItemSelectionModel.Rows | QtGui.QItemSelectionModel.SelectCurrent)
    To copy to clipboard, switch view to plain text mode 

  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: How to reset model without collapsing tree?

    So what's wrong exactly with QAbstractItemModel::moveRows()?
    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.


  5. #5
    Join Date
    Jun 2014
    Posts
    98
    Thanks
    43
    Thanked 4 Times in 4 Posts
    Platforms
    Windows

    Default Re: How to reset model without collapsing tree?

    Wysota I am in PySide, which is bound to Qt 4.8. moveRows came with Qt 5.

    (Aside: even in Qt5, isn't moveRows just a virtual function? As it says int he docs "The base class implementation does nothing and returns false. If you implement your own model, you can reimplement this function if you want to support moving." )

  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: How to reset model without collapsing tree?

    Quote Originally Posted by neuronet View Post
    Wysota I am in PySide, which is bound to Qt 4.8. moveRows came with Qt 5.
    You still have beginMoveRows() and endMoveRows() so you can implement moveRows() yourself.
    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.


  7. #7
    Join Date
    Jun 2014
    Posts
    98
    Thanks
    43
    Thanked 4 Times in 4 Posts
    Platforms
    Windows

    Default Re: How to reset model without collapsing tree?

    wysota bear in mind this is a QStandardItemModel. We don't have to call beginInsertRows and endinsertRows, I believe. That said, even when I do overkill and include begin/endRemoveRows and begin/endInsertRows around the take/insert operattions, the behavior is the exact same. I still get the bad selection behavior after swapping the rows unless I reset the model, at which point I have to expandAll again.

    Is it not possible to reset while keeping the view expanded? If it is not, at least it's easy enough to just invoke expandAll: it just seemed inefficient.
    Last edited by neuronet; 21st February 2015 at 19:10.

  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: How to reset model without collapsing tree?

    The point is you don't have to reset. Using either insert or move commands is enough to update the model and keep the view in sync. If you observe something different then you probably have some flaw in your code somewhere. You shouldn't need to call beginResetModel and friends.
    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. #9
    Join Date
    Jun 2014
    Posts
    98
    Thanks
    43
    Thanked 4 Times in 4 Posts
    Platforms
    Windows

    Lightbulb Sscce

    Here an sscce that reproduces the problem. If I remove the resetmodel stuff, the selection behavior at the end of MyStandardTree.moveRowUp() does not work (instead of just selecting the moved row, it selects two rows, one correct, the other above the correct).

    Qt Code:
    1. from PySide import QtGui, QtCore
    2.  
    3. class MainWindow(QtGui.QMainWindow):
    4. def __init__(self):
    5. QtGui.QMainWindow.__init__(self, parent = None)
    6. self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
    7. self.view = MyStandardTree(self)
    8. self.addMoveAction()
    9. self.setCentralWidget(self.view)
    10.  
    11. def addMoveAction(self):
    12. itemUpAction=QtGui.QAction("Move up", self)
    13. itemUpAction.setIcon(QtGui.QIcon("images/moveItemUp.png"))
    14. itemUpAction.triggered.connect(self.moveRowUp)
    15. self.toolbar = self.addToolBar("Mover")
    16. self.toolbar.addAction(itemUpAction)
    17.  
    18. def moveRowUp(self):
    19. selectedIndexes = self.view.selectedIndexes()
    20. self.view.moveRowUp(selectedIndexes[0])
    21.  
    22. class MyStandardTree(QtGui.QTreeView):
    23. def __init__(self, parent = None):
    24. QtGui.QTreeView.__init__(self, parent)
    25.  
    26. self.model = QtGui.QStandardItemModel()
    27. self.model.setHorizontalHeaderLabels(['Title', 'Summary'])
    28.  
    29. rootItem = self.model.invisibleRootItem()
    30. titles = ['Parent', 'Child0', 'Child1', 'Child2']
    31. summaries =['I am the parent', 'I am child0', 'I am child1', 'I am child2']
    32. dataItems = [list(datPair) for datPair in zip(titles, summaries)]
    33. parentRow = [QtGui.QStandardItem(x) for x in dataItems[0]]
    34. child0 = [QtGui.QStandardItem(x) for x in dataItems[1]]
    35. child1 = [QtGui.QStandardItem(x) for x in dataItems[2]]
    36. child2 = [QtGui.QStandardItem(x) for x in dataItems[3]]
    37. rootItem.appendRow(parentRow)
    38. parentRow[0].appendRow(child0)
    39. parentRow[0].appendRow(child1)
    40. parentRow[0].appendRow(child2)
    41.  
    42. self.setModel(self.model)
    43. self.expandAll()
    44.  
    45. def moveRowUp(self, index):
    46. '''Moves selected row up'''
    47. sourceRowNum = index.row()
    48. if sourceRowNum == 0:
    49. pass
    50. else:
    51. targetRowNum = sourceRowNum - 1
    52. self.model.beginResetModel()
    53. indexParent =index.parent()
    54. if indexParent.isValid():
    55. itemParent = self.model.itemFromIndex(indexParent)
    56. sourceRowItems = itemParent.takeRow(sourceRowNum)
    57. targetRowItems = itemParent.takeRow(targetRowNum)
    58. itemParent.insertRow(targetRowNum, sourceRowItems)
    59. itemParent.insertRow(sourceRowNum, targetRowItems)
    60. else:
    61. sourceRowItems = self.todoModel.takeRow(sourceRowNum)
    62. targetRowItems = self.todoModel.takeRow(targetRowNum)
    63. self.model.insertRow(targetRowNum, sourceRowItems)
    64. self.model.insertRow(sourceRowNum, targetRowItems)
    65.  
    66. self.model.endResetModel()
    67. self.expandAll()
    68.  
    69. selectIndex = self.model.index(targetRowNum, 0, indexParent)
    70. rowSelection = QtGui.QItemSelection(selectIndex, selectIndex)
    71. selectionModel = QtGui.QItemSelectionModel.Rows | QtGui.QItemSelectionModel.SelectCurrent
    72. self.selectionModel().select(rowSelection, selectionModel)
    73.  
    74. import sys
    75. app = QtGui.QApplication(sys.argv)
    76. myTree = MainWindow()
    77. myTree.show()
    78. sys.exit(app.exec_())
    To copy to clipboard, switch view to plain text mode 
    Last edited by neuronet; 22nd February 2015 at 02:39.

  10. #10
    Join Date
    Jun 2014
    Posts
    98
    Thanks
    43
    Thanked 4 Times in 4 Posts
    Platforms
    Windows

    Default Re: Sscce

    deleted post....(link to SO post I subsequently deleted once ChrisW67 pointed out the problem was a boner on my part)
    Last edited by neuronet; 22nd February 2015 at 06:20.

  11. #11
    Join Date
    Mar 2009
    Location
    Brisbane, Australia
    Posts
    7,729
    Thanks
    13
    Thanked 1,610 Times in 1,537 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows
    Wiki edits
    17

    Default Re: Sscce

    Your code as published works as expected using both PySide and PyQt4 on my machine (PySide 1.1.2 and Qt 4.8.5 on Linux)
    If I remove the unnecessary calls to beginResetModel, endResetModel, and expandAll the move behaviour continues to be OK, the tree does not collapse, but your re-selection logic is wrong (tending to select two rows).

    Here is my take on your moveRow function:
    Qt Code:
    1. def moveRowUp(self, index):
    2. '''Moves selected row up'''
    3. sourceRowNum = index.row()
    4. if sourceRowNum > 0:
    5. targetRowNum = sourceRowNum - 1
    6. indexParent =index.parent()
    7. if indexParent.isValid():
    8. itemParent = self.model.itemFromIndex(indexParent)
    9. sourceRowItems = itemParent.takeRow(sourceRowNum)
    10. itemParent.insertRow(targetRowNum, sourceRowItems)
    11. else:
    12. sourceRowItems = self.todoModel.takeRow(sourceRowNum)
    13. self.model.insertRow(targetRowNum, sourceRowItems)
    14.  
    15. selectIndex = self.model.index(targetRowNum, 0, indexParent)
    16. self.selectionModel().clear()
    17. self.selectionModel().select(selectIndex, QtGui.QItemSelectionModel.Rows | QtGui.QItemSelectionModel.SelectCurrent)
    To copy to clipboard, switch view to plain text mode 
    Moving a single row up involves removing and inserting only a single row, not two.

  12. The following user says thank you to ChrisW67 for this useful post:

    neuronet (22nd February 2015)

  13. #12
    Join Date
    Jun 2014
    Posts
    98
    Thanks
    43
    Thanked 4 Times in 4 Posts
    Platforms
    Windows

    Default Re: Sscce

    Chris thanks for helping me understand the logic of row moving better.

    Strangely, with your new and improved moveUpRow, when I have a row with children, when I move that row up among its siblings the moved row collapses. Example follows.

    Further, if I add children to its children, the child also collapses. So it collapses the node that has been moved, and all its descendents. But I suppose it is more efficient to recursively expand() on that one node and its descendents, than to run expandAll(), right? I just wonder why things collapse at all with movement.

    Qt Code:
    1. from PySide import QtGui, QtCore
    2.  
    3. class MainWindow(QtGui.QMainWindow):
    4. def __init__(self):
    5. QtGui.QMainWindow.__init__(self, parent = None)
    6. self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
    7. self.view = MyStandardTree(self)
    8. self.addMoveAction()
    9. self.setCentralWidget(self.view)
    10.  
    11. def addMoveAction(self):
    12. itemUpAction=QtGui.QAction("Move up", self)
    13. itemUpAction.setIcon(QtGui.QIcon("images/moveItemUp.png"))
    14. itemUpAction.triggered.connect(self.moveRowUp)
    15. self.toolbar = self.addToolBar("Mover")
    16. self.toolbar.addAction(itemUpAction)
    17.  
    18. def moveRowUp(self):
    19. selectedIndexes = self.view.selectedIndexes()
    20. self.view.moveRowUp(selectedIndexes[0])
    21.  
    22. class MyStandardTree(QtGui.QTreeView):
    23. def __init__(self, parent = None):
    24. QtGui.QTreeView.__init__(self, parent)
    25.  
    26. self.model = QtGui.QStandardItemModel()
    27. self.model.setHorizontalHeaderLabels(['Title', 'Summary'])
    28.  
    29. rootItem = self.model.invisibleRootItem()
    30. titles = ['Parent', 'Child0', 'Child1', 'Child2', 'Child10', 'Child11']
    31. summaries =['I am the parent', 'I am child0', 'I am child1', 'I am child2', 'foo0', 'foo1']
    32. dataItems = [list(datPair) for datPair in zip(titles, summaries)]
    33. parentRow = [QtGui.QStandardItem(x) for x in dataItems[0]]
    34. child0 = [QtGui.QStandardItem(x) for x in dataItems[1]]
    35. child1 = [QtGui.QStandardItem(x) for x in dataItems[2]]
    36. child2 = [QtGui.QStandardItem(x) for x in dataItems[3]]
    37. child10 = [QtGui.QStandardItem(x) for x in dataItems[4]]
    38. child11 = [QtGui.QStandardItem(x) for x in dataItems[5]]
    39. rootItem.appendRow(parentRow)
    40. parentRow[0].appendRow(child0)
    41. parentRow[0].appendRow(child1)
    42. parentRow[0].appendRow(child2)
    43. child1[0].appendRow(child10)
    44. child1[0].appendRow(child11)
    45.  
    46. self.setModel(self.model)
    47. self.expandAll()
    48.  
    49. def moveRowUp(self, index):
    50. '''Improved version'''
    51. sourceRowNum = index.row()
    52. if sourceRowNum > 0:
    53. targetRowNum = sourceRowNum - 1
    54. indexParent =index.parent()
    55. if indexParent.isValid():
    56. itemParent = self.model.itemFromIndex(indexParent)
    57. sourceRowItems = itemParent.takeRow(sourceRowNum)
    58. itemParent.insertRow(targetRowNum, sourceRowItems)
    59. else:
    60. sourceRowItems = self.model.takeRow(sourceRowNum)
    61. self.model.insertRow(targetRowNum, sourceRowItems)
    62.  
    63. selectIndex = self.model.index(targetRowNum, 0, indexParent)
    64. self.selectionModel().clear()
    65. self.selectionModel().select(selectIndex, QtGui.QItemSelectionModel.Rows | QtGui.QItemSelectionModel.SelectCurrent)
    66.  
    67. import sys
    68. app = QtGui.QApplication(sys.argv)
    69. myTree = MainWindow()
    70. myTree.show()
    71. sys.exit(app.exec_())
    To copy to clipboard, switch view to plain text mode 
    Last edited by neuronet; 22nd February 2015 at 07:02.

  14. #13
    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: Sscce

    If you take an item out of the model then its selection will not be retained. That's why beginMoveRows and endMoveRows should be used instead. No idea if you can do it with QStandardItemModel though.
    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:

    neuronet (23rd February 2015)

  16. #14
    Join Date
    Jun 2014
    Posts
    98
    Thanks
    43
    Thanked 4 Times in 4 Posts
    Platforms
    Windows

    Default Re: Sscce

    wysota I just get weird errors when I start trying to insert those methods in my QStandardItemModel code (not even Python errors, but weird errors about invalid indexes sent directly from Qt to my command line). At any rate, aside from collapsing things, it seems to be working as expected, given what I know about QStandardItemModel which I'm pretty sure takes care of begin/end calls. I'll start a separate question if I need to, as the problem has shifted as I realize now you were right I don't need to reset.

Similar Threads

  1. SQL Tree Model Help
    By MTK358 in forum Newbie
    Replies: 9
    Last Post: 22nd June 2015, 16:02
  2. QTreeView expansion after model reset
    By d_stranz in forum Qt Programming
    Replies: 3
    Last Post: 4th December 2014, 00:42
  3. Replies: 1
    Last Post: 29th August 2013, 06:41
  4. Replies: 5
    Last Post: 10th March 2011, 21:06
  5. Replies: 3
    Last Post: 31st March 2008, 22:23

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.