Python2.7 或 Python3.7
PySide2 Version: 5.11.2
官方文档:http://doc.qt.io/qtforpython/index.html
当qt没有提供我们需要的model的时候,就要考虑自己定义model了,所以就需要继承QAbstractItemModel这个类来实现我们自己的model。
在官方文档有说明,继承QAbstractItemModel类后,至少要实现QAbstractItemModel类中的5个方法,分别是:
QAbstractItemModel.index(row, column[, parent=QModelIndex()])
QAbstractItemModel.parent(child)
QAbstractItemModel.rowCount([parent=QModelIndex()])
QAbstractItemModel.columnCount([parent=QModelIndex()])
QAbstractItemModel.data(index[, role=Qt.DisplayRole])
这些函数用于所有只读的模型,并且是可编辑模型的最基本部分。
先看第一个函数index(row, column[, parent=QModelIndex()]))
需要传入二个必需参数,一个可选参数,row和column是int类型,表示行和列,parent表示这个节点的父母是谁(在树型结构中可能涉及到父子关系),类型为QModelIndex。返回值也是QModelIndex类型。这个函数用于返回在模型中给定行,列和父母节点的项,就是在模型中检索某一项。
parent(child)
传入一个child参数,类型为QModelIndex,返回类型也是QModelIndex。函数用于返回模型中给定项的父项。
rowCount([parent=QModelIndex()])
只有一个可选参数,返回父级下的行数,类型为int,当传入parent并且有效时,用于返回父项中的子项数。
columnCount([parent=QModelIndex()])
只有一个可选参数,用于返回列数。
data(index[, role=Qt.DisplayRole])
需要传入一个索引index,类型为QModelIndex,可选项role默认为Qt.DisplayRole是一个int类型。该函数返回值类型为object。这个role表示数据以什么角色来显示。DisplayRole表示用于文本表示(不可编辑),DecorationRole则用于图标形式表示,EditRole用于编辑器的编辑模式表示(可编辑),更多请查询文档,由于不好找,我放个链接:ItemDataRole
下面看一个实例,是上篇中用于实现json模型自定义的model。
代码原地址(我稍微改动了一下):https://github.com/dridk/QJsonModel/blob/master/qjsonmodel.py
# 用于放在model中的自定义item类
class QJsonTreeItem(object):
def __init__(self, parent=None):
# 由于json是一个数型结构,所以需要定义以下字段
self._parent = parent
self._key = ""
self._value = ""
self._type = None
self._children = list()
# 添加孩子节点
def appendChild(self, item):
self._children.append(item)
# 返回孩子节点
def child(self, row):
return self._children[row]
# 返回父母节点
def parent(self):
return self._parent
# 计算孩子节点个数
def childCount(self):
return len(self._children)
# 返回行数
def row(self):
return (
self._parent._children.index(self)
if self._parent else 0
)
# 获取关键字
@property
def key(self):
return self._key
# 设置关键字
@key.setter
def key(self, key):
self._key = key
# 获取值
@property
def value(self):
return self._value
# 设置值
@value.setter
def value(self, value):
self._value = value
# 获取节点类型
@property
def type(self):
return self._type
# 设置节点类型
@type.setter
def type(self, typ):
self._type = typ
# 载入数据,value为dict或list类型
@classmethod
def load(self, value, parent=None, sort=True):
rootItem = QJsonTreeItem(parent)
rootItem.key = "root"
if isinstance(value, dict):
items = (
sorted(value.items())
if sort else value.items()
)
for key, value in items:
child = self.load(value, rootItem)
child.key = key
child.type = type(value)
rootItem.appendChild(child)
elif isinstance(value, list):
for index, value in enumerate(value):
child = self.load(value, rootItem)
child.key = str(index)
child.type = type(value)
rootItem.appendChild(child)
else:
rootItem.value = value
rootItem.type = type(value)
return rootItem
# 自定义的json模型,继承自QAbstractItemModel类
class QJsonModel(QtCore.QAbstractItemModel):
def __init__(self, parent=None):
super(QJsonModel, self).__init__(parent)
# 生成根节点
self._rootItem = QJsonTreeItem()
self._headers = ("key", "value")
# 自定义load函数,载入数据到model中
def load(self, document):
"""Load from dictionary
Arguments:
document (dict): JSON-compatible dictionary
"""
assert isinstance(document, (dict, list, tuple)), (
"`document` must be of dict, list or tuple, "
"not %s" % type(document)
)
# 开始重新设置model,之前跟model关联的数据将会无效,在重置model之前必须要调用
self.beginResetModel()
self._rootItem = QJsonTreeItem.load(document)
self._rootItem.type = type(document)
# 完成重置过程
self.endResetModel()
return True
# 获取json数据,返回类型为dict
def json(self, root=None):
"""Serialise model as JSON-compliant dictionary
Arguments:
root (QJsonTreeItem, optional): Serialise from here
defaults to the the top-level item
Returns:
model as dict
"""
root = root or self._rootItem
# genJson()用于序列化模型
return self.genJson(root)
def data(self, index, role):
if not index.isValid():
return None
# internalPointer()为QModelIndex类里面的方法,用于返回一个与内部数据结构相关的指针,这里就是QJsonTreeItem对象的指针
item = index.internalPointer()
if role == QtCore.Qt.DisplayRole:
if index.column() == 0:
return item.key
if index.column() == 1:
return item.value
elif role == QtCore.Qt.EditRole:
if index.column() == 1:
return item.value
# 设置数据
def setData(self, index, value, role):
if role == QtCore.Qt.EditRole:
if index.column() == 1:
item = index.internalPointer()
item.value = str(value)
self.dataChanged.emit(index, index, [QtCore.Qt.EditRole])
return True
return False
# 返回头部数据字段
def headerData(self, section, orientation, role):
if role != QtCore.Qt.DisplayRole:
return None
if orientation == QtCore.Qt.Horizontal:
return self._headers[section]
# 返回指定地方的索引
def index(self, row, column, parent=QtCore.QModelIndex()):
if not self.hasIndex(row, column, parent):
return QtCore.QModelIndex()
if not parent.isValid():
parentItem = self._rootItem
else:
parentItem = parent.internalPointer()
childItem = parentItem.child(row)
if childItem:
return self.createIndex(row, column, childItem)
else:
return QtCore.QModelIndex()
# 返回索引的父节点
def parent(self, index):
if not index.isValid():
return QtCore.QModelIndex()
childItem = index.internalPointer()
parentItem = childItem.parent()
if parentItem == self._rootItem:
return QtCore.QModelIndex()
return self.createIndex(parentItem.row(), 0, parentItem)
# 返回列数,或者子项数
def rowCount(self, parent=QtCore.QModelIndex()):
if parent.column() > 0:
return 0
if not parent.isValid():
parentItem = self._rootItem
else:
parentItem = parent.internalPointer()
return parentItem.childCount()
# 因为只有2列,所以直接返回2
def columnCount(self, parent=QtCore.QModelIndex()):
return 2
def flags(self, index):
flags = super(QJsonModel, self).flags(index)
if index.column() == 1:
return QtCore.Qt.ItemIsEditable | flags
else:
return flags
def genJson(self, item):
nchild = item.childCount()
if item.type is dict:
document = {}
for i in range(nchild):
ch = item.child(i)
document[ch.key] = self.genJson(ch)
return document
elif item.type == list:
document = []
for i in range(nchild):
ch = item.child(i)
document.append(self.genJson(ch))
return document
else:
return item.value
关于@property,@key.setter,@classmethod看不懂的需要去补习下python知识。
更多细节请参考:QAbstractItemModel