在图形界面应用中,经常需要显示二维数据信息,如果数据仅仅显示且不频繁变更刷新,使用QTableWidget是更好的选择,QTableWidget内置了标准的数据模型,继承QTableView类。如果需要频繁刷新数据,比如查询和显示数据库相关表的数据,使用QTableView+QSqlQueryModel是比较好的选择。
下面是一个实例代码,程序运行的界面如下:
在Python程序代码所在文件夹中有一个“db”文件夹,db文件夹中有一个Sqlite3数据库文件“database.db”,有一个数据库表“student”,信息如下:
使用Python查询student表数据,并进行分页显示,每页10条记录,详细代码如下:
#! /home/liuyf/miniconda3/envs/py312
# -*- coding: utf-8 -*-
'''
@File : PySide6_QSqlQueryModel.py
@Time : 2023/11/30 01:39:05
@Author : 诚外无物
@Version : 1.0
@Desc : 使用模型/视图框架,QTableView控件绑定QSqlQueryModel模型,查询及分页显示数据库数据信息。
'''
import os
import sys
from typing import Optional
import PySide6.QtCore
from PySide6.QtGui import *
from PySide6.QtCore import *
from PySide6.QtWidgets import *
from PySide6.QtSql import QSqlDatabase,QSqlQuery,QSqlQueryModel
import PySide6.QtWidgets
os.chdir(os.path.dirname(__file__))
ID,NAME,SUBJECT,SEX,AGE,SCODE,DESCRIBE = range(7)
class SqlQueryModelDemo(QWidget):
def __init__(self):
super().__init__()
self.setWindowTitle('查询数据库学生信息--分页查询案例')
self.setWindowIcon(QIcon('./images/cartoon1.ico'))
self.resize(550,400)
# 创建窗口控件及布局
self.createWindow()
# 设置表格
self.setTableView()
# 创建窗口控件及布局函数
def createWindow(self):
# 操作布局: 一组控件进行水平布局
operatorLayout = QHBoxLayout()
# 创建一组按钮
self.prevButton = QPushButton('前一页')
self.nextButton = QPushButton('后一页')
self.firstButton = QPushButton('第一页')
self.lastButton = QPushButton('最后一页')
#switchPageLabel = QLabel('转到第')
# 创建单行文本输入框对象,并设置输入为整数,及宽度
self.switchPageLineEdit = QLineEdit()
self.switchPageLineEdit.setValidator(QIntValidator(self))
self.switchPageLineEdit.setFixedWidth(40)
#pageLabel = QLabel('页')
self.switchPageButton = QPushButton('Go')
# 信号与槽函数
self.nextButton.clicked.connect(self.onNextButtonClick)
self.firstButton.clicked.connect(self.onFirstButtonClick)
self.prevButton.clicked.connect(self.onPrevButtonClick)
self.lastButton.clicked.connect(self.onLastButtonClick)
self.switchPageButton.clicked.connect(self.onSwitchPageButtonClick)
# 把上面控件添加到水平布局对象中
operatorLayout.addWidget(self.firstButton)
operatorLayout.addWidget(self.prevButton)
operatorLayout.addWidget(self.nextButton)
operatorLayout.addWidget(self.lastButton)
operatorLayout.addWidget(QLabel('转到第'))
operatorLayout.addWidget(self.switchPageLineEdit)
operatorLayout.addWidget(QLabel('页'))
operatorLayout.addWidget(self.switchPageButton)
# 添加一个弹簧控件,窗口拉伸,前面的对象均大小不变
operatorLayout.addWidget(QSplitter())
# 状态布局 : 创建及添加3个状态信息Label对象,显示信息
statusLayout = QHBoxLayout()
self.totalPageLabel = QLabel()
self.totalPageLabel.setFixedWidth(70)
self.currentPageLabel = QLabel()
self.currentPageLabel.setFixedWidth(70)
self.totalRecordLabel = QLabel()
self.totalRecordLabel.setFixedWidth(70)
statusLayout.addWidget(self.totalPageLabel)
statusLayout.addWidget(self.currentPageLabel)
statusLayout.addWidget(QSplitter())
statusLayout.addWidget(self.totalRecordLabel)
# 设置表格属性
self.tableView = QTableView()
# 设置表格对象最后一列可扩展,窗口拉伸,则填满控件
self.tableView.horizontalHeader().setStretchLastSection(True)
# 设置表格对象的列宽度,根据内容自动调整
self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
# 创建界面
main_layout = QVBoxLayout()
main_layout.addLayout(operatorLayout)
main_layout.addWidget(self.tableView)
main_layout.addLayout(statusLayout)
self.setLayout(main_layout)
# 设置表格
def setTableView(self):
# 声明查询模型
self.queryModel = QSqlQueryModel(self)
# 设置当前页
self.currentPage = 1
# 设置每页显示记录数
self.pageRecordCount = 10
# 获取的总记录数
self.totalRecordCount = self.getTotalRecordCount()
# 总页数:因为int()实现四舍五入,加上0.5,确保不少一行
self.totalPage = int(self.totalRecordCount / self.pageRecordCount + 0.5)
# 设置总页数文本
self.totalPageLabel.setText('总共%d页' % self.totalPage)
# 设置总记录数文本
self.totalRecordLabel.setText('共%d条' % self.totalRecordCount)
# 设置模型
self.tableView.setModel(self.queryModel)
# 显示首页数据
self.recordQuery(0)
# 刷新状态
self.updateStatus()
# 设置表头
self.queryModel.setHeaderData(ID,Qt.Horizontal,'编号')
self.queryModel.setHeaderData(NAME,Qt.Horizontal,'姓名')
self.queryModel.setHeaderData(SUBJECT,Qt.Horizontal,'科目')
self.queryModel.setHeaderData(SEX,Qt.Horizontal,'性别')
self.queryModel.setHeaderData(AGE,Qt.Horizontal,'年龄')
self.queryModel.setHeaderData(SCODE,Qt.Horizontal,'分数')
self.queryModel.setHeaderData(DESCRIBE,Qt.Horizontal,'说明')
# 得到记录数
def getTotalRecordCount(self):
self.queryModel.setQuery('select count(*) from student')
rowCount = self.queryModel.record(0).value(0)
return rowCount
# 记录查询
def recordQuery(self,limitIndex):
szQuery = ('select * from student limit %d,%d' %(limitIndex,self.pageRecordCount))
print(szQuery)
self.queryModel.setQuery(szQuery)
# 刷新状态
def updateStatus(self):
szCurrentText = '当前第%d页' % self.currentPage
self.currentPageLabel.setText(szCurrentText)
# 设置按钮是否可用
if self.currentPage == 1:
self.firstButton.setEnabled(False)
self.prevButton.setEnabled(False)
self.nextButton.setEnabled(True)
self.lastButton.setEnabled(True)
elif self.currentPage >= self.totalPage -1:
self.firstButton.setEnabled(True)
self.prevButton.setEnabled(True)
self.nextButton.setEnabled(False)
self.lastButton.setEnabled(False)
else:
self.firstButton.setEnabled(True)
self.prevButton.setEnabled(True)
self.nextButton.setEnabled(True)
self.lastButton.setEnabled(True)
# 响应第一页按钮槽函数
def onFirstButtonClick(self):
self.recordQuery(0)
self.currentPage = 1
self.updateStatus()
# 响应前一页按钮槽函数
def onPrevButtonClick(self):
limitIndex = (self.currentPage -2) * self.pageRecordCount
self.recordQuery(limitIndex)
self.currentPage -= 1
self.updateStatus()
# 响应后一页按钮的槽函数
def onNextButtonClick(self):
limitIndex = self.currentPage * self.pageRecordCount
self.recordQuery(limitIndex)
self.currentPage += 1
# 更新状态栏信息
self.updateStatus()
# 响应后一页按钮槽函数
def onLastButtonClick(self):
limitIndex = (self.totalPage -1) * self.pageRecordCount
self.recordQuery(limitIndex)
self.currentPage = self.totalPage
self.updateStatus()
# 响应跳转按钮槽函数
def onSwitchPageButtonClick(self):
szText = self.switchPageLineEdit.text()
# 判断是否为空
if szText == '':
QMessageBox.information(self,'提示','请输入要跳转的页面数字!')
return
# 得到跳转页面,判断是否为合理的页面
pageIndex = int(szText)
if pageIndex > self.totalPage or pageIndex < 1:
QMessageBox.information(self,'提示','没有指定的页面,请输入合适的页数。')
return
# 得到查询起始行号
limitIndex = (pageIndex - 1) * self.pageRecordCount
# 记录查询
self.recordQuery(limitIndex)
# 设置当前页
self.currentPage = pageIndex
# 刷新状态栏提示
self.updateStatus()
if __name__ == '__main__':
app = QApplication(sys.argv)
db = QSqlDatabase.addDatabase('QSQLITE')
db.setDatabaseName('./db/database.db')
if db.open() is not True:
QMessageBox.critical(None,'open error','数据库打开失败。')
exit()
demo = SqlQueryModelDemo()
demo.show()
sys.exit(app.exec())
以上代码学习《PySide6/PyQt6快速开发与实践》一书,逐行敲入书中代码,经验证无误,仅调整部分代码顺序。
在主窗口上创建QTableView控件,绑定了QSqlQueryModel对象,刷新数据其实,就仅仅是更改QSqlQueryModel实例对象的查询sql语句,定义了一个简单的函数,传入参数,使用QSqlQueryModel类的setQuery(sql语句)方法,即可更新QTableView实例对象的数据显示。
def recordQuery(self,limitIndex):
szQuery = ('select * from student limit %d,%d' %(limitIndex,self.pageRecordCount))
self.queryModel.setQuery(szQuery)
在主程序上鼠标单击查询按钮,主要就是调用上面的recordQuery()函数。QSqlQueryModel数据变化,QTableView显示数据即变化。
以上代码体现了Qt的模型/视图框架的灵活性。