取消所有未完成的 QTimer 事件

Posted

技术标签:

【中文标题】取消所有未完成的 QTimer 事件【英文标题】:Cancel all outstanding QTimer events 【发布时间】:2011-02-21 21:19:44 【问题描述】:

我正在寻找一种在 pyqt 中设置多个定时函数调用的方法,并在需要时取消所有挂起的调用。

我正在考虑使用QtCore.QTimer 来设置函数调用,但我不知道有什么好的方法可以取消这些调用。有什么想法吗?

使用QtCore.QTimer.singleShot(1000, self.function) 后,我似乎找不到任何取消的方法。相反,如果我创建一个 QTimer 对象列表,我可以停止它们,但是我不得不管理一个计时器对象列表(创建、删除、活动等),我想避免这种情况。

# Setup a timer object.
timer = QtCore.QTimer(self)
timer.timeout.connect(self.function)
timer.setSingleShot(True)
timer.start(1000)

# To stop the timer object at some later point.
timer.stop()

我还可以管理自己的待处理函数调用队列,如果可能的话,我也想避免这种情况(为了简单起见)。

这是一些虚拟代码,显示了我正在尝试做的事情:

import sys
from PyQt4 import QtCore

class Test(QtCore.QObject):

    def __init__(self, parent=None):
        QtCore.QObject.__init__(self, parent)
        

    def addDelayedCall(self, time, function):
        # Do something here.
        pass


    def clearPendingCalls(self):
        print('  Clearing pending function calls.')
        # Do something here.


    def setupCalls(self):
        self.addDelayedCall(500, self.dummy)
        self.addDelayedCall(1000, self.dummy)
        self.addDelayedCall(1500, self.dummy)
        self.addDelayedCall(2000, self.dummy)


    def dummy(self):
        print('dummy just got called.')


if __name__ == '__main__':
    app = QtCore.QCoreApplication(sys.argv)
    test = Test(app)

    QtCore.QTimer.singleShot(0, test.setupCalls)
    QtCore.QTimer.singleShot(1250, test.clearPendingCalls)
                   
    QtCore.QTimer.singleShot(5000, app.quit)            
    sys.exit(app.exec_())

【问题讨论】:

【参考方案1】:

我想出了一个解决方案,直接使用QObject 提供的计时器功能。我怀疑有一个更优雅的解决方案,但这可能会满足我的需要。

import sys
from PyQt4 import QtCore


class Test(QtCore.QObject):

    def __init__(self, parent=None):
        QtCore.QObject.__init__(self, parent)

        self.timers = 


    def timerEvent(self, event):
        function = self.timers.pop(event.timerId())
        self.killTimer(event.timerId())
        function()


    def addDelayedCall(self, time, function):
        timer_id = self.startTimer(time)

        self.timers[timer_id] = function


    def clearPendingCalls(self):
        print('  Clearing pending function calls.')

        while self.timers:
            timer_id, function = self.timers.popitem()
            self.killTimer(timer_id)


    def setupCalls(self):
        self.addDelayedCall(500, self.dummy)
        self.addDelayedCall(1000, self.dummy)
        self.addDelayedCall(1500, self.dummy)
        self.addDelayedCall(2000, self.dummy)


    def dummy(self):
        print('dummy just got called.')




if __name__ == '__main__':
    app = QtCore.QCoreApplication(sys.argv)
    test = Test(app)

    QtCore.QTimer.singleShot(0, test.setupCalls)
    QtCore.QTimer.singleShot(1250, test.clearPendingCalls)

    QtCore.QTimer.singleShot(5000, app.quit)            
    sys.exit(app.exec_())

【讨论】:

一个更简单的方法是像以前一样使用QTimer,但放弃方便的setSingleShot,而不是显式地创建和配置QTimers。 但是,您不需要手动维护一个列表,您需要做的就是在某处维护一个普通的QObject,然后使用该 QObject 作为您创建的 QTimer 实例的父级 (例如QtCore.QTimer(cancellerObject) - 之后您不需要自己持有 QTimer 实例。然后取消所有挂起的计时器,只需销毁该对象,它会立即销毁其所有子 QTimer 作为副作用。 @JasonC,这听起来不错,而且确实比我的解决方案简单得多。我将不得不解决这个问题并考虑将其用于未来的项目。【参考方案2】:

您可以通过计时器的超时信号disconnect 并在需要时重新连接。一旦断开连接,您将停止接收来自计时器的任何呼叫,即使它仍然处于活动状态。下面是一个小例子:

import sys
from PyQt4 import QtGui, QtCore

class MainForm(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainForm, self).__init__(parent)

        self.button = QtGui.QPushButton("button", self)       
        self.button.resize(100, 30)

        self.connect(self.button, QtCore.SIGNAL('clicked()'), self.on_button_click)

        self.connected = True
        self.timer = QtCore.QTimer(self)
        self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.on_timeout)
        self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.on_timeout_test)
        self.timer.start(1000)

    def on_button_click(self):
        if self.connected:
            self.disconnect(self.timer, QtCore.SIGNAL('timeout()'), self.on_timeout_test)
        else:
            self.connect(self.timer, QtCore.SIGNAL('timeout()'), self.on_timeout_test)
        self.connected = not self.connected

    def on_timeout(self):
        print 'on_timeout'

    def on_timeout_test(self):
        print 'on_timeout_test'

def main():
    app = QtGui.QApplication(sys.argv)
    form = MainForm()
    form.show()
    app.exec_()

if __name__ == '__main__':
    main()

一旦计时器启动,两个插槽 on_timeouton_timeout_test 都会被调用,当您单击 按钮时,on_timeout_test 插槽会断开连接,然后如果 按钮重新连接再次点击。

希望这有帮助,问候

【讨论】:

不幸的是,这对我想要做的事情没有帮助。 (尽管它是一个很好的解决方案。)我在我的问题中添加了一些虚拟代码,希望能更清楚地解释我的目标。

以上是关于取消所有未完成的 QTimer 事件的主要内容,如果未能解决你的问题,请参考以下文章

在 WooCommerce 3+ 中将已付款订单标记为“已完成”,将未付款订单标记为“已取消”[重复]

如何在 iTunes Connect 中删除未完成的应用程序

事件后线程未完成

未触发 JQuery 自动完成选择事件

高级搜索完成事件未在 VBA 中触发

在 Loaded 事件中未完成渲染