Hlavní navigace

Skripty pro automatické ovládání aplikací s GUI postavené na knihovně PyAutoGUI

8. 7. 2021
Doba čtení: 36 minut

Sdílet

 Autor: Depositphotos
Na článek o nástroji xdotool dnes tematicky navážeme. Zaměříme se totiž na knihovnu nazvanou PyAutoGUI. Tato knihovna umožňuje ovládat aplikace s grafickým uživatelským rozhraním (simulovat operace prováděné myší atd.), a to s využitím skriptů naprogramovaných v Pythonu.

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ů

4. Otestování instalace

5. Získání základních informací o systému a obrazovce

6. Spuštění testované aplikace

7. Simulace stisku kláves

8. Názvy kláves použitelných v knihovně PyAutoGUI

9. Stisk a puštění klávesy

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

16. Pomocný nástroj scrot

17. Využití knihovny PyAutoGUI pro testování aplikací – virtuální framebuffer

18. Obsah druhé části článku

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

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:

  1. Simulace stisku klávesy, popř. klávesy s modifikátorem (Shift, Alt, Control + kombinace)
  2. Simulace buď pouze stisku či naopak puštění klávesy
  3. Simulace psaní delšího textu (tedy typicky sekvence alfanumerických kláves)
  4. Interaktivní výběr okna pro další prováděné operace
  5. Automatický výběr okna pro další prováděné operace na základě zadaného kritéria
  6. Simulace stisku vybraného tlačítka myši
  7. Simulace pohybu ukazatelem myši
  8. Další operace s vybraným oknem, například jeho minimalizace apod. (podle možností správce oken)
  9. Naprogramování akce, která se má provést při změně stavu okna (najetí myší apod.)
Poznámka: automatické ovládání aplikací s grafickým uživatelským rozhraním je možné využít v mnoha oblastech. Kromě tvorby „GUI maker“ pro automatizaci některých činností se jedná především o testování. V oblasti webových aplikací (a některé dnešní aplikace jsou založeny na webovém prohlížeči, ať již klasickém, či „schovaném“ ve frameworcích typu Electron) se pro tento účel používá nástroj Selenium, které obsahuje podporu i pro Python a další programovací jazyky. Existují však i další nástroje s podobným zaměřením, například Windmill či Splinter.

2. Od nástroje xdotool ke knihovně PyAutoGUI

Poznámka: důležité upozornění – některé funkce PyAutoGUI nepracují korektně na Waylandu. Existují ovšem způsoby, jak tyto funkce upravit, o čemž se zmíníme v samostatném článku. Alternativně lze pochopitelně použít XWayland.

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:

  1. Simulace stisku klávesy, popř. klávesy s modifikátorem (Shift, Alt, Control + kombinace)
  2. Simulace buď pouze stisku či naopak puštění klávesy
  3. Simulace psaní delšího textu (tedy typicky sekvence alfanumerických kláves)
  4. Simulace stisku vybraného tlačítka myši
  5. Simulace pohybu ukazatelem myši (včetně specifikace rychlosti, zrychlení, zpomalení atd. kurzoru myši)
  6. Vytvoření screenshotu
  7. Nalezení grafického prvku na screenshotu (což je velmi důležitá operace umožňující automatizaci testů)
Poznámka: mnohé z těchto operací se (podle očekávání) překrývají s možnostmi nástroje xdotool. Je zde však jedna důležitá výjimka – PyAutoGUI nedokáže lokalizovat zobrazené okno nebo ovládací prvek podle jeho identifikátoru. To paradoxně znamená, že první krok, který musíme při volání funkcí PyAutoGUI udělat, je vlastně nejsložitější, protože obecně nemáme kontrolu nad tím, na jakém místě a jak se zobrazí okno ovládané nebo testované aplikace. Pro tento účel je možné na Windows či Mac OS použít další knihovnu (nainstalovanou společně s PyAutoGUI), která se jmenuje PyGetWindow. Tato knihovna však prozatím nepodporuje Linux.

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
Poznámka: povšimněte si, že se nainstalovalo relativně velké množství podpůrných knihoven, s nimiž se (alespoň nepřímo) seznámíme v dalším textu.

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>
Poznámka: skutečně zvláštní e-mailová adresa…

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
Poznámka: zde můžeme vidět první relativně velké omezení současné verze nástroje PyAutoGUI. Podporován je totiž pouze primární monitor, což může vést k problémům při testování (či obecně automatizaci) na systémech s větším množstvím monitorů – což se minimálně v oblasti vývoje již dávno stalo standardem.

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 F1F4. 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
Poznámka: povšimněte si, že jednotlivé klávesy byly virtuálně stisknuty a puštěny, protože vždy byla přijata dvojice událostí – KeyPress a KeyRelease.

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")
Poznámka: povšimněte si, že způsob předávání jmen kláves je odlišný od funkce typewrite – zde není použit seznam či n-tice se jmény kláves, ale proměnný počet parametrů.

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")
Poznámka: shell musí být nastaven do režimu Emacsu, což je výchozí nastavení, které lze změnit v souboru .inputrc – viz též https://www.root.cz/clanky/tvorba-aplikaci-s-prikazovym-radkem-v-pythonu-s-vyuzitim-knihoven-gnu-readline-a-prompt-toolkit/#k07.

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
Poznámka: povšimněte si, že pohyb myši je představován událostí typu MotionNotify a součástí informace o události jsou i nové souřadnice kurzoru myši.

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
    ...
    ...
    ...
    ...
Poznámka: z hodnoty root je patrné, jak se postupně mění souřadnice kurzoru myši.

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.

MIF temata

Poznámka: podobné řešení existuje (a to dokonce v několika variantách) i pro Wayland, což je opět zajímavé (a pro mnoho čtenářů i aktuální) téma, kterému se budeme věnovat příště.

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:

# Demonstrační příklad Popis Cesta
1 01_get_info.py přečtení základních informací o systému https://github.com/tisnik/most-popular-python-libs/blob/master/pyautogu­i/01_get_info.py
2 02_screen_size.py získání informací o rozměrech obrazovky https://github.com/tisnik/most-popular-python-libs/blob/master/pyautogu­i/02_screen_size.py
3 03_start_xterm.py spuštění ovládané aplikace xterm z Pythonu https://github.com/tisnik/most-popular-python-libs/blob/master/pyautogu­i/03_start_xterm.py
4 04_start_xev.py spuštění ovládané aplikace xev z Pythonu https://github.com/tisnik/most-popular-python-libs/blob/master/pyautogu­i/04_start_xev.py
5 05_typewrite.py použití funkce typewrite pro spuštění příkazu v terminálu https://github.com/tisnik/most-popular-python-libs/blob/master/pyautogu­i/05_typewrite.py
6 06_keyboard_keys.py výpis symbolických jmen kláves https://github.com/tisnik/most-popular-python-libs/blob/master/pyautogu­i/06_keyboard_keys.py
7 07_key_down_up.py simulace stisku a opětovného puštění klávesy https://github.com/tisnik/most-popular-python-libs/blob/master/pyautogu­i/07_key_down_up.py
8 08_typewrite_key_names.py funkce typewrite volaná s názvy kláves https://github.com/tisnik/most-popular-python-libs/blob/master/pyautogu­i/08_typewrite_key_names.py
9 09_hot_keys.py funkce hotkey simulující současný stisk více kláves https://github.com/tisnik/most-popular-python-libs/blob/master/pyautogu­i/09_hot_keys.py
10 10_mouse_info.py získání základních informací o polohovacím zařízení https://github.com/tisnik/most-popular-python-libs/blob/master/pyautogu­i/10_mouse_info.py
11 11_mouse_move.py simulace pohybu kurzoru myši https://github.com/tisnik/most-popular-python-libs/blob/master/pyautogu­i/11_mouse_move.py
12 12_slow_mouse_move.py zpomalený (lineární) pohyb kurzoru myši https://github.com/tisnik/most-popular-python-libs/blob/master/pyautogu­i/12_slow_mouse_move.py

20. Odkazy na Internetu

  1. Repositář balíčku PyAutoGUI na GitHubu
    https://github.com/asweigar­t/pyautogui
  2. PyAutoGUI 0.9.52 (na PyPi)
    https://pypi.org/project/PyAutoGUI/
  3. Dokumentace ke knihovně PyAutoGUI
    https://pyautogui.readthe­docs.io/en/latest/
  4. Xdotool – Keyboard
    https://www.linux.org/threads/xdotool-keyboard.10528/
  5. How to Use Xdotool to Stimulate Mouse Clicks and Keystrokes in Linux
    https://linuxhint.com/xdo­tool_stimulate_mouse_clic­ks_and_keystrokes/
  6. xdootool.1 (manuálová stránka)
    http://manpages.ubuntu.com/man­pages/trusty/man1/xdotool­.1.html
  7. Repositář projektu xdotool
    https://github.com/jordan­sissel/xdotool
  8. Balíček python-libxdo
    https://pypi.org/project/python-libxdo/
  9. Dokumentace k balíčku python-libxdo
    https://rshk.github.io/python-libxdo/library.html
  10. 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
  11. xwininfo (manuálová stránka)
    https://www.x.org/releases/X11R7­.7/doc/man/man1/xwininfo.1­.xhtml
  12. xev (manuálová stránka)
    http://manpages.ubuntu.com/man­pages/xenial/en/man1/xev.1­.html
  13. X Window System (Wikipedia)
    https://en.wikipedia.org/wi­ki/X_Window_System
  14. scrot na Wikipedii
    https://en.wikipedia.org/wiki/Scrot
  15. scrot na GitHubu
    https://github.com/resurrecting-open-source-projects/scrot
  16. Xvfb
    https://en.wikipedia.org/wiki/Xvfb
  17. XVFB – manuálová stránka
    https://www.x.org/releases/X11R7­.6/doc/man/man1/Xvfb.1.xhtml
  18. Xvfb + Firefox
    https://www.semicomplete.com/blog/ge­ekery/xvfb-firefox/
  19. Headless wrapper for ephemeral X servers
    https://www.semicomplete.com/blog/ge­ekery/headless-wrapper-for-ephemeral-xservers/

Autor článku

Pavel Tišnovský vystudoval VUT FIT a v současné době pracuje ve společnosti Red Hat, kde vyvíjí nástroje pro OpenShift.io.