[Python] How to avoid screenshot error using mss when using a class for tk window?

Discussão em 'Python' iniciado por Stack, Março 2, 2025.

  1. Stack

    Stack Membro Participativo

    The idea is to monitor the secondary screen without needing a physical monitor. The image goes to a projector in another room.

    This error occurs when the secondary screen is clicked (to exit full screen mode):

    root2: You clicked at 295 284
    Exception in Tkinter callback
    Traceback (most recent call last):
    File "/usr/lib/python3.12/tkinter/__init__.py", line 1967, in __call__
    return self.func(*args)
    ^^^^^^^^^^^^^^^^
    File "/usr/lib/python3.12/tkinter/__init__.py", line 861, in callit
    func(*args)
    File "/media/rh/Win11/Python/cv2-player/cv2_mss2.py", line 65, in after_capture
    im_brg = self.sct.grab(self.bounding_box)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    File "/home/rh/.local/lib/python3.12/site-packages/mss/base.py", line 101, in grab
    screenshot = self._grab_impl(monitor)
    ^^^^^^^^^^^^^^^^^^^^^^^^
    File "/home/rh/.local/lib/python3.12/site-packages/mss/linux.py", line 428, in _grab_impl
    ximage = self.xlib.XGetImage(
    ^^^^^^^^^^^^^^^^^^^^
    File "/home/rh/.local/lib/python3.12/site-packages/mss/linux.py", line 230, in _validate
    raise ScreenShotError(msg, details=details)
    mss.exception.ScreenShotError: XGetImage() failed


    The code that works without error, and without being in a class:

    from mss import mss
    from PIL import Image
    from PIL import ImageTk
    import tkinter as tk

    root2 = None
    root2_is_splash = False

    # screen 2: 1366x768
    def root2_init(is_splash=False):
    global root2

    root2 = tk.Toplevel( root )
    root2.transient( root )
    root2.title( "Screen 2" )
    root2.configure(background="blue")

    root2.geometry("1366x768+1920+0") # left: 1920 (right of screen 1), top: 0
    root2.bind("<Button-1>", on_root2_click)

    if is_splash == True:
    root2.wm_attributes('-type', 'splash') # splash: hide title bar

    def on_root_click(event):
    print("root: You clicked at", event.x, event.y)
    root.destroy()

    def on_root2_click(event):
    global root2_is_splash

    print("root2: You clicked at", event.x, event.y)
    root2.destroy()

    if root2_is_splash == False:
    root2_init(True)
    root2_is_splash = True
    else:
    root2_init(False)
    root2_is_splash = False

    def after_capture():
    global label1
    im_brg = sct.grab(bounding_box)

    im_rgb = Image.frombytes('RGB', im_brg.size, im_brg.bgra, 'raw', 'BGRX')

    im_rgb2 = im_rgb.resize((640, 480))

    photo1 = ImageTk.PhotoImage(im_rgb2)
    label1.configure(image=photo1)
    label1.image = photo1

    root.after(10, after_capture)

    bounding_box = {'top': 0, 'left': 1366, 'width': 1366, 'height': 768}

    sct = mss(with_cursor=True)

    # screen 1: 1920x1080
    root = tk.Tk()
    root.title("Tk Example")
    root.minsize(200, 200)
    root.maxsize(500, 500)
    root.geometry("800x600+50+50")
    root.bind("<Button-1>", on_root_click)

    canvas1 = tk.Canvas(root)
    canvas1.pack(fill='both', expand=True)
    photo1 = tk.PhotoImage(width=64, height=64)
    label1 = tk.Label(canvas1, image=photo1)
    label1.pack()
    label1.image = photo1
    canvas1.create_window((0, 0), anchor='nw', window=label1, tags='label1_tag')

    root2_init()

    after_capture()

    if __name__ == "__main__":
    root.mainloop()


    The code that gives the error, using a class App():

    from mss import mss
    from PIL import Image
    from PIL import ImageTk
    import tkinter as tk

    class App(tk.Tk):
    def __init__(self):
    super().__init__()

    self.bounding_box = {'top': 0, 'left': 1366, 'width': 1366, 'height': 768}

    self.sct = mss(with_cursor=True)

    # screen 1: 1920x1080
    self.title("Tk Example")
    self.geometry("800x600+50+50")

    self.canvas1 = tk.Canvas(self)
    self.canvas1.pack(fill='both', expand=True)
    photo1 = tk.PhotoImage(width=64, height=64)
    self.label1 = tk.Label(self.canvas1, image=photo1)
    self.label1.pack()
    self.label1.image = photo1
    self.canvas1.create_window((0, 0), anchor='nw', window=self.label1, tags='label1_tag')

    self.bind("<Button-1>", self.on_root_click)

    self.root2_is_splash = False

    self.root2_init()

    self.after_capture()

    # screen 2: 1366x768
    def root2_init(self, is_splash=False):
    # global root2

    self.root2 = tk.Toplevel( self )
    self.root2.transient( self )
    self.root2.title( "Screen 2" )
    self.root2.configure(background="blue")

    self.root2.geometry("1366x768+1920+0") # left: 1920 (right of screen 1), top: 0
    self.root2.bind("<Button-1>", self.on_root2_click)

    if is_splash == True:
    self.root2.wm_attributes('-type', 'splash') # splash: hide title bar

    def on_root_click(self, event):
    print("root: You clicked at", event.x, event.y)
    self.destroy()

    def on_root2_click(self, event):
    print("root2: You clicked at", event.x, event.y)
    self.root2.destroy()

    if self.root2_is_splash == False:
    self.root2_init(True)
    self.root2_is_splash = True
    else:
    self.root2_init(False)
    self.root2_is_splash = False

    def after_capture(self):
    im_brg = self.sct.grab(self.bounding_box)

    im_rgb = Image.frombytes('RGB', im_brg.size, im_brg.bgra, 'raw', 'BGRX')

    im_rgb2 = im_rgb.resize((640, 480))

    photo1 = ImageTk.PhotoImage(im_rgb2)
    self.label1.configure(image=photo1)
    self.label1.image = photo1

    self.after(10, self.after_capture)

    if __name__ == "__main__":
    App().mainloop()


    Edit: I managed to make an adaptation to restart the capture, but it would be interesting to avoid the mss capture error. This way the capture doesn't get stuck.

    I put an error handler using try:

    from mss import mss
    from PIL import Image
    from PIL import ImageTk
    import tkinter as tk

    class App(tk.Tk):
    def __init__(self):
    super().__init__()

    self.bounding_box = {'top': 0, 'left': 1366, 'width': 1366, 'height': 768}

    # screen 1: 1920x1080
    self.title("Tk Example")
    self.geometry("800x600+50+50")

    self.canvas1 = tk.Canvas(self)
    self.canvas1.pack(fill='both', expand=True)
    photo1 = tk.PhotoImage(width=64, height=64)
    self.label1 = tk.Label(self.canvas1, image=photo1)
    self.label1.pack()
    self.label1.image = photo1
    self.canvas1.create_window((0, 0), anchor='nw', window=self.label1, tags='label1_tag')

    self.bind("<Button-1>", self.on_root_click)

    self.root2_is_splash = False

    self.root2_init()

    self.after_capture()

    # screen 2: 1366x768
    def root2_init(self, is_splash=False):
    # global root2

    self.sct = mss(with_cursor=True)

    self.root2 = tk.Toplevel( self )
    self.root2.transient( self )
    self.root2.title( "Screen 2" )
    self.root2.configure(background="blue")

    self.root2.geometry("1366x768+1920+0") # left: 1920 (right of screen 1), top: 0
    self.root2.bind("<Button-1>", self.on_root2_click)

    if is_splash == True:
    self.root2.wm_attributes('-type', 'splash') # splash: hide title bar

    def on_root_click(self, event):
    print("root: You clicked at", event.x, event.y)
    self.destroy()

    def on_root2_click(self, event):
    print("root2: You clicked at", event.x, event.y)
    self.root2.destroy()

    if self.root2_is_splash == False:
    self.root2_init(True)
    self.root2_is_splash = True
    else:
    self.root2_init(False)
    self.root2_is_splash = False

    self.after_capture()

    def after_capture(self):
    try:
    im_brg = self.sct.grab(self.bounding_box)

    im_rgb = Image.frombytes('RGB', im_brg.size, im_brg.bgra, 'raw', 'BGRX')

    im_rgb2 = im_rgb.resize((640, 480))

    photo1 = ImageTk.PhotoImage(im_rgb2)
    self.label1.configure(image=photo1)
    self.label1.image = photo1

    self.after(10, self.after_capture)
    except:
    print('after_capture() finished')

    if __name__ == "__main__":
    App().mainloop()

    Continue reading...

Compartilhe esta Página