| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171 |
- # PyAutoGUI lets Python control the mouse and keyboard, and other GUI automation tasks. For Windows, macOS, and Linux,
- # on Python 3 and 2.
- # https://github.com/asweigart/pyautogui
- # Al Sweigart al@inventwithpython.com (Send me feedback & suggestions!)
- # TODO - the following features are half-implemented right now:
- # snapshot logging
- # non-qwerty keyboard mapping
- # primary secondary mouse button awareness
- from __future__ import absolute_import, division, print_function
- __version__ = "0.9.54"
- import collections
- import sys
- import time
- import datetime
- import os
- import platform
- import re
- import functools
- from contextlib import contextmanager
- class PyAutoGUIException(Exception):
- """
- PyAutoGUI code will raise this exception class for any invalid actions. If PyAutoGUI raises some other exception,
- you should assume that this is caused by a bug in PyAutoGUI itself. (Including a failure to catch potential
- exceptions raised by PyAutoGUI.)
- """
- pass
- class FailSafeException(PyAutoGUIException):
- """
- This exception is raised by PyAutoGUI functions when the user puts the mouse cursor into one of the "failsafe
- points" (by default, one of the four corners of the primary monitor). This exception shouldn't be caught; it's
- meant to provide a way to terminate a misbehaving script.
- """
- pass
- class ImageNotFoundException(PyAutoGUIException):
- """
- This exception is the PyAutoGUI version of PyScreeze's `ImageNotFoundException`, which is raised when a locate*()
- function call is unable to find an image.
- Ideally, `pyscreeze.ImageNotFoundException` should never be raised by PyAutoGUI.
- """
- if sys.version_info[0] == 2 or sys.version_info[0:2] in ((3, 1), (3, 2)):
- # Python 2 and 3.1 and 3.2 uses collections.Sequence
- from collections import Sequence
- else:
- # Python 3.3+ uses collections.abc.Sequence
- from collections.abc import Sequence
- try:
- from pytweening import (
- easeInQuad,
- easeOutQuad,
- easeInOutQuad,
- easeInCubic,
- easeOutCubic,
- easeInOutCubic,
- easeInQuart,
- easeOutQuart,
- easeInOutQuart,
- easeInQuint,
- easeOutQuint,
- easeInOutQuint,
- easeInSine,
- easeOutSine,
- easeInOutSine,
- easeInExpo,
- easeOutExpo,
- easeInOutExpo,
- easeInCirc,
- easeOutCirc,
- easeInOutCirc,
- easeInElastic,
- easeOutElastic,
- easeInOutElastic,
- easeInBack,
- easeOutBack,
- easeInOutBack,
- easeInBounce,
- easeOutBounce,
- easeInOutBounce,
- )
- # getLine is not needed.
- # getPointOnLine has been redefined in this file, to avoid dependency on pytweening.
- # linear has also been redefined in this file.
- except ImportError:
- def _couldNotImportPyTweening(*unused_args, **unused_kwargs):
- """
- This function raises ``PyAutoGUIException``. It's used for the PyTweening function names if the PyTweening
- module failed to be imported.
- """
- raise PyAutoGUIException(
- "PyAutoGUI was unable to import pytweening. Please install this module to enable the function you tried to call."
- )
- easeInQuad = _couldNotImportPyTweening
- easeOutQuad = _couldNotImportPyTweening
- easeInOutQuad = _couldNotImportPyTweening
- easeInCubic = _couldNotImportPyTweening
- easeOutCubic = _couldNotImportPyTweening
- easeInOutCubic = _couldNotImportPyTweening
- easeInQuart = _couldNotImportPyTweening
- easeOutQuart = _couldNotImportPyTweening
- easeInOutQuart = _couldNotImportPyTweening
- easeInQuint = _couldNotImportPyTweening
- easeOutQuint = _couldNotImportPyTweening
- easeInOutQuint = _couldNotImportPyTweening
- easeInSine = _couldNotImportPyTweening
- easeOutSine = _couldNotImportPyTweening
- easeInOutSine = _couldNotImportPyTweening
- easeInExpo = _couldNotImportPyTweening
- easeOutExpo = _couldNotImportPyTweening
- easeInOutExpo = _couldNotImportPyTweening
- easeInCirc = _couldNotImportPyTweening
- easeOutCirc = _couldNotImportPyTweening
- easeInOutCirc = _couldNotImportPyTweening
- easeInElastic = _couldNotImportPyTweening
- easeOutElastic = _couldNotImportPyTweening
- easeInOutElastic = _couldNotImportPyTweening
- easeInBack = _couldNotImportPyTweening
- easeOutBack = _couldNotImportPyTweening
- easeInOutBack = _couldNotImportPyTweening
- easeInBounce = _couldNotImportPyTweening
- easeOutBounce = _couldNotImportPyTweening
- easeInOutBounce = _couldNotImportPyTweening
- try:
- from pymsgbox import alert, confirm, prompt, password
- except ImportError:
- # If pymsgbox module is not found, those methods will not be available.
- def _couldNotImportPyMsgBox(*unused_args, **unused_kwargs):
- """
- This function raises ``PyAutoGUIException``. It's used for the PyMsgBox function names if the PyMsgbox module
- failed to be imported.
- """
- raise PyAutoGUIException(
- "PyAutoGUI was unable to import pymsgbox. Please install this module to enable the function you tried to call."
- )
- alert = confirm = prompt = password = _couldNotImportPyMsgBox
- def raisePyAutoGUIImageNotFoundException(wrappedFunction):
- """
- A decorator that wraps PyScreeze locate*() functions so that the PyAutoGUI user sees them raise PyAutoGUI's
- ImageNotFoundException rather than PyScreeze's ImageNotFoundException. This is because PyScreeze should be
- invisible to PyAutoGUI users.
- """
- @functools.wraps(wrappedFunction)
- def wrapper(*args, **kwargs):
- try:
- return wrappedFunction(*args, **kwargs)
- except pyscreeze.ImageNotFoundException:
- raise ImageNotFoundException # Raise PyAutoGUI's ImageNotFoundException.
- return wrapper
- try:
- import pyscreeze
- from pyscreeze import center, pixel, pixelMatchesColor, screenshot
- # Change the locate*() functions so that they raise PyAutoGUI's ImageNotFoundException instead.
- @raisePyAutoGUIImageNotFoundException
- def locate(*args, **kwargs):
- return pyscreeze.locate(*args, **kwargs)
- locate.__doc__ = pyscreeze.locate.__doc__
- @raisePyAutoGUIImageNotFoundException
- def locateAll(*args, **kwargs):
- return pyscreeze.locateAll(*args, **kwargs)
- locateAll.__doc__ = pyscreeze.locateAll.__doc__
- @raisePyAutoGUIImageNotFoundException
- def locateAllOnScreen(*args, **kwargs):
- return pyscreeze.locateAllOnScreen(*args, **kwargs)
- locateAllOnScreen.__doc__ = pyscreeze.locateAllOnScreen.__doc__
- @raisePyAutoGUIImageNotFoundException
- def locateCenterOnScreen(*args, **kwargs):
- return pyscreeze.locateCenterOnScreen(*args, **kwargs)
- locateCenterOnScreen.__doc__ = pyscreeze.locateCenterOnScreen.__doc__
- @raisePyAutoGUIImageNotFoundException
- def locateOnScreen(*args, **kwargs):
- return pyscreeze.locateOnScreen(*args, **kwargs)
- locateOnScreen.__doc__ = pyscreeze.locateOnScreen.__doc__
- @raisePyAutoGUIImageNotFoundException
- def locateOnWindow(*args, **kwargs):
- return pyscreeze.locateOnWindow(*args, **kwargs)
- locateOnWindow.__doc__ = pyscreeze.locateOnWindow.__doc__
- except ImportError:
- # If pyscreeze module is not found, screenshot-related features will simply not work.
- def _couldNotImportPyScreeze(*unused_args, **unsed_kwargs):
- """
- This function raises ``PyAutoGUIException``. It's used for the PyScreeze function names if the PyScreeze module
- failed to be imported.
- """
- raise PyAutoGUIException(
- "PyAutoGUI was unable to import pyscreeze. (This is likely because you're running a version of Python that Pillow (which pyscreeze depends on) doesn't support currently.) Please install this module to enable the function you tried to call."
- )
- center = _couldNotImportPyScreeze
- #grab = _couldNotImportPyScreeze # grab() was removed, use screenshot() instead
- locate = _couldNotImportPyScreeze
- locateAll = _couldNotImportPyScreeze
- locateAllOnScreen = _couldNotImportPyScreeze
- locateCenterOnScreen = _couldNotImportPyScreeze
- locateOnScreen = _couldNotImportPyScreeze
- locateOnWindow = _couldNotImportPyScreeze
- pixel = _couldNotImportPyScreeze
- pixelMatchesColor = _couldNotImportPyScreeze
- screenshot = _couldNotImportPyScreeze
- try:
- import mouseinfo
- def mouseInfo():
- """
- Launches the MouseInfo app. This application provides mouse coordinate information which can be useful when
- planning GUI automation tasks. This function blocks until the application is closed.
- """
- mouseinfo.MouseInfoWindow()
- except ImportError:
- def mouseInfo():
- """
- This function raises PyAutoGUIException. It's used for the MouseInfo function names if the MouseInfo module
- failed to be imported.
- """
- raise PyAutoGUIException(
- "PyAutoGUI was unable to import mouseinfo. Please install this module to enable the function you tried to call."
- )
- def useImageNotFoundException(value=None):
- """
- When called with no arguments, PyAutoGUI will raise ImageNotFoundException when the PyScreeze locate*() functions
- can't find the image it was told to locate. The default behavior is to return None. Call this function with no
- arguments (or with True as the argument) to have exceptions raised, which is a better practice.
- You can also disable raising exceptions by passing False for the argument.
- """
- if value is None:
- value = True
- # TODO - this will cause a NameError if PyScreeze couldn't be imported:
- try:
- pyscreeze.USE_IMAGE_NOT_FOUND_EXCEPTION = value
- except NameError:
- raise PyAutoGUIException("useImageNotFoundException() ws called but pyscreeze isn't installed.")
- if sys.platform == "win32": # PyGetWindow currently only supports Windows.
- try:
- from pygetwindow import (
- Window,
- getActiveWindow,
- getActiveWindowTitle,
- getWindowsAt,
- getWindowsWithTitle,
- getAllWindows,
- getAllTitles,
- )
- except ImportError:
- # If pygetwindow module is not found, those methods will not be available.
- def _couldNotImportPyGetWindow(*unused_args, **unused_kwargs):
- """
- This function raises PyAutoGUIException. It's used for the PyGetWindow function names if the PyGetWindow
- module failed to be imported.
- """
- raise PyAutoGUIException(
- "PyAutoGUI was unable to import pygetwindow. Please install this module to enable the function you tried to call."
- )
- Window = _couldNotImportPyGetWindow
- getActiveWindow = _couldNotImportPyGetWindow
- getActiveWindowTitle = _couldNotImportPyGetWindow
- getWindowsAt = _couldNotImportPyGetWindow
- getWindowsWithTitle = _couldNotImportPyGetWindow
- getAllWindows = _couldNotImportPyGetWindow
- getAllTitles = _couldNotImportPyGetWindow
- KEY_NAMES = [
- "\t",
- "\n",
- "\r",
- " ",
- "!",
- '"',
- "#",
- "$",
- "%",
- "&",
- "'",
- "(",
- ")",
- "*",
- "+",
- ",",
- "-",
- ".",
- "/",
- "0",
- "1",
- "2",
- "3",
- "4",
- "5",
- "6",
- "7",
- "8",
- "9",
- ":",
- ";",
- "<",
- "=",
- ">",
- "?",
- "@",
- "[",
- "\\",
- "]",
- "^",
- "_",
- "`",
- "a",
- "b",
- "c",
- "d",
- "e",
- "f",
- "g",
- "h",
- "i",
- "j",
- "k",
- "l",
- "m",
- "n",
- "o",
- "p",
- "q",
- "r",
- "s",
- "t",
- "u",
- "v",
- "w",
- "x",
- "y",
- "z",
- "{",
- "|",
- "}",
- "~",
- "accept",
- "add",
- "alt",
- "altleft",
- "altright",
- "apps",
- "backspace",
- "browserback",
- "browserfavorites",
- "browserforward",
- "browserhome",
- "browserrefresh",
- "browsersearch",
- "browserstop",
- "capslock",
- "clear",
- "convert",
- "ctrl",
- "ctrlleft",
- "ctrlright",
- "decimal",
- "del",
- "delete",
- "divide",
- "down",
- "end",
- "enter",
- "esc",
- "escape",
- "execute",
- "f1",
- "f10",
- "f11",
- "f12",
- "f13",
- "f14",
- "f15",
- "f16",
- "f17",
- "f18",
- "f19",
- "f2",
- "f20",
- "f21",
- "f22",
- "f23",
- "f24",
- "f3",
- "f4",
- "f5",
- "f6",
- "f7",
- "f8",
- "f9",
- "final",
- "fn",
- "hanguel",
- "hangul",
- "hanja",
- "help",
- "home",
- "insert",
- "junja",
- "kana",
- "kanji",
- "launchapp1",
- "launchapp2",
- "launchmail",
- "launchmediaselect",
- "left",
- "modechange",
- "multiply",
- "nexttrack",
- "nonconvert",
- "num0",
- "num1",
- "num2",
- "num3",
- "num4",
- "num5",
- "num6",
- "num7",
- "num8",
- "num9",
- "numlock",
- "pagedown",
- "pageup",
- "pause",
- "pgdn",
- "pgup",
- "playpause",
- "prevtrack",
- "print",
- "printscreen",
- "prntscrn",
- "prtsc",
- "prtscr",
- "return",
- "right",
- "scrolllock",
- "select",
- "separator",
- "shift",
- "shiftleft",
- "shiftright",
- "sleep",
- "space",
- "stop",
- "subtract",
- "tab",
- "up",
- "volumedown",
- "volumemute",
- "volumeup",
- "win",
- "winleft",
- "winright",
- "yen",
- "command",
- "option",
- "optionleft",
- "optionright",
- ]
- KEYBOARD_KEYS = KEY_NAMES # keeping old KEYBOARD_KEYS for backwards compatibility
- # Constants for the mouse button names:
- LEFT = "left"
- MIDDLE = "middle"
- RIGHT = "right"
- PRIMARY = "primary"
- SECONDARY = "secondary"
- # Different keyboard mappings:
- # TODO - finish this feature.
- # NOTE: Eventually, I'd like to come up with a better system than this. For now, this seems like it works.
- QWERTY = r"""`1234567890-=qwertyuiop[]\asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:"ZXCVBNM<>?"""
- QWERTZ = r"""=1234567890/0qwertzuiop89-asdfghjkl,\yxcvbnm,.7+!@#$%^&*()?)QWERTZUIOP*(_ASDFGHJKL<|YXCVBNM<>&"""
- def isShiftCharacter(character):
- """
- Returns True if the ``character`` is a keyboard key that would require the shift key to be held down, such as
- uppercase letters or the symbols on the keyboard's number row.
- """
- # NOTE TODO - This will be different for non-qwerty keyboards.
- return character.isupper() or character in set('~!@#$%^&*()_+{}|:"<>?')
- # The platformModule is where we reference the platform-specific functions.
- if sys.platform.startswith("java"):
- # from . import _pyautogui_java as platformModule
- raise NotImplementedError("Jython is not yet supported by PyAutoGUI.")
- elif sys.platform == "darwin":
- from . import _pyautogui_osx as platformModule
- elif sys.platform == "win32":
- from . import _pyautogui_win as platformModule
- elif platform.system() == "Linux":
- from . import _pyautogui_x11 as platformModule
- else:
- raise NotImplementedError("Your platform (%s) is not supported by PyAutoGUI." % (platform.system()))
- # TODO: Having module-wide user-writable global variables is bad. It makes
- # restructuring the code very difficult. For instance, what if we decide to
- # move the mouse-related functions to a separate file (a submodule)? How that
- # file will access this module vars? It will probably lead to a circular
- # import.
- # In seconds. Any duration less than this is rounded to 0.0 to instantly move
- # the mouse.
- MINIMUM_DURATION = 0.1
- # If sleep_amount is less than MINIMUM_DURATION, time.sleep() will be a no-op and the mouse cursor moves there instantly.
- # TODO: This value should vary with the platform. http://stackoverflow.com/q/1133857
- MINIMUM_SLEEP = 0.05
- # The number of seconds to pause after EVERY public function call. Useful for debugging:
- PAUSE = 0.1 # Tenth-second pause by default.
- # Interface need some catch up time on darwin (macOS) systems. Possible values probably differ based on your system performance.
- # This value affects mouse moveTo, dragTo and key event duration.
- # TODO: Find a dynamic way to let the system catch up instead of blocking with a magic number.
- DARWIN_CATCH_UP_TIME = 0.01
- # If the mouse is over a coordinate in FAILSAFE_POINTS and FAILSAFE is True, the FailSafeException is raised.
- # The rest of the points are added to the FAILSAFE_POINTS list at the bottom of this file, after size() has been defined.
- # The points are for the corners of the screen, but note that these points don't automatically change if the screen resolution changes.
- FAILSAFE = True
- FAILSAFE_POINTS = [(0, 0)]
- LOG_SCREENSHOTS = False # If True, save screenshots for clicks and key presses.
- # If not None, PyAutoGUI deletes old screenshots when this limit has been reached:
- LOG_SCREENSHOTS_LIMIT = 10
- G_LOG_SCREENSHOTS_FILENAMES = [] # TODO - make this a deque
- Point = collections.namedtuple("Point", "x y")
- Size = collections.namedtuple("Size", "width height")
- def _genericPyAutoGUIChecks(wrappedFunction):
- """
- A decorator that calls failSafeCheck() before the decorated function and
- _handlePause() after it.
- """
- @functools.wraps(wrappedFunction)
- def wrapper(*args, **kwargs):
- failSafeCheck()
- returnVal = wrappedFunction(*args, **kwargs)
- _handlePause(kwargs.get("_pause", True))
- return returnVal
- return wrapper
- # General Functions
- # =================
- def getPointOnLine(x1, y1, x2, y2, n):
- """
- Returns an (x, y) tuple of the point that has progressed a proportion ``n`` along the line defined by the two
- ``x1``, ``y1`` and ``x2``, ``y2`` coordinates.
- This function was copied from pytweening module, so that it can be called even if PyTweening is not installed.
- """
- x = ((x2 - x1) * n) + x1
- y = ((y2 - y1) * n) + y1
- return (x, y)
- def linear(n):
- """
- Returns ``n``, where ``n`` is the float argument between ``0.0`` and ``1.0``. This function is for the default
- linear tween for mouse moving functions.
- This function was copied from PyTweening module, so that it can be called even if PyTweening is not installed.
- """
- # We use this function instead of pytweening.linear for the default tween function just in case pytweening couldn't be imported.
- if not 0.0 <= n <= 1.0:
- raise PyAutoGUIException("Argument must be between 0.0 and 1.0.")
- return n
- def _handlePause(_pause):
- """
- A helper function for performing a pause at the end of a PyAutoGUI function based on some settings.
- If ``_pause`` is ``True``, then sleep for ``PAUSE`` seconds (the global pause setting).
- """
- if _pause:
- assert isinstance(PAUSE, int) or isinstance(PAUSE, float)
- time.sleep(PAUSE)
- def _normalizeXYArgs(firstArg, secondArg):
- """
- Returns a ``Point`` object based on ``firstArg`` and ``secondArg``, which are the first two arguments passed to
- several PyAutoGUI functions. If ``firstArg`` and ``secondArg`` are both ``None``, returns the current mouse cursor
- position.
- ``firstArg`` and ``secondArg`` can be integers, a sequence of integers, or a string representing an image filename
- to find on the screen (and return the center coordinates of).
- """
- if firstArg is None and secondArg is None:
- return position()
- elif firstArg is None and secondArg is not None:
- return Point(int(position()[0]), int(secondArg))
- elif secondArg is None and firstArg is not None and not isinstance(firstArg, Sequence):
- return Point(int(firstArg), int(position()[1]))
- elif isinstance(firstArg, str):
- # If x is a string, we assume it's an image filename to locate on the screen:
- try:
- location = locateOnScreen(firstArg)
- # The following code only runs if pyscreeze.USE_IMAGE_NOT_FOUND_EXCEPTION is not set to True, meaning that
- # locateOnScreen() returns None if the image can't be found.
- if location is not None:
- return center(location)
- else:
- return None
- except pyscreeze.ImageNotFoundException:
- raise ImageNotFoundException
- return center(locateOnScreen(firstArg))
- elif isinstance(firstArg, Sequence):
- if len(firstArg) == 2:
- # firstArg is a two-integer tuple: (x, y)
- if secondArg is None:
- return Point(int(firstArg[0]), int(firstArg[1]))
- else:
- raise PyAutoGUIException(
- "When passing a sequence for firstArg, secondArg must not be passed (received {0}).".format(
- repr(secondArg)
- )
- )
- elif len(firstArg) == 4:
- # firstArg is a four-integer tuple, (left, top, width, height), we should return the center point
- if secondArg is None:
- return center(firstArg)
- else:
- raise PyAutoGUIException(
- "When passing a sequence for firstArg, secondArg must not be passed and default to None (received {0}).".format(
- repr(secondArg)
- )
- )
- else:
- raise PyAutoGUIException(
- "The supplied sequence must have exactly 2 or exactly 4 elements ({0} were received).".format(
- len(firstArg)
- )
- )
- else:
- return Point(int(firstArg), int(secondArg)) # firstArg and secondArg are just x and y number values
- def _logScreenshot(logScreenshot, funcName, funcArgs, folder="."):
- """
- A helper function that creates a screenshot to act as a logging mechanism. When a PyAutoGUI function is called,
- this function is also called to capture the state of the screen when that function was called.
- If ``logScreenshot`` is ``False`` (or None and the ``LOG_SCREENSHOTS`` constant is ``False``), no screenshot is taken.
- The ``funcName`` argument is a string of the calling function's name. It's used in the screenshot's filename.
- The ``funcArgs`` argument is a string describing the arguments passed to the calling function. It's limited to
- twelve characters to keep it short.
- The ``folder`` argument is the folder to place the screenshot file in, and defaults to the current working directory.
- """
- if not logScreenshot:
- return # Don't take a screenshot.
- if logScreenshot is None and LOG_SCREENSHOTS is False:
- return # Don't take a screenshot.
- # Ensure that the "specifics" string isn't too long for the filename:
- if len(funcArgs) > 12:
- funcArgs = funcArgs[:12] + "..."
- now = datetime.datetime.now()
- filename = "%s-%s-%s_%s-%s-%s-%s_%s_%s.png" % (
- now.year,
- str(now.month).rjust(2, "0"),
- str(now.day).rjust(2, "0"),
- now.hour,
- now.minute,
- now.second,
- str(now.microsecond)[:3],
- funcName,
- funcArgs,
- )
- filepath = os.path.join(folder, filename)
- # Delete the oldest screenshot if we've reached the maximum:
- if (LOG_SCREENSHOTS_LIMIT is not None) and (len(G_LOG_SCREENSHOTS_FILENAMES) >= LOG_SCREENSHOTS_LIMIT):
- os.unlink(os.path.join(folder, G_LOG_SCREENSHOTS_FILENAMES[0]))
- del G_LOG_SCREENSHOTS_FILENAMES[0]
- screenshot(filepath)
- G_LOG_SCREENSHOTS_FILENAMES.append(filename)
- def position(x=None, y=None):
- """
- Returns the current xy coordinates of the mouse cursor as a two-integer tuple.
- Args:
- x (int, None, optional) - If not None, this argument overrides the x in
- the return value.
- y (int, None, optional) - If not None, this argument overrides the y in
- the return value.
- Returns:
- (x, y) tuple of the current xy coordinates of the mouse cursor.
- NOTE: The position() function doesn't check for failsafe.
- """
- posx, posy = platformModule._position()
- posx = int(posx)
- posy = int(posy)
- if x is not None: # If set, the x parameter overrides the return value.
- posx = int(x)
- if y is not None: # If set, the y parameter overrides the return value.
- posy = int(y)
- return Point(posx, posy)
- def size():
- """Returns the width and height of the screen as a two-integer tuple.
- Returns:
- (width, height) tuple of the screen size, in pixels.
- """
- return Size(*platformModule._size())
- resolution = size # resolution() is an alias for size()
- def onScreen(x, y=None):
- """Returns whether the given xy coordinates are on the primary screen or not.
- Note that this function doesn't work for secondary screens.
- Args:
- Either the arguments are two separate values, first arg for x and second
- for y, or there is a single argument of a sequence with two values, the
- first x and the second y.
- Example: onScreen(x, y) or onScreen([x, y])
- Returns:
- bool: True if the xy coordinates are on the screen at its current
- resolution, otherwise False.
- """
- x, y = _normalizeXYArgs(x, y)
- x = int(x)
- y = int(y)
- width, height = platformModule._size()
- return 0 <= x < width and 0 <= y < height
- # Mouse Functions
- # ===============
- """
- NOTE: Although "mouse1" and "mouse2" buttons usually refer to the left and
- right mouse buttons respectively, in PyAutoGUI 1, 2, and 3 refer to the left,
- middle, and right buttons, respectively. This is because Xlib interprets
- button 2 as the middle button and button 3 as the right button, so we hold
- that for Windows and macOS as well (since those operating systems don't use
- button numbers but rather just "left" or "right").
- """
- def _normalizeButton(button):
- """
- The left, middle, and right mouse buttons are button numbers 1, 2, and 3 respectively. This is the numbering that
- Xlib on Linux uses (while Windows and macOS don't care about numbers; they just use "left" and "right").
- This function takes one of ``LEFT``, ``MIDDLE``, ``RIGHT``, ``PRIMARY``, ``SECONDARY``, ``1``, ``2``, ``3``, ``4``,
- ``5``, ``6``, or ``7`` for the button argument and returns either ``LEFT``, ``MIDDLE``, ``RIGHT``, ``4``, ``5``,
- ``6``, or ``7``. The ``PRIMARY``, ``SECONDARY``, ``1``, ``2``, and ``3`` values are never returned.
- The ``'left'`` and ``'right'`` mouse buttons will always refer to the physical left and right
- buttons on the mouse. The same applies for buttons 1 and 3.
- However, if ``button`` is ``'primary'`` or ``'secondary'``, then we must check if
- the mouse buttons have been "swapped" (for left-handed users) by the operating system's mouse
- settings.
- If the buttons are swapped, the primary button is the right mouse button and the secondary button is the left mouse
- button. If not swapped, the primary and secondary buttons are the left and right buttons, respectively.
- NOTE: Swap detection has not been implemented yet.
- """
- # TODO - The swap detection hasn't been done yet. For Windows, see https://stackoverflow.com/questions/45627956/check-if-mouse-buttons-are-swapped-or-not-in-c
- # TODO - We should check the OS settings to see if it's a left-hand setup, where button 1 would be "right".
- # Check that `button` has a valid value:
- button = button.lower()
- if platform.system() == "Linux":
- # Check for valid button arg on Linux:
- if button not in (LEFT, MIDDLE, RIGHT, PRIMARY, SECONDARY, 1, 2, 3, 4, 5, 6, 7):
- raise PyAutoGUIException(
- "button argument must be one of ('left', 'middle', 'right', 'primary', 'secondary', 1, 2, 3, 4, 5, 6, 7)"
- )
- else:
- # Check for valid button arg on Windows and macOS:
- if button not in (LEFT, MIDDLE, RIGHT, PRIMARY, SECONDARY, 1, 2, 3):
- raise PyAutoGUIException(
- "button argument must be one of ('left', 'middle', 'right', 'primary', 'secondary', 1, 2, 3)"
- )
- # TODO - Check if the primary/secondary mouse buttons have been swapped:
- if button in (PRIMARY, SECONDARY):
- swapped = platformModule._mouse_is_swapped()
- if swapped:
- if button == PRIMARY:
- return RIGHT
- elif button == SECONDARY:
- return LEFT
- else:
- if button == PRIMARY:
- return LEFT
- elif button == SECONDARY:
- return RIGHT
- # Return a mouse button integer value, not a string like 'left':
- return {LEFT: LEFT, MIDDLE: MIDDLE, RIGHT: RIGHT, 1: LEFT, 2: MIDDLE, 3: RIGHT, 4: 4, 5: 5, 6: 6, 7: 7}[button]
- @_genericPyAutoGUIChecks
- def mouseDown(x=None, y=None, button=PRIMARY, duration=0.0, tween=linear, logScreenshot=None, _pause=True):
- """Performs pressing a mouse button down (but not up).
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- x (int, float, None, tuple, optional): The x position on the screen where the
- mouse down happens. None by default. If tuple, this is used for x and y.
- If x is a str, it's considered a filename of an image to find on
- the screen with locateOnScreen() and click the center of.
- y (int, float, None, optional): The y position on the screen where the
- mouse down happens. None by default.
- button (str, int, optional): The mouse button pressed down. TODO
- Returns:
- None
- Raises:
- PyAutoGUIException: If button is not one of 'left', 'middle', 'right', 1, 2, or 3
- """
- button = _normalizeButton(button)
- x, y = _normalizeXYArgs(x, y)
- _mouseMoveDrag("move", x, y, 0, 0, duration=0, tween=None)
- _logScreenshot(logScreenshot, "mouseDown", "%s,%s" % (x, y), folder=".")
- platformModule._mouseDown(x, y, button)
- @_genericPyAutoGUIChecks
- def mouseUp(x=None, y=None, button=PRIMARY, duration=0.0, tween=linear, logScreenshot=None, _pause=True):
- """Performs releasing a mouse button up (but not down beforehand).
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- x (int, float, None, tuple, optional): The x position on the screen where the
- mouse up happens. None by default. If tuple, this is used for x and y.
- If x is a str, it's considered a filename of an image to find on
- the screen with locateOnScreen() and click the center of.
- y (int, float, None, optional): The y position on the screen where the
- mouse up happens. None by default.
- button (str, int, optional): The mouse button released. TODO
- Returns:
- None
- Raises:
- PyAutoGUIException: If button is not one of 'left', 'middle', 'right', 1, 2, or 3
- """
- button = _normalizeButton(button)
- x, y = _normalizeXYArgs(x, y)
- _mouseMoveDrag("move", x, y, 0, 0, duration=0, tween=None)
- _logScreenshot(logScreenshot, "mouseUp", "%s,%s" % (x, y), folder=".")
- platformModule._mouseUp(x, y, button)
- @_genericPyAutoGUIChecks
- def click(
- x=None, y=None, clicks=1, interval=0.0, button=PRIMARY, duration=0.0, tween=linear, logScreenshot=None, _pause=True
- ):
- """
- Performs pressing a mouse button down and then immediately releasing it. Returns ``None``.
- When no arguments are passed, the primary mouse button is clicked at the mouse cursor's current location.
- If integers for ``x`` and ``y`` are passed, the click will happen at that XY coordinate. If ``x`` is a string, the
- string is an image filename that PyAutoGUI will attempt to locate on the screen and click the center of. If ``x``
- is a sequence of two coordinates, those coordinates will be used for the XY coordinate to click on.
- The ``clicks`` argument is an int of how many clicks to make, and defaults to ``1``.
- The ``interval`` argument is an int or float of how many seconds to wait in between each click, if ``clicks`` is
- greater than ``1``. It defaults to ``0.0`` for no pause in between clicks.
- The ``button`` argument is one of the constants ``LEFT``, ``MIDDLE``, ``RIGHT``, ``PRIMARY``, or ``SECONDARY``.
- It defaults to ``PRIMARY`` (which is the left mouse button, unless the operating system has been set for
- left-handed users.)
- If ``x`` and ``y`` are specified, and the click is not happening at the mouse cursor's current location, then
- the ``duration`` argument is an int or float of how many seconds it should take to move the mouse to the XY
- coordinates. It defaults to ``0`` for an instant move.
- If ``x`` and ``y`` are specified and ``duration`` is not ``0``, the ``tween`` argument is a tweening function
- that specifies the movement pattern of the mouse cursor as it moves to the XY coordinates. The default is a
- simple linear tween. See the PyTweening module documentation for more details.
- The ``pause`` parameter is deprecated. Call the ``pyautogui.sleep()`` function to implement a pause.
- Raises:
- PyAutoGUIException: If button is not one of 'left', 'middle', 'right', 1, 2, 3
- """
- # TODO: I'm leaving buttons 4, 5, 6, and 7 undocumented for now. I need to understand how they work.
- button = _normalizeButton(button)
- x, y = _normalizeXYArgs(x, y)
- # Move the mouse cursor to the x, y coordinate:
- _mouseMoveDrag("move", x, y, 0, 0, duration, tween)
- _logScreenshot(logScreenshot, "click", "%s,%s,%s,%s" % (button, clicks, x, y), folder=".")
- if sys.platform == 'darwin':
- for i in range(clicks):
- failSafeCheck()
- if button in (LEFT, MIDDLE, RIGHT):
- platformModule._multiClick(x, y, button, 1, interval)
- else:
- for i in range(clicks):
- failSafeCheck()
- if button in (LEFT, MIDDLE, RIGHT):
- platformModule._click(x, y, button)
- time.sleep(interval)
- @_genericPyAutoGUIChecks
- def leftClick(x=None, y=None, interval=0.0, duration=0.0, tween=linear, logScreenshot=None, _pause=True):
- """Performs a left mouse button click.
- This is a wrapper function for click('left', x, y).
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- x (int, float, None, tuple, optional): The x position on the screen where the
- click happens. None by default. If tuple, this is used for x and y.
- If x is a str, it's considered a filename of an image to find on
- the screen with locateOnScreen() and click the center of.
- y (int, float, None, optional): The y position on the screen where the
- click happens. None by default.
- interval (float, optional): The number of seconds in between each click,
- if the number of clicks is greater than 1. 0.0 by default, for no
- pause in between clicks.
- Returns:
- None
- """
- # TODO - Do we need the decorator for this function? Should click() handle this? (Also applies to other alias functions.)
- click(x, y, 1, interval, LEFT, duration, tween, logScreenshot, _pause=_pause)
- @_genericPyAutoGUIChecks
- def rightClick(x=None, y=None, interval=0.0, duration=0.0, tween=linear, logScreenshot=None, _pause=True):
- """Performs a right mouse button click.
- This is a wrapper function for click('right', x, y).
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- x (int, float, None, tuple, optional): The x position on the screen where the
- click happens. None by default. If tuple, this is used for x and y.
- If x is a str, it's considered a filename of an image to find on
- the screen with locateOnScreen() and click the center of.
- y (int, float, None, optional): The y position on the screen where the
- click happens. None by default.
- interval (float, optional): The number of seconds in between each click,
- if the number of clicks is greater than 1. 0.0 by default, for no
- pause in between clicks.
- Returns:
- None
- """
- click(x, y, 1, interval, RIGHT, duration, tween, logScreenshot, _pause=_pause)
- @_genericPyAutoGUIChecks
- def middleClick(x=None, y=None, interval=0.0, duration=0.0, tween=linear, logScreenshot=None, _pause=True):
- """Performs a middle mouse button click.
- This is a wrapper function for click('middle', x, y).
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- x (int, float, None, tuple, optional): The x position on the screen where the
- click happens. None by default. If tuple, this is used for x and y.
- If x is a str, it's considered a filename of an image to find on
- the screen with locateOnScreen() and click the center of.
- y (int, float, None, optional): The y position on the screen where the
- click happens. None by default.
- Returns:
- None
- """
- click(x, y, 1, interval, MIDDLE, duration, tween, logScreenshot, _pause=_pause)
- @_genericPyAutoGUIChecks
- def doubleClick(x=None, y=None, interval=0.0, button=LEFT, duration=0.0, tween=linear, logScreenshot=None, _pause=True):
- """Performs a double click.
- This is a wrapper function for click('left', x, y, 2, interval).
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- x (int, float, None, tuple, optional): The x position on the screen where the
- click happens. None by default. If tuple, this is used for x and y.
- If x is a str, it's considered a filename of an image to find on
- the screen with locateOnScreen() and click the center of.
- y (int, float, None, optional): The y position on the screen where the
- click happens. None by default.
- interval (float, optional): The number of seconds in between each click,
- if the number of clicks is greater than 1. 0.0 by default, for no
- pause in between clicks.
- button (str, int, optional): The mouse button released. TODO
- Returns:
- None
- Raises:
- PyAutoGUIException: If button is not one of 'left', 'middle', 'right', 1, 2, 3, 4,
- 5, 6, or 7
- """
- # Multiple clicks work different in OSX
- if sys.platform == "darwin":
- x, y = _normalizeXYArgs(x, y)
- _mouseMoveDrag("move", x, y, 0, 0, duration=0, tween=None)
- x, y = platformModule._position()
- platformModule._multiClick(x, y, button, 2)
- _logScreenshot(logScreenshot, 'click', '%s,%s,%s,2' % (x, y, button), folder='.')
- else:
- # Click for Windows or Linux:
- click(x, y, 2, interval, button, duration, tween, logScreenshot, _pause=False)
- @_genericPyAutoGUIChecks
- def tripleClick(x=None, y=None, interval=0.0, button=LEFT, duration=0.0, tween=linear, logScreenshot=None, _pause=True):
- """Performs a triple click.
- This is a wrapper function for click('left', x, y, 3, interval).
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- x (int, float, None, tuple, optional): The x position on the screen where the
- click happens. None by default. If tuple, this is used for x and y.
- If x is a str, it's considered a filename of an image to find on
- the screen with locateOnScreen() and click the center of.
- y (int, float, None, optional): The y position on the screen where the
- click happens. None by default.
- interval (float, optional): The number of seconds in between each click,
- if the number of clicks is greater than 1. 0.0 by default, for no
- pause in between clicks.
- button (str, int, optional): The mouse button released. TODO
- Returns:
- None
- Raises:
- PyAutoGUIException: If button is not one of 'left', 'middle', 'right', 1, 2, 3, 4,
- 5, 6, or 7
- """
- # Multiple clicks work different in OSX
- if sys.platform == "darwin":
- x, y = _normalizeXYArgs(x, y)
- _mouseMoveDrag("move", x, y, 0, 0, duration=0, tween=None)
- x, y = platformModule._position()
- _logScreenshot(logScreenshot, "click", "%s,%s,%s,3" % (x, y, button), folder=".")
- platformModule._multiClick(x, y, button, 3)
- else:
- # Click for Windows or Linux:
- click(x, y, 3, interval, button, duration, tween, logScreenshot, _pause=False)
- @_genericPyAutoGUIChecks
- def scroll(clicks, x=None, y=None, logScreenshot=None, _pause=True):
- """Performs a scroll of the mouse scroll wheel.
- Whether this is a vertical or horizontal scroll depends on the underlying
- operating system.
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- clicks (int, float): The amount of scrolling to perform.
- x (int, float, None, tuple, optional): The x position on the screen where the
- click happens. None by default. If tuple, this is used for x and y.
- y (int, float, None, optional): The y position on the screen where the
- click happens. None by default.
- Returns:
- None
- """
- if type(x) in (tuple, list):
- x, y = x[0], x[1]
- x, y = position(x, y)
- _logScreenshot(logScreenshot, "scroll", "%s,%s,%s" % (clicks, x, y), folder=".")
- platformModule._scroll(clicks, x, y)
- @_genericPyAutoGUIChecks
- def hscroll(clicks, x=None, y=None, logScreenshot=None, _pause=True):
- """Performs an explicitly horizontal scroll of the mouse scroll wheel,
- if this is supported by the operating system. (Currently just Linux.)
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- clicks (int, float): The amount of scrolling to perform.
- x (int, float, None, tuple, optional): The x position on the screen where the
- click happens. None by default. If tuple, this is used for x and y.
- y (int, float, None, optional): The y position on the screen where the
- click happens. None by default.
- Returns:
- None
- """
- if type(x) in (tuple, list):
- x, y = x[0], x[1]
- x, y = position(x, y)
- _logScreenshot(logScreenshot, "hscroll", "%s,%s,%s" % (clicks, x, y), folder=".")
- platformModule._hscroll(clicks, x, y)
- @_genericPyAutoGUIChecks
- def vscroll(clicks, x=None, y=None, logScreenshot=None, _pause=True):
- """Performs an explicitly vertical scroll of the mouse scroll wheel,
- if this is supported by the operating system. (Currently just Linux.)
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- clicks (int, float): The amount of scrolling to perform.
- x (int, float, None, tuple, optional): The x position on the screen where the
- click happens. None by default. If tuple, this is used for x and y.
- y (int, float, None, optional): The y position on the screen where the
- click happens. None by default.
- Returns:
- None
- """
- if type(x) in (tuple, list):
- x, y = x[0], x[1]
- x, y = position(x, y)
- _logScreenshot(logScreenshot, "vscroll", "%s,%s,%s" % (clicks, x, y), folder=".")
- platformModule._vscroll(clicks, x, y)
- @_genericPyAutoGUIChecks
- def moveTo(x=None, y=None, duration=0.0, tween=linear, logScreenshot=False, _pause=True):
- """Moves the mouse cursor to a point on the screen.
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- x (int, float, None, tuple, optional): The x position on the screen where the
- click happens. None by default. If tuple, this is used for x and y.
- If x is a str, it's considered a filename of an image to find on
- the screen with locateOnScreen() and click the center of.
- y (int, float, None, optional): The y position on the screen where the
- click happens. None by default.
- duration (float, optional): The amount of time it takes to move the mouse
- cursor to the xy coordinates. If 0, then the mouse cursor is moved
- instantaneously. 0.0 by default.
- tween (func, optional): The tweening function used if the duration is not
- 0. A linear tween is used by default.
- Returns:
- None
- """
- x, y = _normalizeXYArgs(x, y)
- _logScreenshot(logScreenshot, "moveTo", "%s,%s" % (x, y), folder=".")
- _mouseMoveDrag("move", x, y, 0, 0, duration, tween)
- @_genericPyAutoGUIChecks
- def moveRel(xOffset=None, yOffset=None, duration=0.0, tween=linear, logScreenshot=False, _pause=True):
- """Moves the mouse cursor to a point on the screen, relative to its current
- position.
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- x (int, float, None, tuple, optional): How far left (for negative values) or
- right (for positive values) to move the cursor. 0 by default. If tuple, this is used for x and y.
- y (int, float, None, optional): How far up (for negative values) or
- down (for positive values) to move the cursor. 0 by default.
- duration (float, optional): The amount of time it takes to move the mouse
- cursor to the new xy coordinates. If 0, then the mouse cursor is moved
- instantaneously. 0.0 by default.
- tween (func, optional): The tweening function used if the duration is not
- 0. A linear tween is used by default.
- Returns:
- None
- """
- xOffset, yOffset = _normalizeXYArgs(xOffset, yOffset)
- _logScreenshot(logScreenshot, "moveRel", "%s,%s" % (xOffset, yOffset), folder=".")
- _mouseMoveDrag("move", None, None, xOffset, yOffset, duration, tween)
- move = moveRel # For PyAutoGUI 1.0, move() replaces moveRel().
- @_genericPyAutoGUIChecks
- def dragTo(
- x=None, y=None, duration=0.0, tween=linear, button=PRIMARY, logScreenshot=None, _pause=True, mouseDownUp=True
- ):
- """Performs a mouse drag (mouse movement while a button is held down) to a
- point on the screen.
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- x (int, float, None, tuple, optional): How far left (for negative values) or
- right (for positive values) to move the cursor. 0 by default. If tuple, this is used for x and y.
- If x is a str, it's considered a filename of an image to find on
- the screen with locateOnScreen() and click the center of.
- y (int, float, None, optional): How far up (for negative values) or
- down (for positive values) to move the cursor. 0 by default.
- duration (float, optional): The amount of time it takes to move the mouse
- cursor to the new xy coordinates. If 0, then the mouse cursor is moved
- instantaneously. 0.0 by default.
- tween (func, optional): The tweening function used if the duration is not
- 0. A linear tween is used by default.
- button (str, int, optional): The mouse button released. TODO
- mouseDownUp (True, False): When true, the mouseUp/Down actions are not performed.
- Which allows dragging over multiple (small) actions. 'True' by default.
- Returns:
- None
- """
- x, y = _normalizeXYArgs(x, y)
- _logScreenshot(logScreenshot, "dragTo", "%s,%s" % (x, y), folder=".")
- if mouseDownUp:
- mouseDown(button=button, logScreenshot=False, _pause=False)
- _mouseMoveDrag("drag", x, y, 0, 0, duration, tween, button)
- if mouseDownUp:
- mouseUp(button=button, logScreenshot=False, _pause=False)
- @_genericPyAutoGUIChecks
- def dragRel(
- xOffset=0, yOffset=0, duration=0.0, tween=linear, button=PRIMARY, logScreenshot=None, _pause=True, mouseDownUp=True
- ):
- """Performs a mouse drag (mouse movement while a button is held down) to a
- point on the screen, relative to its current position.
- The x and y parameters detail where the mouse event happens. If None, the
- current mouse position is used. If a float value, it is rounded down. If
- outside the boundaries of the screen, the event happens at edge of the
- screen.
- Args:
- x (int, float, None, tuple, optional): How far left (for negative values) or
- right (for positive values) to move the cursor. 0 by default. If tuple, this is used for xOffset and yOffset.
- y (int, float, None, optional): How far up (for negative values) or
- down (for positive values) to move the cursor. 0 by default.
- duration (float, optional): The amount of time it takes to move the mouse
- cursor to the new xy coordinates. If 0, then the mouse cursor is moved
- instantaneously. 0.0 by default.
- tween (func, optional): The tweening function used if the duration is not
- 0. A linear tween is used by default.
- button (str, int, optional): The mouse button released. TODO
- mouseDownUp (True, False): When true, the mouseUp/Down actions are not performed.
- Which allows dragging over multiple (small) actions. 'True' by default.
- Returns:
- None
- """
- if xOffset is None:
- xOffset = 0
- if yOffset is None:
- yOffset = 0
- if type(xOffset) in (tuple, list):
- xOffset, yOffset = xOffset[0], xOffset[1]
- if xOffset == 0 and yOffset == 0:
- return # no-op case
- mousex, mousey = platformModule._position()
- _logScreenshot(logScreenshot, "dragRel", "%s,%s" % (xOffset, yOffset), folder=".")
- if mouseDownUp:
- mouseDown(button=button, logScreenshot=False, _pause=False)
- _mouseMoveDrag("drag", mousex, mousey, xOffset, yOffset, duration, tween, button)
- if mouseDownUp:
- mouseUp(button=button, logScreenshot=False, _pause=False)
- drag = dragRel # For PyAutoGUI 1.0, we want drag() to replace dragRel().
- def _mouseMoveDrag(moveOrDrag, x, y, xOffset, yOffset, duration, tween=linear, button=None):
- """Handles the actual move or drag event, since different platforms
- implement them differently.
- On Windows & Linux, a drag is a normal mouse move while a mouse button is
- held down. On OS X, a distinct "drag" event must be used instead.
- The code for moving and dragging the mouse is similar, so this function
- handles both. Users should call the moveTo() or dragTo() functions instead
- of calling _mouseMoveDrag().
- Args:
- moveOrDrag (str): Either 'move' or 'drag', for the type of action this is.
- x (int, float, None, optional): How far left (for negative values) or
- right (for positive values) to move the cursor. 0 by default.
- y (int, float, None, optional): How far up (for negative values) or
- down (for positive values) to move the cursor. 0 by default.
- xOffset (int, float, None, optional): How far left (for negative values) or
- right (for positive values) to move the cursor. 0 by default.
- yOffset (int, float, None, optional): How far up (for negative values) or
- down (for positive values) to move the cursor. 0 by default.
- duration (float, optional): The amount of time it takes to move the mouse
- cursor to the new xy coordinates. If 0, then the mouse cursor is moved
- instantaneously. 0.0 by default.
- tween (func, optional): The tweening function used if the duration is not
- 0. A linear tween is used by default.
- button (str, int, optional): The mouse button released. TODO
- Returns:
- None
- """
- # The move and drag code is similar, but OS X requires a special drag event instead of just a move event when dragging.
- # See https://stackoverflow.com/a/2696107/1893164
- assert moveOrDrag in ("move", "drag"), "moveOrDrag must be in ('move', 'drag'), not %s" % (moveOrDrag)
- if sys.platform != "darwin":
- moveOrDrag = "move" # Only OS X needs the drag event specifically.
- xOffset = int(xOffset) if xOffset is not None else 0
- yOffset = int(yOffset) if yOffset is not None else 0
- if x is None and y is None and xOffset == 0 and yOffset == 0:
- return # Special case for no mouse movement at all.
- startx, starty = position()
- x = int(x) if x is not None else startx
- y = int(y) if y is not None else starty
- # x, y, xOffset, yOffset are now int.
- x += xOffset
- y += yOffset
- width, height = size()
- # Make sure x and y are within the screen bounds.
- # x = max(0, min(x, width - 1))
- # y = max(0, min(y, height - 1))
- # If the duration is small enough, just move the cursor there instantly.
- steps = [(x, y)]
- if duration > MINIMUM_DURATION:
- # Non-instant moving/dragging involves tweening:
- num_steps = max(width, height)
- sleep_amount = duration / num_steps
- if sleep_amount < MINIMUM_SLEEP:
- num_steps = int(duration / MINIMUM_SLEEP)
- sleep_amount = duration / num_steps
- steps = [getPointOnLine(startx, starty, x, y, tween(n / num_steps)) for n in range(num_steps)]
- # Making sure the last position is the actual destination.
- steps.append((x, y))
- for tweenX, tweenY in steps:
- if len(steps) > 1:
- # A single step does not require tweening.
- time.sleep(sleep_amount)
- tweenX = int(round(tweenX))
- tweenY = int(round(tweenY))
- # Do a fail-safe check to see if the user moved the mouse to a fail-safe position, but not if the mouse cursor
- # moved there as a result of this function. (Just because tweenX and tweenY aren't in a fail-safe position
- # doesn't mean the user couldn't have moved the mouse cursor to a fail-safe position.)
- if (tweenX, tweenY) not in FAILSAFE_POINTS:
- failSafeCheck()
- if moveOrDrag == "move":
- platformModule._moveTo(tweenX, tweenY)
- elif moveOrDrag == "drag":
- platformModule._dragTo(tweenX, tweenY, button)
- else:
- raise NotImplementedError("Unknown value of moveOrDrag: {0}".format(moveOrDrag))
- if (tweenX, tweenY) not in FAILSAFE_POINTS:
- failSafeCheck()
- # Keyboard Functions
- # ==================
- def isValidKey(key):
- """Returns a Boolean value if the given key is a valid value to pass to
- PyAutoGUI's keyboard-related functions for the current platform.
- This function is here because passing an invalid value to the PyAutoGUI
- keyboard functions currently is a no-op that does not raise an exception.
- Some keys are only valid on some platforms. For example, while 'esc' is
- valid for the Escape key on all platforms, 'browserback' is only used on
- Windows operating systems.
- Args:
- key (str): The key value.
- Returns:
- bool: True if key is a valid value, False if not.
- """
- return platformModule.keyboardMapping.get(key, None) is not None
- @_genericPyAutoGUIChecks
- def keyDown(key, logScreenshot=None, _pause=True):
- """Performs a keyboard key press without the release. This will put that
- key in a held down state.
- NOTE: For some reason, this does not seem to cause key repeats like would
- happen if a keyboard key was held down on a text field.
- Args:
- key (str): The key to be pressed down. The valid names are listed in
- KEYBOARD_KEYS.
- Returns:
- None
- """
- if len(key) > 1:
- key = key.lower()
- _logScreenshot(logScreenshot, "keyDown", key, folder=".")
- platformModule._keyDown(key)
- @_genericPyAutoGUIChecks
- def keyUp(key, logScreenshot=None, _pause=True):
- """Performs a keyboard key release (without the press down beforehand).
- Args:
- key (str): The key to be released up. The valid names are listed in
- KEYBOARD_KEYS.
- Returns:
- None
- """
- if len(key) > 1:
- key = key.lower()
- _logScreenshot(logScreenshot, "keyUp", key, folder=".")
- platformModule._keyUp(key)
- @_genericPyAutoGUIChecks
- def press(keys, presses=1, interval=0.0, logScreenshot=None, _pause=True):
- """Performs a keyboard key press down, followed by a release.
- Args:
- key (str, list): The key to be pressed. The valid names are listed in
- KEYBOARD_KEYS. Can also be a list of such strings.
- presses (integer, optional): The number of press repetitions.
- 1 by default, for just one press.
- interval (float, optional): How many seconds between each press.
- 0.0 by default, for no pause between presses.
- pause (float, optional): How many seconds in the end of function process.
- None by default, for no pause in the end of function process.
- Returns:
- None
- """
- if type(keys) == str:
- if len(keys) > 1:
- keys = keys.lower()
- keys = [keys] # If keys is 'enter', convert it to ['enter'].
- else:
- lowerKeys = []
- for s in keys:
- if len(s) > 1:
- lowerKeys.append(s.lower())
- else:
- lowerKeys.append(s)
- keys = lowerKeys
- interval = float(interval)
- _logScreenshot(logScreenshot, "press", ",".join(keys), folder=".")
- for i in range(presses):
- for k in keys:
- failSafeCheck()
- platformModule._keyDown(k)
- platformModule._keyUp(k)
- time.sleep(interval)
- @contextmanager
- @_genericPyAutoGUIChecks
- def hold(keys, logScreenshot=None, _pause=True):
- """Context manager that performs a keyboard key press down upon entry,
- followed by a release upon exit.
- Args:
- key (str, list): The key to be pressed. The valid names are listed in
- KEYBOARD_KEYS. Can also be a list of such strings.
- pause (float, optional): How many seconds in the end of function process.
- None by default, for no pause in the end of function process.
- Returns:
- None
- """
- if type(keys) == str:
- if len(keys) > 1:
- keys = keys.lower()
- keys = [keys] # If keys is 'enter', convert it to ['enter'].
- else:
- lowerKeys = []
- for s in keys:
- if len(s) > 1:
- lowerKeys.append(s.lower())
- else:
- lowerKeys.append(s)
- keys = lowerKeys
- _logScreenshot(logScreenshot, "press", ",".join(keys), folder=".")
- for k in keys:
- failSafeCheck()
- platformModule._keyDown(k)
- try:
- yield
- finally:
- for k in keys:
- failSafeCheck()
- platformModule._keyUp(k)
- @_genericPyAutoGUIChecks
- def typewrite(message, interval=0.0, logScreenshot=None, _pause=True):
- """Performs a keyboard key press down, followed by a release, for each of
- the characters in message.
- The message argument can also be list of strings, in which case any valid
- keyboard name can be used.
- Since this performs a sequence of keyboard presses and does not hold down
- keys, it cannot be used to perform keyboard shortcuts. Use the hotkey()
- function for that.
- Args:
- message (str, list): If a string, then the characters to be pressed. If a
- list, then the key names of the keys to press in order. The valid names
- are listed in KEYBOARD_KEYS.
- interval (float, optional): The number of seconds in between each press.
- 0.0 by default, for no pause in between presses.
- Returns:
- None
- """
- interval = float(interval) # TODO - this should be taken out.
- _logScreenshot(logScreenshot, "write", message, folder=".")
- for c in message:
- if len(c) > 1:
- c = c.lower()
- press(c, _pause=False)
- time.sleep(interval)
- failSafeCheck()
- write = typewrite # In PyAutoGUI 1.0, write() replaces typewrite().
- @_genericPyAutoGUIChecks
- def hotkey(*args, **kwargs):
- """Performs key down presses on the arguments passed in order, then performs
- key releases in reverse order.
- The effect is that calling hotkey('ctrl', 'shift', 'c') would perform a
- "Ctrl-Shift-C" hotkey/keyboard shortcut press.
- Args:
- key(s) (str): The series of keys to press, in order. This can also be a
- list of key strings to press.
- interval (float, optional): The number of seconds in between each press.
- 0.0 by default, for no pause in between presses.
- Returns:
- None
- """
- interval = float(kwargs.get("interval", 0.0)) # TODO - this should be taken out.
- if len(args) and isinstance(args[0], Sequence) and not isinstance(args[0], str):
- # Let the user pass a list of strings
- args = tuple(args[0])
- _logScreenshot(kwargs.get("logScreenshot"), "hotkey", ",".join(args), folder=".")
- for c in args:
- if len(c) > 1:
- c = c.lower()
- platformModule._keyDown(c)
- time.sleep(interval)
- for c in reversed(args):
- if len(c) > 1:
- c = c.lower()
- platformModule._keyUp(c)
- time.sleep(interval)
- shortcut = hotkey # shortcut() is an alias for htotkey()
- def failSafeCheck():
- if FAILSAFE and tuple(position()) in FAILSAFE_POINTS:
- raise FailSafeException(
- "PyAutoGUI fail-safe triggered from mouse moving to a corner of the screen. To disable this fail-safe, set pyautogui.FAILSAFE to False. DISABLING FAIL-SAFE IS NOT RECOMMENDED."
- )
- def displayMousePosition(xOffset=0, yOffset=0):
- """This function is meant to be run from the command line. It will
- automatically display the location and RGB of the mouse cursor."""
- try:
- runningIDLE = sys.stdin.__module__.startswith("idlelib")
- except AttributeError:
- runningIDLE = False
- print("Press Ctrl-C to quit.")
- if xOffset != 0 or yOffset != 0:
- print("xOffset: %s yOffset: %s" % (xOffset, yOffset))
- try:
- while True:
- # Get and print the mouse coordinates.
- x, y = position()
- positionStr = "X: " + str(x - xOffset).rjust(4) + " Y: " + str(y - yOffset).rjust(4)
- if not onScreen(x - xOffset, y - yOffset) or sys.platform == "darwin":
- # Pixel color can only be found for the primary monitor, and also not on mac due to the screenshot having the mouse cursor in the way.
- pixelColor = ("NaN", "NaN", "NaN")
- else:
- pixelColor = pyscreeze.screenshot().getpixel(
- (x, y)
- ) # NOTE: On Windows & Linux, getpixel() returns a 3-integer tuple, but on macOS it returns a 4-integer tuple.
- positionStr += " RGB: (" + str(pixelColor[0]).rjust(3)
- positionStr += ", " + str(pixelColor[1]).rjust(3)
- positionStr += ", " + str(pixelColor[2]).rjust(3) + ")"
- sys.stdout.write(positionStr)
- if not runningIDLE:
- # If this is a terminal, than we can erase the text by printing \b backspaces.
- sys.stdout.write("\b" * len(positionStr))
- else:
- # If this isn't a terminal (i.e. IDLE) then we can only append more text. Print a newline instead and pause a second (so we don't send too much output).
- sys.stdout.write("\n")
- time.sleep(1)
- sys.stdout.flush()
- except KeyboardInterrupt:
- sys.stdout.write("\n")
- sys.stdout.flush()
- def _snapshot(tag, folder=None, region=None, radius=None):
- # TODO feature not finished
- if region is not None and radius is not None:
- raise Exception("Either region or radius arguments (or neither) can be passed to snapshot, but not both")
- if radius is not None:
- x, y = platformModule._position()
- if folder is None:
- folder = os.getcwd()
- now = datetime.datetime.now()
- filename = "%s-%s-%s_%s-%s-%s-%s_%s.png" % (
- now.year,
- str(now.month).rjust(2, "0"),
- str(now.day).rjust(2, "0"),
- now.hour,
- now.minute,
- now.second,
- str(now.microsecond)[:3],
- tag,
- )
- filepath = os.path.join(folder, filename)
- screenshot(filepath)
- def sleep(seconds):
- time.sleep(seconds)
- def countdown(seconds):
- for i in range(seconds, 0, -1):
- print(str(i), end=" ", flush=True)
- time.sleep(1)
- print()
- def _getNumberToken(commandStr):
- """Gets the number token at the start of commandStr.
- Given '5hello' returns '5'
- Given ' 5hello' returns ' 5'
- Given '-42hello' returns '-42'
- Given '+42hello' returns '+42'
- Given '3.14hello' returns '3.14'
- Raises an exception if it can't tokenize a number.
- """
- pattern = re.compile(r"^(\s*(\+|\-)?\d+(\.\d+)?)")
- mo = pattern.search(commandStr)
- if mo is None:
- raise PyAutoGUIException("Invalid command at index 0: a number was expected")
- return mo.group(1)
- def _getQuotedStringToken(commandStr):
- """Gets the quoted string token at the start of commandStr.
- The quoted string must use single quotes.
- Given "'hello'world" returns "'hello'"
- Given " 'hello'world" returns " 'hello'"
- Raises an exception if it can't tokenize a quoted string.
- """
- pattern = re.compile(r"^((\s*)('(.*?)'))")
- mo = pattern.search(commandStr)
- if mo is None:
- raise PyAutoGUIException("Invalid command at index 0: a quoted string was expected")
- return mo.group(1)
- def _getParensCommandStrToken(commandStr):
- """Gets the command string token at the start of commandStr. It will also
- be enclosed with parentheses.
- Given "(ccc)world" returns "(ccc)"
- Given " (ccc)world" returns " (ccc)"
- Given "(ccf10(r))world" returns "(ccf10(r))"
- Raises an exception if it can't tokenize a quoted string.
- """
- # Check to make sure at least one open parenthesis exists:
- pattern = re.compile(r"^\s*\(")
- mo = pattern.search(commandStr)
- if mo is None:
- raise PyAutoGUIException("Invalid command at index 0: No open parenthesis found.")
- # Check to make sure the parentheses are balanced:
- i = 0
- openParensCount = 0
- while i < len(commandStr):
- if commandStr[i] == "(":
- openParensCount += 1
- elif commandStr[i] == ")":
- openParensCount -= 1
- if openParensCount == 0:
- i += 1 # Remember to increment i past the ) before breaking.
- break
- elif openParensCount == -1:
- raise PyAutoGUIException("Invalid command at index 0: No open parenthesis for this close parenthesis.")
- i += 1
- if openParensCount > 0:
- raise PyAutoGUIException("Invalid command at index 0: Not enough close parentheses.")
- return commandStr[0:i]
- def _getCommaToken(commandStr):
- """Gets the comma token at the start of commandStr.
- Given ',' returns ','
- Given ' ,', returns ' ,'
- Raises an exception if a comma isn't found.
- """
- pattern = re.compile(r"^((\s*),)")
- mo = pattern.search(commandStr)
- if mo is None:
- raise PyAutoGUIException("Invalid command at index 0: a comma was expected")
- return mo.group(1)
- def _tokenizeCommandStr(commandStr):
- """Tokenizes commandStr into a list of commands and their arguments for
- the run() function. Returns the list."""
- commandPattern = re.compile(r"^(su|sd|ss|c|l|m|r|g|d|k|w|h|f|s|a|p)")
- # Tokenize the command string.
- commandList = []
- i = 0 # Points to the current index in commandStr that is being tokenized.
- while i < len(commandStr):
- if commandStr[i] in (" ", "\t", "\n", "\r"):
- # Skip over whitespace:
- i += 1
- continue
- mo = commandPattern.match(commandStr[i:])
- if mo is None:
- raise PyAutoGUIException("Invalid command at index %s: %s is not a valid command" % (i, commandStr[i]))
- individualCommand = mo.group(1)
- commandList.append(individualCommand)
- i += len(individualCommand)
- # Handle the no argument commands (c, l, m, r, su, sd, ss):
- if individualCommand in ("c", "l", "m", "r", "su", "sd", "ss"):
- pass # This just exists so these commands are covered by one of these cases.
- # Handle the arguments of the mouse (g)o and mouse (d)rag commands:
- elif individualCommand in ("g", "d"):
- try:
- x = _getNumberToken(commandStr[i:])
- i += len(x) # Increment past the x number.
- comma = _getCommaToken(commandStr[i:])
- i += len(comma) # Increment past the comma (and any whitespace).
- y = _getNumberToken(commandStr[i:])
- i += len(y) # Increment past the y number.
- except PyAutoGUIException as excObj:
- # Exception message starts with something like "Invalid command at index 0:"
- # Change the index number and reraise it.
- indexPart, colon, message = str(excObj).partition(":")
- indexNum = indexPart[len("Invalid command at index ") :]
- newIndexNum = int(indexNum) + i
- raise PyAutoGUIException("Invalid command at index %s:%s" % (newIndexNum, message))
- # Make sure either both x and y have +/- or neither of them do:
- if x.lstrip()[0].isdecimal() and not y.lstrip()[0].isdecimal():
- raise PyAutoGUIException("Invalid command at index %s: Y has a +/- but X does not." % (i - len(y)))
- if not x.lstrip()[0].isdecimal() and y.lstrip()[0].isdecimal():
- raise PyAutoGUIException(
- "Invalid command at index %s: Y does not have a +/- but X does." % (i - len(y))
- )
- # Get rid of any whitespace at the front:
- commandList.append(x.lstrip())
- commandList.append(y.lstrip())
- # Handle the arguments of the (s)leep and (p)ause commands:
- elif individualCommand in ("s", "p"):
- try:
- num = _getNumberToken(commandStr[i:])
- i += len(num) # Increment past the number.
- # TODO - raise an exception if a + or - is in the number.
- except PyAutoGUIException as excObj:
- # Exception message starts with something like "Invalid command at index 0:"
- # Change the index number and reraise it.
- indexPart, colon, message = str(excObj).partition(":")
- indexNum = indexPart[len("Invalid command at index ") :]
- newIndexNum = int(indexNum) + i
- raise PyAutoGUIException("Invalid command at index %s:%s" % (newIndexNum, message))
- # Get rid of any whitespace at the front:
- commandList.append(num.lstrip())
- # Handle the arguments of the (k)ey press, (w)rite, (h)otkeys, and (a)lert commands:
- elif individualCommand in ("k", "w", "h", "a"):
- try:
- quotedString = _getQuotedStringToken(commandStr[i:])
- i += len(quotedString) # Increment past the quoted string.
- except PyAutoGUIException as excObj:
- # Exception message starts with something like "Invalid command at index 0:"
- # Change the index number and reraise it.
- indexPart, colon, message = str(excObj).partition(":")
- indexNum = indexPart[len("Invalid command at index ") :]
- newIndexNum = int(indexNum) + i
- raise PyAutoGUIException("Invalid command at index %s:%s" % (newIndexNum, message))
- # Get rid of any whitespace at the front and the quotes:
- commandList.append(quotedString[1:-1].lstrip())
- # Handle the arguments of the (f)or loop command:
- elif individualCommand == "f":
- try:
- numberOfLoops = _getNumberToken(commandStr[i:])
- i += len(numberOfLoops) # Increment past the number of loops.
- subCommandStr = _getParensCommandStrToken(commandStr[i:])
- i += len(subCommandStr) # Increment past the sub-command string.
- except PyAutoGUIException as excObj:
- # Exception message starts with something like "Invalid command at index 0:"
- # Change the index number and reraise it.
- indexPart, colon, message = str(excObj).partition(":")
- indexNum = indexPart[len("Invalid command at index ") :]
- newIndexNum = int(indexNum) + i
- raise PyAutoGUIException("Invalid command at index %s:%s" % (newIndexNum, message))
- # Get rid of any whitespace at the front:
- commandList.append(numberOfLoops.lstrip())
- # Get rid of any whitespace at the front and the quotes:
- subCommandStr = subCommandStr.lstrip()[1:-1]
- # Recursively call this function and append the list it returns:
- commandList.append(_tokenizeCommandStr(subCommandStr))
- return commandList
- def _runCommandList(commandList, _ssCount):
- global PAUSE
- i = 0
- while i < len(commandList):
- command = commandList[i]
- if command == "c":
- click(button=PRIMARY)
- elif command == "l":
- click(button=LEFT)
- elif command == "m":
- click(button=MIDDLE)
- elif command == "r":
- click(button=RIGHT)
- elif command == "su":
- scroll(1) # scroll up
- elif command == "sd":
- scroll(-1) # scroll down
- elif command == "ss":
- screenshot("screenshot%s.png" % (_ssCount[0]))
- _ssCount[0] += 1
- elif command == "s":
- sleep(float(commandList[i + 1]))
- i += 1
- elif command == "p":
- PAUSE = float(commandList[i + 1])
- i += 1
- elif command == "g":
- if commandList[i + 1][0] in ("+", "-") and commandList[i + 2][0] in ("+", "-"):
- move(int(commandList[i + 1]), int(commandList[i + 2]))
- else:
- moveTo(int(commandList[i + 1]), int(commandList[i + 2]))
- i += 2
- elif command == "d":
- if commandList[i + 1][0] in ("+", "-") and commandList[i + 2][0] in ("+", "-"):
- drag(int(commandList[i + 1]), int(commandList[i + 2]))
- else:
- dragTo(int(commandList[i + 1]), int(commandList[i + 2]))
- i += 2
- elif command == "k":
- press(commandList[i + 1])
- i += 1
- elif command == "w":
- write(commandList[i + 1])
- i += 1
- elif command == "h":
- hotkey(*commandList[i + 1].replace(" ", "").split(","))
- i += 1
- elif command == "a":
- alert(commandList[i + 1])
- i += 1
- elif command == "f":
- for j in range(int(commandList[i + 1])):
- _runCommandList(commandList[i + 2], _ssCount)
- i += 2
- i += 1
- def run(commandStr, _ssCount=None):
- """Run a series of PyAutoGUI function calls according to a mini-language
- made for this function. The `commandStr` is composed of character
- commands that represent PyAutoGUI function calls.
- For example, `run('ccg-20,+0c')` clicks the mouse twice, then makes
- the mouse cursor go 20 pixels to the left, then click again.
- Whitespace between commands and arguments is ignored. Command characters
- must be lowercase. Quotes must be single quotes.
- For example, the previous call could also be written as `run('c c g -20, +0 c')`.
- The character commands and their equivalents are here:
- `c` => `click(button=PRIMARY)`
- `l` => `click(button=LEFT)`
- `m` => `click(button=MIDDLE)`
- `r` => `click(button=RIGHT)`
- `su` => `scroll(1) # scroll up`
- `sd` => `scroll(-1) # scroll down`
- `ss` => `screenshot('screenshot1.png') # filename number increases on its own`
- `gX,Y` => `moveTo(X, Y)`
- `g+X,-Y` => `move(X, Y) # The + or - prefix is the difference between move() and moveTo()`
- `dX,Y` => `dragTo(X, Y)`
- `d+X,-Y` => `drag(X, Y) # The + or - prefix is the difference between drag() and dragTo()`
- `k'key'` => `press('key')`
- `w'text'` => `write('text')`
- `h'key,key,key'` => `hotkey(*'key,key,key'.replace(' ', '').split(','))`
- `a'hello'` => `alert('hello')`
- `sN` => `sleep(N) # N can be an int or float`
- `pN` => `PAUSE = N # N can be an int or float`
- `fN(commands)` => for i in range(N): run(commands)
- Note that any changes to `PAUSE` with the `p` command will be undone when
- this function returns. The original `PAUSE` setting will be reset.
- TODO - This function is under development.
- """
- # run("ccc") straight forward
- # run("susu") if 's' then peek at the next character
- global PAUSE
- if _ssCount is None:
- _ssCount = [
- 0
- ] # Setting this to a mutable list so that the callers can read the changed value. TODO improve this comment
- commandList = _tokenizeCommandStr(commandStr)
- # Carry out each command.
- originalPAUSE = PAUSE
- _runCommandList(commandList, _ssCount)
- PAUSE = originalPAUSE
- def printInfo(dontPrint=False):
- msg = '''
- Platform: {}
- Python Version: {}
- PyAutoGUI Version: {}
- Executable: {}
- Resolution: {}
- Timestamp: {}'''.format(
- *getInfo()
- )
- if not dontPrint:
- print(msg)
- return msg
- def getInfo():
- return (sys.platform, sys.version, __version__, sys.executable, size(), datetime.datetime.now())
- # Add the bottom left, top right, and bottom right corners to FAILSAFE_POINTS.
- _right, _bottom = size()
- FAILSAFE_POINTS.extend([(0, _bottom - 1), (_right - 1, 0), (_right - 1, _bottom - 1)])
|