基于QThread实现 子线程更新 主线程/UI线程。
在最近的项目中,GUI程序中使用信号-槽的方式实现某一模块功能,当模块的执行时间较长时,主界面会卡顿,直到模块中的功能执行完成后,主界面才会恢复正常。为解决此类问题,要使用多线程的方式;而在PySide2中 使用 Python 的多线程类 threading,在另一个线程直接操作界面,可能会导致意想不到的问题,比如:输出显示不全,甚至程序崩溃,所以要用到Qt特有的线程类QThread。
项目的GUI主要基于PySide2实现,想要实现的功能是:当点击上传数据按键时,连接数据库,弹出提示框,并在报表中记录信息。
如上图所示,当只使用QPushButton的clicked事件,在clicked的槽函数中实现功能模块时,会发现当点击按键后,主线程会被堵塞,当槽函数功能运行完后,才会继续主线程UI界面刷新。
class NewThread(QThread, parent=None):
def __init__(self):
super(QThread,self).__init__(parent)
signal = Signal(object) # 自定义信号, object 为信号对象承载数据的类型
在run()方法中,是要在子线程中实现的功能;当然,也可以在NewTread线程类中定义其他的函数,并在run()方法中进行调用。
def run(self):
# 具体要实现的功能
self.signal.emit(object) # 功能实现完成后 发射信号
开启线程使用线程的start()的方法,实际是调用的我们重写的run()方法
thread = NewThread()
thread.start()
def update(self):
# 具体要实现的功能
self.thread.signal.connect(self.update) # 当信号发出时,执行槽函数
class ctConnectSqlThread(QThread):
def __init__(self, parent=None):
super(ctConnectSqlThread, self).__init__(parent)
self.state = None # 初始化数据库连接状态
class ctConnectSqlThread(QThread):
connected = Signal(object) # 初始化信号
def __init__(self, parent=None):
super(ctConnectSqlThread, self).__init__(parent)
self.state = None # 初始化数据库连接状态
def run(self):
config = {
"host": "",
"port": ,
"database": "",
"charset": "",
"user": "",
"passwd": ""
} # 设置连接数据库的参数
db = tMySqlHelper.ctMySql(**config) # 实例化时就直接传参数
self.state = db.connectInfo # 数据库连接状态赋值
self.connected.emit(self.state) # 数据库连接状态 赋值完成后 发射信号
self.sleep(1) # 线程休眠1秒
self.uploadingThread = tThread.ctConnectSqlThread() # 创建上传事件 线程
self.ui.btnUploding.clicked.connect(self.__f_btnUploadingClicked) # 上传数据按键按下事件连接槽函数
# 上传数据按钮按下处理函数
def __f_btnUploadingClicked(self):
"""
function: 上传数据按钮按下处理函数
in: None
out: None
return: int >0 ok, <0 some wrong
others: handle function of the button loading up clicked
"""
self.ui.tabWidget.setCurrentIndex(0) # 切换到上传数据Tab
self.ui.pteReport.appendPlainText(
f"{datetime.datetime.now().strftime('%Y-%m-%d-%H:%M:%S')}: 尝试连接数据库...") # 报表多行文本框追加信息
self.uploadingThread.start() # 线程开启
# 接收子线程信号后,更新UI函数
def upLoadingChangeUI(self, state_text):
"""
function: 接收子线程信号后,更新UI函数
in: state_text:线程类中的self.state
out: None
return: None
others: Update the UI function after receiving the child thread signal
"""
QMessageBox.information(self.ui, '提示', f'{state_text}', QMessageBox.Yes)
if state_text == "数据库连接成功!":
self.ui.pteReport.appendPlainText(
f"{datetime.datetime.now().strftime('%Y-%m-%d-%H:%M:%S')}: {state_text}") # 报表多行文本框追加信息
else:
self.ui.pteReport.appendPlainText(
f"{datetime.datetime.now().strftime('%Y-%m-%d-%H:%M:%S')}: {state_text}") # 报表多行文本框追加信息
self.uploadingThread.connected.connect(self.upLoadingChangeUI) # 上传线程 开启后 信号发射 连接槽函数