I have a larger tkinter app that I wanted to dinamically set the topmost attribute. I am able to achieve what I want, but everytime I check the state of topmost, the selected menu bar on the screen goes invisible.
To reproduce this, consider the MRE below and upon running the code, click the "menu" button on the menu bar, the cascade opens and the exit button shows, watch it vanish after the check_topmost function runs, but the "menu" button is still pressed somehow.
Commenting out either the line that checks the attribute or the line that sets it as True stops the behaviour
import tkinter as tk
def check_topmost():
print(app.attributes('-topmost')) # comment this
app.after(1000, check_topmost)
app = tk.Tk()
menu_bar = tk.Menu(app)
sub_menu = tk.Menu(menu_bar, tearoff=0)
sub_menu.add_command(label = 'exit', command = app.destroy)
menu_bar.add_cascade(label = "menu", menu = sub_menu)
app.config(menu = menu_bar)
app.attributes('-topmost', 1) # comment this
app.after(1000, check_topmost)
app.mainloop()
What am I doing wrong?
This is not a fix for your issue, but rather a workaround. It seems like the topmost
attribute is really weird... First, it is platform specific. Second, once set, it doesn't just change. If you set "-topmost", 1
for another window both will have the same attribute. From the documentation I found "-topmost gets or sets whether this is a topmost window"
. I assume, that once you call the attribute, it not only checks the attribute, but reassigns the value, therefore giving you the weird behavior with the drop-down menu. Interestingly, if you get all attributes at the same time by app.attributes()
, your drop-down menu is not affected. So you can use this as workaround to check the attribute and only set it if necessary. Most likely, this is not the best solution, but it is the best I could come up with.
import tkinter as tk
def set_topmost():
app2.attributes('-topmost', 1)
print('window 1', app.attributes())
print('window 2', app2.attributes()) # as you can see both windows can have the same topmost attribute
app.attributes('-topmost', 0) # try the behavior after commenting this out
def check_topmost():
print(app.attributes()) # this does not affect your window/drop-down menu
att = str(app.attributes()) # making it a string as workaround
if "'-topmost', 1" in att:
print('already set as topmost')
else:
print('switch topmost')
app.attributes('-topmost', 1) # this will still disrupt your drop down menu
app.after(5000, check_topmost) # i set it to 5 seconds to better see the effects
app = tk.Tk()
menu_bar = tk.Menu(app)
sub_menu = tk.Menu(menu_bar, tearoff=0)
sub_menu.add_command(label = 'exit', command = app.destroy)
menu_bar.add_cascade(label = "menu", menu = sub_menu)
app.config(menu = menu_bar)
app.attributes('-topmost', 1) # comment this
app2 = tk.Toplevel()
app2.geometry('500x500')
btn = tk.Button(app, text='set topmost', command=set_topmost)
btn.pack()
app.after(1000, check_topmost)
app.mainloop()