Saelyth
Saelyth

Reputation: 1734

How to have multiple columns in a QComboBox with a QAbstractTableModel

I've seen questions similar to this one but they are aimed at QTableView. This is not using that,, this is just for a dropdown (QComboBox) with a custom QAbstractTableModel, which needs to have 2 columns.

BIG UPDATE
(Note: Legacy code has been deleted as this is a better approach on the same question, and legacy code was confusing as hell).

Okay, so trying to catch up with what @eyllanesc explained, I changed this from a QAbstractListModel to a QAbstractTableModel. The result is:

class ModelForComboboxesWithID(QAbstractTableModel):
    """Create our basic model"""

    def __init__(self, program, records):
        super(ModelForComboboxesWithID, self).__init__()
        self._data = records
        self.program = program
        self.path_images = program.PATH_IMAGES

    def rowCount(self, index: int = 0) -> int:
        """The length of the outer list. Structure: [row, row, row]"""
        if not self._data:
            return 0  # Doubt: Do we need to return this if self._data is empty?
        return len(self._data)

    def columnCount(self, index: int = 0) -> int:
        """The length of the sub-list inside the outer list. Meaning that Columns are inside rows
        Structure: [row [column], row [column], row [column]]"""
        if not self._data:
            return 0  # Doubt: Do we need to return this if self._data is empty?
        return len(self._data[0])

    def data(self, index, role=None):
        """return the data on this index as data[row][column]"""
        # 1 - Display data based on its content (this edits the text that you visually see)
        if role == Qt.DisplayRole:
            value = self._data[index.row()][index.column()]
            return value
        # 2 - Tooltip displayed when hovering on it
        elif role == Qt.ToolTipRole:
            return f"ID: {self._data[index.row()][1]}"

Which I set this way:

def eventFilter(self, target, event: QEvent):
    if event.type() == QEvent.MouseButtonPress:
        if target == self.Buscadorcombo_cliente:
           records = ... # my query to the database
           set_combo_records_with_ids(self.program, target, records)
           target.currentIndexChanged.connect(self.test)

def set_combo_records_with_ids(program, combobox: QComboBox, records):
    """Clear combobox, set model/data and sort it"""
    combobox.clear()
    model = ModelForComboboxesWithID(program, records)
    combobox.setModel(model)
    combobox.model().sort(0, Qt.AscendingOrder)
    combobox.setModelColumn(0)

The result of this works almost perfect:

Now I am able to get any data of it this way.

def test(self, index):
    data_id = self.Buscadorcombo_cliente.model().index(index, 1).data()
    data_name = self.Buscadorcombo_cliente.model().index(index, 0).data()
    print(data_id)
    print(data_name)

Upvotes: 1

Views: 1522

Answers (1)

eyllanesc
eyllanesc

Reputation: 243945

You have to set a QTableView as a view:

from PySide2 import QtGui, QtWidgets


def main():
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = QtWidgets.QWidget()

    combo = QtWidgets.QComboBox()

    model = QtGui.QStandardItemModel(0, 2)
    for i in range(10):
        items = []
        for j in range(model.columnCount()):
            it = QtGui.QStandardItem(f"it-{i}{j}")
            items.append(it)
        model.appendRow(items)

    combo.setModel(model)

    view = QtWidgets.QTableView(
        combo, selectionBehavior=QtWidgets.QAbstractItemView.SelectRows
    )
    combo.setView(view)

    view.verticalHeader().hide()
    view.horizontalHeader().hide()

    header = view.horizontalHeader()
    for i in range(header.count()):
        header.setSectionResizeMode(i, QtWidgets.QHeaderView.Stretch)

    lay = QtWidgets.QVBoxLayout(w)
    lay.addWidget(combo)
    lay.addStretch()
    w.resize(640, 480)
    w.show()

    sys.exit(app.exec_())


if __name__ == "__main__":
    main()

Upvotes: 2

Related Questions