QT是一种流行的跨平台应用程序开发框架,而PySide2是QT的一个Python绑定库。在QT和PySide2中使用多线程的主要原因是为了提高应用程序的性能和响应能力。
使用多线程可以将耗时的任务分配给不同的线程,在后台并行执行,从而避免阻塞主线程,保持应用程序的流畅性和响应性。当应用程序需要进行复杂的计算、网络请求、文件读写或其他需要较长时间的操作时,使用多线程可以防止这些操作对用户界面的阻塞,使用户能够继续与应用程序进行交互。
需要注意的是在使用多线程时要注意线程安全性和数据同步的问题。多个线程同时访问共享的数据可能导致竞态条件和数据不一致的问题,因此需要采取适当的同步机制,如锁、信号量或其他线程间通信机制,来保证数据的一致性和正确性。
总而言之,使用多线程可以提高QT PySide2应用程序的性能、响应能力和并发性,但需要注意线程安全和数据同步的问题。
提示:以下是本篇文章正文内容,下面案例可供参考
实现效果:界面不会卡顿、可以继续与应用程序进行交互;
开始执行、停止执行思路
import sys
from PySide2.QtWidgets import QApplication, QWidget, QTableWidgetItem
from PySide2.QtUiTools import loadUiType, QUiLoader
from PySide2.QtCore import QFile, Qt
from PySide2.QtGui import QIcon
import time
import threading
class MyThread(threading.Thread):
def __init__(self, qt):
super().__init__()
self.qtGui = qt
self._stop_event = threading.Event()
def stop(self):
self._stop_event.set()
def run(self):
message_list = [f"第{k}次打印" for k in range(100)]
log_content = {'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20}
for message in message_list:
if self._stop_event.is_set() is True:
break
log_content['five'] = message
self.qtGui.logger_show(log_content)
time.sleep(0.1)
log_content['five'] = '答题子线程已停止运行,请进行下一步操作'
time.sleep(5)
[self.qtGui.logger_show(log_content) for kk in range(1)]
return True
class Gui(QWidget):
def __init__(self):
# 加载ui文件,创建qt文件对象,加载文件对象并创建ui对象
QtFileObj = QFile("yk.ui")
QtFileObj.open(QFile.ReadOnly)
QtFileObj.close()
self.ui = QUiLoader().load(QtFileObj)
# 设置界面图标
icon = QIcon("yk.ico")
self.ui.setWindowIcon(icon)
# 变量、对象定义
self.index = 0
self.ui.tableWidgetAnswer.horizontalHeader().setVisible(True) # 设置tableWidget组件的标题显示为True
self.ui.startButton.clicked.connect(self.start_running) # 绑定按钮的方法
self.ui.stopButton.clicked.connect(self.stop_running)
self.answer_thread = None # 答题启动线程对象
def start_running(self):
# 插入内容
logger_item = {
'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,
'five': '程序已经开始运行,请勿多次点击开始运行按钮'
}
[self.logger_show(logger_item) for kk in range(1)]
# 创建线程对象
self.answer_thread = MyThread(self) # 传入qt对象
# 启动线程
self.answer_thread.start()
def stop_running(self):
# 停止线程
self.answer_thread.stop()
logger_item = {
'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,
'five': '答题程序已设置停止线程,请等待最后一个账号运行完再进行下一步操作'
}
[self.logger_show(logger_item) for kk in range(1)]
def logger_show(self, logger_item):
self.ui.tableWidgetAnswer.insertRow(int(self.ui.tableWidgetAnswer.rowCount()))
self.index += 1
new_item_one = QTableWidgetItem(logger_item['one'])
new_item_one.setTextAlignment(Qt.AlignCenter)
new_item_two = QTableWidgetItem(logger_item['two'])
new_item_two.setTextAlignment(Qt.AlignCenter)
new_item_three = QTableWidgetItem(logger_item['three'])
new_item_three.setTextAlignment(Qt.AlignCenter)
new_item_four = QTableWidgetItem(logger_item['four'])
new_item_four.setTextAlignment(Qt.AlignCenter)
new_item_five = QTableWidgetItem(logger_item['five'])
new_item_five.setTextAlignment(Qt.AlignCenter)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 0, new_item_one)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 1, new_item_two)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 2, new_item_three)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 3, new_item_four)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 4, new_item_five)
# 定位至最新行
self.ui.tableWidgetAnswer.verticalScrollBar().setSliderPosition(self.index)
# 刷新
QApplication.processEvents()
if __name__ == '__main__':
app = QApplication(sys.argv)
ykGuiObj = Gui()
ykGuiObj.ui.show()
sys.exit(app.exec_())
实现效果:界面不会卡顿、可以继续与应用程序进行交互,多个子线程同时执行或停止;
开始执行、停止执行思路
import sys
import time
import random
from PySide2.QtWidgets import QApplication, QWidget, QTableWidgetItem
from PySide2.QtUiTools import loadUiType, QUiLoader
from PySide2.QtCore import QFile, Qt
from PySide2.QtGui import QIcon
import threading
import concurrent.futures
from queue import Queue
class MyThread(threading.Thread):
def __init__(self, qt):
super().__init__()
self.qtGui = qt
self.executor = None # 创建线程池
self._stop_event = threading.Event()
def stop(self):
self._stop_event.set()
def run(self):
log_content = {'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20}
message_queue = self.build_log_messages()
thread_number = 5 # 线程数
while message_queue.qsize():
if self.executor is None or self.executor._shutdown:
# 创建线程池
self.executor = concurrent.futures.ThreadPoolExecutor()
for kkk in range(thread_number): # 开启4子线程执行打印方法
self.executor.submit(self.qtGui.logger_show, message_queue.get(), random.randint(1, 15) * 0.1)
time.sleep(0.2)
self.executor.shutdown(wait=True) # 等待任务执行完成
if self._stop_event.is_set() is True: # 当停止按钮被点击后则会进入这个跳出循环条件
break
log_content['five'] = '答题子线程已停止运行,请进行下一步操作'
time.sleep(5)
[self.qtGui.logger_show(log_content) for kk in range(1)]
return True
def build_log_messages(self):
message_queue = Queue()
grapheme_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
num = 0
while num < 100: # 存储需要打印的日志信息
for grapheme in grapheme_list:
num += 1
message_queue.put(
{'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,
'five': f"线程{grapheme} {grapheme * 10} 打印数字:{num}"}
)
return message_queue
class Gui(QWidget):
def __init__(self):
# 加载ui文件,创建qt文件对象,加载文件对象并创建ui对象
QtFileObj = QFile("yk.ui")
QtFileObj.open(QFile.ReadOnly)
QtFileObj.close()
self.ui = QUiLoader().load(QtFileObj)
# 设置界面图标
icon = QIcon("yk.ico")
self.ui.setWindowIcon(icon)
# 变量、对象定义
self.index = 0
self.ui.tableWidgetAnswer.horizontalHeader().setVisible(True) # 设置tableWidget组件的标题显示为True
self.ui.startButton.clicked.connect(self.start_running) # 绑定按钮的方法
self.ui.stopButton.clicked.connect(self.stop_running)
self.answer_thread = None # 答题启动线程对象
def start_running(self):
# 插入内容
logger_item = {
'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,
'five': '程序已经开始运行,请勿多次点击开始运行按钮'
}
[self.logger_show(logger_item) for kk in range(1)]
# 创建线程对象
self.answer_thread = MyThread(self) # 传入qt对象
# 启动线程
self.answer_thread.start()
def stop_running(self):
# 停止线程
self.answer_thread.stop()
logger_item = {
'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,
'five': '答题程序已设置停止线程,请等待最后一个账号运行完再进行下一步操作'
}
[self.logger_show(logger_item) for kk in range(1)]
def logger_show(self, logger_item, sleep=0):
time.sleep(sleep)
self.ui.tableWidgetAnswer.insertRow(int(self.ui.tableWidgetAnswer.rowCount()))
self.index += 1
new_item_one = QTableWidgetItem(logger_item['one'])
new_item_one.setTextAlignment(Qt.AlignCenter)
new_item_two = QTableWidgetItem(logger_item['two'])
new_item_two.setTextAlignment(Qt.AlignCenter)
new_item_three = QTableWidgetItem(logger_item['three'])
new_item_three.setTextAlignment(Qt.AlignCenter)
new_item_four = QTableWidgetItem(logger_item['four'])
new_item_four.setTextAlignment(Qt.AlignCenter)
new_item_five = QTableWidgetItem(logger_item['five'])
new_item_five.setTextAlignment(Qt.AlignCenter)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 0, new_item_one)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 1, new_item_two)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 2, new_item_three)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 3, new_item_four)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 4, new_item_five)
# 定位至最新行
self.ui.tableWidgetAnswer.verticalScrollBar().setSliderPosition(self.index)
# 刷新
QApplication.processEvents()
if __name__ == '__main__':
app = QApplication(sys.argv)
ykGuiObj = Gui()
ykGuiObj.ui.show()
sys.exit(app.exec_())
import time
import threading
from queue import Queue
def task_running(param1, param2):
while True:
task_info = task_queue.get() # 取出任务
time.sleep(1)
print(task_info)
task_queue.task_done() # 全部完成则退出死循环
def thread_controller(task_queue):
thread_number = 3 # 线程数
for j in range(thread_number):
t = threading.Thread(target=task_running, args=(1, 2))
t.daemon = True # 设置线程daemon 主线程退出就直接让子线程跟随退出
t.start()
if __name__ == '__main__':
task_queue = Queue()
thread_controller(task_queue)
# 调用示例
StartButton.clicked.connect(lambda: self.thread_it(self.function, param))
def thread_it(self, func, *args):
'''
将函数打包进线程
'''
self.myThread = threading.Thread(target=func, args=args)
self.myThread.setDaemon(True)
self.myThread.start()
def clear_logger(self, route_name=None):
for i in range(self.index):
self.tableWidget .removeRow(0) # 删除tableWidget组件的第0行数据
# self.ui.tableWidget.clearContents() # 清除/删除所有内容
time.sleep(1)
self.index = 0
def exit_program(self, route_name):
tmp = os.popen("tasklist").readlines()
for tm in tmp:
if 'python' not in tm:
continue
time.sleep(0.5)
pid = tm.strip().replace(' ', '').split('.exe')[-1].split('Console')[0]
subprocess.Popen("taskkill /F /T /PID " + str(pid), shell=True)
return True
import threading
class MyThread(threading.Thread):
def __init__(self, qt):
super().__init__()
self._stop_event = threading.Event()
def stop(self):
self._stop_event.set()
def run(self):
while True:
print("开始运行")
if self._stop_event.is_set() is True: # 当停止按钮被点击后则会进入这个跳出循环条件
break
import ctypes
import threading
class ThreadWithException(threading.Thread):
def __init__(self, _tk, route_number=0):
threading.Thread.__init__(self)
self._tk = _tk
self.route_number = route_number
def run(self):
pass
def get_id(self):
if hasattr(self, '_thread_id'):
return self._thread_id
for id, thread in threading._active.items():
if thread is self:
return id
def raise_exception(self):
thread_id = self.get_id()
# 精髓就是这句话,给线程发过去一个exceptions,线程就那边响应完就停了
response = ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, ctypes.py_object(SystemExit))
if response > 1:
ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
逻辑解释
gui.py
import sys
import time
import random
from PySide2.QtWidgets import QApplication, QWidget, QTableWidgetItem
from PySide2.QtUiTools import loadUiType, QUiLoader
from PySide2.QtCore import QFile, Qt
from PySide2.QtGui import QIcon
import threading
import concurrent.futures
from queue import Queue
from data_save import DataSave
class MyThread(threading.Thread):
def __init__(self, qt):
super().__init__()
self.qtGui = qt
self.executor = None # 创建线程池
self._stop_event = threading.Event()
def stop(self):
self._stop_event.set()
def run(self):
task_queue = Queue()
log_content = {'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20}
message_queue = self.build_log_messages()
thread_number = 5 # 线程数
while message_queue.qsize():
if self.executor is None or self.executor._shutdown:
# 创建线程池
self.executor = concurrent.futures.ThreadPoolExecutor()
for kkk in range(thread_number): # 开启5子线程执行打印方法
data_sava_pipeline = DataSave()
self.executor.submit(data_sava_pipeline.runs, self.qtGui, task_queue, message_queue.get())
time.sleep(0.2)
self.executor.shutdown(wait=True) # 等待任务执行完成
if self._stop_event.is_set() is True: # 当停止按钮被点击后则会进入这个跳出循环条件
break
log_content['five'] = '答题子线程已停止运行,请进行下一步操作'
print('task_queue', task_queue.qsize(), task_queue.queue)
time.sleep(2)
[self.qtGui.logger_show(log_content) for kk in range(1)]
return True
def build_log_messages(self):
message_queue = Queue()
grapheme_list = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']
num = 0
while num < 100: # 存储需要打印的日志信息
for grapheme in grapheme_list:
num += 1
message_queue.put(
{'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,
'five': f"线程{grapheme} {grapheme * 10} 打印数字:{num}"}
)
return message_queue
class Gui(QWidget):
def __init__(self):
# 加载ui文件,创建qt文件对象,加载文件对象并创建ui对象
QtFileObj = QFile("D:\MyStudy\MyProject\PythonProject\python_example\项目接单学习/18_PYQT\可视化编程\yk.ui")
QtFileObj.open(QFile.ReadOnly)
QtFileObj.close()
self.ui = QUiLoader().load(QtFileObj)
# 设置界面图标
icon = QIcon("yk.ico")
self.ui.setWindowIcon(icon)
# 变量、对象定义
self.index = 0
self.ui.tableWidgetAnswer.horizontalHeader().setVisible(True) # 设置tableWidget组件的标题显示为True
self.ui.startButton.clicked.connect(self.start_running) # 绑定按钮的方法
self.ui.stopButton.clicked.connect(self.stop_running)
self.answer_thread = None # 答题启动线程对象
def start_running(self):
# 插入内容
logger_item = {
'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,
'five': '程序已经开始运行,请勿多次点击开始运行按钮'
}
[self.logger_show(logger_item) for kk in range(1)]
# 创建线程对象
self.answer_thread = MyThread(self) # 传入qt对象
# 启动线程
self.answer_thread.start()
def stop_running(self):
# 停止线程
self.answer_thread.stop()
logger_item = {
'one': '-' * 20, 'two': '-' * 20, 'three': '-' * 20, 'four': '-' * 20,
'five': '答题程序已设置停止线程,请等待最后一个账号运行完再进行下一步操作'
}
[self.logger_show(logger_item) for kk in range(1)]
def logger_show(self, logger_item, sleep=0):
time.sleep(sleep)
self.ui.tableWidgetAnswer.insertRow(int(self.ui.tableWidgetAnswer.rowCount()))
self.index += 1
new_item_one = QTableWidgetItem(logger_item['one'])
new_item_one.setTextAlignment(Qt.AlignCenter)
new_item_two = QTableWidgetItem(logger_item['two'])
new_item_two.setTextAlignment(Qt.AlignCenter)
new_item_three = QTableWidgetItem(logger_item['three'])
new_item_three.setTextAlignment(Qt.AlignCenter)
new_item_four = QTableWidgetItem(logger_item['four'])
new_item_four.setTextAlignment(Qt.AlignCenter)
new_item_five = QTableWidgetItem(logger_item['five'])
new_item_five.setTextAlignment(Qt.AlignCenter)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 0, new_item_one)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 1, new_item_two)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 2, new_item_three)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 3, new_item_four)
self.ui.tableWidgetAnswer.setItem(self.index - 1, 4, new_item_five)
# 定位至最新行
self.ui.tableWidgetAnswer.verticalScrollBar().setSliderPosition(self.index)
# 刷新
QApplication.processEvents()
if __name__ == '__main__':
app = QApplication(sys.argv)
ykGuiObj = Gui()
ykGuiObj.ui.show()
sys.exit(app.exec_())
data_save.py
import time
import random
class DataSave(object):
def __init__(self):
pass
def data_save(self, items):
message = items['message']
items['queue'].put(message)
items['qt'].logger_show(message)
def runs(self, qt, queue, message):
sleep = random.randint(2, 10) * 0.1
time.sleep(sleep)
print(f'message:{message} sleep:{sleep}\n', end='')
items = {'qt': qt, 'queue': queue, 'message': message}
self.data_save(items)
1、QT Pyside2 Designer 的基本使用:https://blog.csdn.net/EXIxiaozhou/article/details/131401574
2、入门文档请看白月黑羽:https://www.byhy.net/tut/py/gui/qt_01
3、入门视频请看白月黑羽:https://www.bilibili.com/video/BV19A411H7dS/
4、深入学习博主推荐-国内站点:https://gitee.com/feiyangqingyun
5、深入学习博主推荐-国际站点:https://github.com/feiyangqingyun
6、CSDN个人主页:https://blog.csdn.net/feiyangqingyun
7、官网:https://www.pythonguis.com/topics/pyside2/
这篇博文的示例主要以实现功能为目的,我自己觉得有地方不太合适,欢迎大佬和小伙伴在评论处积极提出;