Skip to content

Commit fa368d6

Browse files
committed
FIX: Adjustments for macOS compatibility with pynput.
1 parent 034321a commit fa368d6

File tree

4 files changed

+54
-43
lines changed

4 files changed

+54
-43
lines changed

botcity/core/bot.py

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,28 @@
55
import random
66
import subprocess
77
import time
8-
from typing import Union
98
import webbrowser
9+
from typing import Union
1010

11-
import pyautogui
1211
import pyperclip
1312
from botcity.base import BaseBot, State
1413
from botcity.base.utils import is_retina, only_if_element
15-
from PIL import Image
16-
17-
from pynput.keyboard import Key, Controller as KbController
14+
from PIL import Image, ImageGrab
15+
from pynput.keyboard import Controller as KbController
16+
from pynput.keyboard import Key
1817
from pynput.mouse import Controller as MouseController
19-
from .input_utils import keys_map, mouse_map, _mouse_click
2018

2119
from . import config, cv2find
20+
from .input_utils import _mouse_click, keys_map, mouse_map
2221

2322
try:
2423
from pywinauto.application import Application, WindowSpecification
25-
from .application.functions import connect, find_window, find_element
24+
25+
from .application.functions import connect, find_element, find_window
2626
except ImportError:
2727
pass
2828

29-
from .application.utils import Backend, if_windows_os, if_app_connected
29+
from .application.utils import Backend, if_app_connected, if_windows_os
3030

3131
try:
3232
from botcity.maestro import BotMaestroSDK
@@ -202,7 +202,7 @@ def _to_dict(lbs, elems):
202202
if elapsed_time > waiting_time:
203203
return _to_dict(labels, results)
204204

205-
haystack = pyautogui.screenshot()
205+
haystack = self.screenshot()
206206
helper = functools.partial(self._find_multiple_helper, haystack, region, matching, grayscale)
207207

208208
with multiprocessing.Pool(processes=n_cpus) as pool:
@@ -225,7 +225,7 @@ def _fix_retina_element(self, ele):
225225
return ele
226226

227227
def _fix_display_size(self):
228-
width, height = pyautogui.size()
228+
width, height = ImageGrab.grab().size
229229

230230
if not is_retina():
231231
return width, height
@@ -473,7 +473,9 @@ def screenshot(self, filepath=None, region=None):
473473
Returns:
474474
Image: The screenshot Image object
475475
"""
476-
img = pyautogui.screenshot(filepath, region)
476+
img = ImageGrab.grab(bbox=region)
477+
if filepath:
478+
img.save(filepath)
477479
return img
478480

479481
def get_screenshot(self, filepath=None, region=None):
@@ -507,7 +509,7 @@ def screen_cut(self, x, y, width=None, height=None):
507509
y = y or 0
508510
width = width or screen_w
509511
height = height or screen_h
510-
img = pyautogui.screenshot(region=(x, y, width, height))
512+
img = self.screenshot(region=(x, y, width, height))
511513
return img
512514

513515
def save_screenshot(self, path):
@@ -518,7 +520,7 @@ def save_screenshot(self, path):
518520
path (str): The filepath in which to save the screenshot
519521
520522
"""
521-
pyautogui.screenshot(path)
523+
self.screenshot(path)
522524

523525
def get_element_coords(self, label, x=None, y=None, width=None, height=None, matching=0.9, best=True):
524526
"""

botcity/core/input_utils.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
import platform
12
import time
3+
24
from pynput.keyboard import Key
35
from pynput.mouse import Button, Controller
46

5-
67
keys_map = {
78
"num0": '0',
89
"num1": '1',
@@ -42,16 +43,23 @@
4243
"volumeup": Key.media_volume_up,
4344
"prevtrack": Key.media_previous,
4445
"nexttrack": Key.media_next,
45-
"numlock": Key.num_lock,
46-
"printscreen": Key.print_screen,
47-
"prntscrn": Key.print_screen,
48-
"prtsc": Key.print_screen,
49-
"prtscr": Key.print_screen,
50-
"print": Key.print_screen,
51-
"scrolllock": Key.scroll_lock,
5246
"return": Key.enter
5347
}
5448

49+
if platform.system() != "Darwin":
50+
keys_map.update(
51+
{
52+
"numlock": Key.num_lock,
53+
"prtsc": Key.print_screen,
54+
"prtscr": Key.print_screen,
55+
"printscreen": Key.print_screen,
56+
"prntscrn": Key.print_screen,
57+
"print": Key.print_screen,
58+
"scrolllock": Key.scroll_lock,
59+
}
60+
)
61+
62+
5563

5664
mouse_map = {
5765
"left": Button.left,
@@ -64,13 +72,17 @@ def _mouse_click(mouse_controller: Controller, x: int, y: int, clicks=1, interva
6472
"""
6573
Moves the mouse and clicks at the coordinate defined by x and y.
6674
"""
67-
mouse_button = mouse_map.get(button, None)
68-
if not mouse_button:
69-
raise ValueError(f'''Invalid mouse button name.
70-
The mouse button has to be one of these values: {list(mouse_map.keys())}''')
75+
if platform.system() == "Darwin":
76+
from . import os_compat
77+
os_compat.osx_click(x=x, y=y, clicks=clicks, interval=interval_between_clicks, button=button)
78+
else:
79+
mouse_button = mouse_map.get(button, None)
80+
if not mouse_button:
81+
raise ValueError(f'''Invalid mouse button name.
82+
The mouse button has to be one of these values: {list(mouse_map.keys())}''')
7183

72-
mouse_controller.position = (x, y)
73-
time.sleep(0.1)
74-
for i in range(clicks):
75-
mouse_controller.click(button=mouse_button, count=1)
76-
time.sleep(interval_between_clicks / 1000.0)
84+
mouse_controller.position = (x, y)
85+
time.sleep(0.1)
86+
for i in range(clicks):
87+
mouse_controller.click(button=mouse_button, count=1)
88+
time.sleep(interval_between_clicks / 1000.0)

botcity/core/os_compat.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
11
# Module for OS Compatibility issues and PyAutoGui
22
import platform
33

4-
import pyautogui
4+
OSX_LEFT = "left"
5+
OSX_MIDDLE = "middle"
6+
OSX_RIGHT = "right"
57

6-
if platform.system() == "Darwin":
7-
import pyautogui._pyautogui_osx as osx
88

9+
if platform.system() == "Darwin":
910
def _multiClick(x, y, button, num, interval=0.0):
1011
import Quartz
1112
btn = None
1213
down = None
1314
up = None
1415

15-
if button == osx.LEFT:
16+
if button == OSX_LEFT:
1617
btn = Quartz.kCGMouseButtonLeft
1718
down = Quartz.kCGEventLeftMouseDown
1819
up = Quartz.kCGEventLeftMouseUp
19-
elif button == osx.MIDDLE:
20+
elif button == OSX_MIDDLE:
2021
btn = Quartz.kCGMouseButtonCenter
2122
down = Quartz.kCGEventOtherMouseDown
2223
up = Quartz.kCGEventOtherMouseUp
23-
elif button == osx.RIGHT:
24+
elif button == OSX_RIGHT:
2425
btn = Quartz.kCGMouseButtonRight
2526
down = Quartz.kCGEventRightMouseDown
2627
up = Quartz.kCGEventRightMouseUp
@@ -34,14 +35,12 @@ def _multiClick(x, y, button, num, interval=0.0):
3435
Quartz.CGEventPost(Quartz.kCGHIDEventTap, mouseEvent)
3536

3637

37-
def click(x=None, y=None, clicks=1, interval=0.0, button="left", **kwargs):
38+
def osx_click(x=None, y=None, clicks=1, interval=0.0, button="left", **kwargs):
3839
"""
3940
This method is here due to issues with pyautogui implementation of multiple clicks on macOS.
4041
For that, the code above from _multiClick was pulled from pyautogui and locally patched.
4142
A PR will be submitted to the pyautogui project to fix the issue upstream and once a new
4243
release with the patch is available we will remove our local patch here.
4344
"""
44-
if platform.system() == "Darwin":
45-
_multiClick(x=x, y=y, button=button, num=clicks, interval=interval)
46-
else:
47-
pyautogui.click(x=x, y=y, clicks=clicks, interval=interval, button=button, **kwargs)
45+
_multiClick(x=x, y=y, button=button, num=clicks, interval=interval)
46+

requirements.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
botcity-framework-base>=0.4.0,<1.0
2-
pyautogui==0.9.53
3-
pyscreeze==0.1.27
42
pyperclip
53
opencv-python
64
python-xlib

0 commit comments

Comments
 (0)