Avtomatiziramo namizno GUI na Pythonu + pywinauto: kako se prijateljski dogovoriti s MS UI Automation

Python knjižnica pywinauto — je odprtokodni projekt za avtomatizacijo namiznih GUI aplikacij v sistemu Windows. V zadnjih dveh letih so se pojavljale nove pomembne funkcije:

  • Podpora tehnologiji MS UI Automation. Vmesnik je ostal enak, zdaj pa podpira: WinForms, WPF, Qt5, Windows Store (UWP) in še več — skoraj vse, kar obstaja v sistemu Windows.
  • Sistem backendov/vtičnikov (zaenkrat sta na voljo dva: privzeti "win32" in nov "uia"). Napredujemo tudi na področju večplatformske podpore.
  • Win32 vpetja za miške in tipkovnice (hitre tipke v slogu pyHook).

Prav tako bomo pripravili kratek pregled odprtokodnih rešitev za avtomatizacijo namiznih aplikacij (brez ambicij po resnih primerjavah).

Ta članek je delno razlaga poročila z konference SQA Days 20 v Minsku (video posnetek in slajdi), delno pa ruska različica Getting Started Guide za pywinauto.

Začnimo s kratkim pregledom odprte kode na tem področju. Za namizne GUI aplikacije je vse nekoliko bolj zapleteno kot pri spletu, pri katerem imamo Selenium. Tukaj so glavni pristopi:

Metoda koordinat

Trdno določamo točke klikov, upamo na uspešna zadetja.
[+] Večplatformno, enostavno za izvedbo.
[+] Enostavno je narediti 'record-replay' posnetke testov.
[-] Najmanj stabilno glede na spremembe ločljivosti zaslona, tem, pisav, velikosti oken itd.
[-] Potrebni so ogromni napori za vzdrževanje, pogosto je lažje ustvariti teste iz nič ali testirati ročno.
[-] Avtomatizira le dejanja, za verifikacijo in ekstrakcijo podatkov so na voljo drugi načini.

Orodja (večplatformna): autopy, PyAutoGUI, PyUserInput in številna druga. Običajno bolj kompleksna orodja vključujejo to funkcionalnost (ne vedno večplatformno).

Vredno je reći da koordinatna metoda može dopuniti ostale pristupe. Na primer, za prilagođenu grafiku možemo kliknuti na relativne koordinate (od gornjeg levog ugla prozora/elementa, a ne celog ekrana) — obično je to dovoljno pouzdano, posebno ako uzmemo u obzir dužinu/širinu celog elementa (tada različitost ekranskih rezolucija neće smetati).

Druga opcija: selektovati samo jedan uređaj za testiranje sa stabilnim postavkama (nije kros-platformen, ali u nekim slučajevima može poslužiti).

Prepoznavanje referenčnih slik

[+] Kros-platformski
[+-] Relativno pouzdan (bolji od koordinatne metode), ali ipak zahteva neke trikove.
[-+] Relativno spor, jer zahteva CPU resurse za algoritme prepoznavanja.
[-] O prepoznavanju teksta (OCR), kao pravilo, se ne govori => nije moguće dobiti tekstualne podatke. Koliko znam, postojeća OCR rešenja nisu previše pouzdana za ovu vrstu zadataka i nemaju široku primenu (dobrodošli u komentare ako više nije tako).

Alati: Sikuli, Lackey (Sikuli-kompatibilan, napisan u čistom Pythonu), PyAutoGUI.

Tehnologije dostopnosti

[+] Najpouzdanija metoda, jer omogućava pretragu po tekstu, bez obzira na to kako je sistem ili framework nacrtao.
[+] Omogućava izvlačenje tekstualnih podataka => lakša verifikacija rezultata testova.
[+] Kao pravilo, najbrža je, jer gotovo ne troši CPU resurse.
[-] Teško je napraviti kros-platformski alat: apsolutno sve open-source biblioteke podržavaju jednu ili dve tehnologije pristupačnosti. Windows/Linux/MacOS niko ne podržava u potpunosti, osim plaćenih poput TestComplete, UFT ili Squish.
[-] Nije uvek takva tehnologija uopšte dostupna. Na primer, testiranje startnog ekrana unutar VirtualBox-a — ovde se ne može bez prepoznavanja slika. Ali u mnogim klasičnim slučajevima pristup pristupačnosti je ipak primenljiv. O tome će dalje biti reči.

Alati: TestStack.White na C#, Winium.Desktop na C# (kompatibilan sa Selenium), MS WinAppDriver na C# (kompatibilan sa Appium), pywinauto, pyatom (kompatibilan sa LDTP), Python-UIAutomation-for-Windows, RAutomation na Ruby, LDTP (Linux Desktop Testing Project) i njegova Windows verzija Cobra.

LDTP — verovatno jedini kros-platformski open-source alat (preciznije, porodica biblioteka) zasnovan na tehnologijama pristupačnosti. Međutim, nije previše popularan. Sam ga nisam koristio, ali prema recenzijama, njegov interfejs nije previše pogodan. Ako ima pozitivnih recenzija, molim vas da podelite u komentarima.

Testni backdoor (t.j. notranji kolesar)

Za večplatformske aplikacije pogosto sami razvijalci ustvarjajo notranji mehanizem za zagotavljanje testabilnosti. Na primer, ustvarijo servisni TCP strežnik v aplikaciji, na katerega se priključijo testi in pošljejo besedilne ukaze: na kaj naj pritisnejo, od kje naj vzamejo podatke itd. Zanesljivo, vendar ne univerzalno.

Glavne tehnologije dostopnosti na namizju

Stari dober Win32 API

Večina Windows aplikacij, napisanih pred prihodom WPF in kasneje Windows Store, je na ta ali oni način zgrajena na Win32 API. Natančneje, MFC, WTL, C++ Builder, Delphi, VB6 — vsi ti orodja uporabljajo Win32 API. Tudi Windows Forms so v veliki meri združljivi z Win32 API.

Alati: AutoIt (podoben VB) in Python prevleka pyautoit, AutoHotkey (lastni jezik, ima IDispatch COM vmesnik), pywinauto (Python), RAutomation (Ruby), win32-autogui (Ruby).

Microsoft UI Automation

Glavna prednost: tehnologija MS UI Automation podpira večino GUI aplikacij v Windows, z redkimi izjemami. Težava: ni veliko lažja za učenje kot Win32 API. Drugače nihče ne bi delal ovitkov nad njo.

Dejansko gre za niz custom COM vmesnikov (pretežno, UIAutomationCore.dll), in ima tudi .NET lupino v obliki namespace System.Windows.Automation. Ta ima mimogrede uvnese napako, zaradi katere lahko nekatere UI elemente spregledamo. Zato je bolje uporabljati UIAutomationCore.dll neposredno (če ste slišali za UiaComWrapper na C#, potem je to to).

Različice COM vmesnikov:

(1) Osnovni IUknown — „koren vsega zla“. Najbolj nizkolegiran, nikoli ni prijazen do uporabnika.
(2) IDispatch in izpeljane (na primer, Excel.Application), kar lahko uporabite v Pythonu s pomočjo paketa win32com.client (vključen v pyWin32). Najbolj udoben in lep način.
(3) Custom vmesniki, s katerimi dela zunanji Python paket comtypes.

Alati: TestStack.White na C#, pywinauto 0.6.0+, Winium.Desktop na C#, Python-UIAutomation-for-Windows (njihov izvorni kod c-ovih ovitkov nad UIAutomationCore.dll ni razkrit), RAutomation na Rubyju.

AT-SPI

Kljub dejstvu, da so skoraj vsi operacijski sistemi družine Linux zgrajeni na X Window System (v Fedora 25 so "kse" zamenjali z Wayland), "kse" omogočajo upravljanje le z okni najvišje ravni in miško/klaviaturo. Za podrobno raziskovanje po gumbih, seznamih in tako naprej — obstaja tehnologija AT-SPI. Najbolj priljubljeni upravljalniki oken imajo tako imenovani AT-SPI registry demon, ki zagotavlja avtomatizirani GUI za aplikacije (vsaj podpirajo Qt in GTK).

Alati: pyatspi2.

pyatspi2, po mojem mnenju, vsebuje preveč odvisnosti, kot je PyGObject. Sama tehnologija je na voljo v obliki običajne dinamične knjižnice libatspi.so. Do nje je dostopna Referenčni priročnik. Za knjižnico pywinauto načrtujemo implementacijo podpore AT-SPI natančno tako: preko nalaganja libatspi.so in modula ctypes. Obstaja le majhna težava pri uporabi prave različice, saj sta za aplikacije GTK+ in Qt nekoliko različni. Predviden izid pywinauto 0.7.0 s popolno podporo za Linux lahko pričakujemo v prvi polovici leta 2018.

Apple Accessibility API

Na MacOS je lastni jezik avtomatizacije AppleScript. Za implementacijo česa podobnega v Pythonu je seveda potrebno uporabiti funkcije iz ObjectiveC. Že od MacOS 10.6 naprej je v prednameščenem Pythonu vključena knjižnica pyobjc. To bo tudi olajšalo seznam odvisnosti za prihodnjo podporo v pywinauto.

Orodja: Poleg jezika Apple Script je vredno pogledati tudi ATOMac, sicer pyatom. Ima združljivo vmesnik z LDTP, je pa tudi samostojna knjižnica. Na njem je primer avtomatizacije iTunes na macOS, ki ga je napisal moj študent. Obstaja znana težava: fleksibilni časovniki (metode waitFor*) ne delujejo. Vendar pa je na splošno to kar dobra stvar.

Kako začeti delo s pywinauto

Najprej je dobro oborožiti se z orodjem za nadzor GUI objektov (tisto, kar imenujejo Spy tool). Pomaga razjasniti aplikacijo od znotraj: kako je struktura elementov, katere lastnosti so na voljo. Najbolj znani inšpektorji objektov so:

  • Spy++ — je del Visual Studio, vključno z različico Express ali Community Edition. Uporablja Win32 API. Znana je tudi njena klon AutoIt Window Info.
  • Inspect.exe — je del Windows SDK. Če ga imate nameščenega, ga na 64-bitnem Windowsu najdete v mapi C:Program Files (x86)Windows Kitsbinx64. V samem inšpektorju je treba izbrati način UI Automation namesto MS AA (Active Accessibility, predhodnik UI Automation).

Ko aplikacijo temeljito razjasnimo, izberemo backend, ki ga bomo uporabili. Dovolj je, da ob ustvarjanju objekta Application navedemo ime backenda.

  • backend=»win32″ — zaenkrat se uporablja privzeto, dobro deluje z MFC, WTL, VB6 in drugimi legacyskimi aplikacijami.
  • backend=»uia» — nov backend za MS UI Automation: popolnoma deluje z WPF in WinForms; prav tako dober za Delphi in Windows Store aplikacije; deluje s Qt5 in nekaterimi Java aplikacijami. Na splošno, če Inspect.exe vidi elemente in njihove lastnosti, pomeni, da je ta backend primeren. V načeloma večina brskalnikov prav tako podpira UI Automation (Mozilla privzeto, Chromu pa je ob zagonu potrebno posredovati parameter ukazne vrstice --force-renderer-accessibility, da si ogledate elemente na straneh v Inspect.exe). Seveda je konkurenca s Selenium v tej panogi skoraj nemogoča. To je le še en način dela z brskalnikom (lahko je koristno za čez-produkten scenarij).

Vhodne točke za avtomatizacijo

Aplikacija je dovolj preučena. Čas je, da ustvarite objekt Application in ga zaženete ali se pridružite že zagnanemu. To ni samo klon standardne klase. subprocess.Popen, temveč uvodni objekt, ki omejuje vse vaše dejanja znotraj meja procesa. To je zelo uporabno, če je zagnanih več primerkov aplikacije, ostalih pa nočete spreminjati.

from pywinauto.application import Application
app = Application(backend="uia").start('notepad.exe')

# Opisali bomo okno, ki ga želimo najti v procesu Notepad.exe
dlg_spec = app.UntitledNotepad
# čakamo, da se okno dejansko pojavi
actionable_dlg = dlg_spec.wait('visible')

Če želite upravljati z več aplikacijami hkrati, vam bo pomagal razred Desktop. Na primer, v kalkulatorju na Win10 je hierarhija elementov raztegnjena po več procesih (ne samo calc.exe). Tako brez objekta Desktop ne gre.

from subprocess import Popen
from pywinauto import Desktop

Popen('calc.exe', shell=True)
dlg = Desktop(backend="uia").Calculator
dlg.wait('visible')

Koreninski objekt (Aplikacija ili Desktop) je edino mesto, kjer je treba navesti backend. Vse ostalo se prosto prilega konceptu „specifikacija->vraper”, o katerem bomo govorili naprej.

Specifikacije oken/elementov

To je osnovni koncept, na katerem temelji vmesnik pywinauto. Okno/element lahko opisujete približno ali bolj podrobno, tudi če še ne obstaja ali je že zaprto. Specifikacija okna (objekt WindowSpecification) vsebuje kriterije, po katerih je treba poiskati pravo okno ali element.

Primer podrobne specifikacije okna:

>>> dlg_spec = app.window(title='Nepodpisano - Notepad')

>>> dlg_spec
<pywinauto.application.WindowSpecification object at 0x0568B790>

>>> dlg_spec.wrapper_object()
<pywinauto.controls.win32_controls.DialogWrapper object at 0x05639B70>

Iskanje okna poteka z vpoklicem metode .wrapper_object(). Ta vrne nekakšen „vraper“ za resnično okno/element ali pa vrže ElementNotFoundError (včasih ElementAmbiguousError, če je bilo najdenih več elementov, kar pomeni, da je potrebno natančneje opredeliti kriterij iskanja). Ta „vraper“ je že sposoben izvajati nekatere akcije z elementom ali pridobivati podatke iz njega.

Python lahko skrije klic .wrapper_object(), tako da postane končna koda krajša. Priporočamo, da se ga uporablja samo za odpravljanje napak. Naslednji dve vrstici delata popolnoma isto:

dlg_spec.wrapper_object().minimize() # debugging
dlg_spec.minimize() # production

Obstaja veliko meril za iskanje specifikacij oken. Tukaj je le nekaj primerov:

# могут иметь несколько уровней
app.window(title_re='.* - Notepad$').window(class_name='Edit')

# можно комбинировать критерии (как AND) и не ограничиваться одним процессом приложения
dlg = Desktop(backend="uia").Calculator
dlg.window(auto_id='num8Button', control_type='Button')

Seznam vseh možnih meril je na voljo v dokumentaciji funkcije pywinauto.findwindows.find_elements(…).

Magični dostop po atributu in ključi

Python poenostavi ustvarjanje specifikacij oken in dinamično prepoznava atribute objekta (v notranjosti je predefinirana metoda __getattribute__). Seveda na ime atributa veljajo enaka pravila kot za ime katerekoli spremenljivke (ni dovoljeno vstavljati presledkov, vejic in drugih posebnih znakov). Na srečo pywinauto uporablja tako imenovani algoritem iskanja »najboljše ujemanje«, ki je odporen na tipkarske napake in majhne razlike.

app.UntitledNotepad
# enako kot
app.window(best_match='UntitledNotepad')

Če potrebujete Unicode nize (na primer za ruščino), presledke itd., lahko dostopate po ključu (kot da bi šlo za običajen slovar):

app['Untitled - Notepad']
# enako kot
app.window(best_match='Untitled - Notepad')

Pet pravil za magična imena

Kako ugotoviti referenčna čarobna imena? Tista, ki so dodeljena elementu pred iskanjem. Če ste navedli ime, ki je dovolj podobno referenci, bo element najden.

  1. Po naslovu (besedilo, ime): app.Properties.OK.click()
  2. Po besedilu in tipu elementa: app.Properties.OKButton.click()
  3. Po tipu in številu: app.Properties.Button3.click() (imena Button0 in Button1 so povezana s prvim najdenim elementom, Button2 — z drugim, in naprej po vrsti — tako je zgodovinsko nastalo)
  4. Po statičnem besedilu (levo ali zgoraj) in tipu: app.OpenDialog.FileNameEdit.set_text("") (koristno za elemente z dinamičnim besedilom)
  5. Po tipu in besedilu znotraj: app.Properties.TabControlSharing.select("General")

Običajno se hkrati uporabijo dve ali tri pravila, redko več. Da preverite, katera konkretna imena so na voljo za vsak element, lahko uporabite metodo print_control_identifiers(). Lahko natisne drevo elementov tako na zaslon kot v datoteko. Za vsak element natisne njegova referenčna čarobna imena. Prav tako lahko od tam kopirate podrobnejše specifikacije podrejenih elementov. Rezultat v skriptu bo izgledal tako:

app.Properties.child_window(title="Contains:", auto_id="13087", control_type="Edit")

Samo drevo elementov — običajno precej dolga vrsta.

>>> app.Properties.print_control_identifiers()

Identifikatori kontrole:

Dijalog - 'Windows NT Properties'    (L688, T518, R1065, B1006)
[u'Windows NT PropertiesDialog', u'Dijalog', u'Windows NT Properties']
child_window(title="Windows NT Properties", control_type="Window")
   |
   | Slika - ''    (L717, T589, R749, B622)
   | [u'', u'0', u'Image1', u'Image0', 'Image', u'1']
   | child_window(auto_id="13057", control_type="Image")
   |
   | Slika - ''    (L717, T630, R1035, B632)
   | ['Image2', u'2']
   | child_window(auto_id="13095", control_type="Image")
   |
   | Edit - 'Ime fascikle:'    (L790, T596, R1036, B619)
   | [u'3', 'Edit', u'Edit1', u'Edit0']
   | child_window(title="Ime fascikle:", auto_id="13156", control_type="Edit")
   |
   | Statička - 'Tip:'    (L717, T643, R780, B658)
   | [u'Tip:Static', u'Static', u'Static1', u'Static0', u'Tip:']
   | child_window(title="Tip:", auto_id="13080", control_type="Text")
   |
   | Edit - 'Tip:'    (L790, T643, R1036, B666)
   | [u'4', 'Edit2', u'Tip:Edit']
   | child_window(title="Tip:", auto_id="13059", control_type="Edit")
   |
   | Statička - 'Lokacija:'    (L717, T669, R780, B684)
   | [u'Lokacija:Static', u'Lokacija:', u'Static2']
   | child_window(title="Lokacija:", auto_id="13089", control_type="Text")
   |
   | Edit - 'Lokacija:'    (L790, T669, R1036, B692)
   | ['Edit3', u'Lokacija:Edit', u'5']
   | child_window(title="Lokacija:", auto_id="13065", control_type="Edit")
   |
   | Statička - 'Veličina:'    (L717, T695, R780, B710)
   | [u'Veličina:Static', u'Veličina:', u'Static3']
   | child_window(title="Veličina:", auto_id="13081", control_type="Text")
   |
   | Edit - 'Veličina:'    (L790, T695, R1036, B718)
   | ['Edit4', u'6', u'Veličina:Edit']
   | child_window(title="Veličina:", auto_id="13064", control_type="Edit")
   |
   | Statička - 'Veličina na disku:'    (L717, T721, R780, B736)
   | [u'Veličina na disku:', u'Veličina na disku:Static', u'Static4']
   | child_window(title="Veličina na disku:", auto_id="13107", control_type="Text")
   |
   | Edit - 'Veličina na disku:'    (L790, T721, R1036, B744)
   | ['Edit5', u'7', u'Veličina na disku:Edit']
   | child_window(title="Veličina na disku:", auto_id="13106", control_type="Edit")
   |
   | Statička - 'Sadrži:'    (L717, T747, R780, B762)
   | [u'Sadrži:1', u'Sadrži:0', u'Sadrži:Static', u'Static5', u'Sadrži:']
   | child_window(title="Sadrži:", auto_id="13088", control_type="Text")
   |
   | Edit - 'Sadrži:'    (L790, T747, R1036, B770)
   | [u'8', 'Edit6', u'Sadrži:Edit']
   | child_window(title="Sadrži:", auto_id="13087", control_type="Edit")
   |
   | Slika - 'Sadrži:'    (L717, T773, R1035, B775)
   | [u'Sadrži:Image', 'Image3', u'Sadrži:2']
   | child_window(title="Sadrži:", auto_id="13096", control_type="Image")
   |
   | Statička - 'Kreirano:'    (L717, T786, R780, B801)
   | [u'Kreirano:', u'Kreirano:Static', u'Static6', u'Kreirano:1', u'Kreirano:0']
   | child_window(title="Kreirano:", auto_id="13092", control_type="Text")
   |
   | Edit - 'Kreirano:'    (L790, T786, R1036, B809)
   | [u'Kreirano:Edit', 'Edit7', u'9']
   | child_window(title="Kreirano:", auto_id="13072", control_type="Edit")
   |
   | Slika - 'Kreirano:'    (L717, T812, R1035, B814)
   | [u'Kreirano:Image', 'Image4', u'Kreirano:2']
   | child_window(title="Kreirano:", auto_id="13097", control_type="Image")
   |
   | Statička - 'Atributi:'    (L717, T825, R780, B840)
   | [u'Atributi:Static', u'Static7', u'Atributi:']
   | child_window(title="Atributi:", auto_id="13091", control_type="Text")
   |
   | Čekboks - 'Samo za čitanje (važi samo za datoteke u fascikli)'    (L790, T825, R1035, B841)
   | [u'CheckBox0', u'CheckBox1', 'CheckBox', u'Samo za čitanje (važi samo za datoteke u fascikli)CheckBox', u'Samo za čitanje (važi samo za datoteke u fascikli)']
   | child_window(title="Samo za čitanje (važi samo za datoteke u fascikli)", auto_id="13075", control_type="CheckBox")
   |
   | Čekboks - 'Sakriveno'    (L790, T848, R865, B864)
   | ['CheckBox2', u'SakrivenoCheckBox', u'Sakriveno']
   | child_window(title="Sakriveno", auto_id="13076", control_type="CheckBox")
   |
   | Dugme - 'Napredno...'    (L930, T845, R1035, B868)
   | [u'Napredno...', u'Napredno...Button', 'Button', u'Button1', u'Button0']
   | child_window(title="Napredno...", auto_id="13154", control_type="Button")
   |
   | Dugme - 'U redu'    (L814, T968, R889, B991)
   | ['Button2', u'U redu', u'OKButton']
   | child_window(title="U redu", auto_id="1", control_type="Button")
   |
   | Dugme - 'Otkaži'    (L895, T968, R970, B991)
   | ['Button3', u'CancelButton', u'Otkazati']
   | child_window(title="Otkaži", auto_id="2", control_type="Button")
   |
   | Dugme - 'Primeni'    (L976, T968, R1051, B991)
   | ['Button4', u'ApplyButton', u'Primeni']
   | child_window(title="Primeni", auto_id="12321", control_type="Button")
   |
   | TabControl - ''    (L702, T556, R1051, B962)
   | [u'10', u'TabControlSharing', u'TabControlPrevious Versions', u'TabControlSecurity', u'TabControl', u'TabControlCustomize']
   | child_window(auto_id="12320", control_type="Tab")
   |    |
   |    | TabItem - 'Opšte'    (L704, T558, R753, B576)
   |    | [u'GeneralTabItem', 'TabItem', u'Opšte', u'TabItem0', u'TabItem1']
   |    | child_window(title="Opšte", control_type="TabItem")
   |    |
   |    | TabItem - 'Deljenje'    (L753, T558, R801, B576)
   |    | [u'Deljenje', u'DeljenjeTabItem', 'TabItem2']
   |    | child_window(title="Deljenje", control_type="TabItem")
   |    |
   |    | TabItem - 'Sigurnost'    (L801, T558, R851, B576)
   |    | [u'Sigurnost', 'TabItem3', u'SigurnostTabItem']
   |    | child_window(title="Sigurnost", control_type="TabItem")
   |    |
   |    | TabItem - 'Prethodne verzije'    (L851, T558, R947, B576)
   |    | [u'Prethodne verzijeTabItem', u'Prethodne verzije', 'TabItem4']
   |    | child_window(title="Prethodne verzije", control_type="TabItem")
   |    |
   |    | TabItem - 'Prilagoditi'    (L947, T558, R1007, B576)
   |    | [u'PrilagoditiTabItem', 'TabItem5', u'Prilagoditi']
   |    | child_window(title="Prilagoditi", control_type="TabItem")
   |
   | TitleBar - 'Nema'    (L712, T521, R1057, B549)
   | ['TitleBar', u'11']
   |    |
   |    | Meni - 'Sistem'    (L696, T526, R718, B548)
   |    | [u'Sistem0', u'Sistem', u'Sistem1', u'Menu', u'SistemMenu']
   |    | child_window(title="Sistem", auto_id="MenuBar", control_type="MenuBar")
   |    |    |
   |    |    | Meni stavka - 'Sistem'    (L696, T526, R718, B548)
   |    |    | [u'Sistem2', u'MenuItem', u'SistemMenuItem']
   |    |    | child_window(title="Sistem", control_type="MenuItem")
   |    |
   |    | Dugme - 'Zatvori'    (L1024, T519, R1058, B549)
   |    | [u'CloseButton', u'Zatvori', 'Button5']
   |    | child_window(title="Zatvori", control_type="Button")

V nekaterih primerih lahko tiskanje celotne strukture upočasni sistem (na primer, v iTunes, kjer je na enem zavihku kar tri tisoč elementov!), vendar lahko uporabite parameter depth (globina): depth=1 — sam element, depth=2 — le neposredni otroci, in tako naprej. Ta parameter lahko prav tako navedete v specifikacijah pri ustvarjanju child_window.

Primeri

Nenehno dopolnjujemo seznam primerov v repozitoriju. Med novimi izstopa avtomatizacija omrežnega analizatorja WireShark (to je dober primer Qt5 aplikacije; čeprav je to nalogo mogoče rešiti tudi brez GUI, saj imamo scapy.Sniffer iz Pythonovega paketa scapy). Prav tako obstaja primer avtomatizacije MS Paint z njegovim Ribbon orodnim trakem.

Še en odličen primer, ki ga je napisal moj študent: povlečenje datoteke iz explorer.exe na Chrome stran za Google Drive (to bo kmalu preneseno v glavni repozitorij).

In seveda, primer naročanja na dogodke tipkovnice (hot keys) in miške:
hook_and_listen.py.

Poděkování

Posebna zahvala gre tistim, ki stalno pomagajo pri razvoju projekta. Zame in Valentina je to stalno hobi. Dva moja študenta iz NNGU sta nedavno zaključila še diplomiranje na to temo. Aleksander je veliko prispeval k podpori MS UI Automation in nedavno začel delati avtomatski generator kode po principu „snemanje-predvajanje“ na osnovi tekstovnih lastnosti (to je najbolj zapletena funkcija), trenutno le za backend „uia“. Ivan razvija nov backend pod Linuxom na osnovi AT-SPI (moduli mouse in keyboard zasnovanom na python-xlib — že v izdajah 0.6.x).

Ker že dolgo poučujem specializirani tečaj za avtomatizacijo v Pythonu, nekateri magistrski študenti naloge opravljajo z uresničevanjem manjših funkcij ali primerov avtomatizacije. Nekatere ključne stvari so se prav tako razvile v raziskavah prav od študentov. Čeprav je včasih potrebno strogo spremljati kakovost kode. To zelo pomaga statični analizatorji (QuantifiedCode, Codacy in Landscape) ter avtomatski testi v oblaku (storitev AppVeyor) s pokritostjo kode okoli 95%.

Hvala tudi vsem, ki puščate povratne informacije, odpravljate napake in pošiljate pull requeste!

Dodatni resursi

Vprašanja spremljamo preko taga na StackOverflow (najnovejši je tag v ruski različici SO) i po ključni besedi na Tostru. Na voljo je rusko govoreči klepet na Gitterju.

Vsak mesec posodabljamo ocene open-source knjižnic za GUI testiranje.Glede na število zvezdic na GitHubu hitreje rastejo le Autohotkey (ima zelo veliko skupnost in dolgo zgodovino) ter PyAutoGUI (predvsem zaradi priljubljenosti knjig njenega avtorja Al Sweigart: „Automate the Boring Stuff with Python“ in drugih).

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster