【问题标题】:QTimer not executed when called inside Singleton/Borg在 Singleton/Borg 中调用 QTimer 时未执行
【发布时间】:2023-03-24 16:37:02
【问题描述】:

我在 Singleton 中使用 QTimer 实现了一个计时器。 Singleton 是使用Borg 模式实现的。如果我在 Singleton 的函数中单次启动 QTimer,它将不会被执行。在 Singleton 之外的函数中进行相同的调用效果很好。

这是代码:

#!/usr/bin/env python
import sys
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QApplication


class Borg():
    _shared_state = {}
    def __init__(self):
        self.__dict__ = self._shared_state


class Timers(Borg):
    def __init__(self):
        Borg.__init__(self)

    def update_not_working(self):
        QTimer().singleShot(2000, Timers().update_not_working)
        print('update not working')


def update_working():
    QTimer().singleShot(2000, update_working)
    print('update working')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    print('start timer')
    Timers().update_not_working()
    update_working()

    sys.exit(app.exec_())

输出为(无错误,无异常):

start timer
update not working
update working
update working
....

为什么一个电话有效而另一个电话无效?我的 Borg 实现或 QTimer 的使用有问题吗?

【问题讨论】:

    标签: python qt pyqt


    【解决方案1】:

    update_not_working 中的print selfupdate working 中的print Timers() 表明事件循环开始之前的Timers 对象与内部的对象不同:

    update not working
    <__main__.Timers instance at 0xb52162cc>
    update working
    <__main__.Timers instance at 0xb52162cc>
    update working
    <__main__.Timers instance at 0xb521650c>
    update working
    <__main__.Timers instance at 0xb521650c>
    update working
    <__main__.Timers instance at 0xb521650c>
    update working
    <__main__.Timers instance at 0xb521650c>
    

    @classmethod 在这里应该有所帮助,因为它允许在实例或类上调用方法,就像在单次语句中所做的那样。

    比较:When should I use @classmethod and when def method(self)?

    【讨论】:

    • @classmethod 解决了这个问题。我仍然感到困惑的是,为什么没有发生错误。如果对象不同,不能被QTimer().singleShot()调用,不应该有异常吗?
    • 不,如果一个 qobject 消失,它通常会被正确地销毁并被取消。这会悄无声息地发生,因为对象通常是被有意破坏的。
    【解决方案2】:

    这实际上只是一个正常的垃圾收集问题。

    如果您像这样在示例中添加一些调试代码:

    class Timers(Borg):
        def __init__(self):
            Borg.__init__(self)
            print('init:', self)
    
        def update_not_working(self):
            QTimer().singleShot(1, Timers().update_not_working)
            print('update not working')
    
        def __del__(self):
            print('deleted:', self)
    

    它将产生如下输出:

    start timer
    init: <__main__.Timers object at 0x7f194bf53eb8>
    init: <__main__.Timers object at 0x7f1940cfdb00>
    deleted: <__main__.Timers object at 0x7f1940cfdb00>
    update not working
    deleted: <__main__.Timers object at 0x7f194bf53eb8>
    update working
    update working
    

    如您所见,两个Timers 实例在单次计时器发送其timeout() 信号之前很久就被删除。当它们被删除时,它们的实例方法也会被删除,这将自动断开它们与信号的连接。这表明Borg 模式不会产生真正的单例:它只是模仿了单例的一些行为。

    如果你使用 real 单例类,像这样:

    class Timers2(object):
        _instance = None
    
        def __new__(cls):
            if Timers2._instance is None:
                Timers2._instance = object.__new__(cls)
            return Timers2._instance
    
        def update_not_working(self):
            QTimer().singleShot(2000, Timers2().update_not_working)
            print('update not working')
    

    您的示例将按预期工作。这是因为只有一个实例,并且通过作为类属性缓存来保持活动状态。

    最后,update_working()之所以成功,是因为它是一个全局定义的函数。与 class 属性一样,这可确保在脚本完成之前不会被垃圾回收。

    【讨论】:

      【解决方案3】:
      class Borg():
          _shared_state = {}
          def __init__(self):
              self.__dict__ = self._shared_state
      
      
      class Timers(Borg):
          def __init__(self):
              Borg.__init__(self)
      
          @classmethod
          def update_not_working(cls):
              QTimer().singleShot(2000, Timers().update_not_working)
              print('update not working')
      
      
      def update_working():
          QTimer().singleShot(2000, update_working)
          print('update working')
      
      if __name__ == '__main__':
          app = QApplication(sys.argv)
          print('start timer')
          Timers().update_not_working()
          update_working()
          sys.exit(app.exec_())
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-08-21
        • 2010-11-22
        • 1970-01-01
        • 1970-01-01
        • 2021-01-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多