After clicking on the combobox, the dropdown list is shown. Wait for 2 seconds and a browser page becomes active and find that the dropdown list remains visible while the test application itself is hidden.
import tkinter as tk
import webbrowser
from tkinter import ttk
root = tk.Tk()
root.geometry("300x200")
combo = ttk.Combobox(root, state="readonly")
combo['values'] = ["Option 1", "Option 2", "Option 3"]
combo.bind('<Button-1>', lambda event: root.after(2000, lambda: webbrowser.open("https://stackoverflow.com")))
combo.pack()
root.mainloop()
With Python 3.12.4
I expect the dropdown disappears along with the test application itself. Also I'd like to receive <FocusOut>
event for root
.
The problem was that the dropdown list
you see is actually a separate window. if you look at its attributes using the "attributes" command, you will see the following:
-alpha 1.0 -transparentcolor {} -disabled 0 -fullscreen 0 -toolwindow 0 -topmost 1
So your window always appears on top of all other windows because topmost
is set to 1. The solution is obvious: set "-topmost" to 0 when creating the dropdown list
so that it doesn't appear on top of everything. You also need to make sure that if it is unmapped, you close it. So, the code:
import tkinter as tk
from tkinter import ttk
import webbrowser
root = tk.Tk()
root.geometry("300x200")
combo = ttk.Combobox(root, state="readonly")
combo['values'] = ["Option 1", "Option 2", "Option 3", "Option 4", "Option 5", "Option 6"]
combo.bind('<Button-1>', lambda event: root.after(2000, lambda: webbrowser.open("https://stackoverflow.com")))
combo.pack()
def on_widget_map(event: tk.Event):
root.eval(f"wm attributes {event.widget} -topmost 0")
root.bind_class("ComboboxPopdown", '<Map>', on_widget_map, add='+')
root.mainloop()
Now you can see that the above code may still not work completely. I think what can be done is to regularly check if the dropdown
is below the root
. If so, then close it. Updated code:
...
combo.pack()
def close_if_not_visible(dropdown_list):
if root.eval(f"winfo ismapped {dropdown_list}") == '0':
return
if root.eval(f"wm stackorder {dropdown_list} isabove .") == '1':
root.after(50, lambda: close_if_not_visible(dropdown_list))
else:
root.focus_set()
def on_widget_map(event: tk.Event):
root.eval(f"wm attributes {event.widget} -topmost 0")
root.bind_class(event.widget, '<Unmap>', lambda _e: root.focus_set())
close_if_not_visible(event.widget)
root.bind_class("ComboboxPopdown", '<Map>', on_widget_map, add='+')
root.mainloop()
If you have any questions about the code or something is still not working properly, feel free to ask.