PySide2学习记录(二十二):QAbstractItemModel

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

你可能感兴趣的:(PySide2学习记录(二十二):QAbstractItemModel)