From bee569d3239d9fb97e61eb0803aad338d342afc9 Mon Sep 17 00:00:00 2001 From: glitchassassin Date: Tue, 28 Feb 2017 08:30:44 -0500 Subject: [PATCH 1/3] Fixed wheel functionality in #57 --- lackey/RegionMatching.py | 18 +++++++++++++++--- tests/test_cases.py | 1 + 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lackey/RegionMatching.py b/lackey/RegionMatching.py index 2092cc1..dbec0f9 100644 --- a/lackey/RegionMatching.py +++ b/lackey/RegionMatching.py @@ -815,11 +815,23 @@ def mouseUp(self, button): return PlatformManager.mouseButtonUp(button) def mouseMove(self, PSRML, dy=0): """ Low-level mouse actions """ - if isinstance(PSRML, int): + if isinstance(PSRML, Pattern): + move_location = self.find(PSRML).getTarget() + elif isinstance(PSRML, basestring): + move_location = self.find(PSRML).getTarget() + elif isinstance(PSRML, Match): + move_location = PSRML.getTarget() + elif isinstance(PSRML, Region): + move_location = PSRML.getCenter() + elif isinstance(PSRML, Location): + move_location = PSRML + elif isinstance(PSRML, int): # Assume called as mouseMove(dx, dy) offset = Location(PSRML, dy) - PSRML = Mouse().getPos().offset(offset) - Mouse().moveSpeed(PSRML) + move_location = Mouse().getPos().offset(offset) + else: + raise TypeError("doubleClick expected Pattern, String, Match, Region, or Location object") + Mouse().moveSpeed(move_location) def wheel(self, PSRML, direction, steps): """ Clicks the wheel the specified number of ticks """ self.mouseMove(PSRML) diff --git a/tests/test_cases.py b/tests/test_cases.py index 8424957..1ef84d5 100644 --- a/tests/test_cases.py +++ b/tests/test_cases.py @@ -24,6 +24,7 @@ def test_movement(self): self.assertEqual(self.mouse.getPos().getTuple(), (10,10)) self.mouse.moveSpeed(lackey.Location(100,200), 0.5) self.assertEqual(self.mouse.getPos().getTuple(), (100,200)) + lackey.wheel(self.mouse.getPos(), 0, 3) # Mostly just verifying it doesn't crash def test_clicks(self): """ From 64ac5b0b7af31aa29944775ced2710d7a3f7a9b0 Mon Sep 17 00:00:00 2001 From: glitchassassin Date: Mon, 6 Mar 2017 15:24:27 -0500 Subject: [PATCH 2/3] Fixed mouse move speed Was not respecting MoveMouseDelay in Settings. This has been corrected. Also fixed an issue with building to EXE in Python 3.5. --- lackey/PlatformManagerWindows.py | 14 -------------- lackey/RegionMatching.py | 13 ++++++------- lackey/__init__.py | 5 ++++- 3 files changed, 10 insertions(+), 22 deletions(-) diff --git a/lackey/PlatformManagerWindows.py b/lackey/PlatformManagerWindows.py index 671cce6..50e0f50 100644 --- a/lackey/PlatformManagerWindows.py +++ b/lackey/PlatformManagerWindows.py @@ -210,20 +210,6 @@ def _check_count(self, result, func, args): raise ctypes.WinError(ctypes.get_last_error()) return args - - """ Clicks the mouse wheel the specified number of steps in the given direction - - Valid directions are 0 (for down) and 1 (for up). These should be provided - as constants by the Mouse class. - """ - if direction == 1: - wheel_moved = steps - elif direction == 0: - wheel_moved = -1*steps - else: - raise ValueError("Expected direction to be 1 or 0") - mouse._os_mouse.wheel(wheel_moved) - ## Screen functions def getBitmapFromRect(self, x, y, w, h): diff --git a/lackey/RegionMatching.py b/lackey/RegionMatching.py index dbec0f9..d330a5f 100644 --- a/lackey/RegionMatching.py +++ b/lackey/RegionMatching.py @@ -106,7 +106,6 @@ def __init__(self, *args): self.autoWaitTimeout = 3.0 # Converts searches per second to actual second interval self._defaultScanRate = 1/Settings.WaitScanRate - self._defaultMouseSpeed = Settings.MoveMouseDelay self._defaultTypeSpeed = 0.05 self._raster = (0, 0) @@ -574,7 +573,7 @@ def click(self, target, modifiers=""): if modifiers != "": Keyboard().keyDown(modifiers) - mouse.moveSpeed(target_location, self._defaultMouseSpeed) + mouse.moveSpeed(target_location, Settings.MoveMouseDelay) time.sleep(0.1) # For responsiveness if Settings.ClickDelay > 0: time.sleep(min(1.0, Settings.ClickDelay)) @@ -604,7 +603,7 @@ def doubleClick(self, target, modifiers=""): if modifiers != "": Keyboard().keyDown(modifiers) - mouse.moveSpeed(target_location, self._defaultMouseSpeed) + mouse.moveSpeed(target_location, Settings.MoveMouseDelay) time.sleep(0.1) if Settings.ClickDelay > 0: time.sleep(min(1.0, Settings.ClickDelay)) @@ -639,7 +638,7 @@ def rightClick(self, target, modifiers=""): if modifiers != "": Keyboard().keyDown(modifiers) - mouse.moveSpeed(target_location, self._defaultMouseSpeed) + mouse.moveSpeed(target_location, Settings.MoveMouseDelay) time.sleep(0.1) if Settings.ClickDelay > 0: time.sleep(min(1.0, Settings.ClickDelay)) @@ -667,7 +666,7 @@ def hover(self, target): else: raise TypeError("hover expected Pattern, String, Match, Region, or Location object") - mouse.moveSpeed(target_location, self._defaultMouseSpeed) + mouse.moveSpeed(target_location, Settings.MoveMouseDelay) def drag(self, dragFrom): """ Starts a dragDrop operation. @@ -687,7 +686,7 @@ def drag(self, dragFrom): dragFromLocation = dragFrom else: raise TypeError("drag expected dragFrom to be Pattern, String, Match, Region, or Location object") - mouse.moveSpeed(dragFromLocation, self._defaultMouseSpeed) + mouse.moveSpeed(dragFromLocation, Settings.MoveMouseDelay) time.sleep(Settings.DelayBeforeMouseDown) mouse.buttonDown() Debug.history("Began drag at {}".format(dragFromLocation)) @@ -710,7 +709,7 @@ def dropAt(self, dragTo, delay=None): else: raise TypeError("dragDrop expected dragTo to be Pattern, String, Match, Region, or Location object") - mouse.moveSpeed(dragToLocation, self._defaultMouseSpeed) + mouse.moveSpeed(dragToLocation, Settings.MoveMouseDelay) time.sleep(delay if delay is not None else Settings.DelayBeforeDrop) mouse.buttonUp() Debug.history("Ended drag at {}".format(dragToLocation)) diff --git a/lackey/__init__.py b/lackey/__init__.py index ec9920a..ee0ada7 100644 --- a/lackey/__init__.py +++ b/lackey/__init__.py @@ -45,7 +45,10 @@ _type = type _input = input -_exit = exit +try: + _exit = exit +except NameError: + pass # `exit` is not always defined, as when building to executable. #_zip = zip ## Sikuli Convenience Functions From 2f474f1fa786fd8f8a132cd3c85b9e86fb22be96 Mon Sep 17 00:00:00 2001 From: glitchassassin Date: Wed, 15 Mar 2017 08:18:32 -0400 Subject: [PATCH 3/3] Replaced Tkinter clipboard with pyperclip module Also cleaned up the old PlatformManager documentation --- docs/source/platformmanager.rst | 34 ------------------------------ lackey/PlatformManagerWindows.py | 36 -------------------------------- lackey/RegionMatching.py | 8 ++++--- requirements.txt | 1 + setup.py | 2 +- tests/appveyor_test_cases.py | 2 -- tests/test_cases.py | 2 +- 7 files changed, 8 insertions(+), 77 deletions(-) diff --git a/docs/source/platformmanager.rst b/docs/source/platformmanager.rst index fab4df9..a8a6d1a 100644 --- a/docs/source/platformmanager.rst +++ b/docs/source/platformmanager.rst @@ -14,40 +14,11 @@ PlatformManager has no public properties. Accessing private properties of a spec Methods ------- -- **Mouse Functions** - - **clickMouse** (button=0): - - Clicks the specified mouse button (0=LEFT, 1=MIDDLE, 2=RIGHT) - - **mouseButtonDown** (button=0): - - Sets the specified mouse button to "down" (0=LEFT, 1=MIDDLE, 2=RIGHT) - - **mouseButtonUp** (button=0): - - Sets the specified mouse button to "up" (0=LEFT, 1=MIDDLE, 2=RIGHT) - - **mouseWheel** (direction, steps): - - Spins the mouse wheel ``steps`` ticks in ``direction`` (0=DOWN, 1=UP) - - **setMousePos** (location): - - Accepts a tuple ``(x,y)`` and sets the mouse position accordingly - - **getMousePos** (): - - Returns a tuple ``(x,y)`` of the current mouse position -- **Keyboard Functions** - - **pressKey** (text): - - Accepts a string of one or more keys in typeKeys() format (see below). Sets all of them to "down." - - **releaseKey** (text): - - Accepts a string of one or more keys in typeKeys() format (see below). Sets all of them to "up." - - **typeKeys** (): - - Accepts a string of one or more keys and translates them into a series of keypresses. Keys may be "special keys" noted by brackets (e.g., ``"Put in a new line{ENTER}"``). - - Uses shift key to enter uppercase letters/symbols. - - ``"Hello!"`` translates to ``SHIFT,h,unshift,e,l,l,o,SHIFT,1,unshift`` - - Also supports SendKeys-style modifiers (e.g., ``"^v"`` to type CTRL+V). - - To enter a modifier as a key, put it in brackets: ``"{+}"``. - - To modify a group of characters, use parentheses: ``"+(bacon)"`` - **Clipboard Functions** - **osCopy** (): - Triggers the OS "copy" keyboard shortcut (usually CTRL+C or CMD+C) - **osPaste** (): - Triggers the OS "paste" keyboard shortcut (usually CTRL+V or CMD+V) - - **setClipboard** (text): - - Sets the system clipboard to ``text`` - - **getClipboard** (): - - Returns any text in the clipboard - **Window Functions** - **getWindowByTitle** (wildcard, windowNum=0): - Returns the native window handle (as a Python object) for a window that matches the specified regex. If multiple windows with the regex exist, return the ``windowNum`` -th of them (starting from 0). @@ -86,11 +57,6 @@ Methods ### Supported Key Codes ### -- **Modifiers** - - ``+``: SHIFT - - ``^``: CTRL - - ``%``: ALT - - ``@``: META/WIN/CMD - **Special Keys** - ``{BACKSPACE}`` - ``{TAB}`` diff --git a/lackey/PlatformManagerWindows.py b/lackey/PlatformManagerWindows.py index 50e0f50..e1c4ae2 100644 --- a/lackey/PlatformManagerWindows.py +++ b/lackey/PlatformManagerWindows.py @@ -485,42 +485,6 @@ def _getVirtualScreenBitmap(self): ## Clipboard functions - def getClipboard(self): - """ Uses Tkinter to fetch any text on the clipboard. - - If a Tkinter root window has already been created somewhere else, - uses that instead of creating a new one. - """ - if tk._default_root is None: - temporary_root = True - root = tk.Tk() - root.withdraw() - else: - temporary_root = False - root = tk._default_root - root.update() - to_return = str(root.clipboard_get()) - if temporary_root: - root.destroy() - return to_return - def setClipboard(self, text): - """ Uses Tkinter to set the system clipboard. - - If a Tkinter root window has already been created somewhere else, - uses that instead of creating a new one. - """ - if tk._default_root is None: - temporary_root = True - root = tk.Tk() - root.withdraw() - else: - temporary_root = False - root = tk._default_root - root.clipboard_clear() - root.clipboard_append(text) - root.update() - if temporary_root: - root.destroy() def osCopy(self): """ Triggers the OS "copy" keyboard shortcut """ k = Keyboard() diff --git a/lackey/RegionMatching.py b/lackey/RegionMatching.py index d330a5f..3166453 100644 --- a/lackey/RegionMatching.py +++ b/lackey/RegionMatching.py @@ -4,6 +4,7 @@ except ImportError: import tkinter as tk import subprocess +import pyperclip import tempfile import platform import numpy @@ -793,15 +794,16 @@ def paste(self, *args): else: raise TypeError("paste method expected [PSMRL], text") - PlatformManager.setClipboard(text) + pyperclip.copy(text) # Triggers OS paste for foreground window PlatformManager.osPaste() time.sleep(0.2) def getClipboard(self): """ Returns the contents of the clipboard - Can be used to pull outside text into the application. """ - return PlatformManager.getClipboard() + Can be used to pull outside text into the application, if it is first + copied with the OS keyboard shortcut (e.g., "Ctrl+C") """ + return pyperclip.paste() def text(self): """ OCR method. Todo. """ raise NotImplementedError("OCR not yet supported") diff --git a/requirements.txt b/requirements.txt index 0176a30..7609f5f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,5 +7,6 @@ numpy pillow opencv-python wheel +pyperclip keyboard>=v0.9.11 twine \ No newline at end of file diff --git a/setup.py b/setup.py index ca7af4c..5d307be 100644 --- a/setup.py +++ b/setup.py @@ -42,7 +42,7 @@ def is_pure(self): ], keywords="automation testing sikuli", packages=find_packages(exclude=['docs', 'tests']), - install_requires=['requests', 'pillow', 'numpy', 'opencv-python', 'keyboard'], + install_requires=['requests', 'pillow', 'numpy', 'opencv-python', 'keyboard', 'pyperclip'], include_package_data=True, distclass=BinaryDistribution ) diff --git a/tests/appveyor_test_cases.py b/tests/appveyor_test_cases.py index 62677f2..cc2e4a1 100644 --- a/tests/appveyor_test_cases.py +++ b/tests/appveyor_test_cases.py @@ -205,8 +205,6 @@ def test_platform_manager_interface(self): self.assertHasMethod(lackey.PlatformManagerWindows, "isPointVisible", 3) ## Clipboard methods - self.assertHasMethod(lackey.PlatformManagerWindows, "getClipboard", 1) - self.assertHasMethod(lackey.PlatformManagerWindows, "setClipboard", 2) self.assertHasMethod(lackey.PlatformManagerWindows, "osCopy", 1) self.assertHasMethod(lackey.PlatformManagerWindows, "osPaste", 1) diff --git a/tests/test_cases.py b/tests/test_cases.py index 1ef84d5..6122108 100644 --- a/tests/test_cases.py +++ b/tests/test_cases.py @@ -7,7 +7,7 @@ #sys.path.insert(0, os.path.abspath('..')) import lackey -from appveyor_test_cases import TestLocationMethods, TestPatternMethods, TestInterfaces, TestConvenienceFunctions +from .appveyor_test_cases import TestLocationMethods, TestPatternMethods, TestInterfaces, TestConvenienceFunctions # Python 3 compatibility try: