Windows平台Tkinter <Visibility>事件异常及窗口可见性监听方案咨询
Hey there, let's break down the issue you're having with Tkinter's <Visibility> event and how to properly monitor window visibility changes.
What's the Normal Behavior of <Visibility>?
First off, the <Visibility> event in Tkinter doesn't track when your window is covered by other windows—that's a common misconception. Its actual purpose is to detect when the window is mapped or unmapped on the screen. That means it triggers when:
- The window is minimized (unmapped) or restored (mapped)
- The window is closed (unmapped)
- The window is initially displayed (mapped)
The event.state values you'll typically see are:
VisibilityUnobscured: The window is mapped and fully recognized as visible by the window system (not minimized/closed)VisibilityObscured: This is rare on modern systems, but can mean the window is mapped but not actually visible (e.g., off-screen or on an inactive virtual desktop)
This is why you're always getting VisibilityUnobscured even when other windows cover yours—obscuration by other apps isn't what this event is designed to detect.
How to Detect Window Occlusion (Being Covered by Other Windows)
Since Tkinter doesn't have a built-in cross-platform way to detect when your window is obscured by others, you'll need to use platform-specific APIs. For Windows, we can use the pywin32 package (which includes win32gui and win32api) to add this functionality.
Here's a modified version of your code that combines the original <Visibility> event for map/unmap detection with Windows-specific occlusion monitoring:
import tkinter as tk import win32gui from threading import Thread import time class GUI: def __init__(self, master): self.master = master master.title("Test GUI") # Bind the original <Visibility> event for map/unmap changes self.master.bind('<Visibility>', self.visibility_changed) self.label = tk.Label(master, text="GUI") self.label.pack() self.close_button = tk.Button(master, text="Close", command=self.on_close) self.close_button.pack() # Get the window handle (HWND) for our Tkinter window self.hwnd = None self.master.update_idletasks() self.hwnd = win32gui.FindWindow(None, master.title()) # Flag to control the monitoring thread self.running = True # Start a daemon thread to check occlusion without freezing the GUI self.occlusion_thread = Thread(target=self.monitor_occlusion, daemon=True) self.occlusion_thread.start() def visibility_changed(self, event): if str(event.type) == "Visibility": print(f"\nVisibility state: {event.state}") def monitor_occlusion(self): while self.running: if self.hwnd: # Get the coordinates of our window's center point window_rect = win32gui.GetWindowRect(self.hwnd) center_x = (window_rect[0] + window_rect[2]) // 2 center_y = (window_rect[1] + window_rect[3]) // 2 # Get the topmost window at the center point top_window = win32gui.WindowFromPoint((center_x, center_y)) # Check if the top window is our app or a child widget is_obscured = True current_window = top_window while current_window != 0: if current_window == self.hwnd: is_obscured = False break # Check if it's a child of our window parent_window = win32gui.GetParent(current_window) if parent_window == self.hwnd: is_obscured = False break current_window = parent_window # Print the occlusion state (overwrite the same line for readability) print(f"Window is obscured: {is_obscured}", end="\r") # Wait 0.5 seconds before checking again to reduce resource usage time.sleep(0.5) def on_close(self): self.running = False self.master.quit() if __name__ == "__main__": root = tk.Tk() my_gui = GUI(root) root.mainloop()
Key Notes on This Implementation:
- We use a daemon thread to check occlusion continuously—this prevents the monitoring loop from freezing the Tkinter main thread (which handles GUI updates).
- The occlusion check works by verifying if the topmost window at our app's center point belongs to our application. If not, we consider the window obscured.
- We keep the original
<Visibility>event to handle cases like minimizing, restoring, or closing the window—this is what it's intended for.
Cross-Platform Considerations
This code is Windows-specific. For macOS, you'd need to use Apple's Cocoa APIs via pyobjc, and for Linux, you could use X11 functions via python-xlib. Tkinter itself doesn't provide a cross-platform solution for occlusion detection.
内容的提问来源于stack exchange,提问作者Toroid

