与昨天一样,不过我们今天要谈一个 PyQt 中非常重要的 QThread 概念 !
今天要谈一个 PyQt 中非常重要的 QThread 概念!
我们要修改昨天的 QProgressBar 功能,将它潜在问题修改并解决他。
https://github.com/howarder3/ironman2021_PyQt5_photoshop/tree/main/day20_qthread
我们今天要来先讨论一下在 PyQt 中非常重要的东西!
这个就叫做 「QThread」 !
为什麽「QThread」很重要,其实在昨天的 QTimer 中我们就已经有说过,
我们的程序一定不会是一直线运行的,程序执行的过程中,
势必要有些「背景处理」的事情!
例如:时间就应该要背景更新,而不是我们主程序随时切换去给时间+1
这样光用想的就知道,万一上一行程序执行慢了一点,时间就会开始有越来越大的误差了吧!
因为 ProgressBar 正好就符合这样的需求,
我们可以试着想想,如果一个 ProgressBar 正在跑,我们就不能同时做其他事情,
这样子的程序,不能说不好 (毕竟还是能动,只是要等他结束才能做其他事XD)
但不觉得很不符合使用者需求吗XD
我们把运行到一半的程序,强制用右上角的「X」关闭看看,
我们发现居然程序「不但关不掉」,甚至还「没有回应」了?!
这就是没有做 QThread 搞得鬼,因为在 Windows 判断一个视窗「没有回应」的判断条件中,
当我们对视窗「进行一个行为(例如:按按钮、关闭视窗)」,却没有得到回应,
没有回应的原因就是因为他被「卡在单一任务的过程中,无法给予回应」
windows 就会判定这个程序是「没有回应
」的
(其实不只 windows, ubuntu, mac 在这个逻辑底下都是)
所以,我们必须把这个任务放入 QThread 执行,使得我们的主线任务可以被空出来,
能应付并接收新的其他任务。
修改昨天的 controller.py
)主要可以分为两个任务:
注意「QThread, pyqtSignal」被宣告在「PyQt5.QtCore」里面
from PyQt5 import QtCore, QtWidgets
from PyQt5.QtGui import QImage, QPixmap
from PyQt5.QtWidgets import QFileDialog
from PyQt5.QtCore import QThread, pyqtSignal
import time
from UI import Ui_MainWindow
我们在 ThreadTask 里面宣告一个 global 的 pyqtSignal,
并指定给 qthread_signal,(需要宣告类别)
结果就像 「qthread_signal = pyqtSignal(int)」
我们把讯号送回去给主程序,我们透过 emit 这个 function,
可以协助我们把值送回主程序,并不影响主程序的任务。
class ThreadTask(QThread):
qthread_signal = pyqtSignal(int)
def start_progress(self):
max_value = 100
for i in range(max_value):
time.sleep(0.1)
self.qthread_signal.emit(i+1)
我们把按钮连结 ButtonClick() 这个 function,
我们在 ButtonClick() 这个任务当中,宣告我们的一份新的 ThreadTask 任务,
并把讯号连接至一个 function,
class MainWindow_controller(QtWidgets.QMainWindow):
def __init__(self):
super().__init__() # in python3, super(Class, self).xxx = super().xxx
self.ui = Ui_MainWindow()
self.ui.setupUi(self)
self.setup_control()
def setup_control(self):
self.ui.progressBar.setMaximum(100)
self.ui.pushButton.clicked.connect(self.ButtonClick)
def ButtonClick(self):
self.qthread = ThreadTask()
self.qthread.qthread_signal.connect(self.progress_changed)
self.qthread.start_progress()
def progress_changed(self, value):
self.ui.progressBar.setValue(value)
我们需要把值的变化传至一个 function 当中,
我们利用「self.qthread.qthread_signal.connect(self.progress_changed) 」
连结「讯号 (qthread_signal)」与 「function - progress_changed()」的关系
而这个 function 会需要保留 value 作为一个栏位,
实际上在连接时,并不是以常见的形式传入这个值
(正确来说,应该是前面的 qthread_signal 被作为 value 传入)
def progress_changed(self, value):
self.ui.progressBar.setValue(value)
我们仔细看,value 就是代表 qthread_signal,
所以我们可以直接从 setValue 去改他。
这部分就没什麽好说的,我们会需要一个 function 帮助我们启动 Thread。
并用 emit 把讯号送回来。
★ 本文也同步发於我的个人网站(会有内容目录与显示各个小节,阅读起来更流畅):【PyQt5】Day 20 - PyQt 最重要的 QThread 概念 / 为什麽 windows, mac, ubuntu (linux) 程序会「没有回应」?
这里的ng并非电影电视中导演说太烂、要再拍一次的NG(No Good), 而是指Angular的ng...
前情提要: 看完记忆体储存差异,现在要来谈谈全域污染这件事。 基本scope概念 所谓的范畴Scop...
因为完美不可能 因为知道要办到的事有多难,所以绝对不会认为次次达标是件好事 完美是在范围(Scope...
元件客制化(LinerLayout和Button) 前几天讲了EditText和Button,不过这...
在上篇文章我们说了「符号解析」,符号解析的任务就是:建立定义与引用之间的关联,而「重定址」的任务就是...