Skip to Content

Как сделать скриншот при помощи Python

Опубликовано в

 И опять я о скриншотах. Только на этот раз не о всяких там скучных программулинах, а о собственноручном их изготовлении. В общем, захотелось мне недавно странного – делать скриншоты из Python. Естественно, что за этим желанием сразу же последовало действие в виде гугления на предмет подходящей библиотеки или кода. Нашлось, конечно же, много всего, но, как известно, не весь код одинаково полезен и не всегда он работает так как хочется. Посему, ниже несколько моих изысканий на эту тему. Предупреждаю сразу, изыскания касаются исключительно Windows.

 На самом деле делать скриншоты Python’ом просто. Смотрите сами:

  1. C:\> easy_install PIL
  2. ...
  3. >>> from PIL import ImageGrab
  4. >>> screen = ImageGrab.grab()
  5. >>> screen.save('screenshot.png','PNG')

Выглядит красиво, работает быстро, пользоваться удобно. Правда есть одно но. Работает это только до тех пор, пока к вашему ПК подключен один монитор, как только число таких девайсов на вашем рабочем столе переваливает за единицу, то… ну вы поняли, да? Короче говоря, второй монитор (и все прочие тоже) PIL не видит в упор. И никакие уговоры и угрозы вам не помогут.

В общем, PIL не самое подходящее средство, и для получения полноценных скриншотов стоит прибегнуть к иным методам. Хотя, если быть точным, метод всего лишь один, отличаются только средства его реализации. Суть его заключается в растеризации объектов находящихся в DC (то бишь в контексте устройства) виртуального экрана при помощи операции Bit BLIT, и последующем сохранении полученного битмапа в файл (тем, кто ничего не понял в этом предложении, рекомендую погуглить на тему GDI).

Прежде чем перейти непосредственно к коду, стоит пояснить, что такое виртуальный экран. Если не вдаваться в подробности, то это прямоугольник, в который вписаны все мониторы доступные в системе. Выглядит это так:

Windows виртуальный экран

Точкой отсчета на виртуальном экране является левый верхний угол основного дисплея, который имеет координаты  x = 0, y = 0. Все что находится левее и выше имеет отрицательные координаты, все что правее и ниже соответственно положительные. Актуальные координаты можно получить при помощи WinAPI метода GetSystemMetrics и только зная их можно сделать скриншот всей виртуальной области.

Ну а теперь примеры того, как это может быть реализовано на практике.

Первый вариант – PyWin32

  1. import win32ui, win32gui, win32con, win32api
  2.  
  3. hwnd = win32gui.GetDesktopWindow()
  4. width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
  5. height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
  6. x = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
  7. y = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)
  8.  
  9. hwndDC = win32gui.GetWindowDC(hwnd)
  10. mfcDC  = win32ui.CreateDCFromHandle(hwndDC)
  11. saveDC = mfcDC.CreateCompatibleDC()
  12.  
  13. saveBitMap = win32ui.CreateBitmap()
  14. saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)
  15. saveDC.SelectObject(saveBitMap)
  16. saveDC.BitBlt(( 0, 0 ), (width, height),  mfcDC,  (x, y),  win32con.SRCCOPY)
  17.  
  18. #Можно сохранить полученный битмап в BPM огромных размеров
  19. saveBitMap.SaveBitmapFile(saveDC,  'screenshot.bmp')
  20.  
  21. #--------------------------------------------------------------
  22. #Либо воспользоваться PIL для сохранения в любом другом формате
  23. import PIL.Image
  24. bmpinfo = saveBitMap.GetInfo()
  25. bmpstr = saveBitMap.GetBitmapBits(True)
  26. image = PIL.Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
  27. image.save('screenshot.png', format = 'png')
  28. #--------------------------------------------------------------
  29.  
  30. saveDC.DeleteDC()
  31. win32gui.DeleteObject(saveBitMap.GetHandle())

Второй вариант – wxPython + ctypes

  1. import wx, ctypes
  2.  
  3. app = wx.PySimpleApp()
  4.  
  5. SM_XVIRTUALSCREEN = 76
  6. SM_YVIRTUALSCREEN = 77
  7. SM_CXVIRTUALSCREEN = 78
  8. SM_CYVIRTUALSCREEN = 79
  9.  
  10. user32 = ctypes.windll.user32
  11. width, height = user32.GetSystemMetrics(SM_CXVIRTUALSCREEN), user32.GetSystemMetrics(SM_CYVIRTUALSCREEN)
  12. x, y = user32.GetSystemMetrics(SM_XVIRTUALSCREEN), user32.GetSystemMetrics(SM_YVIRTUALSCREEN)    
  13.  
  14. screen = wx.ScreenDC()
  15. bmp = wx.EmptyBitmap(width, height)
  16. mem = wx.MemoryDC(bmp)
  17. mem.Blit(0, 0, width, height, screen, x, y)
  18. del mem
  19. bmp.SaveFile('screenshot.png', wx.BITMAP_TYPE_PNG)

Кстати, пока я писал эту заметку, мне пришла в голову мысль, что по сути можно обойтись одним лишь ctypes, тем самым избавившись от сторонних модулей, т.е. используя ctypes.windll.user32 и ctypes.windll.gdi32 получить скриншот и при помощи ctypes.windll.gdiplus преобразовать его в нужный формат.

Но это уж как-нибудь в другой раз.

А пока у меня все.
 

----------

https://pypi.python.org/pypi/

https://pypi.python.org/pypi/mss - кросплатформенно и просто, но чуть-чуть медленно

Отправить комментарий

  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Доступны HTML теги: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Строки и параграфы переносятся автоматически.
  • Pairs of<blockquote> tags will be styled as a block that indicates a quotation.
  • Textual smileys will be replaced with graphical ones.

Подробнее о форматировании