Obsah
1. Skripty pro automatické ovládání aplikací s GUI postavené na knihovně PyAutoGUI
2. Od nástroje xdotool ke knihovně PyAutoGUI
3. Instalace knihovny PyAutoGUI i všech potřebných podpůrných balíčků
5. Získání základních informací o systému a obrazovce
6. Spuštění testované aplikace
8. Názvy kláves použitelných v knihovně PyAutoGUI
10. Funkce typewrite volaná s názvy kláves
11. Simulace stisku kombinace kláves funkcí hotkey
12. Simulace práce s myší nebo s podobným polohovacím zařízením
13. Získání základních informací o polohovacím zařízení
14. Změna polohy kurzoru myši funkcí moveTo
15. Plynulý pohyb kurzoru myši
17. Využití knihovny PyAutoGUI pro testování aplikací – virtuální framebuffer
19. Repositář s demonstračními příklady
1. Skripty pro automatické ovládání aplikací s GUI postavené na knihovně PyAutoGUI
Unixové systémy a od nich odvozené operační systémy (mezi něž v současnosti patří především Linux apod.) jsou používány a oblíbeny mj. i z toho důvodu, že umožňují relativně snadnou automatizaci mnoha operací s využitím skriptů, které mohou nahradit ruční zadávání jednotlivých příkazů a popř. přenos dat mezi nimi. Primárně je skriptování doménou takových nástrojů a utilit, jejichž vstupní či výstupní data jsou reprezentována textovými či (v poněkud menší míře) binárními daty. Ostatně pro tento účel je v unixu dostupných mnoho desítek specializovaných nástrojů (wc, uniq, sort, sed, grep, awk a tak dále). Mnohdy je však užitečné skriptovat (či nějakým způsobem automatizovat) i ty aplikace, které jsou vybaveny grafickým uživatelským rozhraním a ovládají se s využitím klávesnice a myši, popř. pomocí podobně koncipovaných vstupních zařízení (touchpad, trackball, dotykový displej atd.). Některé pokročilejší aplikace automatizaci explicitně podporují, protože jsou skriptovatelné. Jedná se o některé komerční aplikace (dobrým příkladem je AutoCAD), ale například i o GIMP, LibreOffice apod.
Ve skutečnosti je však možné programově ovládat i ostatní aplikace s grafickým uživatelským rozhraním, a to i ty aplikace, které pro tento účel nejsou explicitně připraveny. Relativně nedávno jsme se na stránkách Roota věnovali popisu nástroje nazvaného xdotool, který je určen pro automatizaci některých operací, které jsou běžně prováděny interaktivně uživatelem, tedy primárně s využitím klávesnice a myši. Připomeňme si, že tento nástroj je naprogramovaný primárně v jazyku C, a je volatelný přímo z příkazové řádky a tím pádem i z BASH skriptů atd. xdotool umožňuje provádět mj. i následující operace:
- Simulace stisku klávesy, popř. klávesy s modifikátorem (Shift, Alt, Control + kombinace)
- Simulace buď pouze stisku či naopak puštění klávesy
- Simulace psaní delšího textu (tedy typicky sekvence alfanumerických kláves)
- Interaktivní výběr okna pro další prováděné operace
- Automatický výběr okna pro další prováděné operace na základě zadaného kritéria
- Simulace stisku vybraného tlačítka myši
- Simulace pohybu ukazatelem myši
- Další operace s vybraným oknem, například jeho minimalizace apod. (podle možností správce oken)
- Naprogramování akce, která se má provést při změně stavu okna (najetí myší apod.)
2. Od nástroje xdotool ke knihovně PyAutoGUI
V případě, že budete potřebovat volat xdotool z jiných programovacích jazyků (tedy nikoli pouze ze shellu), je vhodné se poohlédnout po rozhraních k těmto jazykům. V případě Pythonu se například jedná o dnes již poněkud zastaralou knihovnu nazvanou python-libxdo. Pro mnoho účelů – zejména pro již výše zmíněnou tvorbu testů – je však výhodnější použít balíček nazvaný PyAutoGUI, s nímž se seznámíme v dnešním článku (a v článku navazujícím). Důvodem pro přechod ke knihovně PyAutoGUI může být i to, že některé operace poskytované nástrojem xdotool jsou nedostatečné, protože například neumožňují snadno vyhledávat ovládací prvky atd.
Mezi operace, které knihovna PyAutoGUI podporuje, patří především:
- Simulace stisku klávesy, popř. klávesy s modifikátorem (Shift, Alt, Control + kombinace)
- Simulace buď pouze stisku či naopak puštění klávesy
- Simulace psaní delšího textu (tedy typicky sekvence alfanumerických kláves)
- Simulace stisku vybraného tlačítka myši
- Simulace pohybu ukazatelem myši (včetně specifikace rychlosti, zrychlení, zpomalení atd. kurzoru myši)
- Vytvoření screenshotu
- Nalezení grafického prvku na screenshotu (což je velmi důležitá operace umožňující automatizaci testů)
3. Instalace knihovny PyAutoGUI i všech potřebných podpůrných balíčků
Samotná knihovna PyAutoGUI je pochopitelně dostupná na PyPi, takže by teoreticky měla být její instalace jednoduchá a přímočará. Ovšem některé operace vyžadují přístup na úrovni xlib a pro tento účel je nutné explicitně nainstalovat další podpůrný balíček nazvaný python3-xlib. Instalaci tohoto balíčku ovšem neprovedeme přes PyPi, ale s využitím balíčkovacího nástroje daného operačního systému (dnf, apt-get atd.):
# dnf install python3-xlib
Průběh instalace může proběhnout následovně:
Last metadata expiration check: 0:09:59 ago on Sat 06 Feb 2021 06:14:01 AM EST. Dependencies resolved. ================================================================================ Package Architecture Version Repository Size ================================================================================ Installing: python3-xlib noarch 0.28-2.fc32 updates 262 k Transaction Summary ================================================================================ Install 1 Package Total download size: 262 k Installed size: 1.1 M Is this ok [y/N]: Downloading Packages: python3-xlib-0.28-2.fc32.noarch.rpm 402 kB/s | 262 kB 00:00 -------------------------------------------------------------------------------- Total 199 kB/s | 262 kB 00:01 Running transaction check Transaction check succeeded. Running transaction test Transaction test succeeded. Running transaction Preparing : 1/1 Installing : python3-xlib-0.28-2.fc32.noarch 1/1 Running scriptlet: python3-xlib-0.28-2.fc32.noarch 1/1 Verifying : python3-xlib-0.28-2.fc32.noarch 1/1 Installed: python3-xlib-0.28-2.fc32.noarch Complete!
Po instalaci balíčku python3-xlib již můžeme přistoupit k instalaci knihovny PyAutoGUI. Pro tento účel již použijeme známý nástroj pip (či pip3) určený pro Pythonovský ekosystém:
$ pip install --user pyautogui
Průběh instalace:
Collecting pyautogui Downloading https://files.pythonhosted.org/packages/40/0a/1373680148828d7ea3f0e432f06c08f51a33d72306719954d3ae379f5890/PyAutoGUI-0.9.52.tar.gz (55kB) |████████████████████████████████| 61kB 1.8MB/s Collecting pymsgbox Downloading https://files.pythonhosted.org/packages/7d/ff/4c6f31a4f08979f12a663f2aeb6c8b765d3bd592e66eaaac445f547bb875/PyMsgBox-1.0.9.tar.gz Installing build dependencies ... done Getting requirements to build wheel ... done Preparing wheel metadata ... done Collecting PyTweening>=1.0.1 Downloading https://files.pythonhosted.org/packages/b9/f8/c32a58d6e4dff8aa5c27e907194d69f3b57e525c2e4af96f39c6e9c854d2/PyTweening-1.0.3.zip Collecting pyscreeze>=0.1.21 Downloading https://files.pythonhosted.org/packages/b7/7d/a0e85da28a96e2ff2f39e682ff84eb92501b564883fde87d92aee29966a2/PyScreeze-0.1.26.tar.gz Collecting pygetwindow>=0.0.5 Downloading https://files.pythonhosted.org/packages/e1/70/c7a4f46dbf06048c6d57d9489b8e0f9c4c3d36b7479f03c5ca97eaa2541d/PyGetWindow-0.0.9.tar.gz Collecting mouseinfo Downloading https://files.pythonhosted.org/packages/28/fa/b2ba8229b9381e8f6381c1dcae6f4159a7f72349e414ed19cfbbd1817173/MouseInfo-0.1.3.tar.gz Collecting python3-Xlib Downloading https://files.pythonhosted.org/packages/ef/c6/2c5999de3bb1533521f1101e8fe56fd9c266732f4d48011c7c69b29d12ae/python3-xlib-0.15.tar.gz (132kB) |████████████████████████████████| 133kB 3.7MB/s Collecting Pillow>=6.2.1 Downloading https://files.pythonhosted.org/packages/c9/57/a6c81c1d6b8973d288ec6df0993a71a61318c862afc1de71fd3aa1ba1dcd/Pillow-8.1.0-cp38-cp38-manylinux1_x86_64.whl (2.2MB) |████████████████████████████████| 2.2MB 16.7MB/s Collecting pyrect Downloading https://files.pythonhosted.org/packages/2f/68/bd7bf96fc44217e769f27912e6c9bb3e9987cba286054af6120448ce8212/PyRect-0.1.4.tar.gz Collecting pyperclip Downloading https://files.pythonhosted.org/packages/6f/4c/0b1d507ad7e8bc31d690d04b4f475e74c2002d060f7994ce8c09612df707/pyperclip-1.8.1.tar.gz Building wheels for collected packages: pymsgbox Building wheel for pymsgbox (PEP 517) ... done Created wheel for pymsgbox: filename=PyMsgBox-1.0.9-cp38-none-any.whl size=7420 sha256=c0e7f71340220fc6f6ababdb5a98260331111d2260fc7843e5854bb0fa40a5eb Stored in directory: /home/tester/.cache/pip/wheels/16/d7/4e/fffdd54b1cca86608b7655c9a2854890a38f955ce9532848cd Successfully built pymsgbox Installing collected packages: pymsgbox, PyTweening, Pillow, pyscreeze, pyrect, pygetwindow, pyperclip, python3-Xlib, mouseinfo, pyautogui Running setup.py install for PyTweening ... done Running setup.py install for pyscreeze ... done Running setup.py install for pyrect ... done Running setup.py install for pygetwindow ... done Running setup.py install for pyperclip ... done Running setup.py install for python3-Xlib ... done Running setup.py install for mouseinfo ... done Running setup.py install for pyautogui ... done Successfully installed Pillow-8.1.0 PyTweening-1.0.3 mouseinfo-0.1.3 pyautogui-0.9.52 pygetwindow-0.0.9 pymsgbox-1.0.9 pyperclip-1.8.1 pyrect-0.1.4 pyscreeze-0.1.26 python3-Xlib-0.15
Některé operace, například vytvoření screenshotu, vyžadují další nástroje. Zejména se jedná o scrot (SCReenshOT) určený pro získání snímků obrazovky:
$ sudo dnf install scrot
4. Otestování instalace
Nyní otestujme základní instalaci všech potřebných komponent. Nejdříve zjistíme, zda je balíček pyautogui dostupný z interpretru programovacího jazyka Python:
$ $ python3
Následující dva příkazy by se měly provést bez komplikací:
>>> import pyautogui >>> help(pyautogui)
Zobrazená nápověda:
Help on package pyautogui: NAME pyautogui DESCRIPTION # 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!) PACKAGE CONTENTS __main__ _pyautogui_java _pyautogui_osx _pyautogui_win _pyautogui_x11 SUBMODULES platformModule CLASSES builtins.Exception(builtins.BaseException)
Dále zjistíme, jestli je dostupný nástroj scrot pro získání snímků obrazovky:
$ whereis scrot scrot: /usr/bin/scrot /usr/share/man/man1/scrot.1.gz
$ scrot --help Usage : scrot [OPTIONS]... [FILE] Where FILE is the target file for the screenshot. If FILE is not specified, a date-stamped file will be dropped in the current directory. See man scrot for more details -h, --help display this help and exit -v, --version output version information and exit -b, --border When selecting a window, grab wm border too -c, --count show a countdown before taking the shot -d, --delay NUM wait NUM seconds before taking a shot -e, --exec APP run APP on the resulting screenshot -q, --quality NUM Image quality (1-100) high value means high size, low compression. Default: 75. For lossless compression formats, like png, low quality means high compression. ... ... ... This program is free software see the file COPYING for licensing info. Copyright Tom Gilbert 2000 Email bugs to <scrot_sucks@linuxbrit.co.uk>
5. Získání základních informací o systému a obrazovce
Nyní si již můžeme otestovat některé funkce poskytované knihovnou PyAutoGUI. Nejdříve zjistíme základní informace o systému, a to s využitím tohoto skriptu:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pyautogui print(pyautogui.getInfo())
Výsledkem bude n-tice s několika systémovými informacemi, mezi než patří mj. i rozměry obrazovky.
Další skript (stále velmi jednoduchý) již explicitně zjistí a vypíše rozměry obrazovky:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pyautogui width, height = pyautogui.size() print("Screen size: {}x{} pixels".format(width, height))
Příklad výsledku:
$ ./02_screen_size.py Screen size: 1920x1080 pixels
6. Spuštění testované aplikace
Vzhledem k tomu, že není zcela snadné detekovat okno aplikace, která se má s využitím PyAutoGUI ovládat, budeme v demonstračních příkladech celou operaci provádět naopak – danou aplikaci přímo spustíme ze skriptu a na konci skriptu ji ukončíme. Používat budeme klasický emulátor terminálu xterm, který bude mít velikost 40×20 znaků (nikoli pixelů!) a jeho levý horní roh by měl ležet na souřadnicích [100, 100], ovšem za předpokladu, že to umožní správce oken:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import subprocess import time print("Opening xterm") # otevření terminálu p = subprocess.Popen(["xterm", "-geometry", "40x20+100+100"]) assert p is not None print("Opened") time.sleep(5) print("Closing xterm") p.kill() print("Closed")
Dále budeme používat nástroj xev. Tento nástroj dokáže zobrazit všechny události (event), které jako aplikace běžící pod systémem X přijímá. Kromě událostí, které souvisí s činností vlastního systému oken se jedná i o události spojené s klávesnicí a myší (a o ty se dnes budeme zajímat především):
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import subprocess import time print("Opening xev") # otevření xev p = subprocess.Popen(["xev", "-geometry", "400x400+0+0"]) assert p is not None print("Opened") time.sleep(5) print("Closing xev") p.kill() print("Closed")
7. Simulace stisku kláves
Knihovna PyAutoGUI poskytuje trojici základních funkcí určených pro simulaci stisku kláves. První z těchto funkcí se jmenuje keyDown. Tato funkce simuluje stisk klávesy a jejím jediným povinným parametrem je jméno klávesy (viz další kapitolu):
Help on function keyDown in module pyautogui: 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
Druhá funkce, která se jmenuje keyUp, simuluje puštění klávesy:
Help on function keyUp in module pyautogui: 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
Třetí funkce s názvem typewrite slouží pro poslání sekvence kláves do testované/skriptované aplikace – simuluje tak zápis textu uživatelem atd. Zvolit lze jak danou sekvenci kláves, tak i interval mezi jednotlivými stisky (což je u některých aplikací vyžadováno):
Help on function typewrite in module pyautogui: 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
Podívejme se nejdříve na použití poslední zmíněné funkce – s využitím typewrite napíšeme do terminálu příkaz a následně ho spustíme (jednotlivé znaky příkazu jsou zapisovány se sekundovými intervaly):
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import subprocess import time import pyautogui print("Opening xterm") # otevření terminálu p = subprocess.Popen(["xterm", "-geometry", "40x20+100+100"]) assert p is not None print("Opened") time.sleep(5) print("Writing into terminal window...") pyautogui.typewrite("ls --color -la\n", interval=1.0) print("Done") time.sleep(5) print("Closing xterm") p.kill() print("Closed")
8. Názvy kláves použitelných v knihovně PyAutoGUI
Názvy všech kláves rozpoznatelných knihovnou PyAutoGUI přečteme a zobrazíme následujícím jednoduchým skriptem:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pyautogui print("\n".join(pyautogui.KEYBOARD_KEYS))
Výsledkem bude sekvence řádků s názvy kláves (reprezentovaných řetězci):
! " # $ % & ' ( ) * + , - . / 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
9. Stisk a puštění klávesy
V sedmé kapitole jsme si řekli, že pro simulaci stisku a puštění klávesy se používají funkce pojmenované keyDown a keyUp. Těmto funkcím se předávají jména kláves vypsaná v předchozí kapitole. V následujícím demonstračním příkladu se nejprve spustí nástroj xev a následně se simuluje stisk kláves F1 až F4. Až poté jsou klávesy puštěny, ovšem v opačném pořadí:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import subprocess import time import pyautogui print("Opening xev") # otevření xev p = subprocess.Popen(["xev", "-geometry", "400x400+0+0"]) assert p is not None print("Opened") time.sleep(5) print("Key down") pyautogui.keyDown("F1") pyautogui.keyDown("F2") pyautogui.keyDown("F3") pyautogui.keyDown("F4") print("Key up") pyautogui.keyUp("F4") pyautogui.keyUp("F3") pyautogui.keyUp("F2") pyautogui.keyUp("F1") time.sleep(5) print("Closing xev") p.kill() print("Closed")
Nástroj xev postupně vypíše všechny události, které dostal. Mezi ně patří i události související s klávesnicí. Ty jsou zvýrazněny:
Outer window is 0x1200001, inner window is 0x1200002 PropertyNotify event, serial 8, synthetic NO, window 0x1200001, atom 0x27 (WM_NAME), time 2273423, state PropertyNewValue PropertyNotify event, serial 9, synthetic NO, window 0x1200001, atom 0x22 (WM_COMMAND), time 2273423, state PropertyNewValue PropertyNotify event, serial 10, synthetic NO, window 0x1200001, atom 0x28 (WM_NORMAL_HINTS), time 2273423, state PropertyNewValue CreateNotify event, serial 11, synthetic NO, window 0x1200001, parent 0x1200001, window 0x1200002, (10,10), width 50, height 50 border_width 4, override NO PropertyNotify event, serial 14, synthetic NO, window 0x1200001, atom 0x13e (WM_PROTOCOLS), time 2273424, state PropertyNewValue MapNotify event, serial 15, synthetic NO, window 0x1200001, event 0x1200001, window 0x1200002, override NO PropertyNotify event, serial 28, synthetic NO, window 0x1200001, atom 0x1d9 (_FLUXBOX_GROUP_LEFT), time 2273424, state PropertyNewValue ConfigureNotify event, serial 29, synthetic NO, window 0x1200001, event 0x1200001, window 0x1200001, (0,0), width 400, height 400, border_width 0, above 0x800008, override NO ReparentNotify event, serial 29, synthetic NO, window 0x1200001, event 0x1200001, window 0x1200001, parent 0x602fb7, (0,21), override NO MapNotify event, serial 29, synthetic NO, window 0x1200001, event 0x1200001, window 0x1200001, override NO PropertyNotify event, serial 29, synthetic NO, window 0x1200001, atom 0x15f (_NET_WM_DESKTOP), time 2273425, state PropertyNewValue PropertyNotify event, serial 29, synthetic NO, window 0x1200001, atom 0x15c (_NET_FRAME_EXTENTS), time 2273425, state PropertyNewValue PropertyNotify event, serial 30, synthetic NO, window 0x1200001, atom 0x197 (_NET_WM_ALLOWED_ACTIONS), time 2273425, state PropertyNewValue VisibilityNotify event, serial 32, synthetic NO, window 0x1200001, state VisibilityPartiallyObscured Expose event, serial 32, synthetic NO, window 0x1200001, (0,1), width 400, height 9, count 3 Expose event, serial 32, synthetic NO, window 0x1200001, (0,10), width 10, height 58, count 2 Expose event, serial 32, synthetic NO, window 0x1200001, (68,10), width 332, height 58, count 1 Expose event, serial 32, synthetic NO, window 0x1200001, (0,68), width 400, height 332, count 0 EnterNotify event, serial 32, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2273425, (386,16), root:(387,38), mode NotifyNormal, detail NotifyNonlinear, same_screen YES, focus NO, state 8192 KeymapNotify event, serial 32, synthetic NO, window 0x0, keys: 68 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 PropertyNotify event, serial 32, synthetic NO, window 0x1200001, atom 0x13f (WM_STATE), time 2273425, state PropertyNewValue PropertyNotify event, serial 32, synthetic NO, window 0x1200001, atom 0x15f (_NET_WM_DESKTOP), time 2273425, state PropertyNewValue PropertyNotify event, serial 32, synthetic NO, window 0x1200001, atom 0x15c (_NET_FRAME_EXTENTS), time 2273425, state PropertyNewValue ConfigureNotify event, serial 32, synthetic NO, window 0x1200001, event 0x1200001, window 0x1200001, (0,22), width 400, height 400, border_width 0, above 0x602fbe, override NO VisibilityNotify event, serial 32, synthetic NO, window 0x1200001, state VisibilityUnobscured Expose event, serial 32, synthetic NO, window 0x1200001, (0,0), width 400, height 1, count 0 ConfigureNotify event, serial 32, synthetic YES, window 0x1200001, event 0x1200001, window 0x1200001, (1,23), width 400, height 400, border_width 0, above 0x0, override NO FocusIn event, serial 33, synthetic NO, window 0x1200001, mode NotifyNormal, detail NotifyNonlinear KeymapNotify event, serial 33, synthetic NO, window 0x0, keys: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ConfigureNotify event, serial 33, synthetic NO, window 0x1200001, event 0x1200001, window 0x1200001, (0,22), width 400, height 400, border_width 0, above 0x602fbb, override NO ConfigureNotify event, serial 33, synthetic YES, window 0x1200001, event 0x1200001, window 0x1200001, (1,23), width 400, height 400, border_width 0, above 0x0, override NO KeyPress event, serial 33, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2278422, (386,15), root:(387,38), state 0x2000, keycode 67 (keysym 0xffbe, F1), same_screen YES, XLookupString gives 0 bytes: XmbLookupString gives 0 bytes: XFilterEvent returns: False KeyPress event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2278525, (386,15), root:(387,38), state 0x2000, keycode 68 (keysym 0xffbf, F2), same_screen YES, XLookupString gives 0 bytes: XmbLookupString gives 0 bytes: XFilterEvent returns: False KeyPress event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2278627, (386,15), root:(387,38), state 0x2000, keycode 69 (keysym 0xffc0, F3), same_screen YES, XLookupString gives 0 bytes: XmbLookupString gives 0 bytes: XFilterEvent returns: False KeyPress event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2278728, (386,15), root:(387,38), state 0x2000, keycode 70 (keysym 0xffc1, F4), same_screen YES, XLookupString gives 0 bytes: XmbLookupString gives 0 bytes: XFilterEvent returns: False KeyRelease event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2278830, (386,15), root:(387,38), state 0x2000, keycode 70 (keysym 0xffc1, F4), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False KeyRelease event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2278932, (386,15), root:(387,38), state 0x2000, keycode 69 (keysym 0xffc0, F3), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False KeyRelease event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2279033, (386,15), root:(387,38), state 0x2000, keycode 68 (keysym 0xffbf, F2), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False KeyRelease event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2279135, (386,15), root:(387,38), state 0x2000, keycode 67 (keysym 0xffbe, F1), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False Opening xev Opened Key down Key up Closing xev Closed
10. Funkce typewrite volaná s názvy kláves
S funkcí pojmenovanou typewrite jsme se seznámili v sedmé kapitole. Ve skutečnosti je však možné této funkci předat nejenom řetězec, který bude „napsán“ do aplikace – funkce typewrite totiž akceptuje i seznam či n-tici obsahující symbolické názvy kláves. Tímto způsobem je možné nahradit sekvenci funkcí keyDown a keyUp tak, jak je to ukázáno v dalším demonstračním příkladu:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import subprocess import time import pyautogui print("Opening xev") # otevření xev p = subprocess.Popen(["xev", "-geometry", "400x400+0+0"]) assert p is not None print("Opened") time.sleep(5) print("Simulating keyboard...") pyautogui.typewrite(["a", "enter", "f1", "delete", "alt"], interval=1.0) print("Done") time.sleep(5) print("Closing xev") p.kill() print("Closed")
Po spuštění tohoto demonstračního příkladu se otevře aplikace xev a v rámci jejího okna se simuluje stisk klávesy „a“, „Enter“, „Delete“ atd. To lze ostatně zjistit již z výstupu xev (důležité zprávy jsou opět zvýrazněny):
Outer window is 0x1200001, inner window is 0x1200002 PropertyNotify event, serial 8, synthetic NO, window 0x1200001, atom 0x27 (WM_NAME), time 2873377, state PropertyNewValue PropertyNotify event, serial 9, synthetic NO, window 0x1200001, atom 0x22 (WM_COMMAND), time 2873377, state PropertyNewValue PropertyNotify event, serial 10, synthetic NO, window 0x1200001, atom 0x28 (WM_NORMAL_HINTS), time 2873377, state PropertyNewValue CreateNotify event, serial 11, synthetic NO, window 0x1200001, parent 0x1200001, window 0x1200002, (10,10), width 50, height 50 border_width 4, override NO PropertyNotify event, serial 14, synthetic NO, window 0x1200001, atom 0x13e (WM_PROTOCOLS), time 2873377, state PropertyNewValue MapNotify event, serial 15, synthetic NO, window 0x1200001, event 0x1200001, window 0x1200002, override NO PropertyNotify event, serial 28, synthetic NO, window 0x1200001, atom 0x1d9 (_FLUXBOX_GROUP_LEFT), time 2873378, state PropertyNewValue ConfigureNotify event, serial 29, synthetic NO, window 0x1200001, event 0x1200001, window 0x1200001, (0,0), width 400, height 400, border_width 0, above 0x800008, override NO ReparentNotify event, serial 29, synthetic NO, window 0x1200001, event 0x1200001, window 0x1200001, parent 0x603b3b, (0,21), override NO MapNotify event, serial 29, synthetic NO, window 0x1200001, event 0x1200001, window 0x1200001, override NO PropertyNotify event, serial 29, synthetic NO, window 0x1200001, atom 0x15f (_NET_WM_DESKTOP), time 2873378, state PropertyNewValue PropertyNotify event, serial 29, synthetic NO, window 0x1200001, atom 0x15c (_NET_FRAME_EXTENTS), time 2873378, state PropertyNewValue PropertyNotify event, serial 29, synthetic NO, window 0x1200001, atom 0x197 (_NET_WM_ALLOWED_ACTIONS), time 2873378, state PropertyNewValue VisibilityNotify event, serial 32, synthetic NO, window 0x1200001, state VisibilityPartiallyObscured Expose event, serial 32, synthetic NO, window 0x1200001, (0,1), width 400, height 9, count 3 Expose event, serial 32, synthetic NO, window 0x1200001, (0,10), width 10, height 58, count 2 Expose event, serial 32, synthetic NO, window 0x1200001, (68,10), width 332, height 58, count 1 Expose event, serial 32, synthetic NO, window 0x1200001, (0,68), width 400, height 332, count 0 PropertyNotify event, serial 32, synthetic NO, window 0x1200001, atom 0x13f (WM_STATE), time 2873378, state PropertyNewValue PropertyNotify event, serial 32, synthetic NO, window 0x1200001, atom 0x15f (_NET_WM_DESKTOP), time 2873378, state PropertyNewValue PropertyNotify event, serial 32, synthetic NO, window 0x1200001, atom 0x15c (_NET_FRAME_EXTENTS), time 2873379, state PropertyNewValue ConfigureNotify event, serial 32, synthetic NO, window 0x1200001, event 0x1200001, window 0x1200001, (0,22), width 400, height 400, border_width 0, above 0x603b42, override NO VisibilityNotify event, serial 32, synthetic NO, window 0x1200001, state VisibilityUnobscured Expose event, serial 32, synthetic NO, window 0x1200001, (0,0), width 400, height 1, count 0 ConfigureNotify event, serial 32, synthetic YES, window 0x1200001, event 0x1200001, window 0x1200001, (1,23), width 400, height 400, border_width 0, above 0x0, override NO FocusIn event, serial 33, synthetic NO, window 0x1200001, mode NotifyNormal, detail NotifyNonlinear KeymapNotify event, serial 33, synthetic NO, window 0x0, keys: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ConfigureNotify event, serial 33, synthetic NO, window 0x1200001, event 0x1200001, window 0x1200001, (0,22), width 400, height 400, border_width 0, above 0x603b3f, override NO ConfigureNotify event, serial 33, synthetic YES, window 0x1200001, event 0x1200001, window 0x1200001, (1,23), width 400, height 400, border_width 0, above 0x0, override NO KeyPress event, serial 33, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2878376, (739,1054), root:(740,1077), state 0x2000, keycode 38 (keysym 0x61, a), same_screen YES, XLookupString gives 1 bytes: (61) "a" XmbLookupString gives 1 bytes: (61) "a" XFilterEvent returns: False KeyRelease event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2878378, (739,1054), root:(740,1077), state 0x2000, keycode 38 (keysym 0x61, a), same_screen YES, XLookupString gives 1 bytes: (61) "a" XFilterEvent returns: False KeyPress event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2879381, (739,1054), root:(740,1077), state 0x2000, keycode 36 (keysym 0xff0d, Return), same_screen YES, XLookupString gives 1 bytes: (0d) " " XmbLookupString gives 1 bytes: (0d) " " XFilterEvent returns: False KeyRelease event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2879382, (739,1054), root:(740,1077), state 0x2000, keycode 36 (keysym 0xff0d, Return), same_screen YES, XLookupString gives 1 bytes: (0d) " " XFilterEvent returns: False KeyPress event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2880385, (739,1054), root:(740,1077), state 0x2000, keycode 67 (keysym 0xffbe, F1), same_screen YES, XLookupString gives 0 bytes: XmbLookupString gives 0 bytes: XFilterEvent returns: False KeyRelease event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2880386, (739,1054), root:(740,1077), state 0x2000, keycode 67 (keysym 0xffbe, F1), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False KeyPress event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2881389, (739,1054), root:(740,1077), state 0x2000, keycode 119 (keysym 0xffff, Delete), same_screen YES, XLookupString gives 1 bytes: (7f) "" XmbLookupString gives 1 bytes: (7f) "" XFilterEvent returns: False KeyRelease event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2881390, (739,1054), root:(740,1077), state 0x2000, keycode 119 (keysym 0xffff, Delete), same_screen YES, XLookupString gives 1 bytes: (7f) "" XFilterEvent returns: False KeyPress event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2882393, (739,1054), root:(740,1077), state 0x2000, keycode 64 (keysym 0xffe9, Alt_L), same_screen YES, XLookupString gives 0 bytes: XmbLookupString gives 0 bytes: XFilterEvent returns: False KeyRelease event, serial 36, synthetic NO, window 0x1200001, root 0x178, subw 0x0, time 2882394, (739,1054), root:(740,1077), state 0x2008, keycode 64 (keysym 0xffe9, Alt_L), same_screen YES, XLookupString gives 0 bytes: XFilterEvent returns: False Opening xev Opened Simulating keyboard... Done Closing xev Closed
11. Simulace stisku kombinace kláves funkcí hotkey
Funkce typewrite nedokáže simulovat kombinaci současného stisku několika kláves, například Shift+A, Ctrl+X, Alt+Space atd. K tomuto účelu je nutné použít další funkci pojmenovanou hotkey (současně se jedná o poslední funkci související s klávesnicí). Této funkci se v parametrech předá celá klávesová kombinace:
pyautogui.hotkey("shift", "a")
Podívejme se nyní na další demonstrační příklad, v němž otevřeme terminál, zapíšeme do něj znaky „A“ a „Z“ (tedy kombinace Shift+písmeno) a následně s využitím Emacsovských klávesových zkratek Ctrl+A a Ctrl+E přesuneme kurzor na začátek a konec příkazu:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import subprocess import time import pyautogui print("Opening xterm") # otevření terminálu p = subprocess.Popen(["xterm", "-geometry", "40x20+100+100"]) assert p is not None print("Opened") time.sleep(5) print("Writing into terminal window...") pyautogui.hotkey("shift", "a") pyautogui.hotkey("shift", "z") time.sleep(1) print("Moving cursor...") pyautogui.hotkey("ctrl", "a") time.sleep(1) pyautogui.hotkey("ctrl", "e") time.sleep(1) print("Done") time.sleep(5) print("Closing xterm") p.kill() print("Closed")
12. Simulace práce s myší nebo s podobným polohovacím zařízením
Knihovna PyAutoGUI podporuje i simulaci práce s myší. Nejjednodušší je operace napodobující kliknutí vybraným tlačítkem myši, ovšem podporovány jsou i další operace, které si postupně popíšeme.
Pro získání informací o vstupních zařízeních ovládaných uživatelem můžeme použít příkaz xinput, který mj. zobrazí i zařízení typu „core pointer“, což jsou drátové i bezdrátové myši, trackpointy, touch pady atd. Na počítači s trackpointem, touchpadem, drátovou i bezdrátovou myší (použito pouze pro demonstrační účely) může výstup vypadat takto:
$ xinput
S výsledkem:
⎡ Virtual core pointer id=2 [master pointer (3)] ⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)] ⎜ ↳ SynPS/2 Synaptics TouchPad id=12 [slave pointer (2)] ⎜ ↳ TPPS/2 IBM TrackPoint id=13 [slave pointer (2)] ⎜ ↳ Logitech USB Receiver Consumer Control id=15 [slave pointer (2)] ⎜ ↳ Logitech USB Receiver id=17 [slave pointer (2)] ⎜ ↳ Logitech USB-PS/2 Optical Mouse id=18 [slave pointer (2)] ⎣ Virtual core keyboard id=3 [master keyboard (2)] ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] ↳ Power Button id=6 [slave keyboard (3)] ↳ Video Bus id=7 [slave keyboard (3)] ↳ Video Bus id=8 [slave keyboard (3)] ↳ Sleep Button id=9 [slave keyboard (3)] ↳ Integrated Camera: Integrated C id=10 [slave keyboard (3)] ↳ AT Translated Set 2 keyboard id=11 [slave keyboard (3)] ↳ ThinkPad Extra Buttons id=14 [slave keyboard (3)] ↳ Logitech USB Receiver Consumer Control id=16 [slave keyboard (3)]
13. Získání základních informací o polohovacím zařízení
Pro získání základních informací o polohovacím zařízení slouží pomocná aplikace nazvaná mouseInfo. Tu lze spustit funkcí se stejným jménem, tedy mouseInfo:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import pyautogui pyautogui.mouseInfo()
Grafické uživatelské rozhraní této aplikace vypadá následovně:
Obrázek 1: GUI aplikace mouseInfo.
Tato aplikace sleduje polohu kurzoru myši i barvu pixelu pod kurzorem (resp. přesněji řečeno pod aktivním bodem kurzoru). Dokáže tyto údaje logovat a v případě potřeby vytvořit screenshot obrazovky (ten se interně stejně vytváří, aby bylo možné číst barvu pixelu). Kombinace obou údajů, tj. polohy kurzoru myši a screenshotů se používá při tvorbě automatických testů grafického uživatelského rozhraní.
14. Změna polohy kurzoru myši funkcí moveTo
V knihovně PyAutoGUI lze pochopitelně i pohybovat kurzorem myši. Základní funkcí, která to umožňuje, je funkce nazvaná moveTo, které se předá nové (absolutní) pozice kurzoru, přičemž levý horní roh souřadného systému je umístěn v levém horním rohu primárního monitoru. Následující demonstrační příklad otevře aplikaci xev a poté v jejím okně myš projede čtyřmi vrcholy čtverce:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import subprocess import time import pyautogui print("Opening xev") # otevření xev p = subprocess.Popen(["xev", "-geometry", "400x400+0+0"]) assert p is not None print("Opened") time.sleep(5) print("Mouse move") pyautogui.moveTo(100, 100) pyautogui.moveTo(200, 100) pyautogui.moveTo(200, 200) pyautogui.moveTo(100, 200) pyautogui.moveTo(100, 100) print("Done") time.sleep(2) print("Closing xev") p.kill() print("Closed")
Změna pozice kurzoru je pochopitelně předána ve formě událostí (events):
Outer window is 0x3200001, inner window is 0x3200002 PropertyNotify event, serial 8, synthetic NO, window 0x3200001, atom 0x27 (WM_NAME), time 182582290, state PropertyNewValue PropertyNotify event, serial 9, synthetic NO, window 0x3200001, atom 0x22 (WM_COMMAND), time 182582290, state PropertyNewValue PropertyNotify event, serial 10, synthetic NO, window 0x3200001, atom 0x28 (WM_NORMAL_HINTS), time 182582290, state PropertyNewValue CreateNotify event, serial 11, synthetic NO, window 0x3200001, parent 0x3200001, window 0x3200002, (10,10), width 50, height 50 border_width 4, override NO PropertyNotify event, serial 14, synthetic NO, window 0x3200001, atom 0x13e (WM_PROTOCOLS), time 182582290, state PropertyNewValue MapNotify event, serial 15, synthetic NO, window 0x3200001, event 0x3200001, window 0x3200002, override NO PropertyNotify event, serial 28, synthetic NO, window 0x3200001, atom 0x1da (_FLUXBOX_GROUP_LEFT), time 182582291, state PropertyNewValue ConfigureNotify event, serial 29, synthetic NO, window 0x3200001, event 0x3200001, window 0x3200001, (0,0), width 400, height 400, border_width 0, above 0xe00068, override NO ReparentNotify event, serial 29, synthetic NO, window 0x3200001, event 0x3200001, window 0x3200001, parent 0x62573a, (0,21), override NO MapNotify event, serial 29, synthetic NO, window 0x3200001, event 0x3200001, window 0x3200001, override NO PropertyNotify event, serial 29, synthetic NO, window 0x3200001, atom 0x160 (_NET_WM_DESKTOP), time 182582292, state PropertyNewValue PropertyNotify event, serial 29, synthetic NO, window 0x3200001, atom 0x15d (_NET_FRAME_EXTENTS), time 182582292, state PropertyNewValue PropertyNotify event, serial 29, synthetic NO, window 0x3200001, atom 0x19f (_NET_WM_ALLOWED_ACTIONS), time 182582292, state PropertyNewValue VisibilityNotify event, serial 32, synthetic NO, window 0x3200001, state VisibilityPartiallyObscured Expose event, serial 32, synthetic NO, window 0x3200001, (0,1), width 400, height 9, count 3 Expose event, serial 32, synthetic NO, window 0x3200001, (0,10), width 10, height 58, count 2 Expose event, serial 32, synthetic NO, window 0x3200001, (68,10), width 332, height 58, count 1 Expose event, serial 32, synthetic NO, window 0x3200001, (0,68), width 400, height 332, count 0 PropertyNotify event, serial 32, synthetic NO, window 0x3200001, atom 0x13f (WM_STATE), time 182582292, state PropertyNewValue PropertyNotify event, serial 32, synthetic NO, window 0x3200001, atom 0x160 (_NET_WM_DESKTOP), time 182582292, state PropertyNewValue PropertyNotify event, serial 32, synthetic NO, window 0x3200001, atom 0x15d (_NET_FRAME_EXTENTS), time 182582292, state PropertyNewValue ConfigureNotify event, serial 32, synthetic NO, window 0x3200001, event 0x3200001, window 0x3200001, (0,22), width 400, height 400, border_width 0, above 0x625741, override NO VisibilityNotify event, serial 32, synthetic NO, window 0x3200001, state VisibilityUnobscured Expose event, serial 32, synthetic NO, window 0x3200001, (0,0), width 400, height 1, count 0 ConfigureNotify event, serial 32, synthetic YES, window 0x3200001, event 0x3200001, window 0x3200001, (1,23), width 400, height 400, border_width 0, above 0x0, override NO FocusIn event, serial 33, synthetic NO, window 0x3200001, mode NotifyNormal, detail NotifyNonlinear KeymapNotify event, serial 33, synthetic NO, window 0x0, keys: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ConfigureNotify event, serial 33, synthetic NO, window 0x3200001, event 0x3200001, window 0x3200001, (0,22), width 400, height 400, border_width 0, above 0x62573e, override NO ConfigureNotify event, serial 33, synthetic YES, window 0x3200001, event 0x3200001, window 0x3200001, (1,23), width 400, height 400, border_width 0, above 0x0, override NO EnterNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182587289, (99,77), root:(100,100), mode NotifyNormal, detail NotifyNonlinear, same_screen YES, focus YES, state 8208 KeymapNotify event, serial 33, synthetic NO, window 0x0, keys: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 MotionNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182587289, (99,77), root:(100,100), state 0x2010, is_hint 0, same_screen YES MotionNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182587393, (199,77), root:(200,100), state 0x2010, is_hint 0, same_screen YES MotionNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182587496, (199,177), root:(200,200), state 0x2010, is_hint 0, same_screen YES MotionNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182587599, (99,177), root:(100,200), state 0x2010, is_hint 0, same_screen YES MotionNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182587702, (99,77), root:(100,100), state 0x2010, is_hint 0, same_screen YES Opening xev Opened Mouse move Done Closing xev Closed
15. Plynulý pohyb kurzoru myši
Pohyb myši může být i plynulý (což je v mnoha aplikacích nutnost). Knihovna PyAutoGUI podporuje několik metod určených pro nastavení plynulého pohybu myši, ovšem nejjednodušší je předat nám již známé funkci moveTo parametr duration se specifikací, jak dlouho má trvat přesun kurzoru na nové souřadnice. Doba trvání je v tomto případě specifikována v sekundách. Vše si opět ukážeme na aplikaci xev, do níž myší „nakreslíme“ čtverec:
#!/usr/bin/env python3 # vim: set fileencoding=utf-8 import subprocess import time import pyautogui print("Opening xev") # otevření xev p = subprocess.Popen(["xev", "-geometry", "400x400+0+0"]) assert p is not None print("Opened") time.sleep(5) print("Mouse move") pyautogui.moveTo(100, 100, duration=0) pyautogui.moveTo(200, 100, duration=2) pyautogui.moveTo(200, 200, duration=2) pyautogui.moveTo(100, 200, duration=2) pyautogui.moveTo(100, 100, duration=2) print("Done") time.sleep(2) print("Closing xev") p.kill() print("Closed")
Nyní bude vygenerováno mnoho desítek až stovek událostí typu MotionNotify. Následující výpis je zkrácen:
ConfigureNotify event, serial 33, synthetic NO, window 0x3200001, event 0x3200001, window 0x3200001, (0,22), width 400, height 400, border_width 0, above 0x625891, override NO ConfigureNotify event, serial 33, synthetic YES, window 0x3200001, event 0x3200001, window 0x3200001, (1,23), width 400, height 400, border_width 0, above 0x0, override NO MotionNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182601616, (99,77), root:(100,100), state 0x2010, is_hint 0, same_screen YES MotionNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182601770, (99,77), root:(100,100), state 0x2010, is_hint 0, same_screen YES MotionNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182601821, (101,77), root:(102,100), state 0x2010, is_hint 0, same_screen YES MotionNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182601873, (104,77), root:(105,100), state 0x2010, is_hint 0, same_screen YES MotionNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182601925, (107,77), root:(108,100), state 0x2010, is_hint 0, same_screen YES MotionNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182601976, (109,77), root:(110,100), state 0x2010, is_hint 0, same_screen YES MotionNotify event, serial 33, synthetic NO, window 0x3200001, root 0x190, subw 0x0, time 182602028, (111,77), root:(112,100), state 0x2010, is_hint 0, same_screen YES ... ... ... ...
16. Pomocný nástroj scrot
Důležitým pomocným nástrojem, který může být knihovnou PyAutoGUI využit, je nástroj pojmenovaný scrot. Jedná se o aplikaci, která slouží pro získání screenshotů, a to buď screenshotů celé obrazovky, vybrané oblasti obrazovky, či screenshotu jednoho okna. Tento nástroj se ovládá z příkazové řádky a podle očekávání je k němu dodávána i manuálová stránka:
$ man scrot
scrot(1) General Commands Manual scrot(1) NAME scrot - Screen capture using imlib2 SYNOPSIS scrot [options] [file] DESCRIPTION scrot is a screen capture utility using the imlib2 library to aquire and save images. scrot has a few options, detailed below. Specify [file] as the filename to save the screenshot to. If [file] is not specified, a date-stamped file will be dropped in the current directory. ... ... ...
V dalším článku bude scrot použit, ovšem nebude volán přímo – pro tento účel jsou již připraveny příslušné funkce v knihovně PyAutoGUI.
17. Využití knihovny PyAutoGUI pro testování aplikací – virtuální framebuffer
V závěrečné části dnešního článku se ještě zmiňme o problematice testování aplikací s grafickým uživatelským rozhraním. Testování těchto aplikací má jednu nevýhodu – pokud testovanou aplikaci spustíme a současně spustíme i GUI testy, bude samotné desktopové prostředí v době testování nepoužitelné. Navíc někdy potřebujeme otestovat aplikaci i v odlišném desktopovém prostředí nebo i s jiným správcem oken. Jedním z možných řešení je použití virtuálního framebufferu. Ten je v klasickém systému X představován nástrojem nazvaným příznačně Xvfb. Po spuštění tohoto nástroje se vytvoří další instance display serveru, jenž podporuje standardní protokol X11. Ovšem na rozdíl od klasického X se vykreslování provádí do framebufferu vytvořeného v operační paměti a nikoli na monitor. Obsah framebufferu lze relativně snadno zpřístupnit, vytvořit screenshoty atd. Jeden z příkladů použití bude ukázán příště; popř. si můžete přečíst jedno z možných použití – Running the AoE 2 map editor headless.
18. Obsah druhé části článku
V navazujícím článku popis knihovny PyAutoGUI dokončíme. Kromě popisu simulace práce s myší si ukážeme tvorbu screenshotů a využití screenshotů pro lokalizaci ovládacích prvků na obrazovce (a to i v případě, kdy tyto prvky nemají přiřazen identifikátor – což může být případ některých her).
19. Repositář s demonstračními příklady
Zdrojové kódy všech dvanácti dnes popsaných demonstračních příkladů určených pro programovací jazyk Python 3 a knihovnu PyAutoGUI byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář, můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následujících tabulce:
20. Odkazy na Internetu
- Repositář balíčku PyAutoGUI na GitHubu
https://github.com/asweigart/pyautogui - PyAutoGUI 0.9.52 (na PyPi)
https://pypi.org/project/PyAutoGUI/ - Dokumentace ke knihovně PyAutoGUI
https://pyautogui.readthedocs.io/en/latest/ - Xdotool – Keyboard
https://www.linux.org/threads/xdotool-keyboard.10528/ - How to Use Xdotool to Stimulate Mouse Clicks and Keystrokes in Linux
https://linuxhint.com/xdotool_stimulate_mouse_clicks_and_keystrokes/ - xdootool.1 (manuálová stránka)
http://manpages.ubuntu.com/manpages/trusty/man1/xdotool.1.html - Repositář projektu xdotool
https://github.com/jordansissel/xdotool - Balíček python-libxdo
https://pypi.org/project/python-libxdo/ - Dokumentace k balíčku python-libxdo
https://rshk.github.io/python-libxdo/library.html - How do I find the X window ID under the mouse pointer in bash?
https://unix.stackexchange.com/questions/16131/how-do-i-find-the-x-window-id-under-the-mouse-pointer-in-bash - xwininfo (manuálová stránka)
https://www.x.org/releases/X11R7.7/doc/man/man1/xwininfo.1.xhtml - xev (manuálová stránka)
http://manpages.ubuntu.com/manpages/xenial/en/man1/xev.1.html - X Window System (Wikipedia)
https://en.wikipedia.org/wiki/X_Window_System - scrot na Wikipedii
https://en.wikipedia.org/wiki/Scrot - scrot na GitHubu
https://github.com/resurrecting-open-source-projects/scrot - Xvfb
https://en.wikipedia.org/wiki/Xvfb - XVFB – manuálová stránka
https://www.x.org/releases/X11R7.6/doc/man/man1/Xvfb.1.xhtml - Xvfb + Firefox
https://www.semicomplete.com/blog/geekery/xvfb-firefox/ - Headless wrapper for ephemeral X servers
https://www.semicomplete.com/blog/geekery/headless-wrapper-for-ephemeral-xservers/