Tkinter: How to Use After Method

tkinter: how to use after method

You need to give a function to be called after the time delay as the second argument to after:

after(delay_ms, callback=None, *args)

Registers an alarm callback that is called after a given time.

So what you really want to do is this:

tiles_letter = ['a', 'b', 'c', 'd', 'e']

def add_letter():
rand = random.choice(tiles_letter)
tile_frame = Label(frame, text=rand)
tile_frame.pack()
root.after(500, add_letter)
tiles_letter.remove(rand) # remove that tile from list of tiles


root.after(0, add_letter) # add_letter will run as soon as the mainloop starts.
root.mainloop()

You also need to schedule the function to be called again by repeating the call to after inside the callback function, since after only executes the given function once. This is also noted in the documentation:

The callback is only called once for each call to this method. To keep
calling the callback, you need to reregister the callback inside
itself

Note that your example will throw an exception as soon as you've exhausted all the entries in tiles_letter, so you need to change your logic to handle that case whichever way you want. The simplest thing would be to add a check at the beginning of add_letter to make sure the list isn't empty, and just return if it is:

def add_letter():
if not tiles_letter:
return
rand = random.choice(tiles_letter)
tile_frame = Label(frame, text=rand)
tile_frame.pack()
root.after(500, add_letter)
tiles_letter.remove(rand) # remove that tile from list of tiles

Live-Demo: repl.it

How to use Tkinter after() method?

Don't use time.sleep() at all in Tkinter applications. Have the callback schedule a call to itself with after().

def command_Print(counter=0):
Labelvar.set(counter)
if counter < 10:
root.after(1000, lambda: command_Print(counter+1))

Also, range(0, 10, 1) is just range(10). There's no need to repeat the defaults.

How to use Tkinter after method multiple times or is there alternative method for that?

I added a key_pressed function and now it handles multiple key presses you can see it yourself on the console.

from tkinter import *
from PIL import Image, ImageTk
import cv2

root = Tk()

label =Label(root)
label.grid(row=0, column=0)
cap= cv2.VideoCapture(0, cv2.CAP_DSHOW)

def show_frames():
cv2image= cv2.cvtColor(cap.read()[1],cv2.COLOR_BGR2RGB)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image = img)
label.imgtk = imgtk
label.configure(image=imgtk)
label.after(20, show_frames)

def key_pressed(event):
method()

def method():
print("test")

show_frames()
root.bind("<Key>", key_pressed)
root.mainloop()

Python tkinter after method not working as expected

After the first 2 seconds the label starts displaying humongous numbers

Keep in mind, while True, is an infinite loop, you are making infinite calls to root.after() means alot of events are being scheduled to be called after 1 second. Better way to do this is to remove your while and move it all inside your function.

from tkinter import *

root = Tk()

def doSomething():
x = int(l["text"])
l["text"] = x + 1
if int(l["text"]) < 5: # Only repeat this function as long as this condition is met
l.after(1000, doSomething)

root.geometry("300x300")
l = Label(root, text="0")
l.pack()

doSomething()

root.mainloop()

Though the best way to write the function would be to create a variable and increase the value of that variable inside the function and then show it out:

from tkinter import *

root = Tk()

count = 0 # Initial value
def doSomething():
global count # Also can avoid global by using parameters
count += 1 # Increase it by 1
l['text'] = count # Change text
if count < 5:
l.after(1000, doSomething)

root.geometry("300x300")
l = Label(root, text=count)
l.pack()

doSomething() # If you want a delay to call the function initially, then root.after(1000,doSomething)

root.mainloop()

This way you can reduce the complexity of your code too and make use of the variable effectively and avoid nasty type castings ;)

Using Tkinter, Threading and After method

You main problem is that after() and Thread() similar to command= needs only function's name without () and it will later use () to execute it.

Other problem is that after() doesn't stop code but it only send information to mainloop to execute function after 2000ms and later Python runs at once next line after after(). You have to add Label in fucntion executed by after()

def gogogo():
tk.Label(root, text='HELLO WORLD').pack()
print('Please wait 2 seconds')
root.after(2000, next_message) # function's name without ()

def next_message():
tk.Label(root, text='You are inside my world').pack()

# from tkinter import * # PEP8: `import *` is not preferred
import tkinter as tk
import threading
import time

class Make:

def __init__(self, num):
self.num = num.get()

text = 'HELLO WORLD WILL PRINT: {} times, press go to confirm'.format(self.num)
tk.Label(root, text=text).pack()

tk.Button(root, text='go', command=self.starting).pack()

def starting(self):
for count in range(self.num):
t = threading.Thread(target=gogogo) # function's name without ()
t.start()

def gogogo():
tk.Label(root, text='HELLO WORLD').pack()
print('Please wait 2 seconds')
root.after(2000, next_message) # function's name without ()

def next_message():
tk.Label(root, text='You are inside my world').pack()

# --- main ---

root = tk.Tk()
root.geometry('700x700')

tk.Label(root, text='How many times should I print: HELLO WORLD').pack()

num = tk.IntVar()
tk.Entry(root, textvariable=num).pack()

tk.Button(root, text='Start', command=lambda:Make(num)).pack()

root.mainloop()

EDIT: Because gogogo() runs in separated thread so you can also use time.sleep() because it doesn't block mainloop() in main thread

def gogogo():
tk.Label(root, text='HELLO WORLD').pack()
print('Please wait 2 seconds')
time.sleep(2)
tk.Label(root, text='You are inside my world').pack()

Tkinter widget after method

If your goal is to loop through the list, displaying each line at 1500ms intervals, the easiest way is to have a function that does one iteration and then repeats itself using after

Something like this, perhaps:

def read_ref(self, lines):
# remove one item from the list
line = lines.pop(0)

# insert it
self.ref_text.insert("end", line + "\n")

# run again in 1500ms if there's still more work to do
if lines:
self.after(1500, self.read_ref, lines)

Then, call this function exactly once to start the process:

self.read_ref(self, self.reference)

If you want to be able to stop it, you can check for a flag in your function:

def read_ref(self):
...
if self.reference and not self.stop:
self.after(1500, self.read_ref)

The above code slowly removes items from self.reference. If you don't want that to happen, pass a copy of self.reference when you start so that the function will remove items from a copy of the original data.

self.read_ref(self, self.reference[:])


Related Topics



Leave a reply



Submit