123-Model/View-自定义模型QAbstractTableModel

自定义模型QAbstractTableModel

一般来说,通过上面介绍的一些标准模型(如QStringListModel、QFileSystemModel和QStandardItemModel等)结合 QListView、QTableView 和QTreeView 就够用了。

但有时需要更复杂的模型,这就需要用户自己定义。可以通过子类化QAbstractTableModel实现自定义模型,这需要注意以下几点:

  • 必须实现函数rowCount()columnCount()和data()除非是子类化QAbstractTableModel否则函数index()和parent()也要重新实现。
    • 这些功能用于只读模型,并且构成可编辑模型的基础。需要注意的是,在实现 data0函数时不需要支持 Qt.ItemDataRole 中的所有角色可以选择一些常用角色返回即可。
    • 大多数模型至少为 QtDisplayRole 提供文本支持,有些模型会使用QtToolTipRole 和 QtWhatsThisRole 提供更多的信息。
  • 其次,要在模型中启用编辑,还必须实现 setData()函数,并重新实现 flags()函数确保返回 ItemIsEditable。
    • 还可以重新实现函数 headerData()和 setHeaderData()来控制模型标题的呈现方式。
    • 在分别重新实现函数 setData()和 setHeaderData()时,必须显式发射dataChanged 信号和 headerDataChanged 信号。
  • 最后,如果模型结构可以调整,则可以重新实现函数 insertRows()、removeRows0insertColumns()和 removeColumns()。
    • 在实现这些函数时,重要的是要把模型结构上的变化通知连接模型的视图。
      • 实现 insertRows()函数需要在新行插入数据结构之前先调用 beginnsertRows()函数,然后立即调用endInsertRows()函数。
      • 实现 isertColumns()函数需要在新列插入数据结构之前先调用 beginInsertColumns()函数,然后立即调用endInsertColumns()函数。
      • 实现removeRows()函数需要在从数据结构中删除行之前先调用 beginRemoveRows()函数,然后立即调用endRemoveRows()函数。
      • 实现 removeColumns()函数需要在从数据结构中删除列之前先调用 beginRemoveColumns()函数,然后立即调用endRemoveColumns()函数。

如果数据比较大,则可以考虑创建增量填充的模型,重新实现函数 fetchMore()和canFetchMore()。如果通过 fetchMore()函数将行加到模型中,则必须调用函数beginInsertRows()和endInsertRows()。

image-20230326235615136

一是创建数据结构 Student,主要包括科目、姓名、分数这3个属性

二是自定义模型,该模型使用 QAbstractTableModel作为基础模板,重新实现了最基础的函数 data()、headerData()、rowCount()和 columnCount(),支持编辑的函数 flags()和setData(),以及结构调整的函数 insertRows()和removeRows()。在 data()函数中可以根据列的名称和角色设置不同的颜色

三是将 QTableView 和自定义模型结合,这里仅仅实现了增/删行的功能,只提供了setData0函数用于编辑模型,并且只支持 EditRole 一个角色

# -*- coding: UTF-8 -*-
# File date: Hi_2023/3/26 23:55
# File_name: 01-QTableView 控件结合自定义模型.py


from PySide6.QtWidgets import *
from PySide6.QtGui import *
from PySide6.QtCore import *
import sys
import random
import os

os.chdir(os.path.dirname(__file__))

SUBJECT,NAME,SCORE,DESCRIPTION = range(4)


class Student(object):

    def __init__(self,subject,name,score=0,description=""):
        self.subject = subject
        self.name = name
        self.score = score
        self.description = description

    def __hash__(self):
        return super(Student,self).__hash__()

    def __lt__(self,Other):
        if self.name < Other.name:
            return True
        if self.subject < Other.subject:
            return True
        return id(self)< id(Other)

    def __eq__(self,Other):
        if self.name == Other.name:
            return True
        if self.subject == Other.subject:
            return True
        return id(self)== id(Other)


class StudentTableModel(QAbstractTableModel):

    def __init__(self,filename=""):
        super(StudentTableModel,self).__init__()
        self.students = []

    def initData(self):
        for subject in ['语文','数学','外语','综合']:
            for name in ['张三','李四','王五','赵六']:
                score = random.random()* 40 + 60
                if score >= 80:
                    _str = f'{name}{subject}成绩是:优秀'
                else:
                    _str = f'{name}{subject}成绩是:良好'
                student = Student(subject,name,score,_str)
                self.students.append(student)
        self.sortBySubject()

    def sortByName(self):

        self.students = sorted(self.students,key=lambda x:(x.name,x.subject))
        self.endResetModel()

    def sortBySubject(self):
        self.students = sorted(self.students,key=lambda x:(x.subject,x.name))
        self.endResetModel()

    def flags(self,index):
        if not index.isValid():
            return Qt.ItemIsEnabled
        return Qt.ItemFlags(QAbstractTableModel.flags(self,index)| Qt.ItemIsEditable)

    def data(self,index,role=Qt.DisplayRole):
        if not index.isValid()or not(0 <= index.row()< len(self.students)):
            return None
        student = self.students[index.row()]
        column = index.column()
        if role == Qt.DisplayRole:
            if column == SUBJECT:
                return student.subject
            elif column == NAME:
                return student.name
            elif column == DESCRIPTION:
                return student.description
            elif column == SCORE:
                return"{:.2f}".format(student.score)
        elif role == Qt.TextAlignmentRole:
            if column == SCORE:
                return int(Qt.AlignRight | Qt.AlignVCenter)
            return int(Qt.AlignLeft | Qt.AlignVCenter)
        elif role == Qt.ForegroundRole and column == SCORE:
            if student.score < 80:
                return QColor(Qt.black)
            elif student.score < 90:
                return QColor(Qt.darkGreen)
            elif student.score < 100:
                return QColor(Qt.red)
        elif role == Qt.BackgroundRole:
            if student.subject in("数学","语文"):
                return QColor(250,230,250)
            elif student.subject in("外语",):
                return QColor(250,250,230)
            elif student.subject in("综合"):
                return QColor(230,250,250)
            else:
                return QColor(210,230,230)
        return None

    def headerData(self,section,orientation,role=Qt.DisplayRole):
        if role == Qt.TextAlignmentRole:
            if orientation == Qt.Horizontal:
                return int(Qt.AlignLeft | Qt.AlignVCenter)
            return int(Qt.AlignRight | Qt.AlignVCenter)
        if role != Qt.DisplayRole:
            return None
        if orientation == Qt.Horizontal:
            if section == SUBJECT:
                return"科目"
            elif section == NAME:
                return"姓名"
            elif section == SCORE:
                return"分数"
            elif section == DESCRIPTION:
                return"说明"
        return int(section + 1)

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

    def columnCount(self,index=QModelIndex()):
        return 4

    def setData(self,index,value,role=Qt.EditRole):
        if index.isValid()and 0 <= index.row()< len(self.students)and role == Qt.EditRole:
            student = self.students[index.row()]
            column = index.column()
            if column == SUBJECT:
                student.subject = value
            elif column == NAME:
                student.name = value
            elif column == DESCRIPTION:
                student.description = value
            elif column == SCORE:
                try:
                    student.score = int(value)
                except:
                    print('输入错误,请输入数字')

            self.emit(SIGNAL("dataChanged(QModelIndex,QModelIndex)"),index,index)
            return True
        return False

    def insertRows(self,position,rows=1,index=QModelIndex()):
        self.beginInsertRows(QModelIndex(),position,position + rows - 1)
        for row in range(rows):
            self.students.insert(position + row,Student("test","test",0,''))
        self.endInsertRows()
        return True

    def removeRows(self,position,rows=1,index=QModelIndex()):
        self.beginRemoveRows(QModelIndex(),position,position + rows - 1)
        self.students =(self.students[:position] + self.students[position + rows:])
        self.endRemoveRows()
        return True


class QTableViewDemo(QMainWindow):

    def __init__(self,parent=None):
        super(QTableViewDemo,self).__init__(parent)
        self.setWindowTitle("QTableModel案例")
        self.resize(500,600)
        self.tableView = QTableView()
        self.model = StudentTableModel()
        self.model.initData()

        self.tableView.setModel(self.model)
        self.selectModel = QItemSelectionModel()
        self.tableView.setSelectionModel(self.selectModel)
        self.tableView.horizontalHeader().setStretchLastSection(True)

        self.buttonAddRow = QPushButton('增加行')
        self.buttonInsertRow = QPushButton('插入行')
        self.buttonDeleteRow = QPushButton('删除行')
        self.buttonAddRow.clicked.connect(self.onAdd)
        self.buttonInsertRow.clicked.connect(self.onInsert)
        self.buttonDeleteRow.clicked.connect(self.onDelete)

        self.model.setData(self.model.index(3,1),'Python',role=Qt.EditRole)

        layout = QVBoxLayout(self)
        layout.addWidget(self.tableView)
        layoutH = QHBoxLayout()
        layoutH.addWidget(self.buttonAddRow)
        layoutH.addWidget(self.buttonInsertRow)
        layoutH.addWidget(self.buttonDeleteRow)
        layout.addLayout(layoutH)

        widget = QWidget()
        self.setCentralWidget(widget)
        widget.setLayout(layout)

    def onAdd(self):
        rowCount = self.model.rowCount()
        self.model.insertRow(rowCount)

    def onInsert(self):
        index = self.tableView.currentIndex()
        row = index.row()
        self.model.insertRow(row)

    def onDelete(self):
        index = self.tableView.currentIndex()
        row = index.row()
        self.model.removeRow(row)


if __name__ =="__main__":
    app = QApplication(sys.argv)
    demo = QTableViewDemo()
    demo.show()
    sys.exit(app.exec())

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

士别三日,当挖目相待

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值