pythonpyqtpysideqcomboboxqstyleditemdelegate

How to get functional combobox in QTableView


As the title suggests I'm looking to get a combobox in a QTableView. I've looked at several other questions that deal with comboboxes in tableviews, but they mostly concern comboboxes as editors and that is not what I'm looking for.

I would like the combobox to be visible at all times and get its data from the underlying model. It doesn't have to set data in the model.

I tried to adapt this example of a progress bar delegate: How to include a column of progress bars within a QTableView? Leading to this code:

class ComboDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        combo = QStyleOptionComboBox()
        combo.rect = option.rect
        combo.currentText = 'hallo'  # just for testing
        combo.editable = True
        combo.frame = False
        QApplication.style().drawComplexControl(QStyle.CC_ComboBox, combo, painter)

But this gives me a greyed out, non functional combobox.

enter image description here

How do you get a functional combobox there?


Solution

  • As the name suggests, the paint() function only draws a combobox, it does not create one.

    If you want a persistent widget set for an item, and that widget doesn't need to update the model, then you should use setIndexWidget().

    A basic implementation on a static model could be like this:

    class SomeWidget(QWidget):
        def __init__(self):
            # ...
            self.table.setModel(someModel)
            for row in range(someModel.rowCount()):
                combo = QComboBox(editable=True)
                combo.addItem('hallo')
                self.table.setIndexWidget(someModel.index(row, 2), combo)
    

    If the model can change at runtime (and is possibly empty at startup), then you need to connect to the rowsInserted signal:

    class SomeWidget(QWidget):
        def __init__(self):
            # ...
            self.updateCombos()
            someModel.rowsInserted.connect(self.updateCombos)
    
        def updateCombos(self):
            for row in range(self.table.model().rowCount()):
                index = someModel.index(row, 2)
                if self.table.indexWidget(index):
                    continue
                combo = QComboBox(editable=True)
                combo.addItem('hallo')
                self.table.setIndexWidget(index, combo)
    

    Then you can access any combo based on the index row:

        def getComboValue(self, row):
            index = self.table.model().index(row, 2)
            widget = self.table.indexWidget(index)
            if isinstance(widget, QComboBox):
                return widget.currentText()
    

    Remember: whenever you're studying the documentation, you must also review the documentation of all inherited classes. In this case you should not only read the docs about QTableView, but also the whole inheritance tree: QTableView > QAbstractItemView > QAbstractScrollArea > QFrame > QWidget > QObject and QPaintDevice.