通过Python语言,我们可以很方便地对各类数据库进行查询等操作,比如轻量型的SQLite3数据库、MySQL数据库等。而要在GUI界面上显示和操作数据库中的数据,就需要借助PyQt5中的QtSQL模块,其提供了数据库连接、数据表和SQL操作的各种类。QT中的各种SQL相关类如下图所示。
从数据库中获取的数据可以直接用QSqlTableModel、QSqlQueryModel等模型类表示,可以与QTableView等类组成Model/View结构,便于高效实现数据和GUI界面的交互操作。
本期,我们主要针对,Qt SQL中的QSqlTableModel,QDataWidgetMapper,QSqlDatabase三个类并基于一个简单的小项目进行展开介绍。我们打算做这样的一个GUI界面,其主要目的包括:打开db文件或者连接MySQL数据库【下期介绍】,并将数据记录展示在一个QTableView中,同时通过QDataWidgetMapper类与QSqlTableModel数据模型建立连接,然后将数据表的某个字段与界面上其它控件建立映射,那么界面组件就可以自动显示这个字段的数据内容。该GUI界面实现插入、删除、新增、追加、根据表头执行排序等功能,同时还能实现将数据记录另存为cvs格式文件并保存到本地。ui界面设计相关不再赘述,有兴趣请看往期介绍。
1、加载sqlite3数据库db文件
@pyqtSlot()
def on_openFile_triggered(self):
dbFilename, filter = QFileDialog.getOpenFileName(self, "选择数据库文件", "",
"SQL Lite数据库(*.db *.db3)")
if (dbFilename == ''):
return
# QSqlDatabase打开数据库,用于建立与数据库之间的连接,加载所对应的数据库驱动
# 此处为sqlite3数据库,驱动为QSQLITE,mysql数据库对应QMYSQL
self.DB = QSqlDatabase.addDatabase("QSQLITE")
self.DB.setDatabaseName(dbFilename)
# self.DB.setHostName("localhost") 设置主机名
# self.DB.setUserName("root") 设置用户名
# self.DB.setPassword("123456") 设置登录密码
# self.DB.setPort(3306) 设置端口
if self.DB.open():
self.open_table()
else:
QMessageBox.warning(self, "错误", "打开数据库失败")插入代码片
2、Model/View结构数据显示
GUI中操作数据库的目的在于将数据表记录在界面上进行显示和编辑,在Pyqt5中一般采用Model/View的结构进行记录显示。QtableView就是PyQT中常用的数据内容显示View组件,而常见的数据库操作的数据模型类有QSqlQueryModel、QSqlTableModel等。本期我们重点介绍QSqlTableModel。*
def open_table(self):
#建立QSqlTableModel模型,参数为数据表名称,获取数据表的全部、可编辑的记录
self.tableModel = QSqlTableModel(self, self.DB) # 数据模型,参数为数据库名称,通过QFileDialog打开
self.tableModel.setTable("score") # 设置数据表名称
self.tableModel.setEditStrategy(QSqlTableModel.OnManualSubmit) # 数据保存方式,OnManualSubmit , OnRowChange
if (self.tableModel.select() == False):
QMessageBox.critical(self, "错误信息",
"打开数据表错误,错误信息\n" + self.tableModel.lastError().text())
return
self.getFieldNames() #获取字段名称字典的函数
self.tableModel.setHeaderData(self.fldNum["ID"], Qt.Horizontal, "学号")
self.tableModel.setHeaderData(self.fldNum["NAME"], Qt.Horizontal, "姓名")
self.tableModel.setHeaderData(self.fldNum["SEX"], Qt.Horizontal, "性别")
self.tableModel.setHeaderData(self.fldNum["AGE"], Qt.Horizontal, "年龄")
self.tableModel.setHeaderData(self.fldNum["YUWEN"], Qt.Horizontal, "语文")
self.tableModel.setHeaderData(self.fldNum["SHUXUE"], Qt.Horizontal, "数学")
self.tableModel.setHeaderData(self.fldNum["YINGYU"], Qt.Horizontal, "英语")
self.tableModel.setHeaderData(self.fldNum["WULI"], Qt.Horizontal, "物理")
self.tableModel.setHeaderData(self.fldNum["HUAXUE"], Qt.Horizontal, "化学") # 这两个字段不在tableView中显示
self.tableModel.setHeaderData(self.fldNum["SHENGWU"], Qt.Horizontal, "生物")
##创建界面组件与数据模型的字段之间的数据映射
self.mapper = QDataWidgetMapper()
self.mapper.setModel(self.tableModel)
self.mapper.setSubmitPolicy(QDataWidgetMapper.AutoSubmit)
##界面组件与tabModel的具体字段之间的联系
self.mapper.addMapping(self.ui.lineEdit_2, self.fldNum["ID"])
self.mapper.addMapping(self.ui.lineEdit_3, self.fldNum["NAME"])
self.mapper.addMapping(self.ui.comboBox_2, self.fldNum["SEX"])
self.mapper.addMapping(self.ui.lineEdit_5, self.fldNum["AGE"])
self.mapper.addMapping(self.ui.lineEdit_6, self.fldNum["YUWEN"])
self.mapper.addMapping(self.ui.lineEdit_7, self.fldNum["SHUXUE"])
self.mapper.addMapping(self.ui.lineEdit_8, self.fldNum["YINGYU"])
self.mapper.addMapping(self.ui.lineEdit_4, self.fldNum["WULI"])
self.mapper.addMapping(self.ui.lineEdit_9, self.fldNum["HUAXUE"])
self.mapper.addMapping(self.ui.lineEdit_10, self.fldNum["SHENGWU"])
self.mapper.toFirst() # 移动到首记录
self.selectModel = QItemSelectionModel(self.tableModel) # 选择模型
self.selectModel.currentChanged.connect(self.do_currentChanged) # 当前项变化时触发
self.selectModel.currentRowChanged.connect(self.do_currentRowChanged) # 选择行变化时
#将QSqlTableModel模型设置为界面上的QTableView组件的Model/View数据模型就可以显示和编辑数据
self.ui.tableView.setModel(self.tableModel) # 设置数据模型
self.ui.tableView.setSelectionModel(self.selectModel) # 设置选择模型
# 表格宽度的自适应调整
self.ui.tableView.horizontalHeader().setStretchLastSection(True)
self.ui.tableView.horizontalHeader().setSectionsClickable(True)
#设置tableView背景前景
self.ui.tableView.setAlternatingRowColors(True)
self.ui.tableView.setStyleSheet("alternate-background-color: rgb(110, 110, 110)"
"; background-color: rgb(55, 180, 210);")
#设置表头排序功能及表头样式
self.ui.tableView.setSortingEnabled(True)
self.ui.tableView.horizontalHeader().setStyleSheet(
"::section{background-color: pink; color: blue; font-weight: bold}")
self.ui.tableView.verticalHeader().hide()
3、数据操作
此功能界面实现数据插入、追加、保存、删除、排序等操作
@pyqtSlot()
def on_Save_triggered(self):
res = self.tableModel.submitAll()
if (res == False):
QMessageBox.information(self, "消息",
"数据保存错误,错误信息\n" + self.tableModel.lastError().text())
else:
self.ui.Save.setEnabled(False)
@pyqtSlot() ##取消修改
def on_Cancel_triggered(self):
self.tableModel.revertAll()
@pyqtSlot() ##添加记录
def on_Add_triggered(self):
self.tableModel.insertRow(self.tableModel.rowCount(), QModelIndex()) # 在末尾添加一个记录
curIndex = self.tableModel.index(self.tableModel.rowCount() - 1, 1) # 创建最后一行的ModelIndex
self.selModel.clearSelection()
self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select) # 设置刚插入的行为当前选择行
currow = curIndex.row() # 获得当前行
self.tableModel.setData(self.tableModel.index(currow, self.fldNum["ID"]),2 + self.tableModel.rowCount()) # 自动生成编号
self.tableModel.setData(self.tableModel.index(currow, self.fldNum["YINGYU"]), 100)
@pyqtSlot()
def on_insert_triggered(self):
curIndex = self.ui.tableView.currentIndex() # QModelIndex
self.tableModel.insertRow(curIndex.row(), QModelIndex())
self.selModel.clearSelection() # 清除已有选择
self.selModel.setCurrentIndex(curIndex, QItemSelectionModel.Select)
@pyqtSlot()
def on_delete_2_triggered(self):
curIndex = self.selModel.currentIndex() # 获取当前选择单元格的模型索引
self.tabModel.removeRow(curIndex.row()) # 删除当前行
4、数据导出为csv-将数据表文件导出为csv结构化数据
@pyqtSlot()
def on_saveAs_triggered(self):
options = QFileDialog.Options()
file_name, _ = QFileDialog.getSaveFileName(self, "保存为CSV文件", "", "CSV文件 (*.csv)", options=options)
if file_name:
with open(file_name, 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
for row in range(self.tabModel.rowCount()):
row_data = [self.tabModel.data(self.tabModel.index(row, col)) for col in range(self.tabModel.columnCount())]
writer.writerow(row_data)
还有一个重要的功能尚未实现,即数据分页显示的问题,这个我们下期详解。
5、总结
本小节,通过一个下案例详细介绍了PyQt5里面的数据库、表显示及操作。GUI中的Mysql以及更多的内容,我们后期详解。敬请关注。