Tkinter - performing functions over time

I am trying to understand how the tkinter control flow works.

I want to display a right-angled box and make it blink three times. I wrote this code, but it does not work. I think because blink runs before mainloop , and in fact it does not draw anything. If so, how can I change the control flow between blink and mainloop to make it work?

My code is:

 from tkinter import * from time import * def blink(rectangle, canvas): for i in range(3): canvas.itemconfigure(rectangle, fill = "red") sleep(1) canvas.itemconfigure(rectangle, fill = "white") sleep(1) root = Tk() fr = Frame(root) fr.pack() canv = Canvas(fr, height = 100, width = 100) canv.pack() rect = canv.create_rectangle(25, 25, 75, 75, fill = "white") blink(rect, canv) root.mainloop() 
+4
source share
2 answers

Event driven programming requires a different approach to the process code. Your application runs in an endless loop, pulling events from the queue and processing them. To make an animation, all you have to do is place the elements in this queue at the appropriate time.

Tkinter widgets have a method called after , which allows you to schedule the execution of functions after a certain period of time. The first step is to write a function that takes one β€œframe” of your animation. In your case, you define an animation as a transition between two colors. A function that checks the current color, then switches to a different color, all you need is:

 def blink(rect, canvas): current_color = canvas.itemcget(rect, "fill") new_color = "red" if current_color == "white" else "white" canvas.itemconfigure(rect, fill=new_color) 

Now we just need this function to be executed three times with an interval of one second:

 root.after(1000, blink, rect, canv) root.after(2000, blink, rect, canv) root.after(3000, blink, rect, canv) 

When you start your main cycle, after one second the color will change, after a second it will change again, and after a third second it will change again.

This works for your specific need, but it is not a good general solution. A more general solution is to call blink once, and then blink again to call itself after a period of time. blink then should be responsible to know when to stop blinking. You can set a flag or counter to keep track of how many times you blinked. For instance:

 def blink(rect, canvas): ... # call this function again in a second to # blink forever. If you don't want to blink # forever, use some sort of flag or computation # to decide whether to call blink again canvas.after(1000, blink, rect, canvas) 

As a final tip, I recommend that you define your program as a class, and then instantiate this class. This makes it so that you do not need global functions, and you do not need to pass as many arguments. It doesn't really matter for a 20-line program, but it starts to matter if you want to write something significant.

For instance:

 from tkinter import * class MyApp(Tk): def __init__(self): Tk.__init__(self) fr = Frame(self) fr.pack() self.canvas = Canvas(fr, height = 100, width = 100) self.canvas.pack() self.rect = self.canvas.create_rectangle(25, 25, 75, 75, fill = "white") self.do_blink = False start_button = Button(self, text="start blinking", command=self.start_blinking) stop_button = Button(self, text="stop blinking", command=self.stop_blinking) start_button.pack() stop_button.pack() def start_blinking(self): self.do_blink = True self.blink() def stop_blinking(self): self.do_blink = False def blink(self): if self.do_blink: current_color = self.canvas.itemcget(self.rect, "fill") new_color = "red" if current_color == "white" else "white" self.canvas.itemconfigure(self.rect, fill=new_color) self.after(1000, self.blink) if __name__ == "__main__": root = MyApp() root.mainloop() 
+17
source

Each widget has an after function, that is, it can call another function after a certain period of time. So what you want to do is call:

 root.after( 1000, blink ) 

If you want this to be a callback, just call "after" again inside your blink function. The only problem you will encounter is passing arguments to blink - maybe look at using lamda inside the "after" for this.

+3
source

Source: https://habr.com/ru/post/1397207/


All Articles