使用 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.
找到
在下面的示例中,我有一个使用 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.
找到