0

I'm trying to add custom widget (image below) as item delegate in QTreeview. I do see the size of the treeItem getting bigger but my widget does not show up. Not sure what am I missing here. Thanks in advance for all your suggestions.

Custom Widget

item delegate qwidget

Here is the code

ItemNode (node for QAbstractTeeModel)

    class BaseNode:
        def __init__(self, data, parent=None):
            self._data = data
            self._children = list()
            self._parent = None
            if parent:
                parent.addChild(self)

        def addChild(self, node):
            self._children.append(node)
            node._parent = self

        def insertChildren(self, position, children):
            if position < 0:
                return False
            for child in children:
                child._parent = self
                self._children.insert(position, child)
            return True

        def removeChildren(self, position, count):
            if position < 0 or position > len(self._children):
                return False
            for i in range(count):
                child = self._children.pop(position)
                child._parent = None
            return True

        def child(self, row):
            if row < self.rowCount():
                return self._children[row]

        def rowCount(self):
            return len(self._children)

        def columnCount(self):
            return len(self._data)

        def parent(self):
            return self._parent

        def index(self):
            if self._parent:
                return self._parent._children.index(self)
            return 0

        def data(self, column):
            return self._data[column]

QAbstractItemModel

    class NodeModel(QtCore.QAbstractItemModel):
        def __init__(self, parent=None):
            super().__init__(parent)
            self.headers = ["Node"]
            self.root_node = BaseNode(["root"])

        def getNode(self, index):
            if index.isValid():
                node = index.internalPointer()
                if node:
                    return node
            return self.root_node

        def rowCount(self, parent=QtCore.QModelIndex()):
            return self.root_node.rowCount()

        def columnCount(self, parent=QtCore.QModelIndex()):
            return self.root_node.columnCount()

        def data(self, index, role):
            if not index.isValid():
                return
            node = self.getNode(index)
            if role == QtCore.Qt.EditRole or role == QtCore.Qt.EditRole:
                return node

        def headerData(self, section, orientation, role):
            if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
                return self.headers[section]

        def flags(self, index):
            flgs = 0
            if index.isValid():
                flgs = QtCore.Qt.ItemIsEditable
                flgs |= QtCore.Qt.ItemIsEnabled
                flgs |= QtCore.Qt.DisplayRole
            return flgs

        def parent(self, child):
            node = self.getNode(child)
            parent = node.parent()
            if parent == self.root_node:
                return QtCore.QModelIndex()
            return self.createIndex(parent.index(), 0, parent)

        def index(self, row, column, parent):
            parent = self.getNode(parent)
            child = parent.child(row)
            if child:
                return self.createIndex(row, column, child)
            return QtCore.QModelIndex()

        def insertRows(self, row, count, parent=QtCore.QModelIndex(), children=None):
            parent_node = self.getNode(parent)
            self.beginInsertRows(parent, row, row + count - 1)

            result = parent_node.insertChildren(row, children)
            self.endInsertRows()
            return result

        def removeRows(self, row, count, parent=QtCore.QModelIndex()):
            parent_node = self.getNode(parent)
            self.beginRemoveRows(parent, row, row + count - 1)
            success = parent_node.removeChildren(row, count)
            self.endRemoveRows()
            return success

ItemEditor (QWidget for Item Delegate

class ItemEditor(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_Form()  # this is a UI containing 
        self.ui.setupUi(self)
        self.setAutoFillBackground(True)

Item Delegate

    class NodeDelegate(QStyledItemDelegate):
        def __init__(self, parent=None):
            super().__init__(parent)

        def createEditor(self, parent, option, index):
            if index.column() == 0:
                editor = ItemEditor()
                return editor
            else:
                return super().createEditor(self, parent, option, index)

        def setEditorData(self, editor, index):
            if index.column() == 0:
                pass
            else:
                return super().setEditorData(editor, index)

        def setModelData(self, editor, model, index):
            if index.column() == 0:
                pass
            else:
                super().setModelData(editor, model, index)

        def sizeHint(self, option, index):
            editor = ItemEditor()
            return editor.sizeHint()

        def updateEditorGeometry(self, editor, option, index):
            editor.setGeometry(option.rect)

Implementation in TreeView

    app = QtWidgets.QApplication([])
    tw = QtWidgets.QTreeView()
    model = NodeModel()
    tw.setModel(model)
    for i in "abcd":
        print(i)
        node = BaseNode(data=[i])
        index = model.index(model.root_node.rowCount(), 0, QtCore.QModelIndex())
        model.insertRows(1, 1, index, children=[node])
    
    item_delegate = NodeDelegate()
    tw.setItemDelegateForRow(0, item_delegate)
 
    tw.show()
    app.exec_()
Rahul
  • 21
  • 1
  • Typo: `editor = ItemEditor(parent)`. Besides, showing a delegate that big doesn't seem a very good idea. – musicamante Sep 29 '22 at 17:36
  • thanks @musicamante, thanks for your quick reply, I tried that doesn't seems to work. I will not have too much data in this treeview so I was hoping it would be ok to use this dialog box. – Rahul Sep 29 '22 at 17:41
  • Qwidget does show up, If I double click on the row – Rahul Sep 29 '22 at 17:48
  • 1. As said, an interface that big is not a good idea from the UX perspective: delegates should be reasonably relatively small, especially for a tree view, as it would make the UI cumbersome and very uncomfortable to use. Instead, consider using a *real* dialog, or a side panel that updates its fields based on the current index. 2. If you want to immediately show editors, call [`openPersistentEditor()`](//doc.qt.io/qt-5/qabstractitemview.html#openPersistentEditor). 3. Creating a new instance of `ItemEditor` in `sizeHint()`, is a terrible idea, since it's potentially called *hundreds* of times. – musicamante Sep 29 '22 at 17:57
  • @musicamante. 1. makes sense. 2. openPersistentEditor doesn't help :( 3. Could you suggest me what do I replace it with? Coz if I use "self.sender()" it doesnt help – Rahul Sep 29 '22 at 19:27
  • 2. if it doesn't work, you're probably calling it too early (like before setting the delegate) or using the wrong index (remember that to get a valid `index()` from a *child* item of a tree model you need to call it with the correct parent: `row, column, parentIndex`; 3. try removing that `sizeHint()` override, if the UI of the editor is properly done (hence, using layout managers), it should adapt the size automatically. – musicamante Sep 29 '22 at 19:50
  • @musicamante - Everything is working as expected after making the changes you suggested in the last comment. was calling openpersistentEditor in the wrong place and removed the sizeHint() from delegate and it still work. Thanks a lot :) – Rahul Sep 29 '22 at 20:51

0 Answers0