• 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:

  1. Run the program, which opens a navigable single-page GUI.
  2. Select the game you'd like to automate.
  3. Select the script you'd like to run.
  4. Configure the options for the script (this involves opening a new Toplevel window in Tkinter).
  5. Press Play to run the script.
  6. 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 called win.initialize().
  7. 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?

1

There are 1 best solutions below

3
On

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 the tkinter module, like the following example.

import tkinter as tk
import mss

mss_obj = mss.mss()

root = tk.Tk()
...
try:
    root.mainloop()
finally:
    mss_obj.close()