Ubuntu下Python Tkinter关闭Toplevel后MSS截图报错XDefaultRootWindow()失败
环境信息
- Ubuntu v22.04
- MSS v7.0.1
- Python 3.10
此问题与Stack Overflow上一个未解决的同类问题完全一致,可参考其最小复现示例。
项目功能说明
我开发了一个通过Python GUI控制游戏自动化脚本/宏的项目,使用流程如下:
- 运行程序,打开可导航的单页GUI。
- 选择要自动化的游戏。
- 选择要运行的脚本。
- 配置脚本选项(需通过Tkinter打开新的Toplevel窗口)。
- 点击
Play按钮运行脚本。 - 系统逻辑:点击
Play后,程序会对游戏客户端截图以定位窗口及各类UI元素位置,该操作在win.initialize()函数中执行。 - 可选操作:用户可暂停、恢复、停止脚本,或即时切换至其他脚本。
问题描述
问题出现在步骤4与步骤6之间。奇怪的是,第一次打开选项菜单、选择并保存选项后关闭弹窗,脚本可正常运行——MSS截图游戏客户端无报错;即使停止并重启脚本(无需重新配置选项),仍可正常工作。但如果再次打开弹窗修改选项,MSS尝试截图时会抛出如下错误:
Exception in Tkinter callback Traceback (most recent call last): File "/usr/lib/python3.10/tkinter/__init__.py", line 1921, in __call__ return self.func(*args) File "/home/my_project/env/lib/python3.10/site-packages/customtkinter/widgets/ctk_button.py", line 372, in clicked self.command() File "/home/my_project/src/view/info_frame.py", line 139, in play_btn_clicked self.controller.play_pause() File "/home/my_project/src/controller/bot_controller.py", line 21, in play_pause self.model.play_pause() File "/home/my_project/src/model/bot.py", line 103, in play_pause if not self.__initialize_window(): File "/home/my_project/src/model/bot.py", line 132, in __initialize_window self.win.initialize() File "/home/my_project/src/model/runelite_bot.py", line 47, in initialize if not super().initialize(): File "/home/my_project/src/utilities/window.py", line 128, in initialize a = self.__locate_minimap(client_rect) File "/home/my_project/src/utilities/window.py", line 257, in __locate_minimap if m := imsearch.search_img_in_rect(imsearch.BOT_IMAGES.joinpath("minimap.png"), client_rect): File "/home/my_project/src/utilities/imagesearch.py", line 52, in search_img_in_rect im = rect.screenshot() File "/home/my_project/src/utilities/geometry.py", line 64, in screenshot with mss.mss() as sct: File "/home/my_project/env/lib/python3.10/site-packages/mss/factory.py", line 34, in mss return linux.MSS(**kwargs) File "/home/my_project/env/lib/python3.10/site-packages/mss/linux.py", line 297, in __init__ self.root = self.xlib.XDefaultRootWindow(self._get_display(display)) File "/home/my_project/env/lib/python3.10/site-packages/mss/linux.py", line 184, in validate raise ScreenShotError(f"{func.__name__}() failed", details=details) mss.exception.ScreenShotError: XDefaultRootWindow() failed Screenshot error: XDefaultRootWindow() failed, {'retval': <mss.linux.LP_XWindowAttributes object at 0x7f31d8d38ac0>, 'args': (<mss.linux.LP_Display object at 0x7f31d8d38740>,)}
这表明问题与打开和关闭Tk Toplevel弹窗组件有关。 我确认用户关闭弹窗或点击save按钮时,Toplevel组件已被正确销毁,但无法理解为何第一次配置选项时可正常运行,再次修改后就报错。
可能的原因与解决方法
Tkinter干扰X11显示资源
Linux环境下,Toplevel窗口的创建/销毁可能意外修改了MSS依赖的X11显示连接。解决方法:初始化MSS时显式指定显示设备:import os # 替换原有with mss.mss() as sct: with mss.mss(display=os.environ.get("DISPLAY", ":0")) as sct:Toplevel销毁残留资源
即使调用destroy(),Toplevel可能仍有未释放的X11资源。解决方法:关闭弹窗时强制刷新事件循环并解除引用:def close_options_window(toplevel): toplevel.destroy() toplevel.update_idletasks() # 强制刷新事件循环 toplevel = None # 显式解除引用MSS实例重复创建冲突
每次截图都新建MSS实例可能导致资源占用冲突。解决方法:全局复用一个MSS实例:# 项目初始化时创建全局实例 global_sct = mss.mss() # 截图时直接调用全局实例 def take_screenshot(rect): return global_sct.grab(rect)X11访问权限变动
再次打开弹窗后程序可能失去X11根窗口访问权限。临时测试方案:启动程序前执行xhost +local:放宽本地权限(正式环境需配置合规权限)。
内容的提问来源于stack exchange,提问作者ktom
相关产品推荐
相关产品推荐

