Reputation: 2350
I want to capture the screenshot of the existing running window via Python.
Following is my sample code for capture Notepad window:
import time
import win32gui
import win32ui
import win32con
import pyautogui
def getWindowScreenshot(hwnd, output_file):
r = win32gui.GetWindowRect(hwnd)
width = r[2] - r[0]
height = r[3] - r[1]
wDC = win32gui.GetWindowDC(hwnd)
dcObj = win32ui.CreateDCFromHandle(wDC)
cDC = dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, width, height)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0, 0), (width, height), dcObj, (0, 0), win32con.SRCCOPY)
# save the bitmap to a file
dataBitMap.SaveBitmapFile(cDC, output_file)
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
# Press the green button in the gutter to run the script.
if __name__ == '__main__':
window_title = "Untitled - Notepad"
winHandle = win32gui.FindWindow(None, window_title)
if winHandle == 0:
raise RuntimeError(f"Window '{window_title}' is not opening!")
# Bring the window to foreground in case it is overlap by other windows
win32gui.SetForegroundWindow(winHandle)
# Do something with Notepad
time.sleep(3)
# Capture the window
getWindowScreenshot(winHandle, "output.bmp")
However, sometimes the captured image will have the blinking caret indicator on the input textbox.
My question is, how to capture the screenshot without getting the caret indicator?
Solution 1:
# Bring the window to foreground in case it is overlap by other windows
win32gui.SetForegroundWindow(winHandle)
# Do something with Notepad
time.sleep(3)
# Make the "Desktop window" focus
win32gui.SetForegroundWindow(win32gui.GetDesktopWindow())
# Capture the window
getWindowScreenshot(winHandle, "output.bmp")
Before capturing the screenshot, try to set "Desktop" as the foreground window.
However, the command win32gui.SetForegroundWindow(win32gui.GetDesktopWindow())
will raise an error:
pywintypes.error: (0, 'SetForegroundWindow', 'No error message is available')
Solution 2:
Replace win32gui.SetForegroundWindow(win32gui.GetDesktopWindow())
with win32gui.ShowWindow(winHandle, 4)
, where 4
is SW_SHOWNOACTIVATE
.
The code run OK, but the Notepad window doesn't lose focus after execution.
Solution 3:
# Bring the window to foreground in case it is overlap by other windows
win32gui.SetForegroundWindow(winHandle)
# Do something with Notepad
time.sleep(3)
# Click outside of window
r = win32gui.GetWindowRect(winHandle)
pyautogui.click(r[2]+1, r[3]+1)
# Capture the window
getWindowScreenshot(winHandle, "output.bmp")
With this solution, I tried to click outside the Notepad window (click on the desktop). This code seems worked, but will not work if another window is behind the Notepad window like this:
When the code is executed, it will click on the Windows Explorer window. So, the code can perform clicking on something unexpectedly (example: deleting the system file unexpectedly?).
Solution 4 (from @SongZhu-MSFT)
if __name__ == '__main__':
window_title = "Untitled - Notepad"
winHandle = win32gui.FindWindow(None, window_title)
if winHandle == 0:
raise RuntimeError(f"Window '{window_title}' is not opening!")
# Bring the window to foreground in case it is overlap by other windows
win32gui.SetForegroundWindow(winHandle)
# Do something with Notepad
time.sleep(3)
win32gui.EnableWindow(winHandle,False)
# Capture the window
getWindowScreenshot(winHandle, "output.bmp")
time.sleep(0.5)
getWindowScreenshot(winHandle, "output1.bmp")
time.sleep(5)
win32gui.EnableWindow(winHandle,True)
When I run the code, I still see that the caret displaying all time.
And output.bmp
+ output1.bmp
still exists one image with the caret.
System information:
Upvotes: 1
Views: 262
Reputation: 3890
After my test, I can use SendMessageW
to send WM_KILLFOCUS
to make the caret disappear.
This is the code that worked for me:
import time
import win32gui
import win32ui
import win32con
import pyautogui
from ctypes import windll
def getWindowScreenshot(hwnd, output_file):
r = win32gui.GetWindowRect(hwnd)
width = r[2] - r[0]
height = r[3] - r[1]
wDC = win32gui.GetWindowDC(hwnd)
dcObj = win32ui.CreateDCFromHandle(wDC)
cDC = dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, width, height)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0, 0), (width, height), dcObj, (0, 0), win32con.SRCCOPY)
# save the bitmap to a file
dataBitMap.SaveBitmapFile(cDC, output_file)
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
if __name__ == '__main__':
window_title = "Untitled - Notepad"
winHandle = win32gui.FindWindow(None, window_title)
if winHandle == 0:
raise RuntimeError(f"Window '{window_title}' is not opening!")
# Bring the window to foreground in case it is overlap by other windows
win32gui.SetForegroundWindow(winHandle)
# Do something with Notepad
windll.user32.SendMessageW(winHandle, win32con.WM_KILLFOCUS, None, None)
# Capture the window
getWindowScreenshot(winHandle, "output.bmp")
time.sleep(0.5)
getWindowScreenshot(winHandle, "output1.bmp")
Upvotes: 2