使用 QComboBox 的 QTableView 模型

QTableView model using QComboBox

在下面的示例中,我有一个使用 AbstractModel 填充的简单 QTableView。 table 中的每一行显示与名为 Asset 的 class 对象相关的信息。它有一个名为 Items 的 属性,其中包含一个字符串列表。我想知道如何使用显示每行字符串列表的组合框来填充 QTableView。

其次,当用户更改下拉列表中选择的项目时,我想触发一个事件,以便稍后可以使用它根据对象的 [=28] 将彩色点的颜色正确更改为绿色或红色=] 称为 'Status'

状态将指示当前版本(表示下拉列表中的最新项目)是否为所选项目。如果它是列表中的最后一项,意味着最新的项目,它将是绿色的,否则它是红色的。

属性 'Active' 表示当前选择了下拉列表中的哪个项目。

如果状态为 0 则表示它已过时,如果状态为 1 则表示正在使用下拉列表中的最新版本。

import sys
from PySide import QtGui, QtCore


class Asset(object):
    def __init__(self, name, items=None, status=0, active=0):
        self._status = 0
        self._name = ''
        self._items = []
        self._active = active

        self.name = name
        self.items = items if items != None else []
        self.status = status


class AssetModel(QtCore.QAbstractTableModel):

    attr = ["Name", "Options"]


    def __init__(self, *args, **kwargs):
        QtCore.QAbstractTableModel.__init__(self, *args, **kwargs)
        self._items = []


    def clear(self):
        self._items = []
        self.reset()


    def rowCount(self, index=QtCore.QModelIndex()):
        return len(self._items)


    def columnCount(self, index=QtCore.QModelIndex()):
        return len(self.attr)


    def addItem(self, sbsFileObject):
        self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
        self._items.append(sbsFileObject)
        self.endInsertRows()


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


    def getItem(self, index):
        row = index.row()
        if index.isValid() and 0 <= row < self.rowCount():
            return index.data(role=QtCore.Qt.UserRole)
        return None


    def getSelectedItems(self, selection):
        objs = []
        for i, index in enumerate(selection):
            item = self.getItem(index)
            objs.append(item)
        return objs


    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return None
        if 0 <= index.row() < self.rowCount():
            item = self._items[index.row()]
            col = index.column()
            if 0 <= col < self.columnCount():
                if role == QtCore.Qt.DisplayRole:
                    if col == 0:
                        return getattr(item, 'name', '')
                    if col == 1:
                        return (getattr(item, 'items', []))
                elif role == QtCore.Qt.UserRole:
                    if col == 0:
                        return item
                elif role == QtCore.Qt.DecorationRole:
                    if col == 0:
                        status = getattr(item, 'status', 0)

                        col = QtGui.QColor(255,0,0,255)
                        if status == 1:
                            col = QtGui.QColor(255,128,0,255)
                        elif status == 2:
                            col = QtGui.QColor(255,255,0,255)

                        px = QtGui.QPixmap(120,120)
                        px.fill(QtCore.Qt.transparent)
                        painter = QtGui.QPainter(px)
                        painter.setRenderHint(QtGui.QPainter.Antialiasing)
                        px_size = px.rect().adjusted(12,12,-12,-12)
                        painter.setBrush(col)
                        painter.setPen(QtGui.QPen(QtCore.Qt.black, 4,
                            QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin))
                        painter.drawEllipse(px_size)
                        painter.end()

                        return QtGui.QIcon(px)


class Example(QtGui.QWidget):

    def __init__(self):
        super(Example, self).__init__()
        self.resize(400,300)

        # controls
        asset_model = QtGui.QSortFilterProxyModel()
        asset_model.setSortCaseSensitivity(QtCore.Qt.CaseInsensitive)
        asset_model.setSourceModel(AssetModel())

        self.ui_assets = QtGui.QTableView()
        self.ui_assets.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
        self.ui_assets.setModel(asset_model)
        self.ui_assets.verticalHeader().hide()

        main_layout = QtGui.QVBoxLayout()
        main_layout.addWidget(self.ui_assets)
        self.setLayout(main_layout)

        self.unit_test()


    def unit_test(self):
        assets = [
            Asset('Doug', ['v01', 'v02', 'v03'], 0),
            Asset('Amy', ['v10', 'v11', 'v13'], 1),
            Asset('Kevin', ['v11', 'v22', 'v53'], 2),
            Asset('Leslie', ['v13', 'v21', 'v23'], 0)
        ]

        self.ui_assets.model().sourceModel().clear()
        for i, obj in enumerate(assets):
            self.ui_assets.model().sourceModel().addItem(obj)


def main():
    app = QtGui.QApplication(sys.argv)
    ex = Example()
    ex.show()
    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

您有 2 个任务:

  • 使您的模型可编辑,因为当使用组合框时,您必须编辑值,此外,您必须实现新角色以访问资产的所有属性,因为它修改 class 资产:

class Asset(object):
    def __init__(self, name, items=[], active=0):
        self.active = active
        self.name = name
        self.items = items

    @property
    def status(self):
        return self.active == len(self.items) - 1

要制作可编辑模型,您必须实现setData()方法并启用Qt.ItemIsEditable标志:

class AssetModel(QtCore.QAbstractTableModel):
    attr = ["Name", "Options"]
    ItemsRole = QtCore.Qt.UserRole + 1
    ActiveRole = QtCore.Qt.UserRole + 2

    def __init__(self, *args, **kwargs):
        QtCore.QAbstractTableModel.__init__(self, *args, **kwargs)
        self._items = []

    def flags(self, index):
        fl = QtCore.QAbstractTableModel.flags(self, index)
        if index.column() == 1:
            fl |= QtCore.Qt.ItemIsEditable
        return fl

    def clear(self):
        self.beginResetModel()
        self._items = []
        self.endResetModel()

    def rowCount(self, index=QtCore.QModelIndex()):
        return len(self._items)

    def columnCount(self, index=QtCore.QModelIndex()):
        return len(self.attr)

    def addItem(self, sbsFileObject):
        self.beginInsertRows(QtCore.QModelIndex(), self.rowCount(), self.rowCount())
        self._items.append(sbsFileObject)
        self.endInsertRows()

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

    def data(self, index, role=QtCore.Qt.DisplayRole):
        if not index.isValid():
            return None
        if 0 <= index.row() < self.rowCount():
            item = self._items[index.row()]
            col = index.column()
            if role == AssetModel.ItemsRole:
                return getattr(item, 'items')

            if role == AssetModel.ActiveRole:
                return getattr(item, 'active')

            if 0 <= col < self.columnCount():
                if role == QtCore.Qt.DisplayRole:
                    if col == 0:
                        return getattr(item, 'name', '')
                    if col == 1:
                        return getattr(item, 'items')[getattr(item, 'active')]
                elif role == QtCore.Qt.DecorationRole:
                    if col == 0:
                        status = getattr(item, 'status')
                        col = QtGui.QColor(QtCore.Qt.red) if status else QtGui.QColor(QtCore.Qt.green)
                        px = QtGui.QPixmap(120, 120)
                        px.fill(QtCore.Qt.transparent)
                        painter = QtGui.QPainter(px)
                        painter.setRenderHint(QtGui.QPainter.Antialiasing)
                        px_size = px.rect().adjusted(12, 12, -12, -12)
                        painter.setBrush(col)
                        painter.setPen(QtGui.QPen(QtCore.Qt.black, 4,
                                                  QtCore.Qt.SolidLine,
                                                  QtCore.Qt.RoundCap,
                                                  QtCore.Qt.RoundJoin))
                        painter.drawEllipse(px_size)
                        painter.end()

                        return QtGui.QIcon(px)

    def setData(self, index, value, role=QtCore.Qt.EditRole):
        if 0 <= index.row() < self.rowCount():
            item = self._items[index.row()]
            if role == AssetModel.ActiveRole:
                setattr(item, 'active', value)
                return True
        return QtCore.QAbstractTableModel.setData(self, index, value, role)
  • 使用委托,为此您必须覆盖我们创建 QComboBox 的方法 createEditor()setEditorData()setModelData(),更新 QComboBox 使用模型的信息,并使用 QComboBox 的选择更新模型。我们还使用 paint() 使 QComboBox 持久化。

class AssetDelegate(QtGui.QStyledItemDelegate):
    def paint(self, painter, option, index):
        if isinstance(self.parent(), QtGui.QAbstractItemView):
            self.parent().openPersistentEditor(index)
        QtGui.QStyledItemDelegate.paint(self, painter, option, index)

    def createEditor(self, parent, option, index):
        combobox = QtGui.QComboBox(parent)
        combobox.addItems(index.data(AssetModel.ItemsRole))
        combobox.currentIndexChanged.connect(self.onCurrentIndexChanged)
        return combobox

    def onCurrentIndexChanged(self, ix):
        editor = self.sender()
        self.commitData.emit(editor)
        self.closeEditor.emit(editor, QtGui.QAbstractItemDelegate.NoHint)

    def setEditorData(self, editor, index):
        ix = index.data(AssetModel.ActiveRole)
        editor.setCurrentIndex(ix)

    def setModelData(self, editor, model, index):
        ix = editor.currentIndex()
        model.setData(index, ix, AssetModel.ActiveRole)

然后我们建立委托并将其作为父级传递给QTableView,以便它可以自动持久化:

self.ui_assets.setItemDelegateForColumn(1, AssetDelegate(self.ui_assets))

完整的代码可以在下面link.

找到