You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在tkinter中使用root.overrideredirect(True)控制焦点事件?

问题详情
  • Python版本:3.12.3
  • 系统:ArchLinux(装有未聚焦窗口透明合成器)
  • 现象:
    • tkinter的<FocusOut>事件在普通窗口中可正常触发,但设置root.overrideredirect(True)后,窗口会保持置顶,无法接收/失去焦点,<FocusOut>/<FocusIn>事件完全失效
    • 系统的窗口合成器无法识别该窗口,仿佛窗口不存在
    • 尝试过root.attributes('-type', 'splash'),窗口可以被其他窗口压下去,但焦点事件仍然无法触发
需求

不取消root.overrideredirect(True)的前提下,实现:

  1. 移除窗口装饰
  2. 点击窗口外部时自动关闭窗口
示例代码
import tkinter as tk
from pyautogui import position


win_size: tuple = (100, 100)
win_top_left: tuple = position()
win_bottom_right: tuple = (win_top_left[0] + win_size[0], win_top_left[1] + win_size[1])


def destroy(*_) -> None:
    WIN.destroy()


WIN: tk.Tk = tk.Tk()
WIN.title("Window")
WIN.geometry(f"{win_size[0]}x{win_size[1]}+{win_top_left[0]}+{win_top_left[1]}")
WIN.overrideredirect(True)

WIN.protocol("WM_DELETE_WINDOW", WIN.destroy)

WIN.bind("<FocusOut>", destroy)     # 仅移除WIN.overrideredirect(True)时生效
WIN.bind("<FocusIn>", destroy)      # 同上

button: tk.Button = tk.Button(WIN, text="destroy", command=destroy)
button.pack()

WIN.mainloop()
解决方案

方案1:循环检测鼠标位置判断点击区域

利用tkinter的after方法定时检查鼠标位置,判断是否处于窗口区域外,满足条件则关闭窗口。该方法无需额外依赖,跨平台性较好。

修改后的代码:

import tkinter as tk
from pyautogui import position


def destroy(*_) -> None:
    WIN.destroy()

def check_click_outside():
    # 获取当前鼠标坐标
    mouse_x, mouse_y = position()
    # 获取窗口的位置与尺寸
    win_x = WIN.winfo_x()
    win_y = WIN.winfo_y()
    win_width = WIN.winfo_width()
    win_height = WIN.winfo_height()
    # 判断鼠标是否在窗口外部
    if not (win_x <= mouse_x <= win_x + win_width and win_y <= mouse_y <= win_y + win_height):
        destroy()
    # 每100毫秒重复检测一次
    WIN.after(100, check_click_outside)


WIN: tk.Tk = tk.Tk()
WIN.title("Window")
win_size = (100, 100)
WIN.geometry(f"{win_size[0]}x{win_size[1]}+{position()[0]}+{position()[1]}")
WIN.overrideredirect(True)

WIN.protocol("WM_DELETE_WINDOW", destroy)

button: tk.Button = tk.Button(WIN, text="destroy", command=destroy)
button.pack()

# 启动检测循环
check_click_outside()

WIN.mainloop()

方案2:Linux下用Xlib监听焦点变化(需额外依赖)

针对ArchLinux的X11环境,可通过python-xlib库直接监听系统窗口焦点事件,精准判断窗口是否失去焦点。

  1. 先安装依赖:
sudo pacman -S python-xlib
  1. 修改后的代码:
import tkinter as tk
from pyautogui import position
from Xlib import display, X
import threading


def destroy(*_) -> None:
    WIN.destroy()

def check_focus():
    dpy = display.Display()
    win_id = dpy.create_resource_object('window', WIN.winfo_id())
    win_id.change_attributes(event_mask=X.FocusChangeMask)
    while True:
        event = dpy.next_event()
        if event.type == X.FocusOut:
            destroy()


WIN: tk.Tk = tk.Tk()
WIN.title("Window")
win_size = (100, 100)
WIN.geometry(f"{win_size[0]}x{win_size[1]}+{position()[0]}+{position()[1]}")
WIN.overrideredirect(True)

WIN.protocol("WM_DELETE_WINDOW", destroy)

button: tk.Button = tk.Button(WIN, text="destroy", command=destroy)
button.pack()

# 启动Xlib焦点监听线程
threading.Thread(target=check_focus, daemon=True).start()

WIN.mainloop()

方案3:取消置顶属性配合鼠标检测

如果不需要窗口一直置顶,可添加WIN.attributes('-topmost', False),结合方案1的鼠标位置检测,既能保留无装饰窗口,也能避免窗口霸占顶层。


内容的提问来源于stack exchange,提问作者Ayman Al-mankkadhi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 05:53:21