简体   繁体   中英

while loop not working in tkinter animation

I am trying to create an animation of a turning wheel and want to have a small delay in a while loop and then update the wheel every time. I have tried both the "after" function in tkinter as well as the "sleep" function in python but it either crashes or finishes the conputation and only shows me the last position without the actual animation as the wheel is turning.

The function I created for the turning wheel:

def turning():
    #initial wheel position
    global position
    pos(position)

    #infinite loop turning the wheel
    while(1):
        root.after(1000, spin)

def spin():
    global position
    global speed
    delspike() #delete current wheel
    position += speed #calculate next position
    if position > 360:
        position -= 360
    pos(position) #draw new wheel

why is this not working?

This code:

while (1):
    root.after(1000, spin)

.. is going to schedule the spin function to run in 1 second. And it's going to do that thousands of times in the blink of an eye. Even though you're asking spin to run in one second, the while loop itself is going to run as fast as it possibly can and never stop. It will schedule hundreds of thousands of spins before the first spin, and then they will all run one after the other since they are all going to try to run in one second.

The correct way to do animation is to have the function schedule itself again in one second:

def spin():
    ...
    root.after(1000, spin)

Then, you call spin exactly once at the start of your program, and it runs indefinitely.

after is used inside the function you are trying to put in a loop:

def f():
    ...
    root.after(1000, f)

(1000 means 1000 milliseconds which is 1 second. This means the program will perform an operation every 1 second. And you can change it to any number you wish.) Also, keep in mind that, using infinite while loop ( while True , while 1 etc.) in Tkinter will make the window . We have discussed this here a lot. You could find this if you had researched on SO .

I have noticed that many beginners 'who don't know better' try to animate tkinter with a while loop. It turns out that this is not a silly idea. I recently figured out how to make this work using asyncio and the new in 3.5 async-await syntax. I also worked out a generic template for a simple app. I happen to like this style more than using after loops. If you have 3.5.2 or 3.6.0a3 installed (or install either), you can run this code and replace my rotator with yours.

import asyncio
import tkinter as tk


class App(tk.Tk):

    def __init__(self, loop, interval=1/120):
        super().__init__()
        self.loop = loop
        self.protocol("WM_DELETE_WINDOW", self.close)
        self.tasks = []
        self.tasks.append(loop.create_task(
                self.rotator(1/60, 1)))
        self.updater(interval)

    async def rotator(self, interval, d_per_int):
        canvas = tk.Canvas(self, height=600, width=600)
        canvas.pack()
        deg = 0
        arc = canvas.create_arc(100, 100, 500, 500,
                                start=0, extent=deg, fill='blue')
        while True:
            await asyncio.sleep(interval)
            deg = (deg + d_per_int) % 360
            canvas.itemconfigure(arc, extent=deg)

    def updater(self, interval):
        self.update()
        self.loop.call_later(interval, self.updater, interval)

    def close(self):
        for task in self.tasks:
            task.cancel()
        self.loop.stop()
        self.destroy()


loop = asyncio.get_event_loop()
app = App(loop)
loop.run_forever()
loop.close()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM