Раскрывающийся список ttk.Combobox остается видимым после того, как новое окно становится активным

После нажатия на поле со списком отображается раскрывающийся список. Подождите 2 секунды, страница браузера станет активной, и вы обнаружите, что раскрывающийся список остается видимым, а само тестовое приложение скрыто.

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()

С Python 3.12.4

Я ожидаю, что раскрывающийся список исчезнет вместе с самим тестовым приложением. Также я хотел бы получить <FocusOut> мероприятие на root.

🤔 А знаете ли вы, что...
С Python можно создавать кросс-платформенные приложения для Windows, macOS и Linux.


101
1

Ответ:

Решено

Проблема заключалась в том, что dropdown list, который вы видите, на самом деле является отдельным окном. если вы посмотрите на его атрибуты с помощью команды «атрибуты», то увидите следующее:

-alpha 1.0 -transparentcolor {} -disabled 0 -fullscreen 0 -toolwindow 0 -topmost 1

Таким образом, ваше окно всегда появляется поверх всех других окон, потому что topmost установлено в 1. Решение очевидно: установите для «-topmost» значение 0 при создании dropdown list, чтобы оно не появлялось поверх всего. Вам также необходимо убедиться, что, если он не сопоставлен, вы закрываете его. Итак, код:

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()

Теперь вы можете видеть, что приведенный выше код все еще может работать не полностью. Я думаю, что можно регулярно проверять, находится ли dropdown ниже root. Если да, то закройте его. Обновленный код:

...
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()

Если у вас есть какие-либо вопросы по коду или что-то по-прежнему не работает должным образом, не стесняйтесь спрашивать.