How to make a screenshot with python, if connected to multiple monitors?
I tried:
import sys
from PyQt4.QtGui import QPixmap, QApplication
app = QApplication(sys.argv)
QPixmap.grabWindow(QApplication.desktop().winId()).save('test.png', 'png')
import ImageGrab
im = ImageGrab.grab()
im.save('test.png', 'PNG')
Both options provide a screenshot, only the primary monitor
If I use winapi:
hWnd = win32gui.FindWindow(None, win_name)
dc = win32gui.GetWindowDC(hWnd)
i_colour = int(win32gui.GetPixel(dc,int(x),int(y)))
rgb = ((i_colour & 0xff), ((i_colour >> 8) & 0xff), ((i_colour >> 16) & 0xff))
I get a picture from a window in the second monitor. But it will be very slow.
If I press key 'printscreen' in the clipboard will be a normal screenshot, with all monitors. Is there a option to get a Full screenshot in Python?
My Desktopmagic library provides this functionality for Python 2.6, 2.7, and 3.3+. It can return a PIL/Pillow Image or write a BMP.
Using a mix of wxPython, win32api and ctypes:
import wx, win32api, win32gui, win32con, ctypes
class App(wx.App):
def OnInit(self):
dll = ctypes.WinDLL('gdi32.dll')
for idx, (hMon, hDC, (left, top, right, bottom)) in enumerate(win32api.EnumDisplayMonitors(None, None)):
hDeskDC = win32gui.CreateDC(win32api.GetMonitorInfo(hMon)['Device'], None, None)
bitmap = wx.EmptyBitmap(right - left, bottom - top)
hMemDC = wx.MemoryDC()
hMemDC.SelectObject(bitmap)
try:
dll.BitBlt(hMemDC.GetHDC(), 0, 0, right - left, bottom - top, int(hDeskDC), 0, 0, win32con.SRCCOPY)
finally:
hMemDC.SelectObject(wx.NullBitmap)
bitmap.SaveFile('screenshot_%02d.bmp' % idx, wx.BITMAP_TYPE_BMP)
win32gui.ReleaseDC(win32gui.GetDesktopWindow(), hDeskDC)
return False
App(0)
install desktopmagic:
pip install Desktopmagic)
from __future__ import print_function
import desktopmagic
from desktopmagic.screengrab_win32 \
import(getDisplayRects,saveScreenToBmp,getScreenAsImage,getRectAsImage,getDisplaysAsImages)
""" getDisplayRects functions returns a list with all displays, in display order, like [(0, 0, 1280, 1024), (-1280, 0, 0, 1024), (1280, -176, 3200, 1024)] : (left, top, right, bottom)"""
screens=(getDisplayRects())
take a screenshot of the second monitor
rect = getRectAsImage(screens[1])
0 for the first display 1 for the second and ...
#saves screenshot
rect.save('leftscr.png',format='png')
Related
As I found in the following topic : How to make python window run as "Always On Top"?
I know how to put a window on top. But I would like to keep it at the same position. The autor says that he found a work around to find the x and y values. I would like to know how I can achieve that !
How can I get the x, y values of a pygame window ? Maybe it's a wrong way of doing.
The effect I am looking for is that the window goes on top when I trigger it with some function call.
For those who know League of legends, when a game starts, the window goes on top and remains at the same coordinates.
There's a shorter solution using the same function:
from ctypes import windll
SetWindowPos = windll.user32.SetWindowPos
NOSIZE = 1
NOMOVE = 2
TOPMOST = -1
NOT_TOPMOST = -2
def alwaysOnTop(yesOrNo):
zorder = (NOT_TOPMOST, TOPMOST)[yesOrNo] # choose a flag according to bool
hwnd = pygame.display.get_wm_info()['window'] # handle to the window
SetWindowPos(hwnd, zorder, 0, 0, 0, 0, NOMOVE|NOSIZE)
I found a solution that seems pretty well done:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ctypes import windll, Structure, c_long, byref #windows only
class RECT(Structure):
_fields_ = [
('left', c_long),
('top', c_long),
('right', c_long),
('bottom', c_long),
]
def width(self): return self.right - self.left
def height(self): return self.bottom - self.top
def onTop(window):
SetWindowPos = windll.user32.SetWindowPos
GetWindowRect = windll.user32.GetWindowRect
rc = RECT()
GetWindowRect(window, byref(rc))
SetWindowPos(window, -1, rc.left, rc.top, 0, 0, 0x0001)
Now in order to put a window on top, simply call onTop(pygame.display.get_wm_info()['window']) to handle your pygame window.
This worked for me
import win32gui
import win32con
win32gui.SetWindowPos(pygame.display.get_wm_info()['window'], win32con.HWND_TOPMOST, 0,0,0,0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
The window doesn't move because I used the SWP_NOMOVE tag
Getting current window position:
from ctypes import POINTER, WINFUNCTYPE, windll
from ctypes.wintypes import BOOL, HWND, RECT
# get our window ID:
hwnd = pygame.display.get_wm_info()["window"]
# Jump through all the ctypes hoops:
prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
paramflags = (1, "hwnd"), (2, "lprect")
GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
# finally get our data!
rect = GetWindowRect(hwnd)
print "top, left, bottom, right: ", rect.top, rect.left, rect.bottom, rect.right
# bottom, top, left, right: 644 98 124 644
Putting the window on the foreground:
x = rect.left
y = rect.top
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (x,y)
So I have this code:
import win32gui
import win32ui
from ctypes import windll
from PIL import Image
hwnd = win32gui.FindWindow(None, "#chat - Discord")
# Change the line below depending on whether you want the whole window
# or just the client area.
left, top, right, bot = win32gui.GetClientRect(hwnd)
#left, top, right, bot = win32gui.GetWindowRect(hwnd)
w = right - left
h = bot - top
hwndDC = win32gui.GetWindowDC(hwnd)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
saveDC.SelectObject(saveBitMap)
# Change the line below depending on whether you want the whole window
# or just the client area.
result = windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 1)
#result = windll.user32.PrintWindow(hwnd, saveDC.GetSafeHdc(), 0)
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
im = Image.frombuffer(
'RGB',
(bmpinfo['bmWidth'], bmpinfo['bmHeight']),
bmpstr, 'raw', 'BGRX', 0, 1)
if result:
#PrintWindow Succeeded
im.show()
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
The problem with it is that the screenshot it takes is all black. It does this with all windows, not just Discord. What is wrong with it, and what do I need to do to fix it?
Also as a side question - when I take screenshots the command prompt opens and then close real quick, is there also a way to stop that from happening?
The problem with it is that the screenshot it takes is all black.
I understand that you want to take a screenshot?
from PIL.ImageGrab import grab
image = grab()
image.save('Screenshot.png')
If you have the coordinates of the window, try this instead:
from PIL.ImageGrab import grab
box = (0,0,200,200)
image = grab(box)
image.save('Screenshot.png')
PIL has been discontinued though, so you may be able to use this code only for the next immediate future. Also, the command prompt does not appear with this code.
I've got an (apparently) cross-platform screenshot function using wxPython:
def take_screenshot(x=0, y=0, width=None, height=None):
try:
import wx
except ImportError as e:
return 'Screenshot could not be taken - wx could not be imported: %s' %(e)
import os, datetime
folder_name = datetime.date.today().strftime('%Y-%m-%d')
file_name = datetime.datetime.now().strftime('%H-%M-%S') + '.png'
directory = os.path.join(os.getcwd(), 'screenshots', folder_name)
make_directory(directory)
filename = os.path.join(directory, file_name)
app = wx.App()
screen = wx.ScreenDC()
size = screen.GetSize()
if width == None:
width = size[0]
if height == None:
height = size[1]
bmp = wx.EmptyBitmap(width, height)
mem = wx.MemoryDC(bmp)
mem.Blit(0, 0, width, height, screen, x, y)
del mem
bmp.SaveFile(filename, wx.BITMAP_TYPE_PNG)
return 'Screenshot saved to file: %s' %(filename)
Here is a screenshot I took on Windows 7. This code works fine on Linux. I'm running on Python 2.7.8 and wxPython 3.0.2.0
Has anyone seen any similar problems? Am I doing something wrong?
This was discussed a few years ago on Stack and the wxPython mailing list. At that time, there was no reliable cross-platform method of taking a screenshot of the user's screen. I have not heard of any improvements since. You might try the PyQt method mentioned in the Stack link or you could try out the pyscreenshot project.
Update for wxPython 4 and higher
import wx
app = wx.App(False)
screen = wx.ScreenDC()
size = screen.GetSize()
width = size.width
height = size.height
bmp = wx.Bitmap(width, height)
# Create a memory DC that will be used for actually taking the screenshot
memDC = wx.MemoryDC()
# Tell the memory DC to use our Bitmap
# all drawing action on the memory DC will go to the Bitmap now
memDC.SelectObject(bmp)
# Blit (in this case copy) the actual screen on the memory DC
memDC.Blit(
0, 0,
width, height,
screen,
0, 0
)
# Select the Bitmap out of the memory DC by selecting a new bitmap
memDC.SelectObject(wx.NullBitmap)
im = bmp.ConvertToImage()
im.SaveFile('screenshot.png', wx.BITMAP_TYPE_PNG)
As I found in the following topic : How to make python window run as "Always On Top"?
I know how to put a window on top. But I would like to keep it at the same position. The autor says that he found a work around to find the x and y values. I would like to know how I can achieve that !
How can I get the x, y values of a pygame window ? Maybe it's a wrong way of doing.
The effect I am looking for is that the window goes on top when I trigger it with some function call.
For those who know League of legends, when a game starts, the window goes on top and remains at the same coordinates.
There's a shorter solution using the same function:
from ctypes import windll
SetWindowPos = windll.user32.SetWindowPos
NOSIZE = 1
NOMOVE = 2
TOPMOST = -1
NOT_TOPMOST = -2
def alwaysOnTop(yesOrNo):
zorder = (NOT_TOPMOST, TOPMOST)[yesOrNo] # choose a flag according to bool
hwnd = pygame.display.get_wm_info()['window'] # handle to the window
SetWindowPos(hwnd, zorder, 0, 0, 0, 0, NOMOVE|NOSIZE)
I found a solution that seems pretty well done:
#!/usr/bin/python
# -*- coding: utf-8 -*-
from ctypes import windll, Structure, c_long, byref #windows only
class RECT(Structure):
_fields_ = [
('left', c_long),
('top', c_long),
('right', c_long),
('bottom', c_long),
]
def width(self): return self.right - self.left
def height(self): return self.bottom - self.top
def onTop(window):
SetWindowPos = windll.user32.SetWindowPos
GetWindowRect = windll.user32.GetWindowRect
rc = RECT()
GetWindowRect(window, byref(rc))
SetWindowPos(window, -1, rc.left, rc.top, 0, 0, 0x0001)
Now in order to put a window on top, simply call onTop(pygame.display.get_wm_info()['window']) to handle your pygame window.
This worked for me
import win32gui
import win32con
win32gui.SetWindowPos(pygame.display.get_wm_info()['window'], win32con.HWND_TOPMOST, 0,0,0,0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
The window doesn't move because I used the SWP_NOMOVE tag
Getting current window position:
from ctypes import POINTER, WINFUNCTYPE, windll
from ctypes.wintypes import BOOL, HWND, RECT
# get our window ID:
hwnd = pygame.display.get_wm_info()["window"]
# Jump through all the ctypes hoops:
prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
paramflags = (1, "hwnd"), (2, "lprect")
GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
# finally get our data!
rect = GetWindowRect(hwnd)
print "top, left, bottom, right: ", rect.top, rect.left, rect.bottom, rect.right
# bottom, top, left, right: 644 98 124 644
Putting the window on the foreground:
x = rect.left
y = rect.top
import os
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (x,y)
I am using a Windows 11 system with Python 3.11.2 and for some reason, the SetWindowPos from windll.user32 library does not work. However the same SetWindowPos function from win32gui library works perfectly. Just do the exact answer #ZiyadCodes gave earlier.
To install required libraries:
$ pip install win32gui
$ pip install win32con
To keep pygame window always on top:
import win32gui
from win32con import SetWindowPos
SetWindowPos(pygame.display.get_wm_info()['window'], win32con.HWND_TOPMOST, 0,0,0,0, win32con.SWP_NOMOVE | win32con.SWP_NOSIZE)
I'm trying to get 48x48 or 256x256 icons from files in Windows and have come across what seems like a dead-end. At the moment I have a HICON handle(since PySides QFileIconProvider only returns 32x32 icons) in python which I would like to show in a pyside window but functions like QPixmap.fromHICON/HBITMAP are not implemented and also seems to have been removed from the source since Qt 4.8(?). Also, I'm trying to avoid having to save the icon to a file.
So, is there any way to get a HICON or possibly any other things you can turn it into, to any kind of PySide object?
EDIT:
I've been trying to simply rewrite the old function fromWinHBITMAP function in python but it isn't going great. I'm uncertain how I should translate the src line into python and I don't either have any idea how I change the value of the memory buffer returned by QImage.scanLine()
for (int y=0; y<h; ++y) {
QRgb *dest = (QRgb *) image.scanLine(y);
const QRgb *src = (const QRgb *) (data + y * bytes_per_line);
for (int x=0; x<w; ++x) {
dest[x] = src[x] | mask;
}
}
At the moment I create a PyCBITMAP from the HICON with the win32api and retrieves the list of bits.
for y in range(0, hIcon.height):
dest = i.scanLine(y)
src = bitmapbits[y*hIcon.widthBytes:(y*hIcon.widthBytes)+hIcon.widthBytes]
for x in range(0, hIcon.width):
dest[x] = bytes(ctypes.c_uint32(src[x] | 0))
This results in "ValueError: cannot modify size of memoryview object"
The source for the function be found here: http://www.qtcentre.org/threads/19188-Converting-from-HBitmap-to-a-QPixmap?p=94747#post94747
Fixed it!
def iconToQImage(hIcon):
hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
hbmp = win32ui.CreateBitmap()
hbmp.CreateCompatibleBitmap(hdc, hIcon.width, hIcon.height)
hdc = hdc.CreateCompatibleDC()
hdc.SelectObject(hbmp)
win32gui.DrawIconEx(hdc.GetHandleOutput(), 0, 0, hIcon.hIcon, hIcon.width, hIcon.height, 0, None, 0x0003)
bitmapbits = hbmp.GetBitmapBits(True)
image = QtGui.QImage(bitmapbits, hIcon.width, hIcon.height, QtGui.QImage.Format_ARGB32_Premultiplied)
return image
It's a bit hard to get this sort of setup going but from I read around Python Imaging Library (PIL) supports bitmap and ICO files and has downloads for Windows. Assuming you can get a filename of the icon, you can load it up with PIL and then transfer the raw data to a QImage:
from PIL import Image
from PySide.QtGui import QImage, QImageReader, QLabel, QPixmap, QApplication
im = Image.open("my_image.png")
data = im.tostring('raw', 'RGBA')
app = QApplication([])
image = QImage(data, im.size[0], im.size[1], QImage.Format_ARGB32)
pix = QPixmap.fromImage(image)
lbl = QLabel()
lbl.setPixmap(pix)
lbl.show()
app.exec_()
Then work with whatever QImage operation you need to do from there.
While #egs0's answer is accurate, trying to display the output may cause problems because QLabel doesn't handle bitmap very well. To solve these problems, convert the result to another image format.
import win32ui
import win32gui
# Doesn't matter which library. Qt5 should work just as well.
from PySide6 import QtGui, QtCore
def iconToQImage(hIcon, width, height, im_format="PNG"):
hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
hbmp = win32ui.CreateBitmap()
hbmp.CreateCompatibleBitmap(hdc, width, height)
hdc = hdc.CreateCompatibleDC()
hdc.SelectObject(hbmp)
win32gui.DrawIconEx(hdc.GetHandleOutput(), 0, 0, hIcon, width, height, 0, None, 0x0003)
bitmapbits = hbmp.GetBitmapBits(True)
image = QtGui.QImage(bitmapbits, width, height, QtGui.QImage.Format_ARGB32_Premultiplied)
# Write to and then load from a buffer to convert to PNG.
buffer = QtCore.QBuffer()
buffer.setOpenMode(QtCore.QIODevice.ReadWrite)
image.save(buffer, im_format)
image.loadFromData(buffer.data(), im_format)
# Use QtGui.Pixmap.fromImage() to get a pixmap instead.
return image
It's also possible to get the size of an icon using the following function, adapted from here:
def getIconSize(HIcon):
info = win32gui.GetIconInfo(HIcon)
if info[4]: # Icon has color plane.
bmp = win32gui.GetObject(info[4])
width = bmp.bmWidth
height = bmp.bmHeight
else: # Icon has no colour plane, image data stored in mask.
bmp = win32gui.GetObject(info[3])
width = bmp.width
height = bmp.height // 2 # A monochrome icon contains image and XOR mask in the hbmMask.
info[3].close()
info[4].close()
return width, height