i-LJ
i-LJ

Reputation: 3

tkinter (ttk) leaving artifacts when updating widget

When updating a value in a widget using tkinter there's traces of the old widget's state on screen, and I was wondering if there's any fix to this. I'm assuming tkinter just re-draws whatever it needs to, not updating the old widget/old widget's state. Minimal reproducible example follows.

from tkinter import *
from tkinter.ttk import *

window=Tk()
ttk.Style().configure("Info.TLabel",foreground="white", background="#1e2124",relief="sunken")

def update_label(currvar):
    current_var_levels = current_var.get()
    var_label=ttk.Label(window,text=f'{current_var_levels}'+'%',style="Info.TLabel")
    var_label.grid(row=0,column=1)

current_var=IntVar()
scale_bar = ttk.Scale(window, from_=0, to=100, length=200, variable=current_var, command=update_label)
current_var.set(100)
scale_bar.grid(row=0,column=0)

# Initialize scale's display label. Works without this, but it shows everything from the start
var_label=ttk.Label(window,text=f'{current_var.get()}'+'%',style="Info.TLabel")
var_label.grid(row=0,column=1)

window.mainloop()

I've tried conf_label.grid_forget() and then re-grid-ing it on the update function, but that didn't work. I've also tried making 2 separate labels (global/local) ones, although I may be thinking too linearly. I think I'm looking for a "screen refresh" but I don't know what that looks like. This also happens without the fore/background colors, or even without a relief altogether; removing the Style config and the style assignment from the label(s) will still leave a small part of % visible, once part if the current value is a double digit number, and 2 parts if it's a single digit number. A viable botch would be to force length by padding with whitespace, but this is a good learning opportunity. Thanks for reading!

Upvotes: 0

Views: 181

Answers (1)

furas
furas

Reputation: 142651

tkinter doesn't remove old Label but it puts new Label above old Label - so you have two labels in one place.

You could use grid_forget() or destroy() to remove old Label.

But this needs to keep var_label as global variable.

And this can make flickering widget. It shows gray background after removing old Label and before adding new Label.

def update_label(value):
    global var_label   # inform function that new label has to be assigned to global variable `var_label`, instead of local `var_label`

    current_var_levels = current_var.get()
    #current_var_levels = int(float(currvar))

    var_label.destroy()     # remove old label from screen and from memory
    #var_label.grid_forget()  # remove old label from screen but not from memory
    
    var_label = ttk.Label(window, text=f'{current_var_levels}%', style="Info.TLabel")
    var_label.grid(row=0, column=1)

Or you should create Label only once and later replace text in existing Label

def update_label(currvar):
    current_var_levels = current_var.get()
    #current_var_levels = int(float(currvar))

    var_label.config(text=f'{current_var_levels}%')  # replace text
    #var_label['text'] = f'{current_var_levels}%'    # replace text

Full working code which I used for tests:

import tkinter as tk       # `PEP8: `import *` is not preferred
import tkinter.ttk as ttk  # `PEP8: `import *` is not preferred

# --- functions ---  # PEP8: all functions before main code

def update_label_version_1(value):
    global var_label   # inform function that new label has to be assigned to global variable `var_label`, instead of local `var_label`

    current_var_levels = current_var.get()
    #current_var_levels = int(float(currvar))

    var_label.destroy()     # remove old label from screen and from memory
    #var_label.grid_forget()  # remove old label from screen but not from memory

    var_label = ttk.Label(window, text=f'{current_var_levels}%', style="Info.TLabel")
    var_label.grid(row=0, column=1)
    
def update_label_version_2(value):
    current_var_levels = current_var.get()
    #current_var_levels = int(float(value))
        
    var_label.config(text=f'{current_var_levels}%')  # replace text
    #var_label['text'] = f'{current_var_levels}%'    # replace text
    
# --- main ---

window = tk.Tk()

ttk.Style().configure("Info.TLabel", foreground="white", background="#1e2124", relief="sunken")

current_var = tk.IntVar()

#update_label = update_label_version_1   # test version 1
update_label = update_label_version_2   # test version 2

scale_bar = ttk.Scale(window, from_=0, to=100, length=200, variable=current_var, command=update_label)
current_var.set(100)
scale_bar.grid(row=0,column=0)

# Initialize scale's display label. Works without this, but it shows everything from the start
var_label = ttk.Label(window, text=f'{current_var.get()}%', style="Info.TLabel")
var_label.grid(row=0, column=1)

window.mainloop()

PEP 8 -- Style Guide for Python Code

Upvotes: 0

Related Questions