Open JeanSolagnier opened 1 year ago
I can confirm the issue (Windows 10, Python 3.11, CustomTkinter 5.1.2).
(In my opinion, it would definitively help to reduce the code to just one secondary top-level window that opens behind the primary window.)
@TomSchimansky Duplicate of (part of) #1198.
Janky way of forcing it to the top
import customtkinter
app = customtkinter.CTk()
app.attributes('-fullscreen', True)
def open_window():
new_window=customtkinter.CTkToplevel()
new_window.attributes('-fullscreen', True)
label=customtkinter.CTkLabel(new_window, text="New Window")
label.pack()
new_window.after(20, new_window.lift)
button = customtkinter.CTkButton(app, text="Open New Window", command=open_window)
button.pack()
app.mainloop()
@JeanSolagnier Using the example you posted
import customtkinter
class ToplevelWindow1(customtkinter.CTkToplevel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.attributes('-fullscreen', True)
self.label = customtkinter.CTkLabel(self, text="ToplevelWindow1")
self.label.pack(padx=20, pady=20)
self.button_3 = customtkinter.CTkButton(self, text="open toplevel3", command=self.open_toplevel3)
self.button_3.pack(side="top", padx=20, pady=20)
self.toplevel_window3 = None
def open_toplevel3(self):
if self.toplevel_window3 is None or not self.toplevel_window3.winfo_exists():
self.toplevel_window3 = ToplevelWindow3(self) # create window if its None or destroyed
self.toplevel_window3.after(20, self.toplevel_window3.lift)
else:
self.toplevel_window3.focus() # if window exists focus it
class ToplevelWindow2(customtkinter.CTkToplevel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.attributes('-fullscreen', True)
self.label = customtkinter.CTkLabel(self, text="ToplevelWindow2")
self.label.pack(padx=20, pady=20)
class ToplevelWindow3(customtkinter.CTkToplevel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.attributes('-fullscreen', True)
#self.attributes("-topmost", True)
self.label = customtkinter.CTkLabel(self, text="ToplevelWindow3")
self.label.pack(padx=20, pady=20)
self.button_4 = customtkinter.CTkButton(self, text="back", command=self.destroy)
self.button_4.pack(side="top", padx=20, pady=20)
class App(customtkinter.CTk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.attributes('-fullscreen', True)
self.button_1 = customtkinter.CTkButton(self, text="open toplevel1", command=self.open_toplevel1)
self.button_1.pack(side="top", padx=20, pady=20)
self.toplevel_window1 = None
self.button_2 = customtkinter.CTkButton(self, text="open toplevel2", command=self.open_toplevel2)
self.button_2.pack(side="top", padx=20, pady=20)
self.toplevel_window2 = None
def open_toplevel1(self):
if self.toplevel_window1 is None or not self.toplevel_window1.winfo_exists():
self.toplevel_window1 = ToplevelWindow1(self) # create window if its None or destroyed
self.toplevel_window1.after(20, self.toplevel_window1.lift)
else:
self.toplevel_window1.focus() # if window exists focus it
def open_toplevel2(self):
if self.toplevel_window2 is None or not self.toplevel_window2.winfo_exists():
self.toplevel_window2 = ToplevelWindow2(self) # create window if its None or destroyed
self.toplevel_window2.after(20, self.toplevel_window2.lift)
else:
self.toplevel_window2.focus() # if window exists focus it
if __name__ == "__main__":
app = App()
app.mainloop()
Hi, @ElectricCandlelight even though it could be janky but it works, thank you! I've already tried .lift() however it does not work if it's not delayed.
I can confirm the problem and the workaround for Windows 10 as well. The same can be observed for windows without fullscreen (i.e. replacing self.attributes('-fullscreen', True)
with self.geometry("300x200")
).
I also tried the same with Tkinter. Here the toplevel windows are topmost, but - interestingly! - don't have the focus.
The following script includes two "hardcoded" switches customTk
and fullscreen
. I changed the open_toplevel1
method to what I think works as expected in both, Tkinter and CustomTkinter and this changed things.
import tkinter
import customtkinter
customTk = False
fullscreen = True
if customTk:
Toplevel = customtkinter.CTkToplevel
Label = customtkinter.CTkLabel
Button = customtkinter.CTkButton
Tk = customtkinter.CTk
else:
Toplevel = tkinter.Toplevel
Label = tkinter.Label
Button = tkinter.Button
Tk = tkinter.Tk
def fullscreen_option(widget):
if fullscreen:
widget.attributes('-fullscreen', True)
else:
widget.geometry("300x200")
class ToplevelWindow1(Toplevel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
fullscreen_option(self)
self.label = Label(self, text="ToplevelWindow1")
self.label.pack(padx=20, pady=20)
self.button_3 = Button(self, text="open toplevel3", command=self.open_toplevel3)
self.button_3.pack(side="top", padx=20, pady=20)
self.toplevel_window3 = None
def open_toplevel3(self):
if self.toplevel_window3 is None or not self.toplevel_window3.winfo_exists():
self.toplevel_window3 = ToplevelWindow3(self) # create window if its None or destroyed
else:
self.toplevel_window3.focus() # if window exists focus it
class ToplevelWindow2(Toplevel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
fullscreen_option(self)
self.label = Label(self, text="ToplevelWindow2")
self.label.pack(padx=20, pady=20)
class ToplevelWindow3(Toplevel):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
fullscreen_option(self)
self.label = Label(self, text="ToplevelWindow3")
self.label.pack(padx=20, pady=20)
self.button_4 = Button(self, text="back", command=self.destroy)
self.button_4.pack(side="top", padx=20, pady=20)
class App(Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
fullscreen_option(self)
self.button_1 = Button(self, text="open toplevel1", command=self.open_toplevel1)
self.button_1.pack(side="top", padx=20, pady=20)
self.toplevel_window1 = None
self.button_2 = Button(self, text="open toplevel2", command=self.open_toplevel2)
self.button_2.pack(side="top", padx=20, pady=20)
self.toplevel_window2 = None
def open_toplevel1(self):
if self.toplevel_window1 is None or not self.toplevel_window1.winfo_exists():
self.toplevel_window1 = ToplevelWindow1(self) # create window if its None or destroyed
self.toplevel_window1.focus() # if window exists focus it
def open_toplevel2(self):
if self.toplevel_window2 is None or not self.toplevel_window2.winfo_exists():
self.toplevel_window2 = ToplevelWindow2(self) # create window if its None or destroyed
else:
self.toplevel_window2.focus() # if window exists focus it
if __name__ == "__main__":
app = App()
app.mainloop()
My conclusion: always focus()
a newly created top-level window.
Check out the difference between open_toplevel1
and button open_toplevel2
(as can be observed running above script):
open_toplevel1
:
open_toplevel2
:
After all, I'm not sure if this issue should be considered valid or should be reported to CPython or Tk.
EDIT: My apologies, this only seems to work in the IDE, once compiled using pyinstaller, it no longer helps.
This has started happening between version 5.0.5 and 5.1.0 of CustomTkinter as I can reproduce the situation by going between those two versions.
Lines 275 to 277 in ctk_toplevel.py version 5.1.0. Second line is the cause.
if self.focused_widget_before_widthdraw is not None:
self.after(10, self.focused_widget_before_widthdraw.focus)
self.focused_widget_before_widthdraw = None
hello, I have a problem with my script, i've got a main window with a buton that call a progress ba on a separate threading, but i have the same probleme, no focus on the progress bar window with tkinter, and progress bar window behind with customtkinter... i tried lift() and focus method, but no result...
import time
import tkinter as tk
import customtkinter as ctk
import datetime
import threading
from random import randint
today=datetime.datetime.now().strftime("%d/%m/%Y")
last_year=(datetime.datetime.now()-datetime.timedelta(days=365)).strftime("%d/%m/%Y")
color='#c2b0ff'
# class ProgressWindow(ctk.CTkToplevel):
class ProgressWindow(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.title("Traitement en cours.")
self.geometry("300x100")
# self.progressbar = ttk.Progressbar(self, orient="horizontal", length=200, mode="determinate")
self.progressbar=ctk.CTkProgressBar(self, orientation="horizontal", height=30,width=200, mode="determinate")
self.progressbar.set(value=0)
self.progressbar.pack(pady=20)
self.label = ctk.CTkLabel(self, text="En attente du traitement...")
self.label.pack()
def set_progress(self, value):
self.progressbar.set(value = value)
def set_label(self, text):
self.label.configure(text=text)
class MainApp(ctk.CTk):
def __init__(self):
super().__init__()
self.title("Analyse Gestion Projet")
self.create_widgets()
def create_widgets(self):
self.date_frame = ctk.CTkFrame(self,fg_color=color)
self.date_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.validate_btn = ctk.CTkButton(self.date_frame, text="Valider", command=self.start_thread)
self.validate_btn.grid(row=4,column=0,columnspan=2,padx=5,pady=5,stick='we')
self.close_btn= ctk.CTkButton(self.date_frame, text="Fermer", command=self.close)
self.close_btn.grid(row=4,column=2,columnspan=2,padx=5,pady=5,stick='we')
self.info_label = ctk.CTkLabel(self.date_frame, text="Attente du lancement de l'analyse")
self.info_label.grid(row=5,column=0,columnspan=4,padx=5,pady=5,stick='we')
self.list_log=[]
def close(self):
self.destroy()
def valider(self):
self.date_frame.configure(fg_color='red')
self.update()
print('lancement de l\'analyse')
time.sleep(5)
self.date_frame.configure(fg_color=color)
self.update()
def start_thread(self):
self.validate_btn.configure(state='disabled')
self.date_frame.configure(fg_color='red')
self.update()
self.progress_window = ProgressWindow(self)
self.thread = threading.Thread(target=self.long_running_process, args=(self.progress_window,))
self.thread.start()
self.after(50, self.check_thread)
def long_running_process(self, progress_window):
a=randint(1, 5)
len=3
text=''
for i in range(1, len+1):
b=randint(1, 5)
tps=a+b
progress_window.set_label(f"Traitement du fichier {i}/{len}, durée = {tps}s...")
self.analyse(tps,i,len)
progress_window.set_progress(i/len)
def analyse(self, tps,i,len):
time.sleep(tps)
self.list_log.append(f"Traitement du fichier {i}/{len} terminé avec succès")
print(self.list_log)
def check_thread(self):
text=""
if self.thread.is_alive():
self.after(100, self.check_thread)
else:
self.progress_window.destroy()
self.validate_btn.configure(state='normal')
self.date_frame.configure(fg_color=color)
self.info_label.configure(text="Traitement terminé!")
self.update()
for log in self.list_log:
text+=log+'\n'
self.info_label.configure(text=text)
if __name__ == "__main__":
app = MainApp()
app.mainloop()
thank you for your help...
The reason is a bug in the "TopLevel" class that sets the default icon after 200ms. A simple workaround is to use the "after" method with a delay of 200ms or more: self.after(200, lambda: self.iconbitmap("path_to_icon"))
thank you for the answer, but where can i write this line? in the init function of my ProgressWindow class?
Trying to create a fullscreen application however, any new top levels are created behind the current window. so they cant be accessed.
Even in the original CTkToplevel example code, the new window pops up behind the main window.
Is there a way to have the new windows created be the visible one?
methods tried: .attributes('-topmost', True) .lift() .focus() .focus_force()
customtkinter: 5.1.2 OS: Windows 11