Search code examples
pythonpyqtpyqt5qtreewidget

Resize widget inside QTreeWidget (set with setItemWidget)?


I have a QListWidget inside a QTreeWidget and I want it to automatically adjust its height as rows are added or removed. I got the QListWidget autosizing working but it breaks when it's inside the QTreeWidget, the QListWidget grows in size but the QTreeWidget doesn't adjust to it properly and other rows get covered.

from PyQt5 import QtCore, QtGui, QtWidgets, sip
from PyQt5.QtCore import Qt


class MyListWidget(QtWidgets.QListWidget):
    def sizeHint(self):
        width = super().sizeHint().width()
        height = sum([self.sizeHintForRow(i) for i in range(self.count())]) + 5
        return QtCore.QSize(width, height)


app = QtWidgets.QApplication([])

tree = QtWidgets.QTreeWidget()

item1 = QtWidgets.QTreeWidgetItem(tree, ['item1'])
item2 = QtWidgets.QTreeWidgetItem(tree, ['item2'])
item3 = QtWidgets.QTreeWidgetItem(tree, ['item3'])

list_widget = MyListWidget()
list_widget.addItems(list('abcd'))

tree.setItemWidget(item1, 0, list_widget)

button = QtWidgets.QPushButton("Add row")
button.clicked.connect(lambda: [list_widget.addItem("New row"),
                                list_widget.adjustSize(),
                                tree.adjustSize(),
                                tree.updateGeometry()
                                ])

container = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout()
container.setLayout(layout)

layout.addWidget(tree)
layout.addWidget(button)

container.show()
list_widget.show()
app.exec_()

Initial Output:

enter image description here

Output After clicking Add New:

enter image description here

As you can see in the second image, item2 is now covered by the expanded list widget, the desired output would be to shift the item2, such that after adding new item inside the list widget doesn't cover the item below it in the same tree widget.


Solution

  • You can subclass the tree and connect the model's rowsInserted and rowsRemoved signals, then update geometries accordingly, including that of the item:

    class TreeWidget(QtWidgets.QTreeWidget):
        def setItemWidget(self, item, column, widget):
            super().setItemWidget(item, column, widget)
            if isinstance(widget, MyListWidget):
                widget.model().rowsInserted.connect(
                    lambda: self.updateItemWidget(item, column))
                widget.model().rowsRemoved.connect(
                    lambda: self.updateItemWidget(item, column))
    
        def updateItemWidget(self, item, column):
            widget = self.itemWidget(item, column)
            item.setSizeHint(column, widget.sizeHint())
            self.updateGeometries()