this answer may be late but for those looking, the answer is here : https://github.com/asweigart/pyautogui/issues/321
quote - Resolve for me
Answer from Laurent Carmelli on Stack OverflowIf still relevant for someone on windows:
In my opinion the issue is, that the current version of pyscreeze utilizing >ImageGrab (Pillow) on windows only uses single-screen grab.
A dirty quick fix in pyscreeze could be:
- enable all_screen grabbing:
In file:
pyscreeze/__init__.py, function:def _screenshot_win32(imageFilename=None, region=None):change
im = ImageGrab.grab()toim = ImageGrab.grab(all_screens= True)
- handle new introduced negative coordinates due to multiple monitor:
In file:
pyscreeze/__init__.py, function:def locateOnScreen(image, minSearchTime=0, **kwargs):behindretVal = locate(image, screenshotIm, **kwargs)>add
if retVal and sys.platform == 'win32': # get the lowest x and y coordinate of the monitor setup monitors = win32api.EnumDisplayMonitors() x_min = min([mon[2][0] for mon in monitors]) y_min = min([mon[2][1] for mon in monitors]) # add negative offset due to multi monitor retVal = Box(left=retVal[0] + x_min, top=retVal[1] + y_min, width=retVal[2],height=retVal[3])
- don't forget to add the
import win32apiIn file:
pyscreeze/__init__.py:
if sys.platform == 'win32': # TODO - Pillow now supports ImageGrab on macOS. import win32api # used for multi-monitor fix from PIL import ImageGrab
this answer may be late but for those looking, the answer is here : https://github.com/asweigart/pyautogui/issues/321
quote - Resolve for me
If still relevant for someone on windows:
In my opinion the issue is, that the current version of pyscreeze utilizing >ImageGrab (Pillow) on windows only uses single-screen grab.
A dirty quick fix in pyscreeze could be:
- enable all_screen grabbing:
In file:
pyscreeze/__init__.py, function:def _screenshot_win32(imageFilename=None, region=None):change
im = ImageGrab.grab()toim = ImageGrab.grab(all_screens= True)
- handle new introduced negative coordinates due to multiple monitor:
In file:
pyscreeze/__init__.py, function:def locateOnScreen(image, minSearchTime=0, **kwargs):behindretVal = locate(image, screenshotIm, **kwargs)>add
if retVal and sys.platform == 'win32': # get the lowest x and y coordinate of the monitor setup monitors = win32api.EnumDisplayMonitors() x_min = min([mon[2][0] for mon in monitors]) y_min = min([mon[2][1] for mon in monitors]) # add negative offset due to multi monitor retVal = Box(left=retVal[0] + x_min, top=retVal[1] + y_min, width=retVal[2],height=retVal[3])
- don't forget to add the
import win32apiIn file:
pyscreeze/__init__.py:
if sys.platform == 'win32': # TODO - Pillow now supports ImageGrab on macOS. import win32api # used for multi-monitor fix from PIL import ImageGrab
Unfortunately pyautogui currently doesn't work with multiple monitors, you can find it in their FAQ
Q: Does PyAutoGUI work on multi-monitor setups.
A: No, right now PyAutoGUI only handles the primary monitor.
As of searching specific area you can use optional region=(startXValue,startYValue,width,height) parameter as shown here.
Roadmap: Add multi-monitor support
python - Using pyautogui with multiple monitors - Stack Overflow
python - Move mouse cursor to second monitor using pyautogui - Stack Overflow
python - Screenshot on multiple monitor setup pyautogui - Stack Overflow
Videos
Just wondering if anyone else has run into problems using pyautogui.locateCenterOnScreen with multiple monitors and if so, did you find any solution to help the function. I keep getting a None value returned when the image is clearly on the screen.
Thanks
Yes it is possible! Use this code to determine where your mouse is registering on the screen:
import pyautogui
print('Press Ctrl-C to quit.')
try:
while True:
x, y = pyautogui.position()
positionStr = 'X: ' + str(x).rjust(4) + ' Y: ' + str(y).rjust(4)
print(positionStr, end='')
print('\b' * len(positionStr), end='', flush=True)
except KeyboardInterrupt:
print('\nDone.')
This should be run from a command prompt. The output will be nonsensical if run through IDLE.
not sure if this is clear but I subtracted an extended monitor's horizontal resolution from 0 because my 2nd monitor is on the left of my primary display. That allowed me to avoid the out of bounds warning. my answer probably isn't the clearest but I figured I would chime in to let folks know it actually can work.
» pip install PyAutoGUI
mss package can easily solve this problem
1. Install the package
pip install mss
2. Take screenshots
First Monitor
import os
import os.path
import mss
def on_exists(fname):
# type: (str) -> None
"""
Callback example when we try to overwrite an existing screenshot.
"""
if os.path.isfile(fname):
newfile = fname + ".old"
print("{} -> {}".format(fname, newfile))
os.rename(fname, newfile)
with mss.mss() as sct:
filename = sct.shot(output="mon-{mon}.png", callback=on_exists)
print(filename)
Second Monitor
import mss
import mss.tools
with mss.mss() as sct:
# Get information of monitor 2
monitor_number = 2
mon = sct.monitors[monitor_number]
# The screen part to capture
monitor = {
"top": mon["top"],
"left": mon["left"],
"width": mon["width"],
"height": mon["height"],
"mon": monitor_number,
}
output = "sct-mon{mon}_{top}x{left}_{width}x{height}.png".format(**monitor)
# Grab the data
sct_img = sct.grab(monitor)
# Save to the picture file
mss.tools.to_png(sct_img.rgb, sct_img.size, output=output)
print(output)
Use with OpenCV
import mss
import cv2
import numpy as np
with mss.mss() as sct:
# Get information of monitor 2
monitor_number = 2
mon = sct.monitors[monitor_number]
# The screen part to capture
monitor = {
"top": mon["top"],
"left": mon["left"],
"width": mon["width"],
"height": mon["height"],
"mon": monitor_number,
}
output = "sct-mon{mon}_{top}x{left}_{width}x{height}.png".format(**monitor)
# Grab the data
sct_img = sct.grab(monitor)
img = np.array(sct.grab(monitor)) # BGR Image
# Display the picture
cv2.imshow("OpenCV", img)
cv2.waitKey(0)
3. Important Notes
sct.monitorswill contain more than one item even if you have a single monitor because the first item will be the combined screens
>>> sct.monitors # if we have a single monitor
[{'left': 0, 'top': 0, 'width': 1366, 'height': 768},
{'left': 0, 'top': 0, 'width': 1366, 'height': 768}]
>>> sct.monitors # if we have two monitors
[{'left': 0, 'top': 0, 'width': 3286, 'height': 1080},
{'left': 1920, 'top': 0, 'width': 1366, 'height': 768},
{'left': 0, 'top': 0, 'width': 1920, 'height': 1080}]
found an solution here!
from PIL import ImageGrab
from functools import partial
ImageGrab.grab = partial(ImageGrab.grab, all_screens=True)
pyautogui.screenshot() will capture all screens.
I came across a problem today that could be solved by a beginner / intermediate python programmer and a little time. Anyone want to take on a challenge?
The pyautogui.screenshot() function on Windows does not support multiple monitors. This has been a problem for many years, but recently the PIL ImageGrab module (which pyautogui depends on) added the all_screens option.
# primary monitor only import pyautogui im = pyautogui.screenshot() # all monitors from PIL import ImageGrab im = ImageGrab.grab(all_screens=True)
This new feature just needs to be added to pyautogui / pyscreeze. Easy(ish). You would learn a lot, get listed as a contributor to a popular open source project (great for resumes), and it would count toward a hacktoberfest tshirt!
Here's the issue you would close: https://github.com/asweigart/pyautogui/issues/9
Here's the offending line at the core of what needs to be updated: https://github.com/asweigart/pyscreeze/blob/master/pyscreeze/init.py#L427
I'm the creator of PyAutoGUI. The problem you have isn't with the screen resolution, but the screen scaling. Your program will work fine on monitors at different resolutions. But at high resolutions, the text and buttons of your programs become too small and so Windows and macOS fix this with "screen scaling", which can increase the size of UI elements like text and buttons by 150% or 200%. If you have multiple monitors, each monitor can have it's own screen scaling setting; for example one monitor at 100% and another at 150%.
If you take a screenshot while a monitor is at, for example, 100% and then try to use it on a monitor that is scaled at 200%, the image won't match because the screenshot is literally half the width and length of what it would have been on the 200% monitor.
So far, there is no work around for this. Resizing the screenshot might not work because there could be subtle differences and the screenshot mechanism currently needs a pixel-perfect match. You just need to retake the screenshots on the monitor with the different screen scaling setting.
I think you'll need to take a screenshot of the image on the different resolution, and at the start of your program have it detect whether it's on the 1600x900 screen or the 1340x640 screen. Then make all the 'locateOnScreen' pieces take a variable, and depending on the screen size, replace those variables with the path to the correct image.
import pyautogui
def function():
pyautogui.locateOnScreen(x)
...
pyautogui.locateOnScreen(y)
...
screen = pyautogui.size()
if screen = (1600, 900):
x = 'image1_1600_900.png'
y = 'image2_1600_900.png'
else:
x = 'image1_1340_640.png'
y = 'image2_1340_640.png'
function()