Open a-alak opened 1 month ago
Could you attach an image to show what you're seeing?
Yeah sorry, what i tested on was sensitive data, so i could not share. But here is an alternative example with the file explorer. Example 1 shows a tooltip with the url of the Outlook shortcut. Usually i would have expected the tooltip not to show in the screenshot like example 2.
@nulano did you have any insight here?
I can confirm that tooltips are included with both include_layered_windows=False
and include_layered_windows=True
on Win 11 Pro version 23H2, but also on Win 10 Home version 22H2.
I just updated to windows 11.
Do you know which build of Win 10 you were running before you upgraded?
Unfortunately not. And honestly I am more challenged by popups than tool tips, and I cannot show you an example pic of the pop up. It is running on a hospital system, where there often is weird pop up messages. They did not use to show up in ImageGrab, but now they do.
Do these popups come from the same application that you are trying to see in your screenshot, or a different one? I'm wondering if the request of #4415 could be a potential solution.
I think that might solve it! It is popups from another application. Some kind of background internal messaging service, that I can't disable.
I've created #8516 to resolve this by adding a handle
argument to ImageGrab.grab()
that accepts a HDC.
With that, you could do something like
import win32gui
from PIL import ImageGrab
window = win32gui.FindWindow(None, "Insert window title here")
handle = win32gui.GetDC(window)
ImageGrab.grab(handle=handle)
Amazing! Thank you @radarhere! Any idea when you can expect a release including PR #8516, how does pillow release cycles work? Sorry if the question is stupid.
Pillow releases occur every three months - the next one is scheduled for January 2nd.
If you would like to try out the PR in the meantime, I've put together a wheel - pillow-11.1.0.dev0-cp310-cp310-win_amd64.whl.zip
Amazing! Thanks @radarhere!
I am curious whether the tooltip issue can be solved as well? It is a minor problem for us, but I am just going to leave the issue open for that. If it is not possible or not something that people want in pillow I will close the issue 😇
I expect it is something that people would want - I'm just not convinced that Windows makes that feature available.
https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-bitblt is the API that we use to get the screenshot, and CAPTUREBLT
is the option that you can turn on and off with include_layered_windows
that "Includes any windows that are layered on top". Nothing else on that page seems relevant.
https://github.com/python-pillow/Pillow/issues/8456#issuecomment-2427841869 was our attempt to test if there was a change between Windows 10 and 11, and we didn't find any difference.
Thanks for sharing! I just tested out the wheel you send and I could not make it work. The screenshot is just a black screen. I run the exact same code you wrote on win 11 python 3.10.
I just tested out the wheel you send and I could not make it work. The screenshot is just a black screen. I run the exact same code you wrote on win 11 python 3.10.
Yep, that unfortunately matches what I found also, some applications don't seem to be able to be captured in this way: https://github.com/python-pillow/Pillow/pull/8516#pullrequestreview-2413911470
(worth noting that instead of using win32gui I used Microsoft Spy++; I saw that some applications have multiple windows and can only be captured by the outer-most one, but that is the one that has a title so it should also be selected by win32gui)
I wonder if there's another way to solve your problem of these popups covering your application - by moving your application to the foreground? https://stackoverflow.com/questions/66164926/in-python-how-do-i-make-a-specific-window-stay-on-top
Unfortunately the pop-up is somehow forced as foreground, so even though I try to pull target window to the front, it will not work.
Anyway, my colleague wrote this short script that works and does not have a black window:
import time
import ctypes
import win32gui
import win32ui
import win32con
from PIL import Image
# Window Title
window_title = 'SOME TITLE'
# Find the window by title
hwnd = win32gui.FindWindow(None, window_title)
if hwnd:
# Get the window's dimensions
left, top, right, bottom = win32gui.GetWindowRect(hwnd)
width = right - left
height = bottom - top
# Get the window's device context (DC)
window_dc = win32gui.GetWindowDC(hwnd)
mfc_dc = win32ui.CreateDCFromHandle(window_dc)
save_dc = mfc_dc.CreateCompatibleDC()
# Create a bitmap object
bitmap = win32ui.CreateBitmap()
bitmap.CreateCompatibleBitmap(mfc_dc, width, height)
save_dc.SelectObject(bitmap)
# Use ctypes to call PrintWindow
PW_RENDERFULLCONTENT = 2
result = ctypes.windll.user32.PrintWindow(hwnd, save_dc.GetSafeHdc(), PW_RENDERFULLCONTENT)
if result == 1:
# Save the bitmap to a file
bitmap.SaveBitmapFile(save_dc, 'screenshot.bmp')
# Convert the bitmap to a PIL image and save as PNG
bmp_info = bitmap.GetInfo()
bmp_str = bitmap.GetBitmapBits(True)
img = Image.frombuffer(
'RGB',
(bmp_info['bmWidth'], bmp_info['bmHeight']),
bmp_str, 'raw', 'BGRX', 0, 1
)
img.save('screenshot.png')
# Display the screenshot
img.show()
else:
print("Failed to capture the window content.")
# Clean up
win32gui.DeleteObject(bitmap.GetHandle())
save_dc.DeleteDC()
mfc_dc.DeleteDC()
win32gui.ReleaseDC(hwnd, window_dc)
else:
print(f"No window found with title: {window_title}")
I have a hard time figuring out what the difference between your C implementation and the above implementation is, but maybe this could help figuring out the reason for the black screens?
The difference is almost certainly the use of the PrintWindow
function instead of BitBlt
. But I cannot easily say how compatible that is with various programs IIUC BitBlt copies the (already painted) window data from the window manager buffer, and PrintWindow sends a message to the window requesting that it paint itself into a provided buffer; or at least that is what the documentation suggests, but it is possible that it is slightly outdated by now.
The screenshot is just a black screen.
The black screen is the right size for the window though, yes?
Something that may or may not be related to your initial report that Windows 10 and Windows 11 behave differently - https://stackoverflow.com/a/54572219/4093019
this only occurs with the Windows update 1809 from late 2018. Apparently, Windows changed the way it handles clipping with that update so that the Device Context contents are no longer updated for parts of windows that are located offscreen.
But I expect you'll tell me that the window you're trying to capture is always entirely on-screen?
But I expect you'll tell me that the window you're trying to capture is always entirely on-screen?
That was what I observed, yes. A black box of the correct size while capturing a window that is visible and focused.
If both BitBlt
and PrintWindow
are flawed, then my next idea would be for Pillow to run both when capturing a window - if one is non-black, then return that to the user. Otherwise just return the BitBlt
image (it is possible the window is actually black).
Does that sound like a good idea, or it is too expensive/convoluted?
What did you do?
Run PIL.ImageGrab.grab() without arguments.
What did you expect to happen?
Until now this has not included pop-ups and tooltips in the screenshot. I just updated to windows 11. I can see in issue #2569 that it is the expected behavior to not include layered windows, and that the option to include them was added, but not as a default.
I suppose the underlying API has changed in windows 11. Anyone that can share some knowledge about this?
What are your OS, Python and Pillow versions?