Home > Software design >  why does my code not create "Loading..." text in Tkinter?
why does my code not create "Loading..." text in Tkinter?

Time:08-17

I am trying to create a 'loading' screen text where each second I want to change the tkinter label as follows:

Loading.
Loading..
Loading...

With each string replacing the previous one. My code kinda works, but it only displays the first and last label:

import sys
from tkinter import *
import time

root = Tk()
root.wm_title('test')
root.geometry("500x300")

my_label = Label(root, text="Loading.")
my_label.grid(row=0, column=0, columnspan=2)
time.sleep(1)
 
def loading():
    x = 1
    while x < 10: 
        my_label['text'] = "Loading." 
        time.sleep(1)
        my_label['text'] ="Loading.."
        time.sleep(1)
        my_label['text'] ="Loading..."
        #frame.pack(fill="both", expand=True)
        x = x 1
Button(root, text = """test
    """,command = loading).grid(row=2, column=1, columnspan=2)
root.mainloop()

How can I get this to update the label each second?

CodePudding user response:

Seeing that an issue in tkinter with sleep() has been asked many many times on here I want to point out that a little more digging before posting a question would have solved your problem.

That said I have refactored your code to show a working example using after() since that is the correct method to use in Tkinter.

Note that I have changed your import as well for best practice. Since list are one of my favorite solutions to anything related to Tkinter I will use a list to build a looping function that will terminate after a counter has reached zero.

In your case you can change the logic any way that makes since for your tool but this is simple to show how the logic would work until some value it met.

The after method is used to set a time to call the function and the lambda is used to prevent the call being instant as well as providing the values for the next loop.

We also add in a change state for the button so you do not mistakenly press the button again causing the function to be called again with 2 counters.

By writing it this way with after you wont see your application freeze.

import tkinter as tk

root = tk.Tk()
root.wm_title('test')
root.geometry("500x300")

my_label = tk.Label(root, text="Waiting")
my_label.grid(row=0, column=0, columnspan=2)

def loading(counter, ndex=0):
    my_label['state'] = 'disabled'
    load = ["", ".", "..", "..."]
    print(ndex, len(load)-1)
    if counter != 0:
        counter -= 1
        if ndex >= len(load)-1:
            my_label['text'] = f'Loading{load[ndex]}'
            ndex = 0
        else:
            my_label['text'] = f'Loading{load[ndex]}'
            ndex  = 1
        root.after(1000, lambda c=counter, n=ndex: loading(c, n))
    else:
        my_label['text'] = 'Done'
        my_label['state'] = 'normal'


tk.Button(root, text="""test""", command=lambda: loading(10)).grid(row=2, column=1, columnspan=2)
root.mainloop()

Result: enter image description here

CodePudding user response:

You can also use thread aproach that your main thread will not freeze. I used ThreadPoolExecutor class instead of Thread class, because we don't need to create a new thread when every button press. I didn't do any prevention that button can be pressed two times mistakenly, you can modify this according to your situation

import sys
from tkinter import *
import time
from concurrent.futures import ThreadPoolExecutor

root = Tk()
root.wm_title('test')
root.geometry("500x300")

my_label = Label(root, text="Loading.")
my_label.grid(row=0, column=0, columnspan=2)
time.sleep(1)

executor=ThreadPoolExecutor(max_workers=1)

def loading():

    def _loading():
        x = 1
        while x < 10: 
            my_label['text'] = "loading." 
            time.sleep(1)
            my_label['text'] ="loading.."
            time.sleep(1)
            my_label['text'] ="loading..."
            time.sleep(1)
            #frame.pack(fill="both", expand=True)
            x = x 1
    executor.submit(_loading)

Button(root, text = "test",command = loading).grid(row=2, column=1, columnspan=2)
root.mainloop()
  • Related