PySide6 类之源QObject(2)

7.QObject的删除操作

API函数

参数说明

返回值

功能作用

deleteLater(self)

None

None

延迟销毁一个对象

前面说了,在PySide6的内存管理机制中,删除一个对象同时会解除其相关的父子关系,如果父对象被删除,子对象也会被删除(没有谁再指向子对象,子对象被回收)。deleteLater()方法的作用是不将对象立即删除,而是向主消息循环发送了一个event,下一次主消息循环收到这个event之后才会销毁对象。这样做的好处就是可以在这些延迟删除的时间内做一些事情,坏处在于内存释放不及时。

我们要注意其与del()之间的区别,见下例。

example:

# -*- coding:utf-8 -*-
from PySide6.QtCore import QObject
from PySide6.QtWidgets import QWidget,QApplication
import sys

class window(QWidget):
   def __init__(self):
      super(window, self).__init__()
      self.resize(600,600)
      self.move(300,300)
      self.setWindowTitle("object信号断开测试")

      obj1 = QObject()
      obj2 = QObject()
      obj3 = QObject()

      obj3.setParent(obj2)
      obj2.setParent(obj1)
      obj1.setParent(self)

      obj1.destroyed.connect(lambda :print("obj1被销毁了"))
      obj2.destroyed.connect(lambda: print("obj2被销毁了"))
      obj3.destroyed.connect(lambda: print("obj3被销毁了"))

      del obj2

      # obj2.deleteLater()
      # print(obj1.children())

if __name__ == '__main__':
   app = QApplication(sys.argv)
   win = window()
   win.show()
   sys.exit(app.exec())

运行结果:

运行代码,命令行无显示。

既然我们说删除了对象就解除了对象与父对象之间的父子关系,那么删除了obj2,为什么没有触发相应的槽函数而输出"obj2被销毁了"?这是由于del()方法并没有奖obj2对象销毁,被删除的只是内存中存放obj2这个变量名。既然obj2对象并没有被删除,obj3也就不会被删除。

我们将del obj这行代码注释掉,打开obj.deletLater()和print(obj1.children())再运行,再窗口没有关掉的情况下输出结果如下:

[<PySide6.QtCore.QObject(0x1f0e3afc750) at 0x000001F0E3A795C0>]
obj2被销毁了
obj3被销毁了

可以看到,obj2此时被删除,同时触发槽函数,obj3由于失去了父对象,也在执行完后被删除。但是,我们发现最上面一行打印了一个对象。实际上这个对象就是obj2,是print(obj1.children())这行代码输出的结果。而且,我们还可以看出,上面我们说了deletLater()是稍后删除的特性,即如果是立即删除,输出中就不会显示obj2对象,且他的输出顺序应在最后面。所以,如果我们想在Pyside6中彻底删除一个对象,应当使用deleteLater()方法,而不是del()方法。

8.QObject内置简单定时器

在PySide6中,通常我们使用计时器类QTimer来用作一个定时装置,用来实现一些重复或者延迟操纵。比如,制作一个闹钟,定时到几点几分开始报警。或者间隔若干秒发送一个数据等。在QObject类为我们添加了一个便捷方法,可以不用创建QTimer对象,而进行简单的定时器启停操作。

API函数

参数说明

返回值

功能作用

startTimer(interval,timerType)

Interval:int

Timertype:

Qt.TimerType

id:int

创建并启动一个定时器

killTimer(id)

id:int

None

结束一个指定id的定时器

startTimer()方法被调用时会自动生成一个定时器对象,并返回定时器对象的id。也就是说通过startTimer()方法同时开启多个定时器。其参数Interval表示定时器触发一次的间隔时间,单位是毫秒,另个一参数Timertype是一个枚举类,表示定时间设置的时间精度。

枚举类

枚举常量

枚举值

功能描述

Qt.TimerType

CoarseTimer

0

粗精度定时器,只能精确到秒

PreciseTimer

0

高精度定时器,精确到毫秒

VeryCoarseTimer

0

最低精度定时器,与预想值有5%的误差。

另外,我们创建和开启一个定时器不能啥都不干,而进行必要的操作,需要重写QObject的eventTimer()方法。这个事件的作用是,每当定时器时就会调用一次这个方法。

example:

class customObject(QObject):
   def timerEvent(self, event):
      super(customObject, self).timerEvent(event)
      # 获取定时器的ID
      timerID = event.timerId()
      # 不同ID的定时器每隔设置时间打印一次当前时间
      print("id为{}打印了时间{}".format(event.timerId(),datetime.now()))


class window(QWidget):
   def __init__(self):
      super(window, self).__init__()
      self.resize(300,300)
      self.setWindowTitle("object的定时器测试")

      #这里需要创建两个实例属性,否则在self.stop方法中可能会报AttributeError
      #这是由于startTimer()返回值之后调用后才能返回值。没有赋值,就不会有self.id1
      #和self.id2这两个对象
      self.id1 = None
      self.id2 = None

      self.obj = customObject()

      #创建定时器的开始和停止按钮
      start_btn = QPushButton("开始",self)
      stop_btn = QPushButton("停止",self)

      start_btn.clicked.connect(self.start)
      start_btn.move(100,100)
      stop_btn.clicked.connect(self.stop)
      stop_btn.move(100,150)
   def start(self):
      self.id1 = self.obj.startTimer(2000, Qt.TimerType.VeryCoarseTimer)
      self.id2 = self.obj.startTimer(5000, Qt.TimerType.CoarseTimer)

   def stop(self):
      if self.id1 != None:
         self.obj.killTimer(self.id1)
         self.obj.killTimer(self.id2)

if __name__ == '__main__':
   app = QApplication(sys.argv)
   win = window()
   win.show()
   sys.exit(app.exec())

运行结果:

点击开始按钮运行一段时间,点击停止按钮。

id为1打印了时间2024-09-06 06:36:14.328256
id为1打印了时间2024-09-06 06:36:16.327776
id为2打印了时间2024-09-06 06:36:17.327489
id为1打印了时间2024-09-06 06:36:18.326969
id为1打印了时间2024-09-06 06:36:20.325268
id为2打印了时间2024-09-06 06:36:22.322949
id为1打印了时间2024-09-06 06:36:22.322949
id为1打印了时间2024-09-06 06:36:24.320156
id为1打印了时间2024-09-06 06:36:26.329585
id为2打印了时间2024-09-06 06:36:27.327152

需要说明的是,上例中的事件还没有学,只要知道它在本例中的作用即可,将在QWidget中简要介绍事件的用法,并在后面(信号与槽及事件)专篇中学习。

9.QTimer定时器类

由于本人是按照控件一个一个学习的,那么每个控件中遇到的非QWidgets模块中的类都会跟随学习,这样会将知识都联系起来。

1).QTimer介绍(官翻)

QTimer类为计时器提供了一个高级编程接口。要使用它,需要创建一个QTimer,将其timeout()信号连接到适当的插槽,然后调用start()。从那时起,它将以恒定的间隔发出timeout()信号。

1秒(1000毫秒)定时器的示例:

timer = QTimer(self)
timer.timeout.connect(lambda:print("hello world!"))
timer.start(1000)

在调用start()后,每秒钟调用一次update()槽函数。

你可以通过调用setSingleShot (true)将计时器设置为只启动一次。你也可以使用静态方法singleShot()函数在指定的时间间隔后调用一个槽函数。

QTimer.singleShot(200, self.updateCaption)

在多线程应用程序中,可以在任何具有事件循环的线程中使用QTimer。要从非gui线程启动事件循环,请使用exec()。Qt使用定时器的线程亲和性来确定哪个线程将发出timeout()信号。因此,必须在线程中启动和停止定时器;不可能从另一个线程启动计时器。

作为一种特殊情况,启动次数为0的QTimer将立即启动,尽管0定时器与其他事件源之间的顺序没有指定。但0定时器可以用来完成一些工作,同时仍然一个简洁的用户界面予以支持。

timer = QTimer(self)
timer.timeout.connect(self.processOneThing)
timer.start()

start()方法被调用后,processOneThing()将被反复调用。它应该被编写成总是立即返回的方式(通常是在处理完一个数据项之后),这样Qt就可以将事件发送到用户界面,并在完成所有工作后立即停止定时器。这是在GUI应用程序中实现繁重工作的传统方式,但随着多线程在越来越多的平台上变得可用,我们希望0毫秒QTimer对象将逐渐被QThread s取代。(说人话就是别管0定时器是啥,多线程请用QThread类)。

  • 精度和定时器分辨率

定时器的准确性依赖于底层操作系统和硬件。大多数平台支持1毫秒的分辨率,尽管计时器的精度在许多现实世界的情况下并不等于这个分辨率。

精度还取决于定时器的类型。对于PreciseTimer, QTimer将尝试保持1毫秒的精度。精确定时器永远不会比预期提前超时。

对于CoarseTimer和VeryCoarseTimer类型,QTimer可能比预期更早唤醒,在这些类型的间隔内:CoarseTimer为间隔的5%,而VeryCoarseTimer为间隔的500毫秒。

如果系统繁忙或无法提供所要求的精度,所有定时器类型都可能比预期晚启动。在这种超时启动的情况下,Qt只会触发一次timeout()。发射信号后,‌计时器将恢复其原始的间隔时间。‌

  • QTimer的替代方案

使用startTimer()和timerEvent()‌:‌调用对象的startTimer(),‌并在类中重写timerEvent()事件处理器(‌该类必须继承QObject)‌。‌缺点是timerEvent()不支持如单次定时器或信号等高级功能。‌

‌使用QBasicTimer‌:‌通常比直接使用startTimer()更方便。

概述三种方法,‌参见Timers。‌注意,‌某些操作系统限制可使用的定时器数量,‌Qt尝试解决这些限制‌12。‌

2).QTimer相关方法

API函数

参数说明

返回值

功能作用

QTimer(parent)

parent:QObject

None

创建一个定时器

interval(self)Noneint获取定时器设置的启动间隔时间
isActive(self)Nonebool定时正在运行时返回True,否则,返回Flase
isSingleShot(self)Nonebool获取定时器是否只启动一次。
remainingTime(self)Noneint获取定时器发射信号的剩余时间。如果定时器未运行,返回-1.
setInterval(self, msec:)msec: intNone设置定期启动的时间间隔
setSingleShot(self, singleShot)singleShot: boolNone设置定时器是否为单次出发。默认值为Flase。
setTimerType(self, atype)atype: PySide6.QtCore.Qt.TimerTypeNone设置定时器精度
singleShot(msec, context)msec: int, context: PySide6.QtCore.QObject, functor: CallableNone创建并开始一个单此出发的定时器
singleShot(msec, functor)msec: int, functor: CallableNone创建并开始一个单此出发的定时器
 singleShot(msec, receiver, member)msec: int, receiver: PySide6.QtCore.QObject, member: Union[bytes, bytearray, memoryview]None创建并开始一个单此出发的定时器
singleShot(msec, timerType, receiver, member)msec: int, timerType: PySide6.QtCore.Qt.TimerType, receiver: PySide6.QtCore.QObject, member: Union[bytes, bytearray, memoryview]None创建并开始一个单此出发的定时器
start(self)NoneNone启动定时器
start(self, msec: int)msecNone间隔msec后,启动定时器
stop(self)NoneNone停止定时器
timerId(self)Noneint获取定时器ID
timerType(self)NoneQt.TimerType获取定时器精度类型

setInterval()方法是设置定时器的启动间隔时间,参数单位为毫秒。默认情况下参数为0,也就是前面介绍中所说的0定时器,0定时器会立即启动。需要说明的是,当适应setInterval()设置定时器的时间间隔,会改变定时器的timerID。

另外,如果设置了setInterval(msc),点击start()后,定时器已经开始运行了,但是第一次发射信号或是调用TimerEvent()方法是从start()开始间隔msc后才进行的。

remainingTime()则是返回一个时间值,这个值是从调用remainingTime()到定时器最近的下一次发射信号时的差值。

singleShot()是一个静态方法,即可以直接用类来调用,而不用创建实例对象(如:QTimer.singleShot())。它有几个重载函数,这里将几个重载函数的参数介绍一下:mesc是调用方法后到发射信号的间隔时间; receiver是事件接收者,通常是QObject或其子类;member则是时间接受者的成员函数;context表示当前类或者实例对象中的一个成员函数; timerType是定时器精度。

QTimer在多线程中运用比较多,其在多线程中的注意事项主要是正确的将其销毁,防止内存泄漏,这部分内容将在多线程编程中学习。

3).QTimer事件

API函数

参数说明

返回值

功能作用

timerEvent(self, arg__1)arg__1: PySide6.QtCore.QTimerEvent

None

定时器事件

4).QTimer信号

信号

参数说明

返回值

功能作用

timeout()

None

None

定时器设置的时间间隔到期时发射此信号

example:

# -*- coding:utf-8 -*-
from PySide6.QtCore import QTimer
from PySide6.QtWidgets import QWidget,QApplication,QPushButton
import sys
from datetime import datetime

class window(QWidget):
   def __init__(self):
      super(window, self).__init__()
      self.resize(300,300)
      self.setWindowTitle("object的定时器测试")
      # 创建一个定时器
      self.timer = QTimer(self)
      self.timer.setInterval(5000)

      #创建定时器的开始、停止和查看启动剩余时间按钮
      self.start_btn = QPushButton("开始",self)
      self.stop_btn = QPushButton("停止",self)
      self.remaining_btn = QPushButton("剩余时间",self)

      # 按钮连接到槽函数
      self.start_btn.clicked.connect(self.start)
      self.start_btn.move(100,100)
      self.stop_btn.clicked.connect(self.stop)
      self.stop_btn.move(100,150)
      self.remaining_btn.clicked.connect(self.remaining)
      self.remaining_btn.move(100,200)

      self.timer.timeout.connect(lambda :print(datetime.now()))

      # 静态方法再创建一个单词触发定时器,程序开始运行5s后调用start_btn按钮的click()
      # 模拟点击click()
      QTimer.singleShot(5000,self.start_btn,self.start_btn.click())

   def start(self):
      # 点击按钮定时器启动
      self.timer.start()

   def stop(self):
      # 点击按钮定时器停止
      self.timer.stop()

   def remaining(self):
      # 点击按钮打印启动剩余时间
      print(self.timer.remainingTime())

if __name__ == '__main__':
   app = QApplication(sys.argv)
   win = window()
   win.show()
   sys.exit(app.exec())

10.QObject的事件

本小节仅列出QObject事件的相关API和作用,供以后查看。具体将在事件处理机制章节中详细介绍。

API函数

参数说明

返回值

功能作用

event(ev)

ev:QEvent

bool

接受一个事件进行处理

childEvent(ev)

ev:QChildEvent

None

接受并处理子类事件

customEvent(ev)

ev:QEvent

None

自定义事件

eventFilter(watched,event)

Wathched:QObject被安装过滤器对象

Event:需要过滤的事件

None

如果对象被安装过滤器,则过滤事件event。如果在子类中重写此函数,希望过滤则返回true,否则返回Flase。

installEventFilter(obj)

Obj:QObject希望安装过滤器的对象

None

为一个对象安装过滤器

removeEventFilter(obj)

Obj:QObject希望移除过滤器的对象

None

移除一个已安装过滤的对象上的过滤器

timerEvent(ev)

ev:QTimerEvent

Int:定时器id

接受并处理定时器事件11

11.QObject多线程

Pyside6的多线程通常使用QThread类,在后面的章节我们将仔细介绍。这里仅列出QObject与多线程的相关API,供查阅。

        

API函数

参数说明

返回值

功能作用

thread()

None

QThread

返回对象所在的线程

moveToThread(thread)

Thread:QThread

None

更改对象及其子对象的线程相关性。如果对象有父对象,则无法更改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值