Tkinter function "root.after()" doesn't run the function specified - python

When I use the Tkinter function "root.after()" it only runs the function specified once. In my case this is the "move" function, in the line second from the bottom of my code. Overall, I'm trying to just do a basic animation to an oval.
I'm running Python 3.7.1.
from tkinter import *
class shape:
def __init__(self, canvas):
self.canvas = canvas
self.EdgeThickness = 1
self.color="#ffffff"
def animation(self,xposgiven):
self.shape = self.canvas.create_oval(xposgiven,250,250,400,fill=self.color,width=self.EdgeThickness)
print('runninganimation')
root=Tk()
c=Canvas(root, width=1000, height=500)
c.pack()
c.configure(background="#000000")
s=shape(c)
s.xpos=5
def move():
print('runningmove')
s.xpos+=5
s.animation(s.xpos)
root.after(100,move)
root.mainloop()
I expected that the function move() would run every 100 ms but it only runs once. I thought the function root.after(time,func), ran the function 'func' every 'time' ms. But it doesn't appear to be doing this in my code. It only runs once.

after schedules a job to run exactly once. If you want it to run every 100ms, a common strategy is to have your function call after with itself as an argument before returning.
> import tkinter
> help(tkinter.Tk.after)
after(self, ms, func=None, *args)
Call function once after given time.
MS specifies the time in milliseconds. FUNC gives the
function which shall be called. Additional parameters
are given as parameters to the function call. Return
identifier to cancel scheduling with after_cancel.

Related

Python recursive limit and how to keep monitoring something

I am working on a python tkinter program that monitoring computer temperature, and I want it to update the temperature value after a fixed time.
the following function is what I used to do that:
def update():
get_temp()#the function that get the computer temperature value, like cpu temperature.
...
def upd():
update()
time.sleep(0.3)
upd()#recursive call function.
upd()
but this way will hit the recursive limit, so the program will stops after a period of time.
I want it to keep updating the value, what should I do?
I don't know if I change it to after() it will be better or not.
but if I use after(), the tkinter window will freeze a while, so I don't want to use it.
Thank you.
It needs loop.
It should be:
def update():
get_temp()#the function that get the computer temperature value, like cpu temperature.
...
def upd():
while True:#needs a while true here and don't call upd() in this function.
update()
time.sleep(0.3)
upd()#this upd() is outside the function upd(),it used to start the function.
Thanks to everyone who helped me.
Recursion is inadequate in this use-case, use a loop instead.
Tkinter in particular has got a method which allows you to execute a function in an interval without disrupting the GUI's event loop.
Quick example:
from tkinter import *
root = Tk()
INTERVAL = 1000 # in milliseconds
def get_temp()
# ...
root.after(INTERVAL, get_temp)
get_temp()
root.mainloop()

.after not functioning as I would have expected

I am using .after to invoke a function which moves an image inside my GUI. When i run the code and call the function "myFunc" the image does move, but it happens instantaneously. It should gradually move across the screen. I am unsure as to why this is happening.
def movRight():
global img
global imgx
canvas.move(imgx,20,0)
return
def myFunc():
moveController(1,20)
return
def moveController(extruder, position):
global e1current
global e2current
global e3current
global e4current
if extruder == 1:
while position > e1current:
print("moving")
e1current+=1
main.after(500,movRight)
return
.after does not block execution of the loop. It's more like it spawns a seperate thread which begins execution after 500 (in your case) milliseconds. In other words, .after does not block the loop for 500ms, call movRight and then continue the loop. Take a look at this example:
import tkinter as tk
def print_hi():
print("hi")
root = tk.Tk()
for i in range(5):
root.after(1000, print_hi)
print("loop done")
root.mainloop()
This outputs "loop done", and then, approximately one second later, it prints "hi" five times.
Consider this code:
while position > e1current:
main.after(500,movRight)
It is the same as if you had written it this way:
main.after(500,movRight)
main.after(500,movRight)
main.after(500,movRight)
...
Since calling main.after(500,movRight) probably executes in a millisecond or so, you are queueing up hundreds or thousands of calls that will all execute 500ms in the future.
In other words, it's no differen than if you had done this:
def something():
movRight()
movRight()
movRight()
...
main.after(500,something)
The proper way to do an animation is to create a function that does one frame of animation, and then reschedules itself.
For example:
def moveController():
movRight()
e1current+=1
if position > e1current:
main.after(500, moveController)

How to use the after method to make a callback run periodically?

With tkinter, how can I use the after method to make a function run periodically?
For instance, I have a speak function that only prints something in the console:
def speak():
print("Hello, world!")
How can I use the after method to call the speak function every second?
Note: the following code is written and tested in Python 3.5. Minor changes might be needed, for instance, when calling super.
The documentation describes the Widget.after method as follows:
after(delay_ms, callback=None, *args)
Registers an alarm callback that is called after a given time.
Scheduling a function
The after method is primarily used to schedule a function call after a given delay. For instance, the following code schedules a call to a function after one second:
import tkinter as tk
def speak():
print("Hello world!")
root = tk.Tk()
root.after(1000, speak)
# Output
Hello world!
Making a function run periodically
In order to make a function run periodically, one can make it call itself at the end of its own body. However, after is a method from the Widget class, so a widget is needed. Therefore, the best choice is generally to put the scheduled function inside of a class extending Widget.
The following code prints "Hello world!" every other second in the console.
import tkinter as tk
class Foo(tk.Tk):
def periodically_speak(self):
print("Hello world!")
self.after(2000, self.periodically_speak)
foo = Foo()
foo.periodically_speak()
Using parameters
One might want to pass parameters to a method that runs periodically. For this purpose, the after method unpacks every parameter after the callback as the parameters to pass to the callback. For instance, root.after(1000, foo, a, b, c) will schedule a call to foo(a, b, c). The following example shows a use of this feature to determine the behaviour of the function.
import tkinter as tk
class Foo(tk.Tk):
def periodically_speak(self, text):
print(text)
self.after(2000, self.periodically_speak, text)
foo = Foo()
foo.periodically_speak("Good night world!")
Canceling a call
The after methods returns a string, that corresponds to the call's id. It can be passed to the after_cancel method, in order to cancel a call that was scheduled.
The following example will start printing "Hello world!" every second, but will stop when pressing the button.
import tkinter as tk
class Foo(tk.Tk):
def __init__(self):
super().__init__()
self.callId = None
self.button = tk.Button(self, text="Stop", command=self.stop)
self.button.pack()
def periodically_speak(self):
print("Hello world!")
self.callId = self.after(2000, self.periodically_speak)
def stop(self):
if self.callId is not None:
self.after_cancel(self.callId)
foo = Foo()
foo.periodically_speak()
Side notes
The following points should be kept in mind.
The after method does not guarantee that the callback will be called *exactly* after the given delay, but *at least* after it. As a consequence, after should not be used where precision is required.
It might be tempting to use time.sleep in order to schedule or periodically run a function. This must be avoided when working on a GUI, because `sleep` will pause the current thread, which most of the time is the main thread. For example, this could halt the refresh of the widgets, the program would stop responding.

Simple animation with Tkinter Python

I've searched for a simple animation code with Tkinter but I've found very different examples and I can't understand the correct way to write an animation.
Here my working code to display a simple moving circle:
import tkinter as tk
import time
root=tk.Tk()
canvas=tk.Canvas(root,width=400,height=400)
canvas.pack()
circle=canvas.create_oval(50,50,80,80,outline="white",fill="blue")
def redraw():
canvas.after(100,redraw)
canvas.move(circle,5,5)
canvas.update()
canvas.after(100,redraw)
root.mainloop()
In this code I can't correctly understand: how the after method works, where correctly put the update and the move method (before after method ?), is there another way to write an animation code? may you post me another example and comment the code please?
Thanks :)
Calling update
You should not call canvas.update(). As a general rule of thumb you should never call update. For a short essay on why, see this essay written by one of the original developers of the underlying tcl interpreter.
If you take out the call to canvas.update(), you have the proper way to do animation in a tkinter program.
Calling after to start the animation
You don't need to call after immediately before calling root.mainloop(). This works just as well:
...
redraw()
root.mainloop()
The choice to use or not use after in this specific case is dependent on if you want the animation to start immediately (possibly even before the widget is visible) or if you want it to happen after a short delay (possibly after the widget is made visible)
How after works
mainloop is nothing more than an infinite loop that checks the event queue for events. When it finds an event, it pops it off of the list and processes it. after is nothing more than making a request that says "in 100 ms, please add a new event to the queue". When the time limit expires, an event is added to the queue that says, in effect, "run this command". The next time the loop checks for events, it sees this event, pulls it off of the queue, and runs the command.
When you call after from within a method that itself was called by after, you're saying in effect "wait 100ms and do it again", creating an infinite loop. If you put the call to after before moving the object, you're saying "every 100ms run this function". If you put it after you're saying "run this function 100 ms after the last time it was run". The difference is very subtle and usually not perceptible unless your function takes a long time to run.
my code is:
from tkinter import *
import time
tk = Tk()
płótno = Canvas(tk, width=500, height=500)
płótno.pack()
płótno.create_polygon(10,10,10,70,70,10,fill="blue",outline="black")
for x in range(0,51):
płótno.move(1,5,0)
płótno.update()
rest(0.05)
płótno means canvas

Tkinter only calls after_idle once

I am new to Tkinter, so I apologize if this is easy, but I have search for a couple of hours and can't figure it out. What I want to do is after the mainloop is idle, I always want to call the function checkForGroupUpdates(). When I run the code below, it only runs once. I can't figure out to have it run every time the mainloop is idle. I appreciate the help.
from Tkinter import *
import random
class Network(Frame):
""" Implements a stop watch frame widget. """
def __init__(self, parent=None, **kw):
Frame.__init__(self, parent, kw)
self.makeWidgets()
def makeWidgets(self):
""" Make the time label. """
self._canvas = Canvas(self, width=600, height=400)
self._canvas.pack()
def checkForGroupUpdates(self):
print "checking"
h=0
this=10
while this>.0001:
this=random.random()
print h
h=h+1
print "checked"
def main():
root = Tk()
nw = Network(root)
nw.pack(side=TOP)
root.after_idle(nw.checkForGroupUpdates)
root.mainloop()
if __name__ == '__main__':
main()
#user1763510, notice that in Bryan Oakley's answer, he has checkForGroupUpdates call self.after again. This is because self.after only does a single call, so getting repeated calls requires having it call itself within the function that gets called by the first call. This way, it keeps repeatedly calling itself.
The same goes for the after_idle() function. You have to have checkForGroupUpdates call after_idle() again at the bottom.
Here is the documentation for after, after_idle, etc. There is even a little example in the after description, which makes it all clear.
Documentation: http://effbot.org/tkinterbook/widget.htm
Example from link above, under the afterdescription:
#Method 1
class App:
def __init__(self, master):
self.master = master
self.poll() # start polling
def poll(self):
... do something ...
self.master.after(100, self.poll)
To use after_idle instead, it would look like this:
#Method 2
class App:
def __init__(self, master):
self.master = master
self.poll() # start polling
def poll(self):
... do something ...
self.master.update_idletasks()
self.master.after_idle(self.poll)
Notice the addition of the self.master.update_idletasks() line. This draws the GUI and handles button presses and things. Otherwise, after_idle() will suck up all resources and not let the GUI self-update properly in the mainloop().
An alternative to using
self.master.update_idletasks()
self.master.after_idle(self.poll)
is to use:
#Method 3
self.master.update_idletasks()
self.master.after(0, self.poll)
Using self.master.after(0, self.poll) is my preferred technique, as it allows me to easily change the 0 to something else if I decide I don't need to run self.poll constantly. By increasing the delay time to at least 1 ms, you no longer need to call self.master.update_idletasks() at all. So, this works too:
#Method 4
self.master.after(1, self.poll)
Also notice that for all examples above, calling self.poll() in the __init__ function is what kicks it all off, and storing master into self.master is necessary simply so that inside poll you can call the after or after_idle function via self.master.after_idle, for example.
Q: Is this stable/does it work?
A: I ran a test code using Method 3 just above for ~21 hrs, and it ran stably the whole time, allowing the GUI to be usable and all.
Q: What is the speed comparison for each method above?
A:
Method 1: (I didn't speed test it)
Method 2: ~0.44 ms/iteration
Method 3: ~0.44 ms/iteration
Method 4: ~1.61 ms/iteration
Q: Which is my preferred method?
A: Method 3 or 4.
Instead of calling the function all the time when the app is idle, you should just call it once every fraction of a second. For example, if you want to check 10 times every second you would do something like this:
def checkForGroupUpdates(self):
<do whatever you want>
self.after(100, self.checkForGroupUpdates)
Once you call that function once, it will arrange for itself to be called again in 100ms. This will continue until the program exits. If the program goes "non-idle" (ie: while responding to a button click), this function will pause since tkinter is single-threaded. Once the program goes idle again, the check will continue.

Categories

Resources