Windows更新使用Python Xlib打破了它们的绘图

时间:2018-03-25 11:54:40

标签: python screenshot x11 xlib

我正在尝试为自己编写一个有趣的项目,这是一个用Python编写的截图工具。我遇到了一个问题,就是我要在屏幕上绘制一个矩形,获取坐标,然后截取这个区域的截图。我大部分时间都在工作,但是当选择项下方的窗口更新时,它会破坏矩形的绘制。

以下是显示问题的视频:https://gfycat.com/EmotionalThankfulFlycatcher

我已经尝试阅读Xlib文档,看看是否有一种方法可以轻松处理这个问题,但是找不到任何东西。我可以尝试的另一个选择是截取整个屏幕的截图,然后选择一个而不是占用现场桌面的区域。

无论如何我可以处理这个问题,而不是采用整个桌面的截图并裁剪它的路线?

import sys
from Xlib import X, display, Xutil, xobject, Xcursorfont


class xselect:
    def __init__(self):
        # X display
        self.d = display.Display()

        # Screen
        self.screen = self.d.screen()

        # Draw on the root window (desktop surface)
        self.window = self.screen.root

    def select_region(self):

        # Set cursor to crosshair
        font = self.d.open_font('cursor')
        cursor = font.create_glyph_cursor(font, Xcursorfont.crosshair,
                                          Xcursorfont.crosshair+1,
                                          (65535, 65535, 65535), (0, 0, 0))

        self.window.grab_pointer(1, X.PointerMotionMask|X.ButtonReleaseMask|X.ButtonPressMask,
                X.GrabModeAsync, X.GrabModeAsync, X.NONE, cursor, X.CurrentTime)

        colormap = self.screen.default_colormap
        color = colormap.alloc_color(0, 0, 0)
        # Xor it because we'll draw with X.GXxor function
        xor_color = color.pixel ^ 0xffffff

        self.gc = self.window.create_gc(
            line_width = 1,
            line_style = X.LineSolid,
            fill_style = X.FillOpaqueStippled,
            fill_rule  = X.WindingRule,
            cap_style  = X.CapButt,
            join_style = X.JoinMiter,
            foreground = xor_color,
            background = self.screen.black_pixel,
            function = X.GXxor,
            graphics_exposures = False,
            subwindow_mode = X.IncludeInferiors,
        )

        done    = False
        started = False
        start   = dict(x=0, y=0)
        end     = dict(x=0, y=0)
        last    = None

        while not done:
            e = self.d.next_event()

            # Window has been destroyed, quit
            if e.type == X.DestroyNotify:
                sys.exit(0)

            # Mouse button press
            elif e.type == X.ButtonPress:
                # Left mouse button?
                if e.detail == 1:
                    start = dict(x=e.root_x, y=e.root_y)
                    started = True

                # Right mouse button?
                elif e.detail == 3:
                    sys.exit(0)

            # Mouse button release
            elif e.type == X.ButtonRelease:
                end = dict(x=e.root_x, y=e.root_y)
                if last:
                    self.draw_rectangle(start, last)
                done = True
                pass

            # Mouse movement
            elif e.type == X.MotionNotify and started:
                if last:
                    self.draw_rectangle(start, last)
                    last = None

                last = dict(x=e.root_x, y=e.root_y)
                self.draw_rectangle(start, last)
                pass

            # Keyboard key
            elif e.type == X.KeyPress:
                sys.exit(0)

            elif e.type == X.EnterNotify:
                print(' EnterNotify')

        self.d.ungrab_pointer(0)
        self.d.flush()

        coords = self.get_coords(start, end)
        if coords['width'] <= 1 or coords['height'] <= 1:
            sys.exit(0)
        else:
            return coords

    def get_coords(self, start, end):
        safe_start = dict(x=0, y=0)
        safe_end = dict(x=0, y=0)

        if start['x'] > end['x']:
            safe_start['x'] = end['x']
            safe_end['x']   = start['x']
        else:
            safe_start['x'] = start['x']
            safe_end['x']   = end['x']

        if start['y'] > end['y']:
            safe_start['y'] = end['y']
            safe_end['y']   = start['y']
        else:
            safe_start['y'] = start['y']
            safe_end['y']   = end['y']

        return {
            'start': {
                'x': safe_start['x'],
                'y': safe_start['y'],
            },
            'end': {
                'x': safe_end['x'],
                'y': safe_end['y'],
            },
            'width' : safe_end['x'] - safe_start['x'],
            'height': safe_end['y'] - safe_start['y'],
        }

    def draw_rectangle(self, start, end):
        coords = self.get_coords(start, end)
        self.window.rectangle(
            self.gc,
            coords['start']['x'],
            coords['start']['y'],
            coords['end']['x'] - coords['start']['x'],
            coords['end']['y'] - coords['start']['y']
        )

1 个答案:

答案 0 :(得分:0)

我自己还是X11的新手,但是stackoverflow上的X11标签已经死了,所以无论如何我都会尝试回答。

要获取屏幕截图,请查看this。您想使用XGetImage,它允许您指定所需的矩形。

现在绘制矩形,据我所知,在根窗口上绘图可能非常不合适。您可以改为创建一个透明背景的全屏窗口。例如,这也允许您为矩形周围的区域提供更暗的色调。