Knihovna Python — je open source projekt pro automatizaci desktopových GUI aplikací na Windows. V posledních dvou letech se objevily nové významné funkce:
- Podpora technologie MS UI Automation. Rozhraní zůstává stejné a nyní podporuje: WinForms, WPF, Qt5, Windows Store (UWP) a další — téměř vše, co je na Windows.
- Systém backendů/pluginů (v současnosti jsou tam dvě varianty: výchozí
"win32"a nová"uia"). Dále plynule přecházíme k multiplatformnosti. - Win32 hooky pro myš a klávesnici (hot keys ve stylu pyHook).
Také se podíváme na to, co je v open source pro desktopovou automatizaci (bez nároku na seriózní srovnání).
Tento článek je částečně přepisem přednášky z konference SQA Days 20 v Minsku ( a ), částečně česká verze pro pywinauto.
- Hlavní přístupy
- Hlavní desktopové technologie přístupnosti
Začneme krátkým přehledem open source v této oblasti. Pro desktopové GUI aplikace je to trochu složitější než pro web, který má Selenium. Zde jsou hlavní přístupy:
Metoda souřadnic
Kódujeme klikací body, doufáme v úspěšné zásahy.
[+] Multiplatformní, snadno realizovatelné.
[+] Snadno lze provést "record-replay" záznam testů.
[-] Nejvíce nestabilní při změně rozlišení obrazovky, témat, písem, velikostí oken atd.
[-] Vyžaduje obrovské úsilí na udržování, často je jednodušší generovat testy znovu od nuly nebo testovat ručně.
[-] Automatizuje pouze akce, pro verifikaci a extrakci dat jsou k dispozici jiné metody.
Nástroje (multiplatformní): , , a mnoho dalších. Obecně platí, že složitější nástroje zahrnují tuto funkčnost (ne vždy multiplatformně).
Je třeba říci, že metoda souřadnic může doplňovat ostatní přístupy. Například pro vlastní grafiku lze klikat na relativní souřadnice (od levého horního rohu okna/elementu, nikoli celého displeje) — to obvykle funguje dost spolehlivě, zvláště pokud vezmeme v úvahu délku/šířku celého elementu (takže různé rozlišení obrazovky nebude překážkou).
Další možnost: vyčlenit pro testy pouze jeden stroj se stabilními nastaveními (ne přes platformy, ale v některých případech je to použitelné).
Rozpoznávání referenčních obrázků
[+] Přes platformy
[+-] Relativně spolehlivé (lepší než koordinační metoda), ale stále vyžaduje kličky.
[-+] Relativně pomalé, protože vyžaduje zdroje CPU pro algoritmy rozpoznávání.
[-] O rozpoznávání textu (OCR) se obvykle nemluví => nelze získat textová data. Pokud vím, stávající OCR řešení nejsou pro tento typ úkolů příliš spolehlivá a nemají široké využití (vítejte v komentářích, pokud to už neplatí).
Nástroje: , (kompatibilní se Sikuli, v čistém Pythonu), .
Technologie přístupnosti
[+] Nejspolehlivější metoda, protože umožňuje hledat podle textu, bez ohledu na to, jak je vykreslen systémem nebo rámcem.
[+] Umožňuje extrakci textových dat => jednodušší ověřování výsledků testů.
[+] Obvykle nejrychlejší, protože téměř nevyžaduje zdroje CPU.
[-] Těžké vytvořit nástroj přes platformy: téměř všechny open-source knihovny podporují jednu nebo dvě technologie přístupnosti. Windows/Linux/MacOS celkově nikdo nepodporuje kromě placených nástrojů jako TestComplete, UFT nebo Squish.
[-] Taková technologie nemusí být vždy dostupná. Například testování spouštěcí obrazovky uvnitř VirtualBoxu — zde se neobejdeme bez rozpoznávání obrázků. Ale v mnoha klasických případech je přístupná metoda stále použitelná, o které bude dále řeč.
Nástroje: v C#, v C# (kompatibilní se Selenium), v C# (kompatibilní s Appium), , (kompatibilní s LDTP), , v Ruby, (Linux Desktop Testing Project) a jeho verze pro Windows .
LDTP — možná jediný přes platformy open-source nástroj (spíše rodina knihoven) založených na technologiích přístupnosti. Není však příliš populární. Sám jsem ho nepoužíval, ale podle recenzí není jeho rozhraní příliš pohodlné. Pokud máte pozitivní zpětnou vazbu, prosím, podělte se v komentářích.
Testovací backdoor (a.k.a. interní bicykl)
Pro aplikace přes platformy často samotní vývojáři vytvářejí interní mechanismus pro zajištění testovatelnosti. Například vytvářejí služební TCP server v aplikaci, ke kterému se testy připojují a posílají textové příkazy: co stisknout, odkud vzít data atd. Spolehlivé, ale ne univerzální.
Hlavní desktopové technologie přístupnosti
Starý dobrý Win32 API
Většina aplikací pro Windows, které byly napsány před uvedením WPF a poté Windows Store, je postavena více či méně na Win32 API. Tedy MFC, WTL, C++ Builder, Delphi, VB6 — všechny tyto nástroje používají Win32 API. Dokonce i Windows Forms — do značné míry kompatibilní s Win32 API.
Nástroje: (podobný VB) a Python wrapper , (vlastní jazyk, má IDispatch COM rozhraní), (Python), (Ruby), (Ruby).
Microsoft UI Automation
Hlavní výhoda: technologie MS UI Automation podporuje drtivou většinu GUI aplikací na Windows s výjimkami. Problém: není výrazně jednodušší na učení než Win32 API. Jinak by nikdo nevytvářel wrappery.
Ve skutečnosti se jedná o sadu vlastních COM rozhraní (převážně, UIAutomationCore.dll), a má také .NET obal ve formě namespace System.Windows.Automation. Mimochodem, má přidanou chybu, kvůli které mohou být některé UI prvky vynechány. Proto je lepší používání UIAutomationCore.dll přímo (pokud jste slyšeli o UiaComWrapper na C#, tak to je ono).
Druhy COM rozhraní:
(1) Základní IUknown — „kořen všeho zla“. Nejnižší úrovně, vůbec není uživatelsky přívětivý.
(2) IDispatch a odvozené (například, Excel.Application), které lze použít v Pythonu pomocí balíčku win32com.client (součástí pyWin32). Nejdostupnější a nejhezčí varianta.
(3) Vlastní rozhraní, se kterými pracuje externí Python balíček .
Nástroje: v C#, 0.6.0+, v C#, (jejich zdrojový kód C wrapperů nad UIAutomationCore.dll není zveřejněn), na Ruby.
AT-SPI
I když je téměř celý rodinný os Linux postaven na X Window System (ve Fedoře 25 byly „xka“ vyměněny za Wayland), „xka“ umožňují operovat pouze s okny na vrcholu a myší/klávesnicí. Pro podrobné zpracování podle tlačítek, seznamových boxů atd. existuje technologie AT-SPI. Nejpopulárnější správci oken mají takzvaný AT-SPI registry démon, který pro aplikace zajišťuje automatizovatelné GUI (minimálně jsou podporovány Qt a GTK).
Nástroje: .
pyatspi2, podle mého názoru, obsahuje příliš mnoho závislostí jako PygObject. Samotná technologie je dostupná ve formě běžné dynamické knihovny libatspi.so. K tomu je k dispozici . Pro knihovnu pywinauto plánujeme realizovat podporu AT-SPI právě takto: prostřednictvím načítání libatspi.so a modulu ctypes. Je zde malý problém pouze s použitím potřebné verze, neboť pro aplikace GTK+ a Qt jsou mírně odlišné. Pravděpodobné vydání pywinauto 0.7.0 s plnou podporou Linuxu lze očekávat v první polovině roku 2018.
Apple Accessibility API
Na MacOS je vlastní jazyk automatizace AppleScript. Pro implementaci něčeho podobného v Pythonu je samozřejmě potřeba použít funkce z ObjectiveC. Začínaje zřejmě už od MacOS 10.6 je v předinstalovaném Pythonu zahrnut balíček pyobjc. To také usnadní seznam závislostí pro budoucí podporu v pywinauto.
Nástroje: Kromě jazyka Apple Script stojí za pozornost , známý také jako pyatom. Je kompatibilní co do rozhraní s LDTP, ale je také samostatnou knihovnou. Obsahuje , který napsal můj student. Existuje známý problém: nefungují flexibilní časování (metody waitFor*). Ale jinak je to celkem dobrá záležitost.
Jak začít pracovat s pywinauto
Nejdříve je dobré vybavit se inspektorem GUI objektů (to, co se nazývá Spy tool). Pomůže to prostudovat aplikaci zevnitř: jak je uspořádána hierarchie prvků, jaké vlastnosti jsou dostupné. Nejznámější inspektory objektů jsou:
- Spy++ — součást Visual Studio, včetně Express nebo Community Edition. Používá Win32 API. Je také známý jeho klon AutoIt Window Info.
- Inspect.exe — součást Windows SDK. Pokud je nainstalován, můžete jej na 64-bitovém Windows najít ve složce
C:Program Files (x86)Windows Kitsbinx64. V samotném inspektoru je potřeba zvolit režim UI Automation místo MS AA (Active Accessibility, předchůdce UI Automation).
Prozkoumanou aplikaci vybereme backend, který budeme používat. Stačí uvést název backendu při vytváření objektu Application.
- backend=»win32″ — stále se používá jako výchozí, dobře funguje s MFC, WTL, VB6 a dalšími legacy aplikacemi.
- backend=»uia» — nový backend pro MS UI Automation: ideálně funguje s WPF a WinForms; také dobrý pro aplikace Delphi a Windows Store; funguje s Qt5 a některými Java aplikacemi. Pokud Inspect.exe vidí prvky a jejich vlastnosti, pak tento backend vyhovuje. V zásadě většina prohlížečů také podporuje UI Automation (Mozilla ve výchozím nastavení, a Chromu při spuštění je potřeba předat příkazový klíč
--force-renderer-accessibility, aby viděl prvky na stránkách v Inspect.exe). Samozřejmě, soutěž s Selenium v této oblasti je málo pravděpodobná. Je to jen další způsob, jak pracovat s prohlížečem (může být užitečné pro scénáře napříč produkty).
Bod přístupu pro automatizaci
Aplikace je dostatečně prostudována. Je čas vytvořit objekt Application a spustit ho nebo se připojit k již spuštěnému. Není to jen klon standardní třídy subprocess.Popen, tedy vstupní objekt, který omezuje všechny vaše akce v rámci procesu. To je velmi užitečné, pokud je spuštěno více instancí aplikace a ostatní je nechcete zasahovat.
from pywinauto.application import Application
app = Application(backend="uia").start('notepad.exe')
# Popisujeme okno, které chceme najít v procesu Notepad.exe
dlg_spec = app.UntitledNotepad
# čekáme, až okno skutečně objeví
actionable_dlg = dlg_spec.wait('visible')Pokud chcete ovládat několik aplikací najednou, pomůže vám třída Desktop. Například v kalkulačce ve Win10 je hierarchie prvků rozptýlena až na několik procesů (nejen calc.exe). Takže bez objektu Desktop se neobejdete.
from subprocess import Popen
from pywinauto import Desktop
Popen('calc.exe', shell=True)
dlg = Desktop(backend="uia").Calculator
dlg.wait('visible')Koreňový objekt (Aplikace nebo Desktop) — to je jediné místo, kde je třeba uvést backend. Všechno ostatní se transparentně vkládá do konceptu „specifikace->wrapper“, o kterém bude řeč dále.
Specifikace oken/elementů
To je základní koncept, na kterém je postaveno rozhraní pywinauto. Můžete popsat okno/prvek zjednodušeně nebo podrobněji, i když ještě neexistuje nebo již bylo zavřeno. Specifikace okna (objekt WindowSpecification) obsahuje kritéria, podle kterých se má hledat skutečné okno nebo prvek.
Příklad podrobné specifikace okna:
>>> dlg_spec = app.window(title='Untitled - Notepad')
>>> dlg_spec
>>> dlg_spec.wrapper_object()Samotné hledání okna se provádí voláním metody .wrapper_object(). Vrací nějaký „wrapper“ pro skutečné okno/prvek, nebo vyvolá ElementNotFoundError (někdy ElementAmbiguousError, pokud bylo nalezeno více prvků, tedy je třeba upřesnit hledací kritéria). Tento „wrapper“ již umí provádět nějaké akce s prvkem nebo získávat data z něj.
Python může skrývat zavolání .wrapper_object(), takže finální kód se zkracuje. Doporučujeme jej používat pouze pro ladění. Následující dvě řádky dělají přesně to samé:
dlg_spec.wrapper_object().minimize() # debugging
dlg_spec.minimize() # productionExistuje mnoho kritérií hledání pro specifikaci okna. Tady je jen několik příkladů:
# могут иметь несколько уровней
app.window(title_re='.* - Notepad$').window(class_name='Edit')
# можно комбинировать критерии (как AND) и не ограничиваться одним процессом приложения
dlg = Desktop(backend="uia").Calculator
dlg.window(auto_id='num8Button', control_type='Button')Seznam všech možných kritérií najdete v dokumentaci funkce .
Magie přístupu podle atributu a podle klíče
Python usnadňuje vytváření specifikací okna a dynamicky rozpoznává atributy objektu (uvnitř je přepsána metoda __getattribute__). Samozřejmě, na název atributu se vztahují stejná omezení jako na název jakékoliv proměnné (nelze vkládat mezery, čárky a další speciální znaky). Naštěstí pywinauto používá takzvaný algoritmus „best match“ pro hledání, který je odolný vůči překlepům a drobným variacím.
app.UntitledNotepad
# to samé jako
app.window(best_match='UntitledNotepad')Pokud potřebujete Unicode řetězce (například pro ruský jazyk), mezery atd., můžete přistupovat pomocí klíče (jako by to byl běžný slovník):
app['Untitled - Notepad']
# to samé jako
app.window(best_match='Untitled - Notepad')Pět pravidel pro magická jména
Jak zjistit referenční magická jména? Ta, která jsou přiřazena prvku před hledáním. Pokud jste uvedli název, který je dost podobný referenčnímu, prvek bude nalezen.
- Podle názvu (text, jméno):
app.Properties.OK.click() - Podle textu a typu prvku:
app.Properties.OKButton.click() - Podle typu a čísla:
app.Properties.Button3.click()(jménaButton0aButton1jsou přiřazena k prvnímu nalezenému prvku,Button2— ke druhému, a dál už podle pořadí — tak to historicky vzniklo) - Podle statického textu (zleva nebo shora) a podle typu:
app.OpenDialog.FileNameEdit.set_text("")(užitečné pro prvky s dynamickým textem) - Podle typu a textu uvnitř:
app.Properties.TabControlSharing.select("General")
Obvykle se současně používají dvě až tři pravidla, málokdy více. Abychom zkontrolovali, jaká konkrétní jména jsou dostupná pro každý prvek, můžeme použít metodu print_control_identifiers(). Ta může tisknout strom prvků jak na obrazovku, tak do souboru. Pro každý prvek se tisknou jeho referenční magická jména. Také si odtud můžete zkopírovat podrobnější specifikace podřízených prvků. Výsledek ve skriptu bude vypadat takto:
app.Properties.child_window(title="Contains:", auto_id="13087", control_type="Edit")Samotný strom prvků — obvykle docela velká stránka.
>>> app.Properties.print_control_identifiers()
Identifikátory ovládacích prvků:
Dialog - 'Vlastnosti systému Windows NT' (L688, T518, R1065, B1006)
[u'Vlastnosti systému Windows NT', u'Dialog', u'Vlastnosti systému Windows NT']
child_window(title="Vlastnosti systému Windows NT", control_type="Window")
|
| Obraz - '' (L717, T589, R749, B622)
| [u'', u'0', u'Image1', u'Image0', 'Obraz', u'1']
| child_window(auto_id="13057", control_type="Image")
|
| Obraz - '' (L717, T630, R1035, B632)
| ['Image2', u'2']
| child_window(auto_id="13095", control_type="Image")
|
| Úprava - 'Název složky:' (L790, T596, R1036, B619)
| [u'3', 'Úprava', u'Edit1', u'Edit0']
| child_window(title="Název složky:", auto_id="13156", control_type="Edit")
|
| Statický - 'Typ:' (L717, T643, R780, B658)
| [u'Typ:Statický', u'Statický', u'Statický1', u'Statický0', u'Typ:']
| child_window(title="Typ:", auto_id="13080", control_type="Text")
|
| Úprava - 'Typ:' (L790, T643, R1036, B666)
| [u'4', 'Edit2', u'Typ:Úprava']
| child_window(title="Typ:", auto_id="13059", control_type="Edit")
|
| Statický - 'Umístění:' (L717, T669, R780, B684)
| [u'Umístění:Statický', u'Umístění:', u'Statický2']
| child_window(title="Umístění:", auto_id="13089", control_type="Text")
|
| Úprava - 'Umístění:' (L790, T669, R1036, B692)
| ['Edit3', u'Umístění:Úprava', u'5']
| child_window(title="Umístění:", auto_id="13065", control_type="Edit")
|
| Statický - 'Velikost:' (L717, T695, R780, B710)
| [u'Velikost:Statický', u'Velikost:', u'Statický3']
| child_window(title="Velikost:", auto_id="13081", control_type="Text")
|
| Úprava - 'Velikost:' (L790, T695, R1036, B718)
| ['Edit4', u'6', u'Velikost:Úprava']
| child_window(title="Velikost:", auto_id="13064", control_type="Edit")
|
| Statický - 'Velikost na disku:' (L717, T721, R780, B736)
| [u'Velikost na disku:', u'Velikost na disku:Statický', u'Statický4']
| child_window(title="Velikost na disku:", auto_id="13107", control_type="Text")
|
| Úprava - 'Velikost na disku:' (L790, T721, R1036, B744)
| ['Edit5', u'7', u'Velikost na disku:Úprava']
| child_window(title="Velikost na disku:", auto_id="13106", control_type="Edit")
|
| Statický - 'Obsahuje:' (L717, T747, R780, B762)
| [u'Obsahuje:1', u'Obsahuje:0', u'Obsahuje:Statický', u'Statický5', u'Obsahuje:']
| child_window(title="Obsahuje:", auto_id="13088", control_type="Text")
|
| Úprava - 'Obsahuje:' (L790, T747, R1036, B770)
| [u'8', 'Edit6', u'Obsahuje:Úprava']
| child_window(title="Obsahuje:", auto_id="13087", control_type="Edit")
|
| Obraz - 'Obsahuje:' (L717, T773, R1035, B775)
| [u'Obsahuje:Obraz', 'Obraz3', u'Obsahuje:2']
| child_window(title="Obsahuje:", auto_id="13096", control_type="Image")
|
| Statický - 'Vytvořeno:' (L717, T786, R780, B801)
| [u'Vytvořeno:', u'Vytvořeno:Statický', u'Statický6', u'Vytvořeno:1', u'Vytvořeno:0']
| child_window(title="Vytvořeno:", auto_id="13092", control_type="Text")
|
| Úprava - 'Vytvořeno:' (L790, T786, R1036, B809)
| [u'Vytvořeno:Úprava', 'Edit7', u'9']
| child_window(title="Vytvořeno:", auto_id="13072", control_type="Edit")
|
| Obraz - 'Vytvořeno:' (L717, T812, R1035, B814)
| [u'Vytvořeno:Obraz', 'Obraz4', u'Vytvořeno:2']
| child_window(title="Vytvořeno:", auto_id="13097", control_type="Image")
|
| Statický - 'Atributy:' (L717, T825, R780, B840)
| [u'Atributy:Statický', u'Statický7', u'Atributy:']
| child_window(title="Atributy:", auto_id="13091", control_type="Text")
|
| Zaškrtávací políčko - 'Jen pro soubory ve složce' (L790, T825, R1035, B841)
| [u'CheckBox0', u'CheckBox1', 'Zaškrtávací políčko', u'Jen pro soubory ve složceCheckBox', u'Jen pro soubory ve složce']
| child_window(title="Jen pro soubory ve složce", auto_id="13075", control_type="CheckBox")
|
| Zaškrtávací políčko - 'Skryté' (L790, T848, R865, B864)
| ['CheckBox2', u'SkrytéCheckBox', u'Skryté']
| child_window(title="Skryté", auto_id="13076", control_type="CheckBox")
|
| Tlačítko - 'Pokročilé...' (L930, T845, R1035, B868)
| [u'Pokročilé...', u'Pokročilé...Tlačítko', 'Tlačítko', u'Tlačítko1', u'Tlačítko0']
| child_window(title="Pokročilé...", auto_id="13154", control_type="Button")
|
| Tlačítko - 'OK' (L814, T968, R889, B991)
| ['Tlačítko2', u'OK', u'OKTlačítko']
| child_window(title="OK", auto_id="1", control_type="Button")
|
| Tlačítko - 'Zrušit' (L895, T968, R970, B991)
| ['Tlačítko3', u'ZrušitTlačítko', u'Zrušit']
| child_window(title="Zrušit", auto_id="2", control_type="Button")
|
| Tlačítko - 'Použít' (L976, T968, R1051, B991)
| ['Tlačítko4', u'PoužítTlačítko', u'Použít']
| child_window(title="Použít", auto_id="12321", control_type="Button")
|
| Ovládací prvek pro karty - '' (L702, T556, R1051, B962)
| [u'10', u'Ovládací prvek pro sdílení', u'Ovládací prvek pro starší verze', u'Ovládací prvek pro zabezpečení', u'Ovládací prvek', u'Ovládací prvek pro přizpůsobení']
| child_window(auto_id="12320", control_type="Tab")
| |
| | Položka karty - 'Obecné' (L704, T558, R753, B576)
| | [u'ObecnéPoložkaKarty', 'PoložkaKarty', u'Obecné', u'PoložkaKarty0', u'PoložkaKarty1']
| | child_window(title="Obecné", control_type="TabItem")
| |
| | Položka karty - 'Sdílení' (L753, T558, R801, B576)
| | [u'Sdílení', u'SdíleníPoložkaKarty', 'PoložkaKarty2']
| | child_window(title="Sdílení", control_type="TabItem")
| |
| | Položka karty - 'Zabezpečení' (L801, T558, R851, B576)
| | [u'Zabezpečení', 'PoložkaKarty3', u'ZabezpečeníPoložkaKarty']
| | child_window(title="Zabezpečení", control_type="TabItem")
| |
| | Položka karty - 'Starší verze' (L851, T558, R947, B576)
| | [u'Starší verzePoložkaKarty', u'Starší verze', 'PoložkaKarty4']
| | child_window(title="Starší verze", control_type="TabItem")
| |
| | Položka karty - 'Přizpůsobení' (L947, T558, R1007, B576)
| | [u'PřizpůsobeníPoložkaKarty', 'PoložkaKarty5', u'Přizpůsobení']
| | child_window(title="Přizpůsobení", control_type="TabItem")
|
| Titulní řádek - 'Žádný' (L712, T521, R1057, B549)
| ['TitulníŘádek', u'11']
| |
| | Nabídka - 'Systém' (L696, T526, R718, B548)
| | [u'Systém0', u'Systém', u'Systém1', u'Nabídka', u'SystémNabídka']
| | child_window(title="Systém", auto_id="MenuBar", control_type="MenuBar")
| | |
| | | Položka nabídky - 'Systém' (L696, T526, R718, B548)
| | | [u'Systém2', u'NabídkaPoložka', u'SystémNabídkaPoložka']
| | | child_window(title="Systém", control_type="MenuItem")
| |
| | Tlačítko - 'Zavřít' (L1024, T519, R1058, B549)
| | [u'ZavřítTlačítko', u'Zavřít', 'Tlačítko5']
| | child_window(title="Zavřít", control_type="Button")V některých případech může tisk celého stromu zpomalit (například v iTunes na jedné kartě je až tři tisíce prvků!), ale můžete použít parametr depth (hloubka): depth=1 — samotný prvek, depth=2 — pouze přímé děti, a tak dále. Tento parametr lze také specifikovat při vytváření child_window.
Příklady
Neustále doplňujeme . Mezi novinkami stojí za zmínku automatizace síťového analyzátoru WireShark (to je dobrý příklad aplikace Qt5; i když tento úkol lze vyřešit i bez GUI, neboť existuje scapy.Sniffer z Python balíčku ). Také máme příklad automatizace MS Paint s jeho Ribbon nástrojovým panelem.
Dalším skvělým příkladem, který napsal můj student: (ten se později přesune do hlavního repozitáře).
A samozřejmě příklad odběru událostí klávesnice (hot keys) a myši:
.
Poděkování
Zvláštní poděkování patří těm, kteří neustále pomáhají rozvíjet projekt. Pro mě a je to stálá zábava. Dva moji studenti z NNGU nedávno obhájili bakalářské práce na toto téma. přispěl významně k podpoře MS UI Automation a nedávno začal vytvářet automatický generátor kódu na základě principu „záznam-přehrání“ s využitím textových vlastností (to je nejkomplexnější funkce), zatím pouze pro backend „uia“. vyvíjí nový backend pro Linux na základě AT-SPI (moduly mouse a keyboard na základě — už ve verzích 0.6.x).
Jelikož už nějakou dobu učím speciální kurz automatizace v Pythonu, část studentů magisterského studia plní domácí úkoly realizací malých funkcí nebo příkladů automatizace. Některé klíčové věci byly také v minulosti objeveny studenty během výzkumu. Přesto je někdy nutné přísně dohlížet na kvalitu kódu. V tom mi velmi pomáhají statické analyzátory (QuantifiedCode, Codacy a Landscape) a automatické testy v cloudu (služba AppVeyor) s pokrytím kódu kolem 95%.
Také děkuji všem, kteří zanechávají feedback, hlásí chyby a posílají pull requesty!
Další zdroje
Na dotazy dohlížíme prostřednictvím (nedávno se objevil ) a . Existuje .
Každý měsíc aktualizujeme . Počet hvězdiček na GitHubu rychleji roste pouze u Autohotkey (mají velmi velké komunitní zázemí a dlouhou historii) a PyAutoGUI (většinou díky popularitě knih jejího autora Al Sweigarta: „Automatizujte nudné úkoly pomocí Pythonu“ a dalších).
Zdroj: habr.com
