实施 QAbstractProxyModel 方法
Implementing QAbstractProxyModel methods
我有一个 QSqlTableModel,其结构大致如下:
| ID | Name |
---------------
| 0 | Xxxx |
| 2 | Yyyy |
| 5 | Zzzz |
如您所见,ID(唯一)是连续的,但可以跳过;可用范围总是从 0 到 1023。我需要创建一个 table 来填充最多 1024 行的空白,而不更改源模型布局。最终结果将是这样的:
| 0 | Xxxx |
| | |
| 2 | Yyyy |
| | |
| | |
| 5 | Zzzz |
...
|1023| Xyz |
"blank" 项目不会被编辑table,但用户将能够使用拖放来重新排序(这将在与 SQL 模型的内部接口中实现)和add/remove 项,同时始终将 table 大小保持在 1024 行。
我尝试实现 QAbstractProxyModel,但遇到了一些问题。例如,标志不受尊重,并且 every 项目不是 editable,也不是 selectable。即使我特意 return ItemIsEnabled|ItemIsSelectable|ItemIsEditable
,也没有任何改变。此外,单击项目会出现 header 突出显示的奇怪结果。
我想我在 mapToSource/mapFromSource
上做错了什么,但我不确定。
这是我使用 QStandardItemModel 而不是 QSqlTableModel(结果行为相同)制作的一个示例,它有 4 行,代理显示 6 行。
class BaseModel(QtGui.QStandardItemModel):
def __init__(self):
QtGui.QStandardItemModel.__init__(self)
for id, name in [(1, 'One'), (2, 'Two'), (3, 'Three'), (5, 'Five')]:
idItem = QtGui.QStandardItem()
idItem.setData(id, QtCore.Qt.DisplayRole)
nameItem = QtGui.QStandardItem(name)
self.appendRow([idItem, nameItem])
class ProxyModel(QtCore.QAbstractProxyModel):
def data(self, index, role):
source = self.mapToSource(index)
if source.isValid():
return source.data(role)
return None
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
return str(section + 1)
return QtCore.QAbstractProxyModel.headerData(self, section, orientation, role)
def setData(self, index, value, role):
return self.sourceModel().setData(self.mapToSource(index), value, role)
def index(self, row, column, parent=None):
res = self.sourceModel().match(self.sourceModel().index(0, 0), QtCore.Qt.DisplayRole, row, flags=QtCore.Qt.MatchExactly)
if res:
return res[0].sibling(res[0].row(), column)
return self.createIndex(row, column)
def parent(self, index):
return self.sourceModel().index(index.row(), index.column()).parent()
def flags(self, index):
source = self.mapToSource(index)
if source.isValid():
return source.flags()
return QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEditable
def rowCount(self, index):
return 6
def columnCount(self, index):
return 2
def mapToSource(self, index):
res = self.sourceModel().match(self.sourceModel().index(0, 0), QtCore.Qt.DisplayRole, index.row(), flags=QtCore.Qt.MatchExactly)
if res:
return res[0].sibling(res[0].row(), index.column())
return QtCore.QModelIndex()
def mapFromSource(self, index):
if index.row() < 0:
return QtCore.QModelIndex()
row = self.sourceModel().index(index.row(), 0).data()
return self.createIndex(row, index.column())
class Win(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
self.model = BaseModel()
self.proxy = ProxyModel()
self.proxy.setSourceModel(self.model)
table = QtWidgets.QTableView()
table.setModel(self.proxy)
layout.addWidget(table)
没有必要实现 QAbstractProxyModel
,在这种情况下,使用 QIdentityProxyModel
:
就足够了
class ProxyModel(QtCore.QIdentityProxyModel):
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
return str(section + 1)
return QtCore.QIdentityProxyModel.headerData(self, section, orientation, role)
def setData(self, index, value, role=QtCore.Qt.DisplayRole):
if index.column() == 0:
if not (0 <= int(value) < self.rowCount()) :
return False
return QtCore.QIdentityProxyModel.setData(self, index, value, role)
def rowCount(self, index=QtCore.QModelIndex()):
return 6
def index(self, row, column, parent=QtCore.QModelIndex()):
return self.createIndex(row, column)
def mapFromSource(self, sourceIndex):
if sourceIndex.isValid() and 0 <= sourceIndex.row() < self.rowCount():
ix = self.sourceModel().index(sourceIndex.row(), 0)
return self.index(int(ix.data()), sourceIndex.column())
return QtCore.QModelIndex()
def mapToSource(self, proxyIndex):
res = self.sourceModel().match(self.sourceModel().index(0, 0), QtCore.Qt.DisplayRole, proxyIndex.row(), flags=QtCore.Qt.MatchExactly)
if res:
return res[0].sibling(res[0].row(), proxyIndex.column())
return QtCore.QModelIndex()
我有一个 QSqlTableModel,其结构大致如下:
| ID | Name |
---------------
| 0 | Xxxx |
| 2 | Yyyy |
| 5 | Zzzz |
如您所见,ID(唯一)是连续的,但可以跳过;可用范围总是从 0 到 1023。我需要创建一个 table 来填充最多 1024 行的空白,而不更改源模型布局。最终结果将是这样的:
| 0 | Xxxx |
| | |
| 2 | Yyyy |
| | |
| | |
| 5 | Zzzz |
...
|1023| Xyz |
"blank" 项目不会被编辑table,但用户将能够使用拖放来重新排序(这将在与 SQL 模型的内部接口中实现)和add/remove 项,同时始终将 table 大小保持在 1024 行。
我尝试实现 QAbstractProxyModel,但遇到了一些问题。例如,标志不受尊重,并且 every 项目不是 editable,也不是 selectable。即使我特意 return ItemIsEnabled|ItemIsSelectable|ItemIsEditable
,也没有任何改变。此外,单击项目会出现 header 突出显示的奇怪结果。
我想我在 mapToSource/mapFromSource
上做错了什么,但我不确定。
这是我使用 QStandardItemModel 而不是 QSqlTableModel(结果行为相同)制作的一个示例,它有 4 行,代理显示 6 行。
class BaseModel(QtGui.QStandardItemModel):
def __init__(self):
QtGui.QStandardItemModel.__init__(self)
for id, name in [(1, 'One'), (2, 'Two'), (3, 'Three'), (5, 'Five')]:
idItem = QtGui.QStandardItem()
idItem.setData(id, QtCore.Qt.DisplayRole)
nameItem = QtGui.QStandardItem(name)
self.appendRow([idItem, nameItem])
class ProxyModel(QtCore.QAbstractProxyModel):
def data(self, index, role):
source = self.mapToSource(index)
if source.isValid():
return source.data(role)
return None
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
return str(section + 1)
return QtCore.QAbstractProxyModel.headerData(self, section, orientation, role)
def setData(self, index, value, role):
return self.sourceModel().setData(self.mapToSource(index), value, role)
def index(self, row, column, parent=None):
res = self.sourceModel().match(self.sourceModel().index(0, 0), QtCore.Qt.DisplayRole, row, flags=QtCore.Qt.MatchExactly)
if res:
return res[0].sibling(res[0].row(), column)
return self.createIndex(row, column)
def parent(self, index):
return self.sourceModel().index(index.row(), index.column()).parent()
def flags(self, index):
source = self.mapToSource(index)
if source.isValid():
return source.flags()
return QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEditable
def rowCount(self, index):
return 6
def columnCount(self, index):
return 2
def mapToSource(self, index):
res = self.sourceModel().match(self.sourceModel().index(0, 0), QtCore.Qt.DisplayRole, index.row(), flags=QtCore.Qt.MatchExactly)
if res:
return res[0].sibling(res[0].row(), index.column())
return QtCore.QModelIndex()
def mapFromSource(self, index):
if index.row() < 0:
return QtCore.QModelIndex()
row = self.sourceModel().index(index.row(), 0).data()
return self.createIndex(row, index.column())
class Win(QtWidgets.QWidget):
def __init__(self):
QtWidgets.QWidget.__init__(self)
layout = QtWidgets.QGridLayout()
self.setLayout(layout)
self.model = BaseModel()
self.proxy = ProxyModel()
self.proxy.setSourceModel(self.model)
table = QtWidgets.QTableView()
table.setModel(self.proxy)
layout.addWidget(table)
没有必要实现 QAbstractProxyModel
,在这种情况下,使用 QIdentityProxyModel
:
class ProxyModel(QtCore.QIdentityProxyModel):
def headerData(self, section, orientation, role):
if orientation == QtCore.Qt.Vertical and role == QtCore.Qt.DisplayRole:
return str(section + 1)
return QtCore.QIdentityProxyModel.headerData(self, section, orientation, role)
def setData(self, index, value, role=QtCore.Qt.DisplayRole):
if index.column() == 0:
if not (0 <= int(value) < self.rowCount()) :
return False
return QtCore.QIdentityProxyModel.setData(self, index, value, role)
def rowCount(self, index=QtCore.QModelIndex()):
return 6
def index(self, row, column, parent=QtCore.QModelIndex()):
return self.createIndex(row, column)
def mapFromSource(self, sourceIndex):
if sourceIndex.isValid() and 0 <= sourceIndex.row() < self.rowCount():
ix = self.sourceModel().index(sourceIndex.row(), 0)
return self.index(int(ix.data()), sourceIndex.column())
return QtCore.QModelIndex()
def mapToSource(self, proxyIndex):
res = self.sourceModel().match(self.sourceModel().index(0, 0), QtCore.Qt.DisplayRole, proxyIndex.row(), flags=QtCore.Qt.MatchExactly)
if res:
return res[0].sibling(res[0].row(), proxyIndex.column())
return QtCore.QModelIndex()