Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tkinter menu bar goes invisible when checking topmost attribute

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?

like image 667
aaa Avatar asked Mar 27 '26 22:03

aaa


1 Answers

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()
like image 137
steTATO Avatar answered Mar 29 '26 12:03

steTATO



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!