To set widgets on children items on QTreeView

IsaacS picture IsaacS · Mar 8, 2013 · Viewed 8.2k times · Source

Thanks to this thread, I'm able to add widgets to 2nd or later column of QAbstractItemView (in my example QTreeView) of top level items of a view.

But is it possible to add widgets to the children items?

Here's what I've tried which partly went well:

#!/usr/bin/env python
import os

from PyQt4.QtCore import QModelIndex, Qt
from PyQt4.QtGui import QApplication, QItemSelectionModel, \
                        QPushButton, QStandardItem, \
                        QStandardItemModel, QTreeView
from PyQt4.uic import loadUi


class PrvTreeviewNest(QTreeView):
    def __init__(self):
        super(PrvTreeviewNest, self).__init__()

        loadUi('/home/user/yourproject/resource/treeview_nest.ui')

        # row can be 0 even when it's more than 0.
        self._datamodel = QStandardItemModel(0, 2)
        self.setModel(self._datamodel)

        for i in range(4):
            self._add_widget(i + 1)

        self.show()

    def _add_widget(self, n):
        std_item = QStandardItem('{}th item'.format(n))
        self._datamodel.setItem(n, 0, std_item)

        node_widget = QPushButton('{}th button'.format(n))
        qindex_widget = self._datamodel.index(n, 1, QModelIndex())
        self.setIndexWidget(qindex_widget, node_widget)

        if n == 2:
            std_item_child = QStandardItem('child')
            std_item.appendRow(std_item_child)

            node_widget_child = QPushButton('petit button')
            qindex_widget_child = self._datamodel.index(n, 1, QModelIndex())
            self.setIndexWidget(qindex_widget_child, node_widget_child)

if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    # window = TreeviewWidgetSelectProve()
    window = PrvTreeviewNest()
    # window = TreeviewWidgetSelectProve()
    window.resize(320, 240)
    # window.show();
    window.setWindowTitle(
         QApplication.translate("toplevel", "Top-level widget"))
    # window.add_cols()

    sys.exit(app.exec_())

treeview_nest.ui is available.

You see in the image below that the item child doesn't show a button and its parent's button is overwritten. Apparently I have no idea how to write a code for it.

enter image description here


Update) I figured out how to add widget to children. Tricky enough, using QStandardItem.insertRow combined with index does the work. In my sample code above, replace _add_widget with the following:

    def _add_widget(self, n):
        item_toplevel = QStandardItem('{}th item'.format(n))
        self._datamodel.setItem(n, 0, item_toplevel)

        widget_toplevel = QPushButton('{}th button'.format(n))
        qindex_toplevel = self._datamodel.index(n, 1, QModelIndex())
        self.setIndexWidget(qindex_toplevel, widget_toplevel)

        if n == 2:
            item_child_col0 = QStandardItem('child col0')
            item_child_col1 = QStandardItem('child col1')
            #item_toplevel.appendRow(item_child_col0)

            item_toplevel.insertRow(0, [item_child_col0, item_child_col1])

            widget_child = QPushButton('child widget')
            qindex_child = item_child_col1.index()
            self.setIndexWidget(qindex_child, widget_child)

I'm sure this is NOT the best/ideal designed way but seems to work for me. I came up with after inspired by @Phlucious's answer. Thanks!

enter image description here

Answer

Phlucious picture Phlucious · Mar 8, 2013

Your mistake is on this line:

qindex_widget_child = self._datamodel.index(n, 1, QModelIndex())
self.setIndexWidget(qindex_widget_child, node_widget_child)

It's giving you the index on row 2 column 1 of the model, which is the "2th item", not your child. Your child is on row 1 column 1 of std_item. In Qt, especially in QStandardItemModel, Children are stored relative to their parent, not relative to the model.

I'm not familiar enough with PyQt to give you the exact code, but you should be able to do something like this:

qindex_widget_child = std_item_child.index(QModelIndex())
self.setIndexWidget(qindex_widget_child, node_widget_child)

or like this:

qindex_widget_child = std_item.child(0, 1).index(QModelIndex())
self.setIndexWidget(qindex_widget_child, node_widget_child)