add option to capture all monitors on Windows

This commit is contained in:
nulano 2019-07-04 23:52:35 +02:00 committed by Andrew Murray
parent 03452d138d
commit fa6b80fddf
4 changed files with 29 additions and 13 deletions

View File

@ -8,7 +8,11 @@ try:
class TestImageGrab(PillowTestCase): class TestImageGrab(PillowTestCase):
def test_grab(self): def test_grab(self):
for im in [ImageGrab.grab(), ImageGrab.grab(include_layered_windows=True)]: for im in [
ImageGrab.grab(),
ImageGrab.grab(include_layered_windows=True),
ImageGrab.grab(multimonitor=True),
]:
self.assert_image(im, im.mode, im.size) self.assert_image(im, im.mode, im.size)
def test_grabclipboard(self): def test_grabclipboard(self):

View File

@ -11,7 +11,7 @@ or the clipboard to a PIL image memory.
.. versionadded:: 1.1.3 .. versionadded:: 1.1.3
.. py:function:: PIL.ImageGrab.grab(bbox=None, include_layered_windows=False) .. py:function:: PIL.ImageGrab.grab(bbox=None, include_layered_windows=False, multimonitor=False)
Take a snapshot of the screen. The pixels inside the bounding box are Take a snapshot of the screen. The pixels inside the bounding box are
returned as an "RGB" image on Windows or "RGBA" on macOS. returned as an "RGB" image on Windows or "RGBA" on macOS.
@ -21,6 +21,7 @@ or the clipboard to a PIL image memory.
:param bbox: What region to copy. Default is the entire screen. :param bbox: What region to copy. Default is the entire screen.
:param include_layered_windows: Includes layered windows. Windows OS only. :param include_layered_windows: Includes layered windows. Windows OS only.
:param multimonitor: Capture all monitors. Windows OS only.
:return: An image :return: An image
.. py:function:: PIL.ImageGrab.grabclipboard() .. py:function:: PIL.ImageGrab.grabclipboard()

View File

@ -29,7 +29,7 @@ else:
raise ImportError("ImageGrab is macOS and Windows only") raise ImportError("ImageGrab is macOS and Windows only")
def grab(bbox=None, include_layered_windows=False): def grab(bbox=None, include_layered_windows=False, multimonitor=False):
if sys.platform == "darwin": if sys.platform == "darwin":
fh, filepath = tempfile.mkstemp(".png") fh, filepath = tempfile.mkstemp(".png")
os.close(fh) os.close(fh)
@ -37,8 +37,10 @@ def grab(bbox=None, include_layered_windows=False):
im = Image.open(filepath) im = Image.open(filepath)
im.load() im.load()
os.unlink(filepath) os.unlink(filepath)
if bbox:
im = im.crop(bbox)
else: else:
size, data = grabber(include_layered_windows) offset, size, data = grabber(include_layered_windows, multimonitor)
im = Image.frombytes( im = Image.frombytes(
"RGB", "RGB",
size, size,
@ -49,8 +51,10 @@ def grab(bbox=None, include_layered_windows=False):
(size[0] * 3 + 3) & -4, (size[0] * 3 + 3) & -4,
-1, -1,
) )
if bbox: if bbox:
im = im.crop(bbox) x0, y0 = offset
left, top, right, bottom = bbox
im = im.crop((left - x0, top - y0, right - x0, bottom - y0))
return im return im

View File

@ -322,15 +322,15 @@ PyImaging_DisplayModeWin32(PyObject* self, PyObject* args)
PyObject* PyObject*
PyImaging_GrabScreenWin32(PyObject* self, PyObject* args) PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
{ {
int width, height; int x = 0, y = 0, width, height;
int includeLayeredWindows = 0; int includeLayeredWindows = 0, multimonitor = 0;
HBITMAP bitmap; HBITMAP bitmap;
BITMAPCOREHEADER core; BITMAPCOREHEADER core;
HDC screen, screen_copy; HDC screen, screen_copy;
DWORD rop; DWORD rop;
PyObject* buffer; PyObject* buffer;
if (!PyArg_ParseTuple(args, "|i", &includeLayeredWindows)) if (!PyArg_ParseTuple(args, "|ii", &includeLayeredWindows, &multimonitor))
return NULL; return NULL;
/* step 1: create a memory DC large enough to hold the /* step 1: create a memory DC large enough to hold the
@ -339,8 +339,15 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
screen = CreateDC("DISPLAY", NULL, NULL, NULL); screen = CreateDC("DISPLAY", NULL, NULL, NULL);
screen_copy = CreateCompatibleDC(screen); screen_copy = CreateCompatibleDC(screen);
width = GetDeviceCaps(screen, HORZRES); if (multimonitor) {
height = GetDeviceCaps(screen, VERTRES); x = GetSystemMetrics(SM_XVIRTUALSCREEN);
y = GetSystemMetrics(SM_YVIRTUALSCREEN);
width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
} else {
width = GetSystemMetrics(SM_CXSCREEN);
height = GetSystemMetrics(SM_CYSCREEN);
}
bitmap = CreateCompatibleBitmap(screen, width, height); bitmap = CreateCompatibleBitmap(screen, width, height);
if (!bitmap) if (!bitmap)
@ -354,7 +361,7 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
rop = SRCCOPY; rop = SRCCOPY;
if (includeLayeredWindows) if (includeLayeredWindows)
rop |= CAPTUREBLT; rop |= CAPTUREBLT;
if (!BitBlt(screen_copy, 0, 0, width, height, screen, 0, 0, rop)) if (!BitBlt(screen_copy, 0, 0, width, height, screen, x, y, rop))
goto error; goto error;
/* step 3: extract bits from bitmap */ /* step 3: extract bits from bitmap */
@ -376,7 +383,7 @@ PyImaging_GrabScreenWin32(PyObject* self, PyObject* args)
DeleteDC(screen_copy); DeleteDC(screen_copy);
DeleteDC(screen); DeleteDC(screen);
return Py_BuildValue("(ii)N", width, height, buffer); return Py_BuildValue("(ii)(ii)N", x, y, width, height, buffer);
error: error:
PyErr_SetString(PyExc_IOError, "screen grab failed"); PyErr_SetString(PyExc_IOError, "screen grab failed");