如何在PyQt5中用QTableView展示SQLite数据并实时更新
PyQt5+SQLite:批量删除条目与线程安全数据更新方案
我来帮你搞定这两个问题,先看一次性删除所有条目的实现,再聊聊如何用QThread保证UI响应性:
一、一次性删除所有条目
这里有两种实用方案,根据你的数据量选择:
方案1:通过QSqlTableModel批量删除(适合小数据量)
利用模型自带的removeRows方法一次性删除所有行,因为你的模型设置了OnFieldChange的编辑策略,删除操作会自动提交到数据库,UI也会实时更新:
def deleteAllRows(): # 从第0行开始,删除所有行 model.removeRows(0, model.rowCount()) # 如果编辑策略不是即时提交,需要额外调用 model.submitAll()
方案2:直接执行SQL语句(适合大数据量)
如果数据量很大,逐行删除效率很低,直接执行SQL DELETE语句会更快,之后手动刷新模型即可:
def deleteAllRows(): query = QtSql.QSqlQuery() # 执行删除所有条目的SQL if query.exec_("DELETE FROM sportsmen"): # 刷新模型,让QTableView同步显示最新数据 model.select() else: print(f"删除失败:{query.lastError().text()}")
二、验证QThread与SQLite结合的响应性
⚠️ 重要提醒:QSqlTableModel是线程不安全的,绝对不能在子线程中直接操作模型!正确的做法是:子线程只负责执行纯SQL操作(比如批量插入、更新),完成后通过信号通知主线程刷新模型,这样UI就不会被阻塞。
下面是修改后的完整代码,包含批量删除按钮、后台批量插入任务(用来验证响应性):
import sys from PyQt5 import QtCore, QtWidgets, QtSql class DBWorker(QtCore.QThread): # 定义信号,任务完成后通知主线程 taskFinished = QtCore.pyqtSignal() def run(self): # 子线程中执行批量插入操作(模拟耗时任务) query = QtSql.QSqlQuery() query.prepare("INSERT INTO sportsmen (first_name, last_name) VALUES (?, ?)") # 批量插入1000条数据,模拟耗时操作 for i in range(1000): query.addBindValue(f"First_{i}") query.addBindValue(f"Last_{i}") query.exec_() # 任务完成,发出信号 self.taskFinished.emit() def initializeModel(model): model.setTable('sportsmen') model.setEditStrategy(QtSql.QSqlTableModel.OnFieldChange) model.select() model.setHeaderData(0, QtCore.Qt.Horizontal, "ID") model.setHeaderData(1, QtCore.Qt.Horizontal, "First name") model.setHeaderData(2, QtCore.Qt.Horizontal, "Last name") def createView(title, model): view = QtWidgets.QTableView() view.setModel(model) view.setWindowTitle(title) return view def addrow(): ret = model.insertRows(model.rowCount(), 1) print(f"添加行结果:{ret}") def editrow(): model.setData(model.index(1, 1), "LEL") def deleteAllRows(): # 这里用方案2,适合大数据量 query = QtSql.QSqlQuery() if query.exec_("DELETE FROM sportsmen"): model.select() print("所有条目已删除") else: print(f"删除失败:{query.lastError().text()}") def startBatchInsert(): # 启动后台线程执行批量插入 worker = DBWorker() worker.taskFinished.connect(lambda: model.select()) # 完成后刷新模型 worker.start() print("后台批量插入任务已启动,UI可正常操作") if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) db = QtSql.QSqlDatabase.addDatabase('QSQLITE') db.setDatabaseName('sports.db') # 确保数据库连接成功 if not db.open(): print(f"数据库连接失败:{db.lastError().text()}") sys.exit(1) # 初始化表(如果不存在的话) query = QtSql.QSqlQuery() query.exec_(""" CREATE TABLE IF NOT EXISTS sportsmen ( id INTEGER PRIMARY KEY AUTOINCREMENT, first_name TEXT, last_name TEXT ) """) model = QtSql.QSqlTableModel() initializeModel(model) view1 = createView("Table Model (View 1)", model) dialog = QtWidgets.QDialog() layout = QtWidgets.QVBoxLayout() layout.addWidget(view1) # 添加行按钮 addBtn = QtWidgets.QPushButton("Add a row") addBtn.clicked.connect(addrow) layout.addWidget(addBtn) # 编辑行按钮 editBtn = QtWidgets.QPushButton("Edit a row") editBtn.clicked.connect(editrow) layout.addWidget(editBtn) # 删除单行按钮 delBtn = QtWidgets.QPushButton("Delete selected row") delBtn.clicked.connect(lambda: model.removeRow(view1.currentIndex().row())) layout.addWidget(delBtn) # 删除所有行按钮 delAllBtn = QtWidgets.QPushButton("Delete ALL rows") delAllBtn.clicked.connect(deleteAllRows) layout.addWidget(delAllBtn) # 后台批量插入按钮(验证响应性) batchInsertBtn = QtWidgets.QPushButton("Start batch insert (1000 rows)") batchInsertBtn.clicked.connect(startBatchInsert) layout.addWidget(batchInsertBtn) dialog.setLayout(layout) dialog.setWindowTitle("Database Demo") dialog.show() sys.exit(app.exec_())
响应性验证方法:
点击「Start batch insert (1000 rows)」按钮后,你可以尝试拖动窗口、点击其他按钮(比如删除行),UI依然能正常响应,说明子线程的耗时操作没有阻塞主线程。这是因为所有模型操作都在主线程完成,子线程只负责执行SQL,完全符合Qt的线程安全要求。
内容的提问来源于stack exchange,提问作者n00p
相关产品推荐
相关产品推荐

