И опять я о скриншотах. Только на этот раз не о всяких там скучных программулинах, а о собственноручном их изготовлении. В общем, захотелось мне недавно странного – делать скриншоты из Python. Естественно, что за этим желанием сразу же последовало действие в виде гугления на предмет подходящей библиотеки или кода. Нашлось, конечно же, много всего, но, как известно, не весь код одинаково полезен и не всегда он работает так как хочется. Посему, ниже несколько моих изысканий на эту тему. Предупреждаю сразу, изыскания касаются исключительно Windows.
На самом деле делать скриншоты Python’ом просто. Смотрите сами:
- C:\> easy_install PIL
- ...
- >>> from PIL import ImageGrab
- >>> screen = ImageGrab.grab()
- >>> screen.save('screenshot.png','PNG')
Выглядит красиво, работает быстро, пользоваться удобно. Правда есть одно но. Работает это только до тех пор, пока к вашему ПК подключен один монитор, как только число таких девайсов на вашем рабочем столе переваливает за единицу, то… ну вы поняли, да? Короче говоря, второй монитор (и все прочие тоже) PIL не видит в упор. И никакие уговоры и угрозы вам не помогут.
В общем, PIL не самое подходящее средство, и для получения полноценных скриншотов стоит прибегнуть к иным методам. Хотя, если быть точным, метод всего лишь один, отличаются только средства его реализации. Суть его заключается в растеризации объектов находящихся в DC (то бишь в контексте устройства) виртуального экрана при помощи операции Bit BLIT, и последующем сохранении полученного битмапа в файл (тем, кто ничего не понял в этом предложении, рекомендую погуглить на тему GDI).
Прежде чем перейти непосредственно к коду, стоит пояснить, что такое виртуальный экран. Если не вдаваться в подробности, то это прямоугольник, в который вписаны все мониторы доступные в системе. Выглядит это так:
Точкой отсчета на виртуальном экране является левый верхний угол основного дисплея, который имеет координаты x = 0, y = 0. Все что находится левее и выше имеет отрицательные координаты, все что правее и ниже соответственно положительные. Актуальные координаты можно получить при помощи WinAPI метода GetSystemMetrics и только зная их можно сделать скриншот всей виртуальной области.
Ну а теперь примеры того, как это может быть реализовано на практике.
Первый вариант – PyWin32
- import win32ui, win32gui, win32con, win32api
- hwnd = win32gui.GetDesktopWindow()
- width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
- height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
- x = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
- y = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)
- hwndDC = win32gui.GetWindowDC(hwnd)
- mfcDC = win32ui.CreateDCFromHandle(hwndDC)
- saveDC = mfcDC.CreateCompatibleDC()
- saveBitMap = win32ui.CreateBitmap()
- saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)
- saveDC.SelectObject(saveBitMap)
- saveDC.BitBlt(( 0, 0 ), (width, height), mfcDC, (x, y), win32con.SRCCOPY)
- #Можно сохранить полученный битмап в BPM огромных размеров
- saveBitMap.SaveBitmapFile(saveDC, 'screenshot.bmp')
- #--------------------------------------------------------------
- #Либо воспользоваться PIL для сохранения в любом другом формате
- import PIL.Image
- bmpinfo = saveBitMap.GetInfo()
- bmpstr = saveBitMap.GetBitmapBits(True)
- image = PIL.Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
- image.save('screenshot.png', format = 'png')
- #--------------------------------------------------------------
- saveDC.DeleteDC()
- win32gui.DeleteObject(saveBitMap.GetHandle())
Второй вариант – wxPython + ctypes
- import wx, ctypes
- app = wx.PySimpleApp()
- SM_XVIRTUALSCREEN = 76
- SM_YVIRTUALSCREEN = 77
- SM_CXVIRTUALSCREEN = 78
- SM_CYVIRTUALSCREEN = 79
- user32 = ctypes.windll.user32
- width, height = user32.GetSystemMetrics(SM_CXVIRTUALSCREEN), user32.GetSystemMetrics(SM_CYVIRTUALSCREEN)
- x, y = user32.GetSystemMetrics(SM_XVIRTUALSCREEN), user32.GetSystemMetrics(SM_YVIRTUALSCREEN)
- screen = wx.ScreenDC()
- bmp = wx.EmptyBitmap(width, height)
- mem = wx.MemoryDC(bmp)
- mem.Blit(0, 0, width, height, screen, x, y)
- del mem
- bmp.SaveFile('screenshot.png', wx.BITMAP_TYPE_PNG)
Кстати, пока я писал эту заметку, мне пришла в голову мысль, что по сути можно обойтись одним лишь ctypes, тем самым избавившись от сторонних модулей, т.е. используя ctypes.windll.user32 и ctypes.windll.gdi32 получить скриншот и при помощи ctypes.windll.gdiplus преобразовать его в нужный формат.
Но это уж как-нибудь в другой раз.
А пока у меня все.










- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии и скачивать файлы



Последние комментарии
1 неделя 5 дней назад
2 недели 6 дней назад
2 недели 6 дней назад
4 недели 5 дней назад
7 недель 5 дней назад
8 недель 3 дня назад
8 недель 4 дня назад
8 недель 4 дня назад
9 недель 1 день назад
11 недель 6 дней назад