如何获取屏幕截图并更改剪贴板上的 DPI?
How to get screenshot and change DPI on the clipboard?
在Win7下我想获取剪贴板上window的内容和set/adjust剪贴板上的DPI设置并将其复制到最终应用程序。
下面的 MCVE 尚未按预期运行。
有一个问题:
有时可能会发生 window 显然尚未设置为前景并且 ImageGrab.grab(bbox)
获取错误内容的情况。等待一段时间(2-5 秒)会有所帮助,但不是很实用。如何避免或解决这个问题?
代码如下:
from io import BytesIO
from PIL import Image,ImageGrab
import win32gui, win32clipboard
import time
def get_screenshot(window_name, dpi):
hwnd = win32gui.FindWindow(None, window_name)
if hwnd != 0:
win32gui.SetForegroundWindow(hwnd)
time.sleep(2) ### sometimes window is not yet in foreground. delay/timing problem???
bbox = win32gui.GetWindowRect(hwnd)
screenshot = ImageGrab.grab(bbox)
width, height = screenshot.size
lmargin = 9
tmargin = 70
rmargin = 9
bmargin = 36
screenshot = screenshot.crop(box = (lmargin,tmargin,width-rmargin,height-bmargin))
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
output = BytesIO()
screenshot.convert("RGB").save(output, "BMP", dpi=(dpi,dpi))
data = output.getvalue()[14:]
output.close()
win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)
win32clipboard.CloseClipboard()
print("Screenshot taken...")
else:
print("No window found named:", window_name)
window_name = "Gnuplot (window id : 0)"
get_screenshot(window_name,200)
编辑:
此外,这种改进尝试有时仍然会得到错误的内容。也许有人可以解释为什么?
win32gui.SetForegroundWindow(hwnd)
for i in range(1000):
print(i)
time.sleep(0.01)
if win32gui.GetForegroundWindow() == hwnd:
break
bbox = win32gui.GetWindowRect(hwnd)
加法:
这就是我(通常)在删除具有延迟时间的线路时得到的 time.sleep(2)
。
左:想要的内容,右:收到的内容。我怎样才能可靠地捕获所需的内容 window?代码有什么问题?我设置的延迟时间越大,我获得所需内容的可能性就越高。但我不想等待几秒钟才能确定。如何检查系统是否准备好截图?
感谢@Tarun Lalwani 指向
this answer,我终于有了一个暂时适合我的代码。但是,在我看来,有很多不同的模块相当冗长。也许它仍然可以简化。欢迎提出建议。
代码:
### get the content of a window and crop it
import win32gui, win32ui, win32clipboard
from io import BytesIO
from ctypes import windll
from PIL import Image
# user input
window_name = 'Gnuplot (window id : 0)'
margins = [8,63,8,31] # left, top, right, bottom
dpi = 96
hwnd = win32gui.FindWindow(None, window_name)
left, top, right, bottom = win32gui.GetWindowRect(hwnd)
width = right - left
height = bottom - top
crop_box = (margins[0],margins[1],width-margins[2],height-margins[3])
hwndDC = win32gui.GetWindowDC(hwnd)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)
saveDC.SelectObject(saveBitMap)
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).crop(crop_box)
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
output = BytesIO()
im.convert("RGB").save(output, "BMP", dpi=(dpi,dpi))
data = output.getvalue()[14:]
output.close()
win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)
win32clipboard.CloseClipboard()
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
print('"'+window_name+'"', "is now on the clipboard with", dpi, "dpi.")
### end of code
正如所讨论的,您可以使用下面讨论的方法
Python Screenshot of inactive window PrintWindow + win32gui
import win32gui
import win32ui
from ctypes import windll
import Image
hwnd = win32gui.FindWindow(None, 'Calculator')
# 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)
print result
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
im = Image.frombuffer(
'RGB',
(bmpinfo['bmWidth'], bmpinfo['bmHeight']),
bmpstr, 'raw', 'BGRX', 0, 1)
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
if result == 1:
#PrintWindow Succeeded
im.save("test.png")
在Win7下我想获取剪贴板上window的内容和set/adjust剪贴板上的DPI设置并将其复制到最终应用程序。
下面的 MCVE 尚未按预期运行。
有一个问题:
有时可能会发生 window 显然尚未设置为前景并且 ImageGrab.grab(bbox)
获取错误内容的情况。等待一段时间(2-5 秒)会有所帮助,但不是很实用。如何避免或解决这个问题?
代码如下:
from io import BytesIO
from PIL import Image,ImageGrab
import win32gui, win32clipboard
import time
def get_screenshot(window_name, dpi):
hwnd = win32gui.FindWindow(None, window_name)
if hwnd != 0:
win32gui.SetForegroundWindow(hwnd)
time.sleep(2) ### sometimes window is not yet in foreground. delay/timing problem???
bbox = win32gui.GetWindowRect(hwnd)
screenshot = ImageGrab.grab(bbox)
width, height = screenshot.size
lmargin = 9
tmargin = 70
rmargin = 9
bmargin = 36
screenshot = screenshot.crop(box = (lmargin,tmargin,width-rmargin,height-bmargin))
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
output = BytesIO()
screenshot.convert("RGB").save(output, "BMP", dpi=(dpi,dpi))
data = output.getvalue()[14:]
output.close()
win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)
win32clipboard.CloseClipboard()
print("Screenshot taken...")
else:
print("No window found named:", window_name)
window_name = "Gnuplot (window id : 0)"
get_screenshot(window_name,200)
编辑:
此外,这种改进尝试有时仍然会得到错误的内容。也许有人可以解释为什么?
win32gui.SetForegroundWindow(hwnd)
for i in range(1000):
print(i)
time.sleep(0.01)
if win32gui.GetForegroundWindow() == hwnd:
break
bbox = win32gui.GetWindowRect(hwnd)
加法:
这就是我(通常)在删除具有延迟时间的线路时得到的 time.sleep(2)
。
左:想要的内容,右:收到的内容。我怎样才能可靠地捕获所需的内容 window?代码有什么问题?我设置的延迟时间越大,我获得所需内容的可能性就越高。但我不想等待几秒钟才能确定。如何检查系统是否准备好截图?
感谢@Tarun Lalwani 指向 this answer,我终于有了一个暂时适合我的代码。但是,在我看来,有很多不同的模块相当冗长。也许它仍然可以简化。欢迎提出建议。
代码:
### get the content of a window and crop it
import win32gui, win32ui, win32clipboard
from io import BytesIO
from ctypes import windll
from PIL import Image
# user input
window_name = 'Gnuplot (window id : 0)'
margins = [8,63,8,31] # left, top, right, bottom
dpi = 96
hwnd = win32gui.FindWindow(None, window_name)
left, top, right, bottom = win32gui.GetWindowRect(hwnd)
width = right - left
height = bottom - top
crop_box = (margins[0],margins[1],width-margins[2],height-margins[3])
hwndDC = win32gui.GetWindowDC(hwnd)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, width, height)
saveDC.SelectObject(saveBitMap)
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).crop(crop_box)
win32clipboard.OpenClipboard()
win32clipboard.EmptyClipboard()
output = BytesIO()
im.convert("RGB").save(output, "BMP", dpi=(dpi,dpi))
data = output.getvalue()[14:]
output.close()
win32clipboard.SetClipboardData(win32clipboard.CF_DIB, data)
win32clipboard.CloseClipboard()
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
print('"'+window_name+'"', "is now on the clipboard with", dpi, "dpi.")
### end of code
正如所讨论的,您可以使用下面讨论的方法
Python Screenshot of inactive window PrintWindow + win32gui
import win32gui
import win32ui
from ctypes import windll
import Image
hwnd = win32gui.FindWindow(None, 'Calculator')
# 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)
print result
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
im = Image.frombuffer(
'RGB',
(bmpinfo['bmWidth'], bmpinfo['bmHeight']),
bmpstr, 'raw', 'BGRX', 0, 1)
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnd, hwndDC)
if result == 1:
#PrintWindow Succeeded
im.save("test.png")