在一般情况下,应用程序都是单线程运行的,但是对于 GUI程序来说,单线程有时满足不了需求。
例如,如果需要执行一个特别耗时的操作,在执行过程中整个程序就会卡顿,此时用户可能以为程序出错,所以就把程序关闭了;
或者 Widows 系统也认为程序出错,自动关闭程序。要解决这种问题就涉及多线程的知识。
一般来说,多线程技术涉及3种方法:
- 一是使用计时器模块QTimer
- 二是使用多线程模块QThread
- 三是使用事件处理功能。
定时器QTimer
定时器 QTimer 像个闹钟,其作用是经过一个固定的时间间隔发送一个信号,执行与信号连接的槽函数,实现自动完成某些功能。
可以设置定时器只发送一次信号,或多次发送信号;
可以启动发送信号,也可以停止发送信号。
用QTimer 创建定时器的方法如下所示,其中parent 是继承自QObejct 的对象。QTimer 是不可见的,当父类删除时,定时器也同时删除。
from PySide6.QtCore import QTimer
QTimer(parent: Union[PySide6.QtCore.QObject,NoneType]= None)-> None
如果要在应用程序中周期性地执行某个操作,如周期性地检测主机的 CPU值,则需要使用QTimer(定时器),QTimer 类提供了重复的和单次的定时器。
要使用定时器,需要先创建一个QTimer实例,将其 timeout 信号连接到相应的槽,并调用start()函数。
然后定时器会以恒定的间隔发射 timeout 信号。start(2000)表示设置时间间隔为2秒并启动定时器,代码如下:
from PySide6.QtCore import QTimer
# 初始化一个定时器
self.timer = QTimer(self)
# 计时结束并调用 operate()
# 设置时间间隔并启动定时器
self.timer.timeout.connect(self.operate)
self.timer.start(2000)
在默认情况下,isSingleShot0返回 False,如果返回 True,则计时器信号只会触发一次,可以通过 setSingleShot(True)修改默认值
计时器的另一种使用方法是延迟计时,这种方法要使用 sigleShot 信号(前者是timeout 信号),如 singleShot(5000,receiver)表示5 秒之后会触发 receiver 信号。
定时器QTimer的说明
QTimer类为定时器提供了一个高级编程接口。要使用它,请创建一个QTimer,将其timeout()信号连接到适当的插槽,然后调用start()。从那时起,它将以恒定的间隔发出timeout()信号。
1秒(1000毫秒)计时器的示例(来自模拟时钟示例):
timer = QTimer(self)
timer.timeout.connect(this,QOverload<>::of(&AnalogClock::update))
timer.start(1000)
从那时起,update()槽将每秒调用一次。
您可以通过调用setSingleShot(true)将计时器设置为仅超时一次。您也可以使用静态singleShot()函数在指定的时间间隔后调用插槽:
QTimer::singleShot(200,self.updateCaption)
在多线程应用程序中,可以在任何具有事件循环的线程中使用QTimer。要从非GUI线程启动事件循环,请使用exec()。Qt使用计时器的线程相关性来确定哪个线程将发出timeout()信号。因此,您必须在其线程中启动和停止计时器;不可能从另一个线程启动计时器。
作为一种特殊情况,超时为0的QTimer将尽快超时,尽管零计时器和其他事件源之间的顺序未指定。零定时器可以用来做一些工作,同时仍然提供快速的用户界面:
timer = QTimer(self)
从那时起,processOneThing()将被反复调用。它应该以这样一种方式编写,即它总是快速返回(通常在处理一个数据项之后),以便Qt可以将事件传递到用户界面,并在完成所有工作后立即停止计时器。这是在GUI应用程序中实现繁重工作的传统方式,但随着多线程现在在越来越多的平台上变得可用,我们预计零毫秒的QTimer对象将逐渐被QThread s所取代。
精度和计时器分辨率
计时器的准确性取决于底层操作系统和硬件。大多数平台支持1毫秒的分辨率,尽管在许多现实世界的情况下,计时器的精度不会等于这个分辨率。
精确度也取决于计时器类型。对于PreciseTimer,QTimer将尝试将精度保持在1毫秒。精确的计时器也永远不会比预期的更早超时。
对于CoarseTimer和VeryCoarseTimer类型,QTimer可能比预期更早醒来,在这些类型的裕度范围内:CoarseTimer5%的间隔,VeryCoarceTimer 500ms的间隔。
如果系统繁忙或无法提供所要求的精度,则所有定时器类型的超时时间可能晚于预期。在这种超时超时的情况下,即使多个超时已经过期,Qt也只会发出一次timeout(),然后将恢复原始间隔。
QTimer的替代品
使用QTimer的另一种方法是为对象调用startTimer(),并在类(必须继承QObject)中重新实现timerEvent()事件处理程序。缺点是timerEvent()不支持诸如单次定时器或信号之类的高级功能。
另一种选择是QBasicTimer。它通常没有直接使用startTimer()那么麻烦。请参阅Timers以了解所有三种方法的概述。
一些操作系统限制了可以使用的定时器的数量;Qt试图绕过这些限制。
定时器QTimer的属性
属性 | 描述 | 访问功能 |
---|---|---|
active: bool | 如果计时器正在运行,则此布尔属性为true;否则为false。 | isActive() |
interval: int | 此属性保存超时间隔(以毫秒为单位)。 此属性的默认值为0。一旦处理完窗口系统的事件队列中的所有事件,超时间隔为0的QTimer就会超时。 设置活动计时器的间隔会更改其timerId()。 | interval() setInterval(msec) |
remainingTime: int | 此属性保存剩余时间(以毫秒为单位)。 返回计时器的剩余值(以毫秒为单位),直到超时为止。如果计时器处于非活动状态,则返回的值将为-1。如果计时器过期,则返回的值将为0。 | remainingTime() |
singleShot: bool | 此属性保存计时器是否为单次计时器。 单触发计时器只触发一次,非单触发计时器每隔几毫秒触发一次。 此属性的默认值为false。 | isSingleShot() setSingleShot(singleShot) |
timerType: TimerType | 此属性用于控制计时器的准确性。 此属性的默认值为Qt::CoarseTimer。 | timerType() setTimerType(atype) |
定时器QTimer的常用方法
定时器QTimer的常用方法如表所示,主要方法介绍如下
QTimer的方法及参数类型 | 返回值的类型 | 说明 |
---|---|---|
setInterval(msec:int) | None | 设置信号发送的时间间隔(毫秒) |
interval() | int | 获取信号发送的时间间隔(毫秒) |
isActive() | bool | 获取定时器是否激活 如果计时器正在运行(挂起),则返回true;否则返回false。 属性的Getter处于活动状态。 |
setSingleShot(singleShot) | 设置取计时器是否为单次计时器。 | |
isSingleShot() | bool | 获取计时器是否为单次计时器。 单触发计时器只触发一次,非单触发计时器每隔几毫秒触发一次。 此属性的默认值为false。 |
remainingTime() | int | 获取距下次发送信号的时间(毫秒) |
setSingleShot(bool) | None | 设置定时器是否为单次发送 |
isSingleShot() | bool | 获取定时器是否为单次发送 |
setTimerType(atype: Qt.TimerType) | None | 设置定时器的精度类型 |
timerType() | Qt.TimerType | 获取定时器的精度类型 |
[slot]start(msec:int) | None | 经过msec毫秒后启动定时器 |
[slot]start([msec]) | None | 此函数重载start()。 在间隔中指定的超时时间内启动或重新启动计时器。 如果计时器已经在运行,它将停止并重新启动。 如果singleShot为true,则计时器将只激活一次。 |
[slot]stop() | None | 停止定时器 |
timerId() | int | 获取定时器的ID号 |
[static]singleShot(int,Callable) | None | 经过int毫秒后,调用Python的可执行函数 Callable |
[static]singleShot(msec: int,receiver: QObject,member: str) | None | 这个静态函数在给定的时间间隔后调用一个槽。 使用此函数非常方便,因为您不需要麻烦处理timerEvent或创建本地QTimer对象。 from PySide6.QtWidgets import QApplication from PySide6.QtCore import QTimer if name ==“main”: app = QApplication([]) QTimer.singleShot(600000,app,QCoreApplication.quit) … sys.exit(app.exec()) 此示例程序在10分钟(600000毫秒)后自动终止。 接收器是接收对象,成员是插槽。时间间隔为毫秒。 |
[static]singleShot(msec: int,timerType: Qt.TimerType,receiver:QObject,member: str) | None | 这是一个重载函数。 这个静态函数在给定的时间间隔后调用一个槽。 使用此函数非常方便,因为您不需要麻烦处理timerEvent或创建本地QTimer对象。 接收器是接收对象,成员是插槽。时间间隔为毫秒。计时器类型会影响计时器的准确性。 |
-
定时器的使用
- 一般是先建立定时器对象,用setInterval(int)方法设置定时器发送信号的时间间隔,然后将定时器的信号 timeout 与某个槽函数关联最后用start()方法启动定时器。
- 如果只需要定时器发送1次信号,可以设置 setSingleShot(bool)为True,否则将会连续不断地发送信号,可以用stop()方法停止定时器信号的发送
- 如果只是 1次发送信号,也可以不用创建定时器对象,用定时器类的静态方法singleShot()直接连接某个控件的槽函数。
- 如果定义了多个定时器,可以用timeld()方法获取定时器的编号。
-
定时器的精度
-
与系统和硬件有关用setTimerType(QtTimerType)方法可以设置定时器的精度,其中参数PySide6.QtCore.Qt.TimerType的取值如表所示:
Constant Description Qt.PreciseTimer 精密计时器试图保持毫秒的精度 Qt.CoarseTimer 粗略计时器试图将精度保持在所需间隔的5%以内 Qt.VeryCoarseTimer 非常粗糙的计时器只能保持完全的秒精度
-
定时器QTimer的信号
定时器只有一个信号 timeout(),每经过固定的时间间隔发送一次信号,或者只发送1次信号
也可以自定义信号,给定时间间隔后,在调用一个槽函数时发射信号
定时器QTimer 的应用实例
下面的程序定义了两个定时器,第1个定时器用于窗口背景图片的切换,第2个定时器用于设置按钮激活的时间,并改变按钮显示的文字,这里设置单击按钮后 10 秒激活按钮
# -*- coding: UTF-8 -*-
# File date: Hi_2023/2/28 22:00
# File_name: 定时器QTimer 的应用实例.py
from PySide6.QtWidgets import QApplication,QWidget,QPushButton
from PySide6.QtGui import QPainter,QPixmap,QBitmap
from PySide6.QtCore import QRect,QTimer
import sys
class MyWindow(QWidget):
def __init__(self,parent=None):
super().__init__(parent)
self.setWindowTitle("定时器")
path = r"../../Resources/animal//m1.png"
self.pix = QPixmap(path)
self.bit = QBitmap(path)
self.rect = QRect(0,0,self.pix.width(),self.pix.height())
self.resize(self.rect.size())
self.timer_1 = QTimer(self)# 第1个定时器
self.timer_1.setInterval(2000)# 第1个定时器的时间间隔连接
self.timer_1.timeout.connect(self.timer_1_slot)# 第1个定时器信号与槽函数的
self.timer_1.start()# 启动第1个定时器
self.status = True # 指示变量
self.timer_2 = QTimer(self)# 第2个定时器
self.timer_2.setInterval(1000)# 第2个定时器的时间间隔
self.timer_2.timeout.connect(self.pushButton_enable)# 第2个定时器信号与槽函数的连接
self.duration = 9 # 按钮激活时间
self.pushButton = QPushButton("单击发送验证码",self)
self.pushButton.setGeometry(10,10,200,30)
self.pushButton.clicked.connect(self.timer_2.start)# 按钮单击信号与槽函数的#连接
def timer_1_slot(self):
self.status = not self.status
self.update()# 更新窗口会发paintEvent(),调用paintEvent()函数
def paintEvent(self,event): # paintEvent 事件
painter = QPainter(self)
if self.status:
painter.drawPixmap(self.rect,self.pix)
else:
painter.drawPixmap(self.rect,self.bit)
def timer_2_start(self): # 按钮的槽函数
self.timer_2.start()
self.pushButton.setEnabled(False)
self.pushButton.setText(str(self.duration + 1)+"后可重新发送验证码")
def pushButton_enable(self):
if self.duration > 0:
self.pushButton.setText(str(self.duration)+"后可重新发送验证码")
self.duration = self.duration - 1
else:
self.pushButton.setEnabled(True)
self.pushButton.setText("单击发送验证码")
self.timer_2.stop()# 停止定时器
self.duration = 9
if __name__ == '__main__':
app = QApplication(sys.argv)
win = MyWindow()
win.show()
sys.exit(app.exec())