Python中的Tkinter after_cancel

11 投票
2 回答
34243 浏览
提问于 2025-04-20 04:43

附上的代码是用“after”方法在画布上随机画方块。我本来想用“after_cancel”方法在我点击鼠标按钮时停止绘制,但它还是继续画。

下面是代码。

#! /usr/bin/env python
from Tkinter import *
import time
import random


tk = Tk()
canvas = Canvas(tk, width=1920, height=1080, background="grey")
canvas.pack()

def xy(event):
    xm, ym = event.x, event.y

def task():
    w=random.randint(1,1000)
    h=random.randint(1,1000)
    canvas.create_rectangle(w,h,w+150,h+150)
    def callback(event):
        if True:
            print("clicked2")    
            tk.after_cancel(task)
    canvas.bind("<Button-1>",callback)        
    tk.after(1000,task)           
tk.after(1000,task)

tk.mainloop()   

2 个回答

1

当你使用 after()after_idle() 来安排一个任务时,这个调用会返回一个ID,用来标识这个待处理的动作,这样你就可以取消它。所以记得保存这个返回值,以后可以用到。

对你的代码做最小的修改(带注释)看起来是这样的:

#! /usr/bin/env python
from tkinter import *
import time
import random


tk = Tk()
canvas = Canvas(tk, width=1920, height=1080, background="grey")
canvas.pack()

def xy(event):
    xm, ym = event.x, event.y

def task():
    w=random.randint(1,1000)
    h=random.randint(1,1000)
    canvas.create_rectangle(w,h,w+150,h+150)
    def callback(event):
        if True:  # not sure why this is here...
            print("clicked2")    
            tk.after_cancel(tk.after_id)    # cancel it
    canvas.bind("<Button-1>",callback)        
    tk.after_id = tk.after(1000,task)
    # ^^^^^^^^^^^ this captures the after ID for later canceling.
tk.after(1000,task)
# no need to capture this ID since there's no way to cancel it yet

tk.mainloop()   

如果你愿意接受建议,有一些方法可以改善你的编码风格……首先,通常把所有的函数放在一起,放在最上面,这样比和变量声明混在一起要整洁得多。

另外,你的 callback() 函数嵌套在 task() 调用里面,这样会导致 canvas.bind() 被重复调用,没必要。如果把这个函数提出来,和其他函数放在同一层级,代码会更整洁、更高效。

还有一些其他的建议,但这里有一个你可能会觉得有用的代码替代版本:

#! /usr/bin/env python
from tkinter import *
import time
import random


def xy(event): # unused...
    xm, ym = event.x, event.y

def task():
    w=random.randint(1,1000)
    h=random.randint(1,1000)
    canvas.create_rectangle(w,h,w+150,h+150)
    tk.after_id = tk.after(1000,task)           

def callback(event):
    print("clicked2")    
    if tk.after_id:
        tk.after_cancel(tk.after_id)
        tk.after_id = None

if __name__ == '__main__':
    tk = Tk()
    tk.after_id = None
    canvas = Canvas(tk, width=1920, height=1080, background="grey")
    canvas.pack()
    canvas.bind("<Button-1>", callback)        
    task() # tk.after(1000,task)

    tk.mainloop()   

另外,顺便提一下,after(0)after_idle() 调用允许你传递参数给被调用的函数。所以,比如说,你可以有一个这样的函数:

def my_after_function(one, two, three='None'):
    print(one, two, three)

然后你可以这样调用:

tk.after(100, my_after_function, 'first', 'second')

这会给你返回:

first second None

你不能传递关键字参数,但可以传递位置参数。

15

好的,你已经快要搞定了。你需要做的是把 tk.after(...) 设置成一个变量,而不是你想要停止的那个重复执行的函数。然后当你想用 after_cancel(...) 的时候,就调用那个变量,这样就能停止所有用这个名字的 after 方法了。

来源: 如何停止 tkinter 的 after 函数?

这样做就可以了:

from Tkinter import *
import time
import random


tk = Tk()
canvas = Canvas(tk, width=1920, height=1080, background="grey")
canvas.pack()

def xy(event):
    xm, ym = event.x, event.y

def task():
    w=random.randint(1,1000)
    h=random.randint(1,1000)
    canvas.create_rectangle(w,h,w+150,h+150)
    def callback(event):
        if True:
            print("clicked2") 
            # 'solve' is used here to stop the after... methods.
            tk.after_cancel(solve)
    canvas.bind("<Button-1>",callback)        
    solve = tk.after(1000,task)
# above and below tk.after is set to 'solve' a variable.          
solve = tk.after(1000,task)

tk.mainloop() 

这样可以停止 after 方法,同时让 tk 窗口继续运行。

另外一种解决方案是使用 tk.destroy()tk.quit()sys.exit() 等等,在回调函数中使用... 这些方法会关闭 tk 窗口。

撰写回答