PyQt實現(xiàn)異步數(shù)據(jù)庫請求的實戰(zhàn)記錄
需求
開發(fā)軟件的時候不可避免要和數(shù)據(jù)庫發(fā)生交互,但是有些 SQL 請求非常耗時,如果在主線程中發(fā)送請求,可能會造成界面卡頓。這篇博客將會介紹一種讓數(shù)據(jù)庫請求變得和前端的 ajax 請求一樣簡單,且不會阻塞界面的異步請求方法。
實現(xiàn)過程
在實現(xiàn)異步請求之前,需要先明確一下函數(shù)簽名:
def sqlRequest(
service: str,
method: str,
slot,
params: dict = None
)
各個參數(shù)的解釋如下:
service: 業(yè)務名method: 接口名slot: 拿到數(shù)據(jù)后調(diào)用的回調(diào)函數(shù)params: 請求參數(shù)
總體流程如下圖所示,包括子界面發(fā)送請求、數(shù)據(jù)庫線程處理請求、主界面調(diào)用回調(diào)函數(shù)來消費響應結(jié)果三個步驟。

信號總線
在 Qt 中,子線程無法直接更新主界面,只能發(fā)送信號通知主線程,然后在主線程中更新界面。在之前的博客《如何在 pyqt 中實現(xiàn)全局事件總線》介紹了信號總線的使用,通過引入信號總線,可實現(xiàn)任意層級的組件之間的通信。
本文的信號總線只含有兩個信號,一個用來請求數(shù)據(jù),一個用來消費數(shù)據(jù):
class SignalBus(QObject):
""" Signal bus """
fetchDataSig = Signal(SqlRequest) # 請求數(shù)據(jù)信號
dataFetched = Signal(SqlResponse) # 響應數(shù)據(jù)信號
signalBus = SignalBus()
class SqlRequest:
""" Sql request """
def __init__(self, service: str, method: str, slot=None, params: dict = None):
self.service = service
self.method = method
self.slot = slot
self.params = params or {}
class SqlResponse:
""" Sql response """
def __init__(self, data, slot):
self.slot = slot
self.data = data
發(fā)送請求
子界面中通過調(diào)用 sqlRequest() 函數(shù)來發(fā)起異步 SQL 請求,該函數(shù)只是將參數(shù)封裝為 SqlRequest 對象,然后通過 signalBus 的 fetchDataSig 信號發(fā)送給數(shù)據(jù)庫子線程:
def sqlRequest(service: str, method: str, slot=None, params: dict = None):
""" query sql from database """
request = SqlRequest(service, method, slot, params)
signalBus.fetchDataSig.emit(request)
比如下圖中商品類型下拉框的數(shù)據(jù)就來自于數(shù)據(jù)庫:

在組件 LicenseCard 中使用下述代碼就能完成數(shù)據(jù)的請求和消費(組件庫參見 https://qfluentwidgets.com/zh/ ):
from qfluentwidgets import HeaderCardWidget, ComboBox
class LicenseCard(HeaderCardWidget):
def __init__(self, parent=None):
super().__init__("許可證", parent)
self.goodsComboBox = ComboBox(self)
# 請求商品信息
sqlRequest("goodsService", "listAll", self.onGoodsFetched)
def onGoodsFetched(self, goods: List[Goods]):
""" 將商品信息添加到下拉框中 """
for good in goods:
self.goodsComboBox.addItem(good.name, userData=good)
處理請求
子線程 DatabaseThread 中維護著一個請求隊列 tasks,每當收到信號總線的 fetchDataSig 信號時,就會使用反射機制將請求中攜帶的 service 和 method 字符串轉(zhuǎn)換為數(shù)據(jù)庫業(yè)務類的方法指針,并將這個指針添加到隊列中等待調(diào)用。調(diào)用方法返回的數(shù)據(jù)會被封裝為 SqlResponse 對象,接著通過信號總線發(fā)送給主界面。
class DatabaseThread(QThread):
""" Database thread """
def __init__(self, db: QSqlDatabase = None, parent=None):
super().__init__(parent=parent)
self.database = Database(db, self)
self.tasks = deque()
# 處理請求信號
signalBus.fetchDataSig.connect(self.onFetchData)
def run(self):
""" 處理請求 """
while self.tasks:
task, request = self.tasks.popleft()
result = task(**request.params)
signalBus.dataFetched.emit(SqlResponse(result, request.slot))
def onFetchData(self, request: SqlRequest):
""" 將請求添加到隊列中 """
service = getattr(self.database, request.service)
task = getattr(service, request.method)
self.tasks.append((task, request))
if not self.isRunning():
self.start()
class Database(QObject):
""" Database """
def __init__(self, db: QSqlDatabase = None, parent=None):
super().__init__(parent=parent)
self.orderService = OrderService(db)
self.userService = UserService(db)
self.goodsService = GoodsService(db)
處理響應結(jié)果
主界面中只需將信號總線的 dataFetched 信號連接槽函數(shù),然后在槽函數(shù)中對取出 response 對象中的數(shù)據(jù),并調(diào)用回調(diào)函數(shù)來消費數(shù)據(jù)即可:
from qfluentwidgets import MSFluentWindow
class MainWindow(MSFluentWindow):
""" 主界面 """
def __init__(self):
super().__init__()
# 處理響應結(jié)果
signalBus.dataFetched.connect(self.onDataFetched)
def onDataFetched(self, response: SqlResponse):
if response.slot:
response.slot(response.data)到此這篇關(guān)于PyQt實現(xiàn)異步數(shù)據(jù)庫請求的實戰(zhàn)記錄的文章就介紹到這了,更多相關(guān)PyQt異步數(shù)據(jù)庫請求內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python實現(xiàn)增強版Docx與JSON雙向轉(zhuǎn)換的完整指南與代碼解析
在日常辦公和軟件開發(fā)中,我們經(jīng)常需要處理文檔格式轉(zhuǎn)換的需求,本文將詳細介紹一個增強版的Python工具,它可以實現(xiàn)Docx與JSON之間的高質(zhì)量雙向轉(zhuǎn)換,有需要的小伙伴可以了解下2025-12-12
Python中Async語法協(xié)程的實現(xiàn)
這篇文章主要介紹了Python中Async語法協(xié)程的實現(xiàn),文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下2022-06-06
用virtualenv建立多個Python獨立虛擬開發(fā)環(huán)境
這篇文章主要為大家詳細介紹了用virtualenv建立多個Python獨立虛擬開發(fā)環(huán)境,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07
從基礎(chǔ)到高級詳解Python讀寫二進制結(jié)構(gòu)數(shù)組的完全指南
二進制數(shù)據(jù)處理是Python編程中??至關(guān)重要??的技能,本文將深入探討Python中讀寫二進制結(jié)構(gòu)數(shù)組的各種方法和技術(shù),感興趣的小伙伴可以跟隨小編一起學習一下2025-09-09

