pythonpyqt5qsortfilterproxymodel

QSortFilterProxyModel hiding QWidget


I have a QStandardItemModel that accomodates data. In one of the columns, I would like to add some QWidgets (clickable pictures). After adding QSortFilterProxyModel for soring/filtering purpose, however, the QSortFilterProxyModel hides my desired QWidgets.

I've searched the Internet but could not find how to keep the QWidget and QSortFilterProxyModel simultaneously. It would be much appreciated if someone may guide me on this issue. Thanks.

A minimal example, using QPushButton as my desired QWidget:

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *

class Buttons(QWidget):
    def __init__(self):
        super().__init__()
        layout = QHBoxLayout(self)
        layout.addWidget(QPushButton('btn1'))
        layout.addWidget(QPushButton('btn2'))
        self.setLayout(layout)

class MainWindow(QMainWindow):
    def __init__(self, *args, **kwargs):
        super(MainWindow, self).__init__(*args, **kwargs)
        tab = QTableView()
        sti = QStandardItemModel()
        if True:    # This shows the buttons in a cell
            tab.setModel(sti)
        else:       # This does not show the buttons
            proxy = QSortFilterProxyModel()
            proxy.setSourceModel(sti)
            tab.setModel(proxy)
        sti.appendRow([QStandardItem(str(i)) for i in range(5)])
        tab.setIndexWidget(sti.index(0, 0), QPushButton("hi"))
        sti.appendRow([])
        tab.setIndexWidget(sti.index(1, 2), Buttons())
        self.setCentralWidget(tab)

app = QApplication([])
window = MainWindow()
window.resize(800, 600)
window.show()
app.exec_()

Solution

  • Widgets added using seyIndexWidget are added to the view, not to the model. The index passed to the function is only a reference that the view uses to know where those widget will be placed, and that reference must be that of the actual model used in the view.

    If you're using a proxy model in the view, you must give the index of the proxy, not that of the source.

    tab.setIndexWidget(proxy.index(0, 0), QPushButton("hi"))
    

    Or, better:

    tab.setIndexWidget(tab.model().index(0, 0), QPushButton("hi"))
    

    Note that this obviously means that you might face some inconsistency whenever the model is altered because of filtering or sorting, and that's another reason for which index widgets should only be used for static and simple models, and delegates are the preferred solution.