首页 > 数据库 >如何在 PyQt 中实现异步数据库请求

如何在 PyQt 中实现异步数据库请求

时间:2023-12-07 18:11:34浏览次数:38  
标签:__ 异步 请求 service slot 数据库 PyQt self def

需求

开发软件的时候不可避免要和数据库发生交互,但是有些 SQL 请求非常耗时,如果在主线程中发送请求,可能会造成界面卡顿。这篇博客将会介绍一种让数据库请求变得和前端的 ajax 请求一样简单,且不会阻塞界面的异步请求方法。

实现过程

在实现异步请求之前,需要先明确一下函数签名:

def sqlRequest(
    service: str, 
    method: str, 
    slot, 
    params: dict = None
)

各个参数的解释如下:

  • service: 业务名
  • method: 接口名
  • slot: 拿到数据后调用的回调函数
  • params: 请求参数

总体流程如下图所示,包括子界面发送请求、数据库线程处理请求、主界面调用回调函数来消费响应结果三个步骤。

image

信号总线

在 Qt 中,子线程无法直接更新主界面,发送信号通知主线程,然后在主线程中更新界面。在之前的博客《如何在 pyqt 中实现全局事件总线》介绍了信号总线的使用,通过引入信号总线,可实现任意层级的组件之间的通信。

本文的信号总线只含有两个信号,一个用来请求数据,一个用来消费数据:

class SignalBus(QObject):
    """ Signal bus """
    fetchDataSig = Signal(SqlRequest)    # 请求数据信号
    dataFetched = Signal(SqlResponse)    # 响应数据信号

    
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

发送请求

子界面中通过调用 sqlRequest() 函数来发起异步 SQL 请求,该函数只是将参数封装为 SqlRequest 对象,然后通过 signalBusfetchDataSig 信号发送给数据库子线程:

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)

比如下图中商品类型下拉框的数据就来自于数据库:

image

在组件 LicenseCard 中使用下述代码就能完成数据的请求和消费(组件库参见 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", lambda i: self.onGoodsFetched(i))

    def onGoodsFetched(self, goods: List[Goods]):
        """ 将商品信息添加到下拉框中 """
        for good in goods:
            self.goodsComboBox.addItem(good.name, userData=good)

处理请求

子线程 DatabaseThread 中维护着一个请求队列 tasks,每当收到信号总线的 fetchDataSig 信号时,就会使用反射机制将请求中携带的 servicemethod 字符串转换为数据库业务类的方法指针,并将这个指针添加到队列中等待调用。调用方法返回的数据会被封装为 SqlResponse 对象,接着通过信号总线发送给主界面。

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)

处理响应结果

主界面中只需将信号总线的 dataFetched 信号连接槽函数,然后在槽函数中对取出 response 对象中的数据,并调用回调函数来消费数据即可:

from qfluentwidgets import MSFluentWindow

class MainWindow(MSFluentWindow):
    """ 主界面 """
    
    def __init__(self):
        super().__init__()
        
        # 处理响应结果
        signalBus.dataFetched.connect(self.onDataFetched)

    def onDataFetched(self, response: SqlResponse):
        if response.slot:
            response.slot(response.data)

总结

在这篇博客中我们使用子线程和信号总线完成了异步数据库请求操作,界面所使用的组件全部来自于 https://qfluentwidgets.com/zh/ ,以上~~

标签:__,异步,请求,service,slot,数据库,PyQt,self,def
From: https://www.cnblogs.com/zhiyiYo/p/17883607.html

相关文章

  • 11K+ Star!图解计算机网络、操作系统、计算机组成、数据库!
    大家好,我是Java陈序员。俗话说得好,面试造火箭,入职拧螺丝。我们在工作中,其实很少用到一些计算机底层知识,往往只要编码完事。但是,知其然还要知其所以然,我们不仅要做一个合格的“CV工程师”,更是要掌握一些底层原理!计算机基础知识,作为计算机的底层原理,往往是晦涩难懂,如果没用心的......
  • windows 安装mysql 和不能通过ip访问到数据库
     一、确认windows是否已经安装mysql1.按【Win+R】打开运行2.输入【services.msc】打开服务3、在服务列表中查找MySQL,如果有mysql服务则表示Windows已经安装MySQL,如果没有MySQL服务则表示Windows没有安装MySQL二、下载MySQL安装包1、mysql官网下载地址  https://d......
  • 各种类型数据库的连接字符串
    DataType.MySqlDataSource=127.0.0.1;Port=3306;UserID=root;Password=root;DataBase=cccddd;Charset=utf8;SslMode=none;Minpoolsize=1DataType.PostgreSQLHost=192.168.164.10;Port=5432;Username=postgres;Password=123456;Database=tedb;Pooling=true;Minimu......
  • 【故障公告】数据库服务器今年第七次 CPU 100% 故障
    自11月9日第六次数据库服务器CPU100%故障之后,今天下午又出现数据库服务器CPU100%故障,是今年的第七次。今天的故障发生于16:01~16:07期间,发现故障后我们立即重启阿里云RDS实例,重启后恢复正常。正准备发布这篇故障公告时,数据库服务器又出现CPU100%,我们立即改用主备切换......
  • 在.net中通过自定义LoggerProvider将日志保存到数据库方法(以mysql为例)
     在.NET中,Microsoft.Extensions.Logging是一个灵活的日志库,它允许你将日志信息记录到各种不同的目标,包括数据库。在这个示例中,我将详细介绍如何使用Microsoft.Extensions.Logging将日志保存到MySQL数据库。我们将使用EntityFrameworkCore来与MySQL数据库进行交互。步骤一:创......
  • 活动预告 | 中国数据库联盟(ACDU)中国行第四站定档西安,邀您探讨数据库前沿技术
    作为墨天轮社区与中国数据库联盟的品牌活动之一,【ACDU中国行】已走过深圳、杭州、成都三大城市,在线下汇集数据库领域的行业知名人士,共同探讨数据库前沿技术及其应用,促进行业发展和创新,同时也为开发者们提供一个友好交流的平台。 12月23日下午,【ACDU中国行】将前往古城西安,......
  • Python 操作 MySQL 数据库
    Python标准数据库接口为PythonDB-API,PythonDB-API为开发人员提供了数据库应用编程接口。Python数据库接口支持非常多的数据库,你可以选择适合你项目的数据库:GadFlymSQLMySQLPostgreSQLMicrosoftSQLServer2000InformixInterbaseOracleSybase你可以访问Python数据库接口及API......
  • 数据库数据恢复—sqlserver数据库和备份被加密,数据库文件名被篡改的数据恢复案例
    SQLServer数据库故障:某公司服务器上的SQLServer数据库被加密,无法使用。被加密的数据库有2个,数据库的MDF、LDF、log文件名字被篡改。数据库被加密截图:数据库备份被加密,文件名字被篡改:SQLServer数据库数据恢复过程:1、将故障数据库内的数据备份至北亚企安数据恢复中心的专用存......
  • MySQL数据库中SQL语句分几类?
    SQL语句主要分为四类,分别是数据查询语言(DQL)、数据操作语言(DML)、数据定义语言(DDL)和数据控制语言(DCL)。1.数据查询语言(DQL):用于从数据库中的一个或多个表中查询数据,主要使用SELECT语句。2.数据操作语言(DML):用于修改数据库中的数据,包括插入(INSERT)、更新(UPDATE)和删除(DELETE)等操作。3.数......
  • 【SQLServer2019管理】备份环境包含数据库
    恢复报错信息:sp_configure值'containeddatabaseauthentication'必须设置为1才能创建包含的数据库。您可能需要使用RECONFIGURE设置value_in_use。(MicrosoftSQLServer,错误:12824)execsp_configure'containeddatabaseauthentication',1GOreconfigure;Go ......