[Python] Custom Pyside 2 QAbstractTabelModel with different flags for different QTableViews

Discussão em 'Python' iniciado por Stack, Junho 16, 2025.

  1. Stack

    Stack Membro Participativo

    I'm trying to generate an example of a backend table model that can drive two different views. I think I've gotten most of what I want at this point, but I can't figure out how to set the flags differently based on the view I'm looking at.

    I've tried a custom QStyledItemDelegate. Similar to my ComboDelegate. But that doesn't seem to be getting called.

    I've tried the QAbstractProxyModel though it just crashes the application when I double click on it.

    I'm not exactly sure how to proceed at this point.

    For this simplified example, I want to be able to edit columns 3 and 4 in the second table view.

    import sys
    import logging

    from PySide2 import QtWidgets, QtGui, QtCore

    LOG = logging.getLogger(__name__)


    class ItemData(object):
    def __init__(self, id, name, items):
    self.id = id
    self.enabled = True
    self.name = name
    self.items = items
    self.selected_item = self.items[0]
    self.start = 100
    self.end = 200

    def __repr__(self):
    return f"<id:{self.id}, sel:{self.selected_item}, enabled:{self.enabled}>"


    class TableModel(QtCore.QAbstractTableModel):
    def __init__(self, data, parent=None):
    super(TableModel, self).__init__(parent)
    self._data = data
    self.header = ["ID", "Name", "Items", "Start", "End"]

    def add_item(self, item):
    self.beginInsertRows(QtCore.QModelIndex(), len(self._data), len(self._data) )
    self._data.append(item)
    self.endInsertRows()

    def flags(self, index):
    set_flags = QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable
    if index.column() in [0]:
    set_flags = set_flags | QtCore.Qt.ItemIsUserCheckable

    return set_flags

    def headerData(self, section, orientation, role=...):
    if orientation == QtCore.Qt.Horizontal and role == QtCore.Qt.DisplayRole:
    return self.header[section]

    def rowCount(self, parent=...):
    return len(self._data)

    def columnCount(self, parent=...):
    return len(self.header)

    def data(self, index, role=...):
    row = index.row()
    column = index.column()

    if role == QtCore.Qt.CheckStateRole:
    if column == 0:
    state = QtCore.Qt.Unchecked
    if self._data[row].enabled:
    state = QtCore.Qt.Checked
    return state

    if role == QtCore.Qt.DisplayRole:
    if column == 0:
    return self._data[row].id
    if column == 1:
    return self._data[row].name
    if column == 2:
    return self._data[row].selected_item
    if column == 3:
    return self._data[row].start
    if column == 4:
    return self._data[row].end

    if role == QtCore.Qt.EditRole:
    if column == 2:
    return self._data[row].items
    if column == 3:
    return self._data[row].start
    if column == 4:
    return self._data[row].end

    def setData(self, index, value, role=...):
    def _emit():
    self.dataChanged.emit(index, index)

    row = index.row()
    column = index.column()
    if role == QtCore.Qt.CheckStateRole:
    check_bool = convert_qt_check_state(value)
    if column == 0:
    self._data[row].enabled = check_bool
    _emit()

    if role == QtCore.Qt.EditRole:
    if column == 2:
    if value == "":
    return
    self._data[row].selected_item = value
    _emit()
    if column == 3:
    self._data[row].start = value
    _emit()
    if column == 4:
    self._data[row].end = value
    _emit()

    return True


    def convert_qt_check_state(state:QtCore.Qt.CheckState):
    return state == QtCore.Qt.CheckState.Checked


    class ComboDelegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, parent, model):
    super().__init__(parent)
    self.model = model

    def paint(self, painter, option, index):
    if isinstance(self.parent(), QtWidgets.QAbstractItemView):
    self.parent().openPersistentEditor(index)
    super(ComboDelegate, self).paint(painter, option, index)

    def createEditor(self, parent, option, index):
    combo_box = QtWidgets.QComboBox(parent)
    combo_box.addItems(self.model.data(index, QtCore.Qt.EditRole))
    return combo_box


    class EditDelegate(QtWidgets.QStyledItemDelegate):
    def __init__(self, parent):
    super().__init__(parent)

    def createEditor(self, parent, option, index):
    print("BLEP")
    editor = QtWidgets.QLineEdit()
    return editor

    def setEditorData(self, editor, index):
    value = index.model().data(index, QtCore.Qt.EditRole)
    editor.setText(str(value))

    def setModelData(self, editor, model, index):
    value = editor.text()
    model.setData(index, value, QtCore.Qt.EditRole)


    class EditTableModel(QtCore.QAbstractProxyModel):
    def __init__(self, parent):
    super().__init__(parent)

    def flags(self, index):
    set_flags = self.sourceModel().flags(index)
    if index.column() in [3, 4]:
    set_flags = set_flags | QtCore.Qt.ItemIsEditable

    return set_flags

    def rowCount(self, parent=...):
    return self.sourceModel().rowCount(parent)

    def columnCount(self, parent=...):
    return self.sourceModel().columnCount(parent)

    def index(self, row, column, parent=...):
    return self.sourceModel().index(row, column, parent)

    def mapToSource(self, proxyIndex):
    return self.sourceModel().index(proxyIndex.row(), proxyIndex.column(), QtCore.QModelIndex())

    def mapFromSource(self, sourceIndex):
    return self.sourceModel().index(sourceIndex.row(), sourceIndex.column(), QtCore.QModelIndex())


    class TableWork(QtWidgets.QWidget):
    def __init__(self, parent):
    super(TableWork, self).__init__(parent)
    self._setup()

    self.data = [
    ItemData(0, "ARG", ["A", "B", "C"]),
    ItemData(1, "FOO", ["D", "E", "F"]),
    ItemData( 2, "RIV", ["G", "H", "I"])
    ]
    self.model = TableModel(self.data)
    self.table.setModel(self.model)
    self.table.setItemDelegateForColumn(2, ComboDelegate(self.table, self.model))
    for col_ in [3, 4]:
    self.table.hideColumn(col_)

    # Proxy Model Example
    # self.model_two = EditTableModel(None)
    # self.model_two.setSourceModel(self.model)

    # Delegate Example
    self.table_two.setModel(self.model)
    self.table.setItemDelegateForColumn(3, EditDelegate(self.table))
    self.table_two.hideColumn(1)

    def check_cb(self):
    print("-\n")
    for item in self.data:
    print(item)

    def add_cb(self):
    self.model.add_item(ItemData(3, "NAR", ["J", "K", "L"]))
    print(self.data)


    def _setup(self):
    main_layout = QtWidgets.QVBoxLayout()
    self.setLayout(main_layout)
    self.table = QtWidgets.QTableView()
    self.table.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows)
    main_layout.addWidget(self.table)

    self.table_two = QtWidgets.QTableView()
    self.table_two.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectionBehavior.SelectRows)
    main_layout.addWidget(self.table_two)

    check_button = QtWidgets.QPushButton("Check Data")
    check_button.clicked.connect(self.check_cb)
    main_layout.addWidget(check_button)

    add_button = QtWidgets.QPushButton("Add Data")
    add_button.clicked.connect(self.add_cb)
    main_layout.addWidget(add_button)


    if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    widget = TableWork(None)
    widget.show()
    sys.exit(app.exec_())

    Continue reading...

Compartilhe esta Página