- Ubuntu v22.04
- MSS v7.0.1
- Python 3.10
This issue is practically identical to: Python: Tkinter + MSS = mss.exception.ScreenShotError: XDefaultRootWindow() failed - however, their question is unsolved. For a minimum reproducible example, see there.
How my project functions
I have a project that involves controlling video game automation scripts/macros via a Python GUI. Using the tool goes something like this:
- Run the program, which opens a navigable single-page GUI.
- Select the game you'd like to automate.
- Select the script you'd like to run.
- Configure the options for the script (this involves opening a new Toplevel window in Tkinter).
- Press
Play
to run the script. - System: When the
Play
button is pressed, my program screenshots the video game client to locate its window position as well as various UI element positions. This occurs in a function calledwin.initialize()
. - Optional: The user may pause, resume, or stop the script, or switch to a different script on the fly.
The problem
Please see this video for a demonstration:
The issue I'm experiencing happens between step 4 and 6. Strangely, the first time I open the options menu, select/save my options, and close that little pop-up window, the script will run successfully - i.e., MSS will not encounter an error when screenshotting the game client. If I stop/restart the script (which does not require me to re-select options), it will still work properly. However, if I change the options by opening the pop-up window again, MSS will throw this error when it attempts to screenshot:
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>,)}
This indicates that the issue has something to do with opening and closing the TkToplevel (pop-up) widget. I cannot explain why this works the first time I select options. From what I can see, the Toplevel widget is being destroyed properly whenever the user closes it or presses the save
button.
Does anyone know what might be going on here?
It's a bug in the current MSS. Please report it on this.
The current implementation changes the current X11 error handler and leaves it afterwards, and it causes a conflict with Tcl/Tk(the backend of Python
tkinter
).(See this for detail.)To avoid this, you can initialize an MSS instance before calling any
tkinter
API and reuse it, so not disturbing thetkinter
module, like the following example.