第一种:直接使用QThread类更新UI界面
写一个SonThread类继承于QThread,直接在run函数中写我们要执行的动作(更新UI界面)。优点比较简单易懂,缺点只能被一种事件使用,不能重复利用。
# -*- coding: utf-8 -*-
import sys
import time
from PySide6.QtCore import *
from PySide6.QtWidgets import QApplication, QWidget, QTextBrowser, QPushButton, QHBoxLayout
# 创建QApplication对象
if not QApplication.instance():
app = QApplication([])
else:
app = QApplication.instance()
class SonThread(QThread):
"""子线程"""
update_date = Signal(str) # 定义信号
def __init__(self, data):
"""子线程初始化"""
super().__init__() # 初始化父级
self.result = data # 把传入的参数data赋值给类变量result
def run(self):
"""子线程执行时执行此函数"""
for i in range(5):
self.update_date.emit(str(f"倒计时:{5-i}")) # 发送信号,信号内容为(倒计时:5-i)
time.sleep(1) # 延时1秒(不管延时多久,UI界面都不会出现假死状态)
self.update_date.emit(str(self.result)) # 发送信号,信号内容为类变量result
class MyWindow(QWidget):
"""UI线程"""
def __init__(self):
"""UI线程初始化"""
super().__init__() # 初始化父级
self.son_thread = None # 创建子线程变量
self.setWindowTitle('子线程更新UI线程实例') # 设置窗口标题
self.text = QTextBrowser() # 定义文本浏览框
self.button = QPushButton("发送信号") # 定义按钮
self.main_layout = QHBoxLayout(self) # 定义水平布局容器
self.main_layout.addWidget(self.text) # 布局文本浏览框
self.main_layout.addWidget(self.button) # 布局按钮
self.button.clicked.connect(self.send_sign) # 按钮点击事件绑定(send_sign)函数
def send_sign(self):
"""点击按钮时执行此函数"""
self.son_thread = SonThread("hello everyone") # 定义子线程并传入参数(hello everyone)
self.son_thread.update_date.connect(self.add_text) # 给信号绑定(add_text)函数
self.son_thread.start() # 执行子线程
def add_text(self, data):
"""
收到信号时执行此函数
:param data: 信号内容
:return:
"""
self.text.append(data) # 在文本浏览框中追加内容(data)
def main():
widget = MyWindow() # 实例化UI线程
widget.resize(300, 200) # 设置窗口大小
widget.show() # 展示窗口
sys.exit(app.exec()) # 持续刷新窗口
if __name__ == '__main__':
main()
第二种:在其他线程中使用QThread类更新UI界面
使用threading来创建新线程,在新线程中使用SonThread来更新UI界面。只用写一个更新UI界面的类,可以被很多的事件使用。
# -*- coding: utf-8 -*-
import sys
import threading
import time
from PySide6.QtCore import *
from PySide6.QtWidgets import QApplication, QWidget, QTextBrowser, QPushButton, QHBoxLayout, QVBoxLayout
# 创建QApplication对象
if not QApplication.instance():
app = QApplication([])
else:
app = QApplication.instance()
def thread_ui(func, *args):
"""
开启一个新线程任务
:param func: 要执行的线程函数;
:param args: 函数中需要传入的参数 Any
:return:
"""
t = threading.Thread(target=func, args=args) # 定义新线程
t.setDaemon(True) # 开启线程守护
t.start() # 执行线程
class SonThread(QThread):
"""子线程"""
update_date = Signal(str) # 定义信号
def __init__(self, data):
"""子线程初始化"""
super().__init__() # 初始化父级
self.result = data # 把传入的参数data赋值给类变量result
def run(self):
"""子线程执行时执行此函数"""
self.update_date.emit(str(self.result)) # 发送信号,信号内容为类变量result
class MyWindow(QWidget):
"""UI线程"""
def __init__(self):
"""UI线程初始化"""
super().__init__() # 初始化父级
self.son_thread = None # 创建子线程变量
self.setWindowTitle('子线程更新UI线程实例') # 设置窗口标题
self.text = QTextBrowser() # 定义文本浏览框
self.button = QPushButton("发送信号") # 定义发送信号按钮
self.clear_button = QPushButton("清除文本") # 定义清除文本按钮
self.main_layout = QHBoxLayout(self) # 定义水平布局容器
self.son_layout = QVBoxLayout() # 定义垂直布局容器
self.main_layout.addWidget(self.text) # 布局文本浏览框
self.main_layout.addLayout(self.son_layout) # 布局垂直布局容器
self.son_layout.addWidget(self.button) # 布局发送信号按钮
self.son_layout.addWidget(self.clear_button) # 布局清除文本按钮
self.button.clicked.connect(self.button_click) # 发送信号按钮点击事件绑定(button_click)函数
self.clear_button.clicked.connect(self.clear_click) # 清除文本按钮点击事件绑定(clear_click)函数
def button_click(self):
"""点击发送信号按钮时执行此函数"""
def button_thread():
for i in range(5):
self.send_sign(f"倒计时:{5-i}") # 发送信号(倒计时:{5-i})
time.sleep(1) # 延时1秒(不管延时多久,UI界面都不会出现假死状态)
self.send_sign("你好啊") # 发送信号(你好啊)
return thread_ui(button_thread) # 开启新线程来执行(button_thread)函数
def clear_click(self):
"""点击清除文本按钮时执行此函数"""
def clear_text():
for i in range(5):
self.send_sign(f"倒计时:{5 - i}") # 发送信号(倒计时:{5-i})
time.sleep(1) # 延时1秒(不管延时多久,UI界面都不会出现假死状态)
self.send_sign("clear") # 发送信号(clear)
return thread_ui(clear_text) # 开启新线程来执行(clear_text)函数
def send_sign(self, data):
"""
使用SonThread发送信号
:param data: 发送的内容
:return:
"""
self.son_thread = SonThread(data) # 定义子线程并传入参数(data)
self.son_thread.update_date.connect(self.add_text) # 给信号绑定(add_text)函数
self.son_thread.start() # 执行子线程
def add_text(self, data):
"""
收到信号时执行此函数
:param data: 接收的内容
:return:
"""
if data == 'clear': # 判断接收的内容是否为clear
self.text.clear() # 是clear就清空文本框内容
else: # 不是clear
self.text.append(data) # 在文本浏览框中追加内容(data)
def main():
widget = MyWindow() # 实例化UI线程
widget.resize(300, 200) # 设置窗口大小
widget.show() # 展示窗口
sys.exit(app.exec()) # 持续刷新窗口
if __name__ == '__main__':
main()
第三种:创建信号线程(最优)
鉴于前面两种方式,都有一些问题,后来我想到了第三种方式。这种方式可以使用一个信号类实例化出多个信号对象,每个信号都绑定一个的方法,每个信号都是独立存在的。我们同时可以发送多种信号去影响一个UI元素,而且每个信号之间都不会发生干扰。文章来源:https://www.toymoban.com/news/detail-595413.html
# -*- coding: utf-8 -*-
import sys
import threading
import time
from PySide6.QtCore import *
from PySide6.QtWidgets import QApplication, QWidget, QTextBrowser, QPushButton, QHBoxLayout, QVBoxLayout, QMessageBox
# 创建QApplication对象
if not QApplication.instance():
app = QApplication([])
else:
app = QApplication.instance()
def thread_ui(func, *args):
"""
开启一个新线程任务\n
:param func: 要执行的线程函数;
:param args: 函数中需要传入的参数 Any
:return:
"""
t = threading.Thread(target=func, args=args) # 定义新线程
t.setDaemon(True) # 开启线程守护
t.start() # 执行线程
class SignThread(QThread):
"""信号线程"""
def __new__(cls, parent: QWidget, func, *types: type):
cls.update_date = Signal(*types) # 定义信号(*types)一个信号中可以有一个或多个类型的数据(int,str,list,...)
return super().__new__(cls) # 使用父类__new__方法创建SignThread实例对象
def __init__(self, parent: QWidget, func, *types: type):
"""
信号线程初始化\n
:param parent: 界面UI控件
:param func: 信号要绑定的方法
:param types: 信号类型,可以是一个或多个(type,...)
"""
super().__init__(parent) # 初始化父类
self.sign = None
self.update_date.connect(func) # 绑定信号与方法
def send_sign(self, *args):
"""
使用SonThread发送信号\n
:param args: 信号的内容
:return:
"""
self.sign = args # 信号元组(type,...)
self.start()
def run(self):
"""信号线程执行时执行此函数"""
self.update_date.emit(*self.sign) # 发送信号元组(type,...)
class MyWindow(QWidget):
"""UI界面"""
def __init__(self):
"""UI界面初始化"""
super().__init__() # 初始化父级
self.setWindowTitle('子线程更新UI线程实例') # 设置窗口标题
self.text = QTextBrowser() # 定义文本浏览框
self.button = QPushButton("添加文本") # 定义发送信号按钮
self.clear_button = QPushButton("清除文本") # 定义清除文本按钮
self.main_layout = QHBoxLayout(self) # 定义水平布局容器
self.son_layout = QVBoxLayout() # 定义垂直布局容器
self.main_layout.addWidget(self.text) # 布局文本浏览框
self.main_layout.addLayout(self.son_layout) # 布局垂直布局容器
self.son_layout.addWidget(self.button) # 布局发送信号按钮
self.son_layout.addWidget(self.clear_button) # 布局清除文本按钮
self.button.clicked.connect(self.button_click) # 发送信号按钮点击事件绑定(button_click)函数
self.clear_button.clicked.connect(self.clear_click) # 清除文本按钮点击事件绑定(clear_click)函数
self.add_thread = SignThread(self, self.add_text, str, int) # 创建信号线程,并绑定add_text函数,信号使用str和int两种数据
self.clear_thread = SignThread(self, self.clear_text, int) # 创建信号线程,并绑定clear_text函数,信号只使用int数据
def button_click(self):
"""点击发送信号按钮时执行此函数"""
def button_thread():
for i in range(5):
self.add_thread.send_sign(f"添加倒计时:{5-i}", i) # 发送信号(倒计时:{5-i}, i)
time.sleep(1) # 延时1秒(不管延时多久,UI界面都不会出现假死状态)
self.add_thread.send_sign('你好啊', 99) # 发送信号(你好啊, 99)
return thread_ui(button_thread) # 开启新线程来执行(button_thread)函数
def clear_click(self):
"""点击清除文本按钮时执行此函数"""
def clear_text():
for i in range(5):
self.clear_thread.send_sign(0) # 发送信号(0)
time.sleep(1) # 延时1秒(不管延时多久,UI界面都不会出现假死状态)
self.clear_thread.send_sign(1) # 发送信号(1)
return thread_ui(clear_text) # 开启新线程来执行(clear_text)函数
def add_text(self, *args):
"""
收到add_thread信号时执行此函数\n
:param args: 接收的信号元组
:return:
"""
self.text.append(f'str: {args[0]} int: {args[1]}') # 在文本浏览框中追加内容(data)
def clear_text(self, *args):
"""
收到clear_thread信号时执行此函数\n
:param args: 接收的信号元组
:return:
"""
self.text.clear() # 清除文本浏览框中的内容
if args[0] == 1: # 当信号元组的第一个元素为整型1时
msg = QMessageBox(self)
msg.setText(f"清除文本结束")
msg.exec()
def main():
widget = MyWindow() # 实例化UI线程
widget.resize(300, 200) # 设置窗口大小
widget.show() # 展示窗口
sys.exit(app.exec()) # 持续刷新窗口
if __name__ == '__main__':
main()
在这个实例中,我们可以同时点击添加文本和清除文本按钮,让文本框一边添加文本一边删除文本,两个信号差不多同时进行,互不影响。文章来源地址https://www.toymoban.com/news/detail-595413.html
到了这里,关于【无标题】PySide6在非UI线程更新UI界面实例的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!