【问题标题】:Pause button does not work, how do I use after_cancel properly?暂停按钮不起作用,如何正确使用 after_cancel?
【发布时间】:2021-09-18 18:52:34
【问题描述】:

所以我正在制作一个有计时器的程序并且计时器工作,现在我正在使用暂停功能。经过一番研究,我发现了一个名为 after_cancel 的函数。这个函数应该取消后函数,因为在这种情况下后函数会创建一个无限循环。在这种情况下如何正确使用 after_cancel 还是有其他可能的解决方案?

提前致谢。

t = 60000

global timerState
timerState = True

def pause():
    timerLabel.after_cancel(countdown)
    timerState = False
    timerButton.config(text="Play", command=countdown)


def countdown():
    global t

    if t == 0:
        timer = "00:00"
        timerLabel.config(text=timer)
        return

    if timerState == False:
        timerLabel.after_cancel(countdown)
        timerButton.config(text="Play", command=countdown)
        return

    mins = t / 60000

    secs = t / 1000
    secs = secs - int(mins) * 60

    mills = t

    mills = mills - int(secs) * 1000



    if timerState == True:
        timer = "{:02d}:{:02d}".format(int(mins),int(secs))
        timerLabel.config(text=timer)
        t -= 1
        timerLabel.after(1, countdown)

        timerButton.config(text="Pause", command=pause)

【问题讨论】:

  • 您需要将.after()返回的ID传递给.after_cancel()。例如:after_id = timerLabel.after(1, countdown),然后是 timerLabel.after_cancel(after_id)。您可能需要将after_id 设为全局变量。
  • 好的,那么我如何获得 after id?
  • 我已经在评论中提到了。
  • 哦,现在我明白了,对不起,我有点糊涂了。谢谢!

标签: python tkinter


【解决方案1】:

大多数时候.after_cancel 脚本可以通过使用if 语句来避免。例如看这个:

import tkinter as tk

t = 60000

def pause():
    global timerState
    timerState = False
    timerButton.config(text="Play", command=start_countdown)

def start_countdown():
    global timerState
    timerState = True
    timerButton.config(text="Pause", command=pause)
    countdown()

def countdown():
    global t

    if timerState:
        timerLabel.config(text=t)
        t -= 1
        if t > 0:
            timerLabel.after(1, countdown)


root = tk.Tk()

timerLabel = tk.Label(root, text="")
timerLabel.pack()

timerButton = tk.Button(root, text="Play", command=start_countdown)
timerButton.pack()

root.mainloop()

我修改了您的代码以显示t,但没有采用mm:ss 格式。要点是,如果timerStateFalse,则永远不会调用timerLabel.after(1, countdown),因此.after_cancel 毫无意义。

注意:您尚未考虑其他代码所花费的时间,因此 t 并不是真正以毫秒为单位(至少对于我的慢速计算机而言)。

【讨论】:

  • 这是另一种方法,但我仍然会使用 after_cancel,因为它更简单。无论如何,谢谢!
  • @DaveWilson 我不想争论,但使用if 语句至少对我来说更快更直观。
  • 是的,我同意,对于下一个项目,我很可能会使用 if 语句。但是因为在我的情况下,使用.after_cancel() 函数更容易。
【解决方案2】:

这里是afterafter_cancel的演示

每个after 都需要取消才能清除事件队列。

在这个程序中,每次按下按钮都会产生一个延时事件。 事件ID存储在self.after_time

出于演示目的,我已将延迟值设置为每次按下按钮时增加 100 毫秒。 它将主人从视野中撤出。

当时间延迟事件完成时,它会调用self.action

self.actionafter_cancel( self.after_time ) 取消事件 并且主人是可见的,准备好按下下一个按钮。


import tkinter

class after_demo:

    delay = 100

    def __init__( self ):

        self.master = tkinter.Tk()
        self.master.title( 'After Demo' )
        self.control = tkinter.Button(
            self.master, text = 'Begin Demo',
            width = 40, command = self.pause )
        self.control.grid(row=0,column=0,sticky='nsew')

    def action( self ):

        self.master.after_cancel( self.after_time )
        self.control[ 'text' ] = 'Delay( {} ) ms'.format( self.delay )

        self.master.deiconify()
        
    def pause( self ):

        self.after_time = self.master.after( self.delay, self.action )
        self.delay += 100

        self.master.withdraw()

if __name__ == '__main__':

    timer = after_demo( )
    tkinter.mainloop()

【讨论】:

  • 据我所知,如果 .after 脚本正在执行/已经执行,则无需取消它们。
  • 我认为这是一种很好的编程习惯。虽然如果它只使用一次......
  • 我很确定tkinter 在使用.after 脚本完成后会自行清理。 .after_cancel 用于阻止未来尚未运行的 .after 脚本。我会检查这个。
  • 刚刚检查过。 tkinter 在命令执行后删除它们。要对其进行测试,请在我的答案中将 print(timerLabel._tclCommands) 添加到 countdown 函数的开头。因此,如果命令已经/正在执行,则无需调用.after_cancel
  • 没有。它只有 1 次使用。如果你调用.after,那么由于某种原因你改变主意在调用函数之前你可以取消它。把它想象成一个时间表。您可以取消将要发生的事件,但取消已经发生的事件是没有意义的。如果您对日程安排很聪明,那么您很少需要取消活动。这就是为什么我说“大多数时候.after_cancel 脚本可以通过使用if 语句来避免。”
猜你喜欢
  • 1970-01-01
  • 2016-06-09
  • 2013-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多