153-数据库操作和模型-QSQL模型-单表读写模型 QSqlTableModel

单表读写模型 QSqlTableModel

数据库表格模型 QSglTableModel 借助视图控件可以对查询到的数据进行修改、插入、删除和排序等操作,同时将修改后的数据更新到数据库中。

用QSqlTableModel 创建数据库表格模型的方法如下所示。

from PySide6.QtSql import QSqlTableModel

QSqlTableModel(parent: Union[PySide6.QtCore.QObject,NoneType] = None,db: PySide6.QtSql.QSqlDatabase = Default(QSqlDatabase))-> None 
表格模型QSqlTableModel的官方说明

QSqlTableModel是一个高级接口,用于从单个表中读取和写入数据库记录。它构建在较低级别的QSqlQuery之上,可用于为视图类(如QTableView)提供数据。例如:

model = QSqlTableModel()
model.setTable("employee")
model.setEditStrategy(QSqlTableModel.OnManualSubmit)
model.select()
model.setHeaderData(0, Qt.Horizontal, tr("Name"))
model.setHeaderData(1, Qt.Horizontal, tr("Salary"))
view = QTableView()
view.setModel(model)
view.hideColumn(0)# don't show the ID
view.show()

我们设置了SQL表的名称和编辑策略,然后设置了视图标题中显示的标签。编辑策略规定了用户在视图中所做的更改何时实际应用于数据库。可能的值有OnFieldChange、OnRowChange和OnManualSubmit。
QSqlTableModel也可以用于以编程方式访问数据库,而无需将其绑定到视图:

model = QSqlTableModel()
model.setTable("employee")
model.select()
salary = model.record(4).value("salary").toInt()

上面的代码片段从来自employee的查询SELECT*的结果集中的记录4中提取了salary字段。
可以使用setFilter()设置筛选器,也可以使用setSort()修改排序顺序。最后,您必须调用select()来用数据填充模型。
表模型示例说明了如何使用QSqlTableModel作为QTableView的数据源。
QSqlTableModel不直接支持外键。如果要解析外键,请使用QSqlRelationalTableModel和QSqlRelationalDelegate。

表格模型QSqlTableModel的常用方法

数据库表格模型QSqlTableModel的常用方法如表所示,主要方法介绍如下

  • 在视图控件(如QTableView)中对数据库表格模型中的数据进行修改提交时,有3种模式可供选择

    • 立即模式行模式和手动模式。用setEditStrategy(strategy:QSqlTableModel,EditStrategy)方法设置修改提交模式,参数 strategy 的取值是QSqlTableModel.EditStrategy 的枚举值,可取以下值,对应的值分别是0 1和2
      • QSalTableModel.OnFieldChange
      • QSqlTableModel.OnRowChange
      • QSglTableModel.OnManualSubmit
    • 用editStrategy()方法可获得当前的模式。
      • OnFieldChange模式表示对模型的修改会立即更新到数据库中;
      • OnRowChange 模式表示修改完一行,再选择其他行后把修改应用到数据库中;
      • OnManualSubmit模式表示修改后不会立即更新到数据库中,而是保存到缓存中,
    • 调用submitAl1()方法后才把修改应用到数据库中,
    • 调用revertAll()方法可撤销修改并恢复原状。
  • QSglTableModel 还提供了-些与修改提交模式无关的低级方法,例如…这些低级方法可以直接修改数据库

    • deleteRowFromTable(row:int)

    • insertRowIntotable(values: QSqlRecord)

    • updateRowInTable(row: int,QSglRecord)

  • 对数据库的查询

    • 可以先用setTable(tableName:str)方法setFilter(filter:str)方法和 setSort(column:int,order:Qt.SortOrder)方法分别设置需要查询的数据表SQL的WHERE从句和SORT BY从句,最后调用select()方法,也可以直接用setQuery(query:QSqlQuery)方法进行查询。
  • 在视图控件(如QTableView)中对数据库表格模型中的数据进行修改提交时

    • 在OnManualSubmit模式下,用revert()方法撤销在代理控件中所作的更改并恢复原状;
    • 用revertAl1()方法复原所有未提交的更改;用submit()方法提交在代理控件中所作的更改;
    • 用submitA11()方法提交所有的更改。
  • 用setRecord(row:int;record;QSglRecord)方法可以对某行内容用字段进行替换

  • 用insertRecord(row:int,record:QSgIRecord)方法可以在指定行插入一条记录

    • 用insertRows(row:int,count;int)方法可以在指定的行位置处插入多个空行;
    • 用insertColumns(column:intcount;int)方法可以在指定的列位置处插人多列
  • 用removeRow(row:int)方法和removeColumn(column:int)方法可以分别删除指定的行和列;

    • 用removeRows(row:int,count;int)方法和 removeColumns(column:int,count:int)方法可以分别从指定行或列位置处删除多行和多列。
  • 用setSort(column;int,order:Qt.SortOrder)方法可以将数据模型中的数据按照某列的值进行排序,参数 order 可取:

    • Qt.AscendingOrder(升序)
    • Qt.DescendingOrder(降序)
QSqlTableModel的方法及参数类型返回值类型说 明
setEditStrategy(strategy: QSqlTableModel.EditStrategy)None设置修改提交模式
database()QSqlDatabase获取关联的数据库连接
deleteRowFromTable(row:int)bool直接删除数据表中指定的行(记录)
fieldIndex(fieldName: str)int获取字段的索引,一1表示没有对应的字段
insertRecord(row:int,record: QSqlRecord)bo0l在指定行位置插人记录,row 取负值表示 在末尾位置,成功则返回True
insertRowIntotable(values: QSqlRecord)bool直接在数据表中插人行,成功则返回True
insertRows(row: int,count:int)bool插人多个空行,在 OnFieldChange 和 OnRowChange 模式下每次只能插人一行,成功则返回True
insertColumns(column:int.count:int)bool .插人多个空列,成功则返回True
isDirty()bool获取模型中是否有脏数据,脏数据是指修 改过但还没有更新到数据库中的数据
isDirty(index:QModelIndex)bool根据索引获取数据是否是脏数据
primaryValues(row: int)QSqlRecord返回指定行的含有表格字段的记录
record()QSqlRecord返回仅包含字段名称的空记录
record(row:int)QSqlRecord返回指定行的记录,如果模型没有初始化,则返回空记录
removeColumn(column:int)bool删除指定的列,成功则返回 True
removeColumns(column:int,count:int)bool删除多列,成功则返回True
romoveRow(row:int)bool删除指定的行,成功则返回True
removeRows(row:int,count:int)bool删除多行,成功则返回True
[slot]revert()None撤销代理控件所作更改并恢复原状
[slot]submit()boo1往数据库中提交在代理控件中对行所作的 更改,成功则返回True
[slot]revertAll()None复原所有未提交的更改
[slot]submitAll()bool提交所有更改,成功则返回True
revertRow(row:int)None复原指定行的更改
rowCount()int获取行的数量
columnCount()int获取列的数量
setData(index: QModelIndex,value: Any,role: int = Qt.ItemDataRole.EditRole)bool设置指定索引的数据项的角色值,成功则 返回 True
data(idx: QModelIndex,role: int= Qt.ItemDataRole.DisplayRole)Any获取角色值
setQuery(query:QSqlQuery)None直接设置数据库查询
query()QSqlQuery获取数据查询对象
setRecord(row: int,record: QSqlRecord)bool用指定的记录填充指定的行
setTable(tableName: str)None获取数据表中的字段名称
setFilter(filter:str)None设置SELECT查询语句中WHERE从句 部分,但不包含WHERE
filter()Str获取 WHERE 从句
setSort(column:int,order:Qt.SortOrder)None设置SELECT语句中ORDER BY从句 部分
orderByClause()Str获取ORDER BY从句部分
[slot]select()bool执行SELECT命令,获取新查询结果
[slot]selectRow(row:int)bool用数据库中的行更新模型中的数据
selectStatement()Str获取"SELECT…WHRER…ORDER BY"
sort(column:int,order:Qt.SortOrder)None直接对结果进行排序
updateRowInTable(row:int,QSqLRecord)bool直接用记录更新数据库中的行
tableName()str获取数据库中的数据表名称
setHeaderData(section:int,orientation: Qt.Orientation,value:Any,role:int= Qt.ItemDataRole.EditRole)bool设置视图控件(如 QTableView)表头某角 色的值
index(row: int,column:int,parent: QModelIndex=Invalid(QModelIndex))QModelIndex获取子索引
parent(child:QModelIndex)QModelIndex获取子索引的父索引
sibling(row:int,column: int,idx: QModelIndex)QModelIndex获取同级别索引
clear()None清空模型中的数据
clearItemData(index: QModelIndex)bool根据索引清除数据项中的数据
表格模型QSqlTableModel的信号

数据库表格模型QSqlTableModel 的信号如表所示

QSqlTableModel的信号及参数类型说 二明
beforeDelete(row:int)在调用deleteRowFromTable(row:int)方法删除指定的行之前发送信号
beforeInsert(record: QSqlRecord)在将新行插入当前活动的数据库表之前,insertRowIntoTable()会发出此信号。将要插入的值存储在记录中,并且可以在插入之前进行修改。
beforeUpdate(row: int,record: QSqlRecord)在使用记录中的值更新当前活动数据库表中的行之前,updateRowInTable()会发出此信号。
请注意,只有标记为已生成的值才会更新。生成的标志可以用setGenerated()设置,也可以用isGenerated(()检查。
primeInsert(row: int,record; QSqlRecord)当在当前活动数据库表的给定行中启动插入时,insertRows()会发出此信号。记录参数可以被写入(因为它是一个引用),例如用默认值填充一些字段,并设置字段的生成标志。在处理此信号时,不要试图通过其他方式编辑记录,如setData()或setRecord()。
数据记录 QSqlRecord 的方法

记录 QSqlRecord 表示数据表中的一行数据,一行数据中每个字段有不同的值,可用QSqlTableModel的 record(row;int)方法获取 QSqlRecord 对象,以获取数据表中的一行数据。

QSqlRecord类封装了数据库记录(通常是数据库中表或视图中的一行)的功能和特征。QSqlRecord支持添加和删除字段以及设置和检索字段值。

可以使用setValue()按名称或位置设置记录字段的值;如果要将字段设置为null,请使用setNull()。要按名称查找字段的位置,请使用indexOf(),要在特定位置查找字段的名称,请使用fieldName()。使用field()检索给定字段的QSqlField对象。使用contains()查看记录是否包含特定的字段名。

当生成要在数据库上执行的查询时,只有那些isGenerated()为true的字段才会包含在生成的SQL中。

一条记录可以用append()或insert()添加字段,用replace()替换字段,用remove()移除字段。可以使用clear()删除所有字段。字段的数量由count()给出;它们的所有值都可以使用clearValues()清除(为null)。

用QSqlRecord 创建记录实例对象的方法如下所示

from PySide6.QtSql import QSqlRecord

QSqlRecord(self)-> None
QSqlRecord(Other: PySide6.QtSql.QSqlRecord)-> None 

QSqlRecord 的常用方法如表所示。

  • 用append(field:QSglField)方法可以在末尾添加字段;
  • 用insert(pos:int,field;QSalField)方法插人字段;
  • 用remove(pos:int)方法移除字段;
  • 用setValue(i:int,val;Any)方法或 setValue(name; str,val:Any)方法根据字段索引或字段名称设置字段的值;
  • 用setGenerated(i: int,generated: bool)方法或setGenerated(name:str,generated;bool)方法根据索引或名称设置字段值是否已经生成只有已经生成的字段值才能
  • 用QSqlTableModel 的 updateRowInTable(row: int,values:QSqlRecord)方法更新到数据库中,默认值是 True。
QSqlRecord的方法及参数类型返回值的类型说明
append(field: QSqlPield)None在末尾添加字段
insert(pos: int,field: QSqlField)None在指定的位置插入字段
remove(pos:int)None根据位置移除字段
replace(pos: int,field: QSqlField)None根据位置替换字段的值
setValue(i: int,val: Any)None根据字段索引值设置字段的值
setValue(name: str,val:Any)None根据字段名称设置字段的值
value(i:int)Any根据字段索引获取字段的值
value(name: str)Any根据字段名称获取字段的值
setNull(i:int)None根据字段索引设置空值
setNull(name: str)None根据字段名称设置空值
isNull(i:int)bool根据字段名称或位置,当指定的字段值为
isNull(name:str)boolNone或不存在该字段时返回True
clear()None删除所有的字段
isEmpty()bo0l获取是否含有字段
clearValues()None删除所有字段的值,字段值为None
contains(name:str)bool获取是否包含指定的字段
count()int获取字段的个数
field(i:int)QSqlField根据字段索引获取字段对象
field(name:str)QSqlField根据字段名称获取字段对象
fieldName(i: int)Str获取字段的名称
indexOf(name:str)int获取字段名称对应的索引
keyValues(keyFields: QSqlRecord)QSqlRecord获取与给定的记录具有相同字段名称的 记录
setGenerated(i: int,generated: bool)None根据索引或名称设置字段值是否已经生 成,只有已经生成的字段值才能被更新到 数据库中。generated 的默认值是 True
setGenerated(name: str,generated: boo1)None根据索引或名称设置字段值是否已经生 成,只有已经生成的字段值才能被更新到 数据库中。generated 的默认值是 True
isGenerated(i: int)bool根据索引获取字段是否已经生成
isGenerated(name:str)bool根据名称获取字段是否已经生成
字段 QSqlField 的方法

QSqlField类处理SQL数据库表和视图中的字段。

字段 QSqlField 是数据表中的列,一个记录由多个字段构成。

字段的属性有字段名、字段类型和字段值等。用QSqlRecord 的 field(i:int)方法或 field(name: str)方法可获得QSqlField。

from PySide6.QtSql import QSqlField

QSqlField(fieldName: str = '', type: Union[PySide6.QtCore.QMetaType, PySide6.QtCore.QMetaType.Type] = Default(QMetaType), tableName: str = '')-> None
QSqlField(other: PySide6.QtSql.QSqlField)-> None 

用QSqlField 创建字段的方法如下所示,其中 type 用于定义字段的类型,可取以下值,QMetaType 类在 QtCore 模块中,这里设置的类型可能与实际的不符,例如太长的整数可能存储成字符串。

QMetaType.Bool、QMetaType.Int、QMetaType.UInt、QMetaType.Double,QMetaType.QString,QMetaType.QByteArray
QMetaType.Long、QMetaType.LongLong、QMetaType.Short、QMetaType.ULong、QMetaType.ULongLong、QMetaType.UShort、
QMetaType.UChar、QMetaType.Float、QMetaType.QCursor,QMetaType.QDate、QMetaType.QSize、QMetaType.Time、QMetaType.QPolygon
QMetaType.QPolygonF、QMetaType。QColor、QMetaType.QSizeF、QMetaType.QRectF、QMetaType.QLine、QMetaType.QIcon、QMetaType.QPen
QMetaType.QLineF、QMetaType.QRect、QMetaType.QPoint、QMetaType QUrl、QMetaType.QDateTime、QMetaType.QPointF、QMetaType,QPalette
QMetaType.QFont、QMetaType.QBrush、QMetaType.QRegion、QMetaType.QImage、QMetaType.QPixmap、QMetaType.QBitmap、QMetaType.QTransform 

QSqlField表示数据库表或视图中单个列的特征,例如数据类型和列名。字段还包含数据库列的值,可以查看或更改该值。
字段数据值存储为QVariants。不允许使用不兼容的类型。例如:

field = QSqlField("age", QMetaType.fromType())
    field.setValue(QPixmap())# WRONG

但是,在可能的情况下,字段将尝试将某些数据类型强制转换为字段数据类型:

field = QSqlField("age", QMetaType.fromType())
    field.setValue(QString("123"))# casts QString to int

QSqlField对象很少在应用程序代码中显式创建。它们通常是通过已经包含字段列表的QSqlRecord间接访问的。例如:

query = QSqlQuery()         ...

QSqlField对象可以提供有关字段的一些元数据,例如,它的名称()、变量类型()、长度()、精度()、默认值()、类型ID()以及它的requiredStatus()、isGenerated()和isReadOnly()。可以检查字段的数据以查看它是否为null(),并检索其值()。编辑数据时,可以使用setValue()设置数据,也可以使用clear()设置为NULL。

字段QSqlField 的常用方法如表所示。主要方法是

  • 用setName(name:str)方法设置字段名称;

  • 用setValue(value: Any)方法设置字段的值;

  • 用setDefaultValue(value:Any)方法设置字段的默认值;

  • 用setReadOnly(readOnly:bool)方法设置是否是只读

    • 在只读状态不能更改字段的值例如不能用setValue()方法和 clear()方法改变其值;
  • 用setRequired(required: bool)方法或 setRequiredStatus(status; QSqlField. RequiredStatus)方法设置字段的值是必须要输入的还是可选的,其中参数 status 可取:

    • QSqlField.Required
    • QSqlField.Optional
    • QSqlField.Unknown
  • PySide6.QtSql.QSqlField.RequiredStatus

    指定该字段是必需字段还是可选字段。

    ConstantDescription
    QSqlField.Required插入记录时必须指定字段。
    QSqlField.Optional插入记录时不必指定字段。
    QSqlField.Unknown数据库驱动程序无法确定该字段是必需的还是可选的。
QSqlField的方法及参数类型返回值的类型说明
setName(name:str)None设置字段的名称
name()str获取字段的名称
setValue(value:Any)None设置字段的值,只读时不能设置值
value()Any获取字段的值
setDefaultValue(value:Any)None设置字段的默认值
defaultValue()Any获取字段的默认值
setMetaType(type:QMetaType)None设置字段的类型
metaType()QMetaType获取存储在数据库中的类型
setReadOnly(readOnly:bool)None设置是否是只读,只读时不能修改字段 的值
isReadOnly()bool获取是否只读
setRequired(required:bool)None设置字段的值是必须要输入还是可选的
setRequiredStatus(status: QSqlField.RequiredStatus)None设置可选状态
setGenerated(gen:bool)None设置字段的生成状态
isGenerated()bool获取字段的生成状态
setLength(fieldLength: int)None设置字段的长度,类型是字符串时是字符 串的最大长度,其他类型无意义
length()int获取字段的长度,负值表示无法确定
setPrecision(precision: int)None设置浮点数的精度,只对数值类型有意义
precision()int获取精度,负数表示不能确定精度
set TableName(tableName: str)None设置数据表名称
tableName()str获取数据表名称
setAuto Value(autoVal:bool)None将字段的值标记成是由数据库自动生成的
isAutoValue()bool获取字段的值是否是由数据库自动生成的
isValid()bool获取字段的类型是否有效
clear()None清除字段的值并设置成 None
isNull()bool如果字段的值是None,则返回True
数据库表格模型QSqITableModel的应用实例

下面的程序用菜单打开一个SQLite 数据库用QTableView 控件显示出数据表中的数据,用QComboBox 控件显示数据库中的数据表名称,可以插入记录和删除记录。在QTableView中可以修改字段的值。由于修改提交模式选择 OnFieldChange,因此对数据库的操作会自动保存到数据库中

image-20230313011041540

# -*- coding: UTF-8 -*-
# File date: Hi_2023/3/13 0:34
# File_name: 04-数据库表格模型QSqITableModel的应用实例.py


from PySide6.QtWidgets import QApplication,QWidget,QComboBox,QTableView,QLabel,QPushButton,QHBoxLayout,QVBoxLayout,QFileDialog,QMenuBar,QGroupBox,QLineEdit,QSpinBox,QDoubleSpinBox
from PySide6.QtSql import QSqlTableModel,QSqlRecord,QSqlDatabase
from PySide6.QtCore import Qt
import sys


class Mywidget(QWidget):
    def __init__(self,parent=None):
        super().__init__(parent)

        self.setupUi()

    def setupUi(self):  # 建立界面
        menuBar = QMenuBar()
        fileMenu = menuBar.addMenu("文件(&E)")

        fileMenu.addAction("打开(&O)").triggered.connect(self.actionOpen)
        fileMenu.addSeparator()
        fileMenu.addAction("关闭(&E)").triggered.connect(self.close)
        label1 = QLabel("选择数据表:")
        self.combox = QComboBox()
        H1 = QHBoxLayout()
        H1.addWidget(label1,stretch=0)
        H1.addWidget(self.combox,stretch=1)
        label2 = QLabel("学号")
        self.spinID = QSpinBox()
        label3 = QLabel("姓名:")
        self.lineEdit_name = QLineEdit()
        label4 = QLabel("语文:")
        self.doubleSpin_chinese = QDoubleSpinBox()
        label5 = QLabel("数学")
        self.doubleSpin_math = QDoubleSpinBox()
        self.pushButton_add = QPushButton("在当前位置添加记录")
        self.groupBox1 = QGroupBox("添加记录")
        self.groupBox1.setEnabled(False)

        H2 = QHBoxLayout(self.groupBox1)
        H2.addWidget(label2,0)
        H2.addWidget(self.spinID,1)
        H2.addWidget(label3,0)
        H2.addWidget(self.lineEdit_name,1)
        H2.addWidget(label4,0)
        H2.addWidget(self.doubleSpin_chinese,1)
        H2.addWidget(label5,0)
        H2.addWidget(self.doubleSpin_math,1)
        H2.addWidget(self.pushButton_add)

        label6 = QLabel("删除行:")
        self.spin_deleteLine = QSpinBox()
        self.pushButton_delete = QPushButton("删除指定的记录")
        self.pushButton_delete_cur = QPushButton("删除当前记录")
        self.groupBox2 = QGroupBox("删除记录")
        self.groupBox2.setEnabled(False)
        H3 = QHBoxLayout(self.groupBox2)
        H3.addWidget(label6,0)
        H3.addWidget(self.spin_deleteLine,1)
        H3.addWidget(self.pushButton_delete)
        H3.addWidget(self.pushButton_delete_cur)

        self.tableView = QTableView()
        self.tableView.setAlternatingRowColors(True)
        V = QVBoxLayout(self)
        V.addWidget(menuBar)
        V.addLayout(H1)
        V.addWidget(self.groupBox1)
        V.addWidget(self.groupBox2)
        V.addWidget(self.tableView)

        # 信号与槽连接
        self.combox.currentTextChanged.connect(self.comboxTextChanged)
        self.pushButton_add.clicked.connect(self.pushButton_add_clicked)
        self.pushButton_delete.clicked.connect(self.pushButton_delete_clicked)
        self.pushButton_delete_cur.clicked.connect(self.pushButton_delete_cur_clicked)

    def actionOpen(self):  # 打开数据库的槽函数
        dbFile,fil = QFileDialog.getOpenFileName(self,dir="./db",filter="SQLite(*.db *.db3)")
        if dbFile:
            self.setWindowTitle(dbFile)
            self.combox.clear()
            self.db = QSqlDatabase.addDatabase('QSQLITE')
            self.db.setDatabaseName(dbFile)
            if self.db.open():
                self.sqlTableModel = QSqlTableModel(self,self.db)# 数据库表格模型
                self.sqlTableModel.setEditStrategy(QSqlTableModel.EditStrategy.OnFieldChange)
                self.tableView.setModel(self.sqlTableModel)
                tables = self.db.tables()
                if len(tables)> 0:
                    self.combox.addItems(tables)
                    self.groupBox1.setEnabled(True)
                    self.groupBox2.setEnabled(True)

                else:
                    self.groupBox1.setEnabled(False)
                    self.groupBox2.setEnabled(False)

    def comboxTextChanged(self,text):
        self.sqlTableModel.setTable(text)
        self.sqlTableModel.select()
        header = self.sqlTableModel.record()

        for i in range(header.count()):
            self.sqlTableModel.setHeaderData(i,Qt.Orientation.Horizontal,header.fieldName(i),Qt.DisplayRole)

    def pushButton_add_clicked(self):
        record = QSqlRecord(self.sqlTableModel.record())
        record.setValue("ID",self.spinID.value())
        record.setValue("name",self.lineEdit_name.text())
        record.setValue("语文",self.doubleSpin_chinese.value())
        record.setValue("数学",self.doubleSpin_math.value())
        self.spinID.setValue(self.spinID.value()+ 1)
        currentRow = self.tableView.currentIndex().row()
        if not self.sqlTableModel.insertRecord(currentRow + 1,record):
            self.sqlTableModel.select()

    def pushButton_delete_clicked(self):
        row = self.spin_deleteLine.value()
        if row > 0 and row <= self.sqlTableModel.rowCount():
            if self.sqlTableModel.removeRow(row - 1):
                self.sqlTableModel.select()

    def pushButton_delete_cur_clicked(self):
        currentRow = self.tableView.currentIndex().row()
        if self.sqlTableModel.removeRow(currentRow):
            self.sqlTableModel.select()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    win = Mywidget()

    win.show()
    sys.exit(app.exec())

表格模型QSqlTableModel排序过滤例子

image-20230326020817601

# -*- coding: UTF-8 -*-
# File date: Hi_2023/3/26 2:02
# File_name: 08-表格排序过滤QSgITableModel.py


from PySide6.QtWidgets import *
from PySide6.QtGui import *
from PySide6.QtCore import *
from PySide6.QtSql import QSqlDatabase,QSqlTableModel
import sys
import os

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


class QTableViewDemo(QMainWindow):

    def __init__(self,parent=None):
        super(QTableViewDemo,self).__init__(parent)
        self.setWindowTitle("QSqlTableModel案例")
        self.resize(500,600)

        self.createTable()
        self.createWindow()
        self.onUpdate()

    def createWindow(self):
        # 排序:字段排序
        labelSort = QLabel('排序:')
        self.comboBoxSort = QComboBox()
        self.comboBoxSort.addItems(self.fieldList)
        self.comboBoxSort.setCurrentText(self.fieldList[0])
        layoutSort = QHBoxLayout()
        layoutSort.addWidget(labelSort)
        layoutSort.addWidget(self.comboBoxSort)
        self.comboBoxSort.currentIndexChanged.connect(lambda: self.onSort(self.sortType))

        # 排序:升序 or 降序
        buttonGroupSort = QButtonGroup(self)
        radioAsecend = QRadioButton("升序")
        radioAsecend.setChecked(True)
        buttonGroupSort.addButton(radioAsecend)
        radioDescend = QRadioButton("降序")
        buttonGroupSort.addButton(radioDescend)
        layoutSort.addWidget(radioAsecend)
        layoutSort.addWidget(radioDescend)
        buttonGroupSort.buttonClicked.connect(lambda button: self.onSort(button.text()))

        # 性别筛选按钮
        buttonGroupSex = QButtonGroup(self)
        layoutSexButton = QHBoxLayout()
        layoutSexButton.addWidget(QLabel('性别:'))
        radioAll = QRadioButton("All")
        radioAll.setChecked(True)
        buttonGroupSex.addButton(radioAll)
        layoutSexButton.addWidget(radioAll)
        radioMen = QRadioButton("男")
        buttonGroupSex.addButton(radioMen)
        layoutSexButton.addWidget(radioMen)
        radioWomen = QRadioButton("女")
        buttonGroupSex.addButton(radioWomen)
        layoutSexButton.addWidget(radioWomen)
        buttonGroupSex.buttonClicked.connect(self.onFilterSex)

        # 科目过滤
        labelSubject = QLabel('科目:')
        self.comboBoxSubject = QComboBox()
        self.comboBoxSubject.addItems(['All','语文','数学','外语','综合'])
        self.comboBoxSubject.setCurrentText('All')
        self.comboBoxSubject.currentTextChanged.connect(self.onSubjectChange)
        layoutSubject = QHBoxLayout()
        layoutSubject.addWidget(labelSubject)
        layoutSubject.addWidget(self.comboBoxSubject)

        # 第一排按钮管理
        layoutOne = QHBoxLayout()
        layoutOne.addLayout(layoutSort)
        layoutOne.addLayout(layoutSexButton)
        layoutOne.addLayout(layoutSubject)
        layoutOne.addStretch(1)

        # 增删按钮管理
        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)
        layoutEdit = QHBoxLayout()
        layoutEdit.addWidget(self.buttonAddRow)
        layoutEdit.addWidget(self.buttonInsertRow)
        layoutEdit.addWidget(self.buttonDeleteRow)

        layoutEdit.addStretch(1)
        # 明细标签
        self.labelCount = QLabel('共xxx行')
        self.labelCurrent = QLabel('row:,col:')
        layoutEdit.addWidget(self.labelCurrent)
        layoutEdit.addWidget(self.labelCount)
        selectModel = self.tableView.selectionModel()
        selectModel.currentChanged.connect(self.onCurrentChange)

        # 汇总管理
        layout = QVBoxLayout(self)
        layout.addLayout(layoutOne)
        layout.addWidget(self.tableView)
        layout.addLayout(layoutEdit)

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

    def createTable(self):
        self.db = QSqlDatabase.addDatabase('QSQLITE')
        self.db.setDatabaseName('./db/database.db')
        if self.db.open()is not True:
            QMessageBox.critical(self,"警告","数据连接失败,程序即将退出")
            exit()

        self.model = QSqlTableModel()
        self.model.setTable('student')
        self.model.setHeaderData(0,Qt.Horizontal,"编号")
        self.model.setHeaderData(1,Qt.Horizontal,"姓名")
        self.model.setHeaderData(2,Qt.Horizontal,"科目")
        self.model.setHeaderData(3,Qt.Horizontal,"性别")
        self.model.setHeaderData(4,Qt.Horizontal,"年纪")
        self.model.setHeaderData(5,Qt.Horizontal,"成绩")
        self.model.setHeaderData(6,Qt.Horizontal,"说明")
        self.model.setEditStrategy(QSqlTableModel.OnRowChange)

        # 初始化数据表
        self.model.select()

        fileRecord = self.model.record()
        self.fieldList = []
        for i in range(fileRecord.count()):
            name = fileRecord.fieldName(i)
            self.fieldList.append(name)
        self.filterSex = ''# 性别选择
        self.sortType = '升序'# 升序 or 降序
        self.filterSubject = ''# 科目选择

        self.tableView = QTableView()

        self.tableView.setModel(self.model)
        # self.selectModel = QItemSelectionModel()
        # self.tableView.setSelectionModel(self.selectModel)
        # 表格宽度的自适应调整
        self.tableView.horizontalHeader().setStretchLastSection(True)
        self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)

    def onAdd(self):
        self.tableView.scrollToBottom()
        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)

    def onCurrentChange(self,current: QModelIndex,previous: QModelIndex):
        self.labelCurrent.setText(f'row:{current.row()},col:{current.column()}')

    def onUpdate(self):
        if self.filterSex == ''and self.filterSubject == '':
            textFilter = ''
        elif self.filterSex != ''and self.filterSubject != '':
            textFilter = self.filterSex + 'and '+ self.filterSubject
        else:
            textFilter = self.filterSex + self.filterSubject
        self.model.setFilter(textFilter)

        if self.sortType == '升序':
            self.model.setSort(self.comboBoxSort.currentIndex(),Qt.AscendingOrder)
        else:
            self.model.setSort(self.comboBoxSort.currentIndex(),Qt.DescendingOrder)
        self.model.select()

        self.labelCount.setText(f'共 {self.model.rowCount()} 行')

    def onSort(self,sortType='升序'):
        self.sortType = sortType
        self.onUpdate()

    def onFilterSex(self,button: QRadioButton):
        text = button.text()
        if text != 'All':
            self.filterSex = f'sex="{button.text()}"'
        else:
            self.filterSex = ''
        self.onUpdate()

    def onSubjectChange(self,text):
        if text != 'All':
            self.filterSubject = f'subject="{text}"'
        else:
            self.filterSubject = ''
        self.onUpdate()


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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

士别三日,当挖目相待

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

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

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

打赏作者

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

抵扣说明:

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

余额充值