I've written a tkinter script with animation, that works fine on Xubuntu, but when I run it on Mac, the animation doesn't work. Here's a little script that demonstrates the problem:
import tkinter as tk
from time import sleep
root = tk.Tk()
canvas = tk.Canvas(root, height=200, width = 200)
canvas.pack()
this = canvas.create_rectangle(25,25, 75, 75, fill='blue')
that = canvas.create_rectangle(125, 125, 175, 175, fill = 'red')
def blink(event):
global this, that
for _ in range(9):
canvas.itemconfigure(this, fill='red')
canvas.itemconfigure(that, fill = 'blue')
canvas.update_idletasks()
sleep(.4)
this, that = that, this
canvas.bind('<ButtonRelease-1>', blink)
root.mainloop()
This draws a red square and a blue square on a canvas. When the user clicks the canvas, the squares repeatedly switch colors. On Xubuntu, it works as intended.
On Mac, when I click the canvas, I get spinning beach ball, and after a few seconds, we see that squares have switched colors, because they switch colors an odd number of times in the code.
It seems to me that update_idletasks isn't working. Is there some way to fix this? I am running python 3.9.5 with Tk 8.6 on Big Sur.
CodePudding user response:
I found that using update instead of update_idletasks works on both platforms. It's my understanding though, that the latter is much preferred. See the accepted answer to this question for example. This solves my immediate problem, but does anyone know if update_idletasks ever works on the Mac?
CodePudding user response:
I think what you can do is avoid tasks that will block the mainloop, in this case time.sleep(). So your code can be remade by emulating a for loop with after, and I see nothing that stops this general code from running OS independent:
count = 0 # Think of this as the `_` in for _ in range(9)
def blink(event=None):
global this, that, count
if count < 9: # Basically repeats everytime `count` is less than 9, like a for loop
canvas.itemconfigure(this, fill='red')
canvas.itemconfigure(that, fill='blue')
this, that = that, this
count = 1 # Increase count
root.after(400,blink) # Repeat this code block every 400 ms or 0.4 seconds
else:
count = 0 # After it is 9, set it to 0 for the next click to be processed
