I have been making this downloader app in python using tkinter and tkinter.ttk and added a recent feature in the app for the users to see a log of previous actions done by the program. It works fine, however, I recently discovered a bug.
I have a settings button which creates another Toplevel and then you can manage the default directories and change the switch the mode of the app to light or night mode or a custom theme using three ttk.Radiobuttons but whenever I hit the first two radiobuttons (the first two are responsible for switching the theme from light mode to dark mode and vise versa) my ttk.treeview's width gets added by one for no reason. the strange part is that it does not happen to the third radiobutton, responsible for making a custom mode.
I tried setting a maxsize for the log's toplevel but it is affecting the ttk.treeview itself. Why is this happening? How can I prevent this?
A simulation of my app (this is just a demonstration so I didn't write the entire theme code):
from tkinter import *
from tkinter import colorchooser
from tkinter import ttk
columns = ("Operation", "URL", "File Path", "Status", "Start Date", "End Date")
log_data = [("Download (File)", "https://www.youtube.com", "C:/Users/Mypc/Downloads/youtube.html", "Finished", "2021-03-30 13:15:30", "2021-03-30 13:15:33"),
("Format Fetch", "https://www.youtube.com/watch?v=xxNxqveseyI", "------", "Finished", "2021-03-30 13:15:30", "2021-03-30 13:15:33")]
font_color, bg_color = "black", "white"
root = Tk()
root.configure(bg=bg_color)
root.resizable(False, False)
night_on = IntVar(value=1)
style = ttk.Style()
style.configure("Treeview", rowheight=25, font=('Arial', 10))
style.configure("Treeview.Heading", font=('Arial', 10))
style.configure("TLabel", foreground=font_color, background=bg_color)
style.configure('my.TButton', font=('Helvetica', 20, 'italic'))
style.configure("TRadiobutton", foreground=font_color, background=bg_color, font=('Arial', 10))
font_color_var = StringVar(value=f"Current font color: \t {font_color}")
bg_color_var = StringVar(value=f"Current background color: \t {bg_color}")
log_top = Toplevel(root)
log_top.withdraw()
log_top.resizable(False, False)
log_fr = Frame(log_top)
log_scroll = ttk.Scrollbar(log_fr, orient=VERTICAL)
log_tree = ttk.Treeview(log_fr, selectmode="browse", yscrollcommand=log_scroll.set, height=12, columns=columns)
log_scroll.config(command=log_tree.yview)
def clear_records():
for child in log_tree.get_children():
log_tree.delete(child)
clr_log_btn = ttk.Button(log_top, text="Clear Log", takefocus=False, style="my.TButton", command=clear_records)
log_tree.column("#0", width=0, stretch=NO)
log_tree.column("Operation", width=100, anchor=CENTER)
log_tree.column("URL", width=100, anchor=CENTER)
log_tree.column("File Path", width=100, anchor=CENTER)
log_tree.column("Status", width=80, anchor=CENTER)
log_tree.column("Start Date", width=126, anchor=CENTER)
log_tree.column("End Date", width=126, anchor=CENTER)
for head in columns:
log_tree.heading(head, text=head, anchor=CENTER)
for item_indices, element in enumerate(log_data):
log_tree.insert(parent='', index=0, iid=item_indices, values=element)
log_tree.pack(side=LEFT)
log_scroll.pack(side=RIGHT, fill=Y)
clr_log_btn.pack(side=BOTTOM, fill=X)
log_fr.pack()
log_top.protocol("WM_DELETE_WINDOW", log_top.withdraw)
log_lbl = Label(root, text="Show Log", fg="blue", bg=bg_color, cursor="hand2")
def show_log(event):
if log_top.state() == "withdrawn":
log_top.deiconify()
elif log_top.state() == "normal":
log_top.focus_set()
def settings_win():
settings_top = Toplevel(root, bg=bg_color)
settings_top.resizable(False, False)
def set_night():
global bg_color, font_color
bg_color = "black"
font_color = "white"
settings_top.config(bg=bg_color)
style.configure("TLabel", foreground=font_color, background=bg_color)
style.configure("TRadiobutton", foreground=font_color, background=bg_color)
def set_light():
global bg_color, font_color
bg_color = "white"
font_color = "black"
settings_top.config(bg=bg_color)
style.configure("TLabel", foreground=font_color, background=bg_color)
style.configure("TRadiobutton", foreground=font_color, background=bg_color)
def set_custom():
global font_color, bg_color
color_fr = Toplevel(root, bg=bg_color)
color_fr.resizable(False, False)
current_font_color = ttk.Label(color_fr, textvariable=font_color_var)
current_bg_color = ttk.Label(color_fr, textvariable=bg_color_var)
def change_color(name):
global font_color, bg_color
new_color = colorchooser.askcolor()[1]
if new_color is not None:
if name == "font":
font_color = new_color
font_color_var.set(f"Current font color: \t {font_color}")
style.configure("TLabel", foreground=font_color)
style.configure("TRadiobutton", foreground=font_color)
elif name == "bg":
bg_color = new_color
bg_color_var.set(f"Current background color: \t {bg_color}")
color_fr.config(bg=bg_color)
style.configure("TLabel", background=bg_color)
style.configure("TRadiobutton", background=bg_color)
change_font = ttk.Button(color_fr, text="Change! ", command=lambda: change_color("font"),
takefocus=False)
change_bg = ttk.Button(color_fr, text="Change! ", command=lambda: change_color("bg"), takefocus=False)
current_font_color.grid(row=0, column=0, pady=(10, 0), padx=5)
change_font.grid(row=0, column=1, padx=(0, 7))
current_bg_color.grid(row=1, column=0, pady=(10, 0), padx=5)
change_bg.grid(row=1, column=1, padx=(0, 7))
night_mode = ttk.Radiobutton(settings_top, text="Night Mode", variable=night_on, value=0,
command=set_night, takefocus=False)
light_mode = ttk.Radiobutton(settings_top, text="Light Mode", variable=night_on, value=1,
command=set_light, takefocus=False)
custom_mode = ttk.Radiobutton(settings_top, text="Custom Mode", variable=night_on, value=2,
command=set_custom, takefocus=False)
night_mode.grid(row=0, column=0)
light_mode.grid(row=1, column=0)
custom_mode.grid(row=2, column=0)
log_lbl.bind("<Button-1>", show_log)
log_lbl.pack(side=LEFT, pady=30, padx=30)
settings_btn = ttk.Button(root, text="Open Settings", takefocus=False, command=settings_win)
settings_btn.pack(side=RIGHT, padx=30, pady=30)
root.focus_set()
root.mainloop()