操作数据库
一、关于数据库和数据库引擎
1、SQLite
- 我在这里采用的数据库和数据库引擎是 SQLite,这是一个轻量级的数据库,麻雀虽小五脏俱全,具有自给自足、零配置、无服务器、事务性的 SQL 数据库引擎,同时相比 SQL Server 和 MySQL 具有更高的可移植性,只需将 .db 文件移植到装有 SQLite 的电脑就可以照样使用数据库了,适合作为应用配套的数据库。
- SQLite 能够识别并执行通用的标准查询语言——SQL 语言
2、SQLite Studio
- 如果不习惯命令窗口查看数据库的数据,想要像 SQL Server 或 MySQL 那样以可视化的表格视图查看数据,可以去 SQLitestudio 的官网下载便携版的 SQLitestudio。
3、简单示例
- 建立一个 Python 脚本,代码如下:
# -*- coding:utf-8 -*- # Time : 2019/10/01 上午 10:07 # Author : 御承扬 # e-mail:2923616405@qq.com # project: PyQt5 # File : dbOption01.py # @software: PyCharm import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtSql import * def createDB(): db = QSqlDatabase.addDatabase('QSQLITE') db.setDatabaseName('./db/database.db') if not db.open(): QMessageBox.critical(None, ("无法打开数据库连接"), ("无法建立到数据库的连接,这个例子需要SQLite支持"), QMessageBox.Cancel) return False query = QSqlQuery() query.exec_("create table people(id int primary key, name varchar(20), address varchar(30))") query.exec_("insert into people values(1, 'zhangsan', 'beijing')") query.exec_("insert into people values(2, 'wangwu', 'shanghai')") query.exec_("insert into people values(3, 'lisi', 'guangzhou')") query.exec_("insert into people values(4, 'zhaoliu', 'kunming')") db.close() return True if __name__ == "__main__": app = QApplication(sys.argv) createDB() sys.exit(app.exec_())
- 运行后会在脚本的同级目录的 db 文件夹下新建一个名为“database.db”的文件,使用 SQLiteStudio 打开数据库,查看里面的数据,如下图:
二、操作数据库
1、数据库模型视图
1.1、单页查询模型
- PyQt5 提供了一个高级接口:QSqlTableModel,使得程序能够以可编辑的列表视图形式打开数据库中的数据表。
- QSqlTableModel 类是一个可以读写的表格模型,当连接到数据库后,使用 setTable() 函数设置要查询的表,使用 setFilter() 函数设置过滤器条件(SQL 中 where 语句的条件一样),使用 select() 函数执行查询,使用 setEditStrategy() 函数设置“编辑策略”,编辑策略的可取值如下:
编辑策略 描述 QSqlTableModel.OnFieldChange 所有变更实时更新到数据库中 QSqlTableModel.OnRowChange 当用户选择不同的行时,在当前行进行变更 QSqlTableModel.OnManualSubmit 手动变更,不自动变更 - 示例代码如下:
# -*- coding:utf-8 -*- # Time : 2019/10/01 下午 2:37 # Author : 御承扬 # e-mail:2923616405@qq.com # project: PyQt5 # File : dbOption02.py # @software: PyCharm import sys from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtSql import * def initializeModel(model): model.setTable('people') model.setEditStrategy(QSqlTableModel.OnFieldChange) model.select() model.setHeaderData(0, Qt.Horizontal, "ID") model.setHeaderData(1, Qt.Horizontal, "Name") model.setHeaderData(2, Qt.Horizontal, "address") def createView(title, model): view = QTableView() view.setModel(model) view.setWindowTitle(title) return view def addRow(): ret = model.insertRows(model.rowCount(), 1) print('insertRows=%s' % str(ret)) def findRow(i): delRow = i.row() print('del row=%s' % str(delRow)) if __name__ == "__main__": app = QApplication(sys.argv) db = QSqlDatabase.addDatabase('QSQLITE') db.setDatabaseName('./db/database.db') model = QSqlTableModel() delRow = -1 initializeModel(model) view1 = createView("Table Model(View 1)", model) view1.clicked.connect(findRow) dlg = QDialog() layout = QVBoxLayout() layout.addWidget(view1) addBtn = QPushButton('添加一行') addBtn.clicked.connect(addRow) layout.addWidget(addBtn) delBtn = QPushButton('删除一行') delBtn.clicked.connect(lambda: model.removeRow(view1.currentIndex().row())) layout.addWidget(delBtn) dlg.setLayout(layout) dlg.setWindowTitle('Database Demo') dlg.setWindowIcon(QIcon("./images/Python2.ico")) dlg.resize(430, 450) dlg.show() sys.exit(app.exec_())
- 效果如下:
1.2、分页查询模型
- PyQt5 中除了上面那种可编辑的单页的列表视图,还提供了分页不可编辑的列表视图,示例如下:
# -*- coding:utf-8 -*- # Time : 2019/10/02 上午 9:10 # Author : 御承扬 # e-mail:2923616405@qq.com # project: PyQt5 # File : dbOption03.py # @software: PyCharm import sys import re from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtSql import * class DataGrid(QWidget): def __init__(self): super(DataGrid, self).__init__() self.setWindowIcon(QIcon("./images/Python2.ico")) self.setWindowTitle("分页查询示例") self.resize(750, 300) # 查询模型 self.queryModel = None # 数据表 self.tableView = None # 总数页文本 self.totalPageLabel = None # 当前页文本 self.currentPageLabel = None self.totalRecordLabel = None # 转到页输入框 self.switchPageLineEdit = None # 前一页按钮 self.prevButton = None # 后一页按钮 self.nextButton = None # 转到页按钮 self.switchPageButton = None # 当前页 self.currentPage = 0 # 总页数 self.totalPage = 0 # 总记录数 self.totalRecrodCount = 0 # 每页显示记录数 self.PageRecordCount = 5 self.db = None self.initUI() def initUI(self): self.createWindow() self.setTableView() self.prevButton.clicked.connect(self.onPrevButtonClick) self.nextButton.clicked.connect(self.onNextButtonClick) self.switchPageButton.clicked.connect(self.onSwitchPageButtonClick) def closeEvent(self, event): self.db.close() def createWindow(self): # 操作布局 operatorLayout = QHBoxLayout() self.prevButton = QPushButton("前一页") self.nextButton = QPushButton("后一页") self.switchPageButton = QPushButton("Go") self.switchPageLineEdit = QLineEdit() self.switchPageLineEdit.setFixedWidth(40) switchPage = QLabel("转到第") page = QLabel("页") operatorLayout.addWidget(self.prevButton) operatorLayout.addWidget(self.nextButton) operatorLayout.addWidget(switchPage) operatorLayout.addWidget(self.switchPageLineEdit) operatorLayout.addWidget(page) operatorLayout.addWidget(self.switchPageButton) operatorLayout.addWidget(QSplitter()) # 状态布局 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.Stretch) # 创建界面 mainLayout = QVBoxLayout(self) mainLayout.addLayout(operatorLayout) mainLayout.addWidget(self.tableView) mainLayout.addLayout(statusLayout) self.setLayout(mainLayout) def setTableView(self): print('*** step2 SetTableView') self.db = QSqlDatabase.addDatabase('QSQLITE') self.db.setDatabaseName('./db/datagrid.db') self.db.open() self.queryModel = QSqlQueryModel(self) self.currentPage = 1 self.totalRecrodCount = self.getTotalRecordCount() self.totalPage = self.getPageCount() self.updateStatus() self.setTotalPageLabel() self.setTotalRecordLabel() self.recordQuery(0) self.tableView.setModel(self.queryModel) print('totalRecordCount=' + str(self.totalRecrodCount)) print('totalPage=' + str(self.totalPage)) self.queryModel.setHeaderData(0, Qt.Horizontal, '编号') self.queryModel.setHeaderData(1, Qt.Horizontal, '姓名') self.queryModel.setHeaderData(2, Qt.Horizontal, '性别') self.queryModel.setHeaderData(3, Qt.Horizontal, '年龄') self.queryModel.setHeaderData(4, Qt.Horizontal, '院系') def getTotalRecordCount(self): self.queryModel.setQuery("select * from student") rowCount = self.queryModel.rowCount() print('rowCount=' + str(rowCount)) return rowCount def getPageCount(self): if self.totalRecrodCount % self.PageRecordCount == 0: return self.totalRecrodCount / self.PageRecordCount else: return self.totalRecrodCount / self.PageRecordCount + 1 def recordQuery(self, limitIndex): szQuery = ("select * from student limit %d,%d" % (limitIndex, self.PageRecordCount)) print('query sql=' + szQuery) self.queryModel.setQuery(szQuery) def updateStatus(self): szCurrentText = ("当前第%d页" % self.currentPage) self.currentPageLabel.setText(szCurrentText) if self.currentPage == 1: self.prevButton.setEnabled(False) self.nextButton.setEnabled(True) elif self.currentPage == self.totalPage: self.prevButton.setEnabled(True) self.nextButton.setEnabled(False) else: self.prevButton.setEnabled(True) self.nextButton.setEnabled(True) def setTotalPageLabel(self): szPageCountText = ("总共%d页" % self.totalPage) self.totalPageLabel.setText(szPageCountText) def setTotalRecordLabel(self): szTotalRecordText = ("共%d条" % self.totalRecrodCount) print('*** setTotalRecordLabel szTotalRecordText=' + szTotalRecordText) self.totalRecordLabel.setText(szTotalRecordText) def onPrevButtonClick(self): print('*** onPrevButtonClick '); limitIndex = (self.currentPage - 2) * self.PageRecordCount self.recordQuery(limitIndex) self.currentPage -= 1 self.updateStatus() def onNextButtonClick(self): print('*** onNextButtonClick '); limitIndex = self.currentPage * self.PageRecordCount self.recordQuery(limitIndex) self.currentPage += 1 self.updateStatus() def onSwitchPageButtonClick(self): # 得到输入字符串 szText = self.switchPageLineEdit.text() # 数字正则表达式 pattern = re.compile(r'^[0-9]*[1-9][0-9]*$') match = pattern.match(szText) # 判断是否为数字 if not match: QMessageBox.information(self, "提示", "请输入数字") return # 是否为空 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) win = DataGrid() win.show() sys.exit(app.exec_())
- 效果如下: