-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Allow window ID to be passed to ImageGrab.grab() on macOS #9070
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
5ce88db
1f7e9c3
7eaac3f
79914ec
53302c2
610d564
ca3528f
c680ff0
424168b
88247a9
65c32ec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -43,17 +43,45 @@ def grab( | |||||||||||||
| fh, filepath = tempfile.mkstemp(".png") | ||||||||||||||
| os.close(fh) | ||||||||||||||
| args = ["screencapture"] | ||||||||||||||
| if bbox: | ||||||||||||||
| if window: | ||||||||||||||
| args += ["-l", str(window)] | ||||||||||||||
| elif bbox: | ||||||||||||||
| left, top, right, bottom = bbox | ||||||||||||||
| args += ["-R", f"{left},{top},{right-left},{bottom-top}"] | ||||||||||||||
| subprocess.call(args + ["-x", filepath]) | ||||||||||||||
| im = Image.open(filepath) | ||||||||||||||
| im.load() | ||||||||||||||
| os.unlink(filepath) | ||||||||||||||
| if bbox: | ||||||||||||||
| im_resized = im.resize((right - left, bottom - top)) | ||||||||||||||
| im.close() | ||||||||||||||
| return im_resized | ||||||||||||||
| if window: | ||||||||||||||
| # Determine if the window was in retina mode or not | ||||||||||||||
hugovk marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||||||
| # by capturing it without the shadow, | ||||||||||||||
| # and checking how different the width is | ||||||||||||||
| fh, filepath = tempfile.mkstemp(".png") | ||||||||||||||
| os.close(fh) | ||||||||||||||
| subprocess.call( | ||||||||||||||
| ["screencapture", "-l", str(window), "-o", "-x", filepath] | ||||||||||||||
| ) | ||||||||||||||
| with Image.open(filepath) as im_no_shadow: | ||||||||||||||
| retina = im.width - im_no_shadow.width > 100 | ||||||||||||||
|
||||||||||||||
| retina = im.width - im_no_shadow.width > 100 | |
| # Detect Retina mode by checking if the width difference is >30% of the no-shadow image width | |
| retina = (im.width - im_no_shadow.width) / im_no_shadow.width > 0.3 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current comment is
Determine if the window was in Retina mode or not by capturing it without the shadow, and checking how different the width is
I think it's reasonably apparent that we're using an absolute threshold
Copilot
AI
Nov 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] In Retina mode, the code uses resize() with a box parameter to crop. However, resize() is primarily intended for scaling, not cropping. While this works (the box parameter specifies the region to sample from the source), it's unconventional and potentially confusing.
Consider using a two-step approach for clarity:
if retina:
left, top, right, bottom = bbox
im_cropped = im.crop(tuple(coord * 2 for coord in bbox))
im_cropped = im_cropped.resize((right - left, bottom - top))Or document why resize() with box is used instead of the more idiomatic crop() + resize() combination.
| im_cropped = im.resize( | |
| (right - left, bottom - top), | |
| box=tuple(coord * 2 for coord in bbox), | |
| ) | |
| im_cropped = im.crop(tuple(coord * 2 for coord in bbox)) | |
| im_cropped = im_cropped.resize((right - left, bottom - top)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling two methods would not be more efficient, from a performance perspective, than one.
Copilot
AI
Nov 30, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The variable name im_cropped is used here, while a similar operation at line 82 uses im_resized. For consistency and accuracy, consider using im_cropped in both places since both operations effectively crop/extract a region from the original image (one uses crop() and one uses resize() with a box parameter, but both achieve cropping).
| im_resized = im.resize((right - left, bottom - top)) | |
| im.close() | |
| return im_resized | |
| im_cropped = im.resize((right - left, bottom - top)) | |
| im.close() | |
| return im_cropped |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a resize operation, not a crop operation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's windowid? There's no other mention in the docs or code.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've pushed a commit to describe it as a CGWindowID instead.