The attached code randomly draws squares on the canvas using the "after" method. I expected to stop the drawing when I clicked the mouse button using the "after_cancel" method, bu it continues to draw..
Here is the code.
#! /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()
config is used to access an object's attributes after its initialisation. For example, here, you define. l = Label(root, bg="ivory", fg="darkgreen") but then you want to set its text attribute, so you use config : l.
window.mainloop() tells Python to run the Tkinter event loop. This method listens for events, such as button clicks or keypresses, and blocks any code that comes after it from running until you close the window where you called the method.
The tkinter. dnd module provides drag-and-drop support for objects within a single application, within the same window or between windows. To enable an object to be dragged, you must create an event binding for it that starts the drag-and-drop process.
Alright, so you were close to getting it to work, what you need to do is set tk.after(...)
to a variable, not the recurring function you want to stop, then when you want to use after_cancel(...)
, you call that variable and it will stop all after methods with that name
source: How do I stop tkinter after function?
This will do it:
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()
This will stop the after methods and leave a tk window running.
Another solution this would be to use tk.destroy()
, or tk.quit()
or sys.exit()
or similar in the callback function... These will kill the tk window though.
When you schedule an after()
or an after_idle()
task, the call returns an ID to identify the pending action, so that you can cancel it. So grab the return value and then use it later.
Minimum changes to your code (with comments) look like this:
#! /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()
There are some things you can do to improve your coding, if you're open to suggestions... First, it's usually cleanest to keep all funcs together, up at the top, rather than mixing them in with variable declarations, etc.
Also, you have the callback()
func nestled inside the task()
call, which causes the canvas.bind()
call to happen over and over again needlessly. If you pull that func out and make it at the same level as the other funcs, the code is cleaner and more efficient.
There are a few other things, too, that I would recommend. But here's an alternate version of your code, which you may or may not find helpful:
#! /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()
Also, as a side note, the after(0
and after_idle()
calls allow you to pass arguments to be passed to the called function. So, for example, you could have a function:
def my_after_function(one, two, three='None'):
print(one, two, three)
and you could call:
tk.after(100, my_after_function, 'first', 'second')
which would give you:
first second None
You can't pass kwargs, but you can pass positional args.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With