Python中的Tkinter after_cancel
附上的代码是用“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 个回答
当你使用 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
你不能传递关键字参数,但可以传递位置参数。
好的,你已经快要搞定了。你需要做的是把 tk.after(...)
设置成一个变量,而不是你想要停止的那个重复执行的函数。然后当你想用 after_cancel(...)
的时候,就调用那个变量,这样就能停止所有用这个名字的 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 窗口。