Sir-Sorg
Sir-Sorg

Reputation: 315

tkinter wont refresh widget

I want to write a program that has only a button, and after pressing that, program will start making 3 labels and then change the color of each one every 1 second only once.

It looks very simple and I wrote the following code :

import tkinter as tk
from time import sleep


def function():
    mylist=list()
    for i in range(3):
        new_label=tk.Label(window,text='* * *',bg='yellow')
        new_label.pack()
        mylist.append(new_label)
    print('First state finished')
    sleep(1)
    for label in mylist:
        label.config(bg='red')
        print('one label changed')
        sleep(1)
    
window = tk.Tk()
window.geometry('300x300')

btn=tk.Button(window,text='start',command=function)
btn.pack()

tk.mainloop()

First the app is look like this (that is OK):

enter image description here

Second its look like this (its not OK because its print on the terminal but didn't update the lable) :

enter image description here

Third its look like this (at the end the app must be look like this and its OK) :

enter image description here

But I need to see the changes in the moment and use sleep for that reason. Thank you All.

Upvotes: 1

Views: 807

Answers (3)

Meritor
Meritor

Reputation: 166

I would recommend to use .after(delay, callback) method of the tkinter to set the colour.

Hope this is what you want.

import tkinter as tk


def start():
    global mylist
    mylist = list()
    for i in range(3):
        new_label = tk.Label(window, text='* * *', bg='yellow')
        new_label.pack()
        mylist.append(new_label)

    delay = 1000 # delay in seconds
    for label in mylist:
        # Additional delay so that next color change 
        # is scheduled after previous label color change
        delay += 1000 
        schedule_color_change(delay, label)


def schedule_color_change(delay, label):
    print("schedule color change for:", label)
    label.after(delay, set_color, label)



def set_color(label):
    print("setting color of:", label)
    label.config(bg="red")


window = tk.Tk()
window.geometry('300x300')
btn = tk.Button(window, text='start', command=start)
btn.pack()

tk.mainloop()

Upvotes: 2

Sir-Sorg
Sir-Sorg

Reputation: 315

As Meritor guided me, I followed the after method and wrote the following recursive code without sleep :

import tkinter as tk


def recursive(i, listt):
    lbl = listt[i]
    if i >= 0:
        lbl.config(bg='orange')
        i -= 1
        lbl.after(500, recursive, i, listt)


def function():
    mylist = list()
    for i in range(3):
        new_label = tk.Label(window, text='* * *', bg='yellow')
        new_label.pack()
        mylist.append(new_label)
    print('all label created')
    # 2 is length of list minus 1
    recursive(2, mylist)


window = tk.Tk()
window.geometry('300x300')

tk.Button(window, text='start', command=function).pack()

tk.mainloop()

Most likely my code is not optimized because it uses recursive and if you know anything better please tell me

Upvotes: 0

Erling Olsen
Erling Olsen

Reputation: 740

Problem

The problem is your sleep(1), because it's a function that suspends the execution of the current thread for a set number of seconds, so it's like there is a stop to the whole script

Solution

The solution is to instantiate Thread with a target function, call start(), and let it start working. So you have to use timer which is included in the threading, then a timer from the threading module (import threading)

Inside the first "for" loop, remove your sleep(1) and write for example Time_Start_Here = threading.Timer (2, function_2) and then of course Time_Start_Here.start() to start.

start_time=threading.Timer(1,function_2)
start_time.start()

Instead you have to remove the second "for" loop and write what's inside ... inside the new function that will be called. Next you need to create the function

def function_2():
    for label in mylist:
        label.config(bg='red')
        label.pack()
        print('one label changed')

Upvotes: 1

Related Questions