1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Anuncie Aqui
    Anuncie aqui você Também: fdantas@4each.com.br

[Python] Qt - Model takeRow corrupts associated widget in view

Discussão em 'Python' iniciado por Stack, Janeiro 11, 2025.

  1. Stack

    Stack Membro Participativo

    I'm currently dealing with a problem in my PySide6 application. I have a tree view, which should support moving items with drag&drop, but I'm also using custom widgets for some of the items. Now neither of these things is hard to implement, I simplified the tree view item moving to the following code:

    class MoveItemTreeView(QTreeView):
    def moveItem(self, source_row: int, destination_row: int) -> None:
    """Move an item in the tree widget from source to destination."""
    model = typing.cast(QStandardItemModel, self.model())
    columns = model.takeRow(source_row)
    model.insertRow(destination_row, columns)


    Which simply takes the item out and places it after the destination row.
    This works correctly, and provides the desired behavior, my issue is that this operations seems to remove any widgets associated with that index using setIndexWidget, the widgets simply disappear.

    I'll provide an image showcasing the behavior:

    [​IMG]

    This is the minimal reproducible example that generated that image:

    import sys
    import typing
    from PySide6.QtGui import QStandardItem, QStandardItemModel
    from PySide6.QtWidgets import (
    QApplication,
    QLabel,
    QMainWindow,
    QTreeView,
    QVBoxLayout,
    QWidget,
    )

    class MoveItemTreeView(QTreeView):
    def moveItem(self, source_row: int, destination_row: int) -> None:
    """Move an item in the tree widget from source to destination."""
    model = typing.cast(QStandardItemModel, self.model())

    columns = model.takeRow(source_row)
    model.insertRow(destination_row, columns)

    class MainWindow(QMainWindow):
    def __init__(self) -> None:
    super().__init__()

    central_widget = QWidget(self)
    self.setCentralWidget(central_widget)
    layout = QVBoxLayout(central_widget)

    tree_view = MoveItemTreeView()
    layout.addWidget(tree_view)

    model = QStandardItemModel()
    tree_view.setModel(model)

    # Add some items
    for i in range(5):
    item = QStandardItem(f"Item {i}")
    model.appendRow(item)

    for j in range(3):
    child = QStandardItem()
    item.appendRow(child)
    tree_view.setIndexWidget(child.index(), QLabel(f"Child {j}"))

    tree_view.moveItem(2, 3)
    tree_view.expandAll()
    self.setGeometry(100, 100, 600, 600)

    class App(QApplication):
    def __init__(self) -> None:
    super().__init__()

    self.window = MainWindow()
    self.window.show()

    if __name__ == "__main__":
    app = App()
    sys.exit(app.exec())


    I thought this may be an issue of the widget being associated with a specific row, and since the item is not at that row anymore, the association would be broken. But the operation shifts around the other items, and those still have valid widgets.

    I tried manually reassociating the widgets in moveItem, gathering them before the move and using setIndexWidget after it, but that only resulted in freezes and crashes.

    My question is therefore this: Am I doing something wrong, is this an undefined or undocumented behavior or did I encounter a bug?

    I know I can simply regenerate the widgets, I'm simply wondering what exactly is going wrong, because I didn't find anything about this behavior in the documentation.

    Edit: I know that using setIndexWidget is usually not appropriate, and a custom QStyledItemDelegate is preferred, in my use-case, I use custom widgets that act as editors - a task the delegate would be ideal for, the issue was that the delegate doesn't really offer a nice way to "always show" the editor, instead being dependent on the view's editor triggers. I tried to make it work, but simply using a widget was a much simpler (and in my opinion, neater) solution, especially since it just works.

    Continue reading...

Compartilhe esta Página