0

I've created this code that updates a label every second to indicate that something is loading (run the code to see what I mean). I'm using the threading module with tkinter but I feel like there must be a more efficient way to do this.

Here is my code:

from tkinter import *
from time import sleep
import threading

root = Tk()
new_var = StringVar()
new_var.set('Loading')

def change_text():
    array = [".", "..", "...", ""]
    while True:
        for num in range(4):
            sleep(1)
            new_var.set(f"Loading{array[num]}")
            root.update_idletasks()

l = Label(root, textvariable = new_var)
l.pack()

Loading_animation = threading.Thread(target=change_text)
Loading_animation.start()
root.mainloop()

Also, if there isn't a better way to do this how do I prevent the error that I keep receiving whenever I close the root window?

Thank you!

1
  • @IgorS this doesn't address the underlying error. I believe the underlying error is that I haven't rejoined the second thread that I created, but I don't know how to do that since my code is stuck in the "root.mainloop()" at the end. Commented Jul 13, 2018 at 21:31

2 Answers 2

2

Here is a simpler method that doesn't involve threading.

Keep a counter and every second call the function. In the function simply set the text to each item in the list by the counter as an index.

Update: To answer your question in the comments.

This will not get stuck in some loop that stops us from reaching the mainloop() because this code only adds a command to be run on the event list at a regular interval of 1 second. What is actually happening is the after() method will add a new even to run no sooner than 1 second (1000 milliseconds). Because Tkinter is event-driven Tkinter will handle each even in the list as it comes after every mainloop() cycle.

import tkinter as tk

root = tk.Tk()
counter = 0

def change_text():
    global counter
    my_list = [".", "..", "...", ""]
    if counter != 3:
        l.config(text="Loading{}".format(my_list[counter]))
        counter += 1
        root.after(1000, change_text)
    else:
        l.config(text="Loading{}".format(my_list[counter]))
        counter = 0
        root.after(1000, change_text)

l = tk.Label(root, text = "")
l.pack()

change_text()
root.mainloop()
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Mike the code works exactly as I'd like, but there's one thing I don't understand. Shouldn't the code be stuck in the change_text() loop and therefore be unable to execute root.mainloop() to create the window.
2

Here is the same answer as @Mike-SMT, but using the cycle function to make it a lot neater.

import tkinter as tk
from itertools import cycle

root = tk.Tk()
my_list = cycle([".", "..", "...", ""])

def change_text():
    l.config(text="Loading{}".format(next(my_list)))
    root.after(1000, change_text)

l = tk.Label(root)
l.pack()

change_text()
root.mainloop()

4 Comments

I have the same question for you as I did Mike - how is the script able to execute root.mainloop() as shouldn't it be stuck in the infinite change_test() loop?
The mainloop is an infinite loop that constantly checks if an event has happened that it needs to respond to. It also checks a list of scheduled events to see if it's time to do one of those. The after function IS NOT a loop, all it does is add an event to the scheduled events list, in your case it adds "at current_time + 1000 run the change_test function". Then it returns. It does not block like sleep does. When the mainloop sees that the time is greater than the scheduled time, it runs change_text, which of course causes another addition to the event schedule.
TLDR: after is not a loop, but it modifies the mainloop, so we can use it as if it were a loop.
@Novel I like this. I was aware of the itertools however never had a need for them thus far and so it didn't cross my mind.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.