Automatizimi i GUI-së së desktopit në Python + pywinauto: si të bëni miq me MS UI Automation

Biblioteka Python pywinauto është një projekt me burim të hapur për të automatizuar aplikacionet GUI të desktopit në Windows. Gjatë dy viteve të fundit, karakteristika të reja kryesore janë shfaqur në të:

  • Mbështetje për teknologjinë MS UI Automation. Ndërfaqja është e njëjtë, dhe tani ajo mbështet: WinForms, WPF, Qt5, Windows Store (UWP) dhe kështu me radhë - pothuajse gjithçka që është në Windows.
  • Sistemi i mbështetësve/pluginave (aktualisht ka dy prej tyre nën kapuç: default "win32" dhe të reja "uia"). Pastaj ne lëvizim pa probleme drejt ndër-platformës.
  • Grepa Win32 për miun dhe tastierën (çelësat e nxehtë në frymën e pyHook).

Ne gjithashtu do të japim një përmbledhje të shkurtër të asaj që është e disponueshme në burim të hapur për automatizimin e desktopit (pa pretenduar të jetë një krahasim serioz).

Ky artikull është një transkript i pjesshëm i një raporti nga konferenca SQA Days 20 në Minsk (videokasetë и rrëshqitje), versioni pjesërisht rus Udhëzuesi i fillimit për pywinauto.

Le të fillojmë me një përmbledhje të shkurtër të burimit të hapur në këtë fushë. Për aplikacionet e desktopit GUI, gjithçka është disi më e ndërlikuar sesa për ueb-in, i cili ka Selenium. Këtu janë qasjet kryesore:

Metoda e koordinatave

Hardcod pikat e klikimeve, shpresojmë për hite të suksesshme.
[+] Ndër-platformë, e lehtë për t'u zbatuar.
[+] Është e lehtë për të "regjistruar-përsëritur" regjistrimet testuese.
[-] Më e paqëndrueshme për ndryshimin e rezolucionit të ekranit, temës, shkronjave, madhësive të dritareve, etj.
[-] Kërkohen përpjekje të mëdha mbështetëse; shpesh është më e lehtë të rigjenerosh testet nga e para ose të testosh manualisht.
[-] Automatizon vetëm veprimet; ka metoda të tjera për verifikimin dhe marrjen e të dhënave.

Mjetet (ndër-platformë): autopi, PyAutoGUI, PyUserInput dhe shume te tjere. Në mënyrë tipike, mjetet më komplekse përfshijnë këtë funksionalitet (jo gjithmonë ndër-platformë).

Vlen të thuhet se metoda e koordinatave mund të plotësojë qasje të tjera. Për shembull, për grafikë të personalizuar, mund të klikoni në koordinatat relative (nga këndi i sipërm i majtë i dritares/elementit, dhe jo i gjithë ekranit) - kjo zakonisht është mjaft e besueshme, veçanërisht nëse merrni parasysh gjatësinë/gjerësinë e i gjithë elementi (atëherë rezolucione të ndryshme të ekranit nuk do të dëmtojnë).

Një opsion tjetër: ndani vetëm një makinë me cilësime të qëndrueshme për testim (jo ndër-platformë, por në disa raste është mirë).

Njohja e imazhit të referencës

[+] Ndër-platformë
[+-] Relativisht e besueshme (më mirë se metoda e koordinatave), por ende kërkon disa truke.
[-+] Relativisht i ngadaltë, sepse Kërkon burime CPU për algoritmet e njohjes.
[-] Njohja e tekstit (OCR), si rregull, nuk bëhet fjalë => të dhënat e tekstit nuk mund të merren. Me sa di unë, zgjidhjet ekzistuese OCR nuk janë shumë të besueshme për këtë lloj detyre dhe nuk përdoren gjerësisht (mirë se vini në komente nëse nuk është tashmë rasti).

Instrumente: Sikuli, Lakej (Python i pastër i pajtueshëm me Sikuli), PyAutoGUI.

Teknologjia e aksesit

[+] Metoda më e besueshme, sepse ju lejon të kërkoni sipas tekstit, pavarësisht nga mënyra se si është dhënë nga sistemi ose korniza.
[+] Ju lejon të nxjerrni të dhëna teksti => më lehtë për të verifikuar rezultatet e testit.
[+] Si rregull, më i shpejti, sepse nuk konsumon pothuajse asnjë burim CPU.
[-] Është e vështirë të bësh një mjet ndër-platformë: absolutisht të gjitha bibliotekat me burim të hapur mbështesin një ose dy teknologji aksesi. Windows/Linux/MacOS nuk mbështetet plotësisht nga askush, përveç atyre me pagesë si TestComplete, UFT ose Squish.
[-] Një teknologji e tillë nuk është gjithmonë e disponueshme në parim. Për shembull, testimi i ekranit të ngarkimit brenda VirtualBox - kjo nuk mund të bëhet pa njohjen e imazhit. Por në shumë raste klasike, qasja e aksesueshmërisë është ende e zbatueshme. Kjo do të diskutohet më tej.

Instrumente: TestStack.E bardhë në C#, Winium.Desktop në C# (përputhshëm me Selenium), Shoferi MS WinApp në C# (i pajtueshëm me Appium), pywinauto, piatom (i pajtueshëm me LDTP) Python-UIAutomation-for-Windows, Rautomatizimi në Rubin, LDTP (Projekti i Testimit të Desktopit Linux) dhe versioni i tij Windows Gjarpër me syze.

LDTP është ndoshta i vetmi mjet me burim të hapur ndër-platformë (më saktë, një familje bibliotekash) bazuar në teknologjitë e aksesueshmërisë. Megjithatë, nuk është shumë popullor. Unë nuk e kam përdorur vetë, por sipas rishikimeve, ndërfaqja e tij nuk është më e përshtatshme. Nëse keni komente pozitive, ju lutemi ndani ato në komente.

Testoni derën e pasme (i njohur ndryshe si biçikleta e brendshme)

Për aplikacionet ndër-platformë, vetë zhvilluesit shpesh krijojnë një mekanizëm të brendshëm për të siguruar testueshmëri. Për shembull, ata krijojnë një server TCP shërbimi në aplikacion, testet lidhen me të dhe dërgojnë komanda me tekst: çfarë të klikoni, ku të merrni të dhënat, etj. E besueshme, por jo universale.

Teknologjitë bazë të aksesueshmërisë në desktop

API e vjetër e mirë Win32

Shumica e aplikacioneve të Windows të shkruara përpara lëshimit të WPF dhe më pas Dyqani i Windows u ndërtuan në API Win32 në një mënyrë ose në një tjetër. Gjegjësisht, MFC, WTL, C++ Builder, Delphi, VB6 - të gjitha këto mjete përdorin Win32 API. Edhe format e Windows janë kryesisht të pajtueshëm me API-në Win32.

Instrumente: Në çati (i ngjashëm me VB) dhe mbështjellës Python pyautoit, AutoHotkey (gjuhën e vet, ekziston një ndërfaqe IDispatch COM), pywinauto (Python) Rautomatizimi (Rubin) win32-autogui (Rubin).

Automatizimi i ndërfaqes së përdoruesit të Microsoft

Avantazhi kryesor: Teknologjia MS UI Automation mbështet shumicën dërrmuese të aplikacioneve GUI në Windows me përjashtime të rralla. Problemi: Nuk është shumë më e lehtë për t'u mësuar sesa API Win32. Përndryshe, askush nuk do të bënte mbështjellës mbi të.

Në fakt, ky është një grup ndërfaqesh COM me porosi (kryesisht UIAutomationCore.dll), dhe gjithashtu ka një mbështjellës .NET në formë namespace System.Windows.Automation. Nga rruga, ai ka një gabim të futur për shkak të të cilit mund të mungojnë disa elementë UI. Prandaj, është më mirë të përdorni direkt UIAutomationCore.dll (nëse keni dëgjuar për UiaComWrapper në C#, atëherë kjo është ajo).

Llojet e ndërfaqeve COM:

(1) IU-ja themelore e njohur - "rrënja e të gjitha të këqijave". Niveli më i ulët, asnjëherë miqësor për përdoruesit.
(2) IDdispatch dhe derivatet (p.sh Excel.Application), i cili mund të përdoret në Python duke përdorur paketën win32com.client (përfshirë me pyWin32). Opsioni më i përshtatshëm dhe i bukur.
(3) Ndërfaqet e personalizuara me të cilat mund të punojë një paketë Python e palës së tretë comtypes.

Instrumente: TestStack.E bardhë në C#, pywinauto 0.6.0+, Winium.Desktop në C#, Python-UIAutomation-for-Windows (kodi i tyre burimor për mbështjellësit C mbi UIAutomationCore.dll nuk zbulohet), Rautomatizimi në Ruby.

AT-SPI

Përkundër faktit se pothuajse të gjitha akset e familjes Linux janë ndërtuar në sistemin X Window (në Fedora 25, "X" u ndryshuan në Wayland), "X" ju lejojnë të përdorni vetëm dritaret e nivelit të lartë dhe miun/ tastierë. Për një analizë të detajuar të butonave, kutive të listave, e kështu me radhë, ekziston teknologjia AT-SPI. Menaxherët më të njohur të dritareve kanë të ashtuquajturin demon të regjistrit AT-SPI, i cili siguron një GUI të automatizuar për aplikacionet (të paktën Qt dhe GTK mbështeten).

Instrumente: pyatspi2.

pyatspi2, për mendimin tim, përmban shumë varësi si PyGObject. Vetë teknologjia është e disponueshme si një bibliotekë e rregullt dinamike libatspi.so. Aty eshte nje Manual referimi. Për bibliotekën pywinauto ne planifikojmë të implementojmë mbështetjen AT-SPI në këtë mënyrë: përmes ngarkimit të libatspi.so dhe modulit ctypes. Ka një problem të vogël vetëm në përdorimin e versionit të kërkuar, sepse për aplikacionet GTK+ dhe Qt ato janë paksa të ndryshme. Lëshimi i mundshëm i pywinauto 0.7.0 me mbështetje të plotë Linux mund të pritet në gjysmën e parë të 2018.

Apple Accessibility API

MacOS ka gjuhën e vet të automatizimit, AppleScript. Për të zbatuar diçka të tillë në Python, sigurisht, duhet të përdorni funksione nga ObjectiveC. Duke filluar, me sa duket, me MacOS 10.6, paketa pyobjc përfshihet në Python të para-instaluar. Kjo gjithashtu do ta bëjë më të lehtë renditjen e varësive për mbështetjen e ardhshme në pywinauto.

Mjetet: Përveç gjuhës Apple Script, ia vlen t'i kushtohet vëmendje ATOMac, i njohur si pyatom. Është ndërfaqe e pajtueshme me LDTP, por është gjithashtu një bibliotekë e pavarur. Ajo ka shembull i automatizimit të iTunes në macOs, shkruar nga studenti im. Ekziston një problem i njohur: oraret fleksibël nuk funksionojnë (metodat waitFor*). Por në përgjithësi, jo një gjë e keqe.

Si të filloni me pywinauto

Hapi i parë është të armatoseni me një inspektor të objektit GUI (ai që quhet mjet spiun). Do t'ju ndihmojë të studioni aplikacionin nga brenda: si është strukturuar hierarkia e elementeve, cilat veçori janë të disponueshme. Inspektorët më të famshëm të faqes:

  • Spiun ++ - i përfshirë me Visual Studio, duke përfshirë Express ose Botim në Komunitet. Përdor Win32 API. Njihet edhe kloni i tij Informacioni i dritares AutoIt.
  • Inspect.exe - përfshihet në Windows SDK. Nëse e keni të instaluar, atëherë në Windows 64-bit mund ta gjeni në dosje C:Program Files (x86)Windows Kits<winver>binx64. Në vetë inspektorin ju duhet të zgjidhni një mënyrë Automatizimi i UI në vend të MS AA (Active Accessibility, paraardhësi i UI Automation).

Pasi kemi ekzaminuar aplikacionin deri në fund, ne zgjedhim backend-in që do të përdorim. Mjafton të specifikoni emrin e backend-it gjatë krijimit të objektit Application.

  • backend=”win32″ — ndërsa përdoret si parazgjedhje, funksionon mirë me MFC, WTL, VB6 dhe aplikacione të tjera të trashëgimisë.
  • backend=”uia” — backend i ri për MS UI Automation: punon në mënyrë perfekte me WPF dhe WinForms; gjithashtu i mirë për aplikacionet Delphi dhe Windows Store; punon me Qt5 dhe disa aplikacione Java. Dhe në përgjithësi, nëse Inspect.exe sheh elementet dhe vetitë e tyre, atëherë ky backend është i përshtatshëm. Në parim, shumica e shfletuesve mbështesin gjithashtu Automatizimin UI (Mozilla si parazgjedhje, dhe Chrome duhet të ushqehet me çelësin e linjës së komandës kur fillon --force-renderer-accessibilitypër të parë elementet në faqet në Inspect.exe). Sigurisht, konkurrenca me Selenin në këtë fushë është e vështirë. Vetëm një mënyrë tjetër për të punuar me shfletuesin (mund të jetë e dobishme për një skenar ndërproduktesh).

Pikat e hyrjes për automatizimin

Aplikacioni është hulumtuar gjerësisht. Është koha për të krijuar një objekt aplikacioni dhe për ta ekzekutuar atë ose për t'i bashkangjitur një objekti tashmë të ekzekutuar. Ky nuk është vetëm një klon i një klase standarde subprocess.Popen, domethënë një objekt hyrës që kufizon të gjitha veprimet tuaja në kufijtë e procesit. Kjo është shumë e dobishme nëse funksionojnë disa raste të aplikacionit, por ju nuk dëshironi të prekni pjesën tjetër.

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

# Опишем окно, которое хотим найти в процессе Notepad.exe
dlg_spec = app.UntitledNotepad
# ждем пока окно реально появится
actionable_dlg = dlg_spec.wait('visible')

Nëse dëshironi të menaxhoni disa aplikacione në të njëjtën kohë, kjo klasë do t'ju ndihmojë Desktop. Për shembull, në një kalkulator në Win10, hierarkia e elementeve shpërndahet në disa procese (jo vetëm calc.exe). Pra asnjë objekt Desktop jo mjaftueshem.

from subprocess import Popen
from pywinauto import Desktop

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

Objekti rrënjë (Application ose Desktop) është i vetmi vend ku duhet të specifikoni backend-in. Çdo gjë tjetër hyn në mënyrë transparente në konceptin "specifikim->mbështjellës", i cili do të diskutohet më vonë.

Specifikimet e dritares/elementit

Ky është koncepti thelbësor mbi të cilin është ndërtuar ndërfaqja pywinauto. Ju mund ta përshkruani dritaren/elementin përafërsisht ose më në detaje, edhe nëse nuk ekziston ende ose është mbyllur tashmë. Specifikimi i dritares (objekt Specifikimi i dritares) ruan kriteret me të cilat kërkohet një dritare ose element i vërtetë.

Shembull i një specifikimi të detajuar të dritares:

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

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

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

Vetë kërkimi i dritares ndodh duke thirrur metodën .wrapper_object(). Ai kthen një "mbështjellës" të caktuar për një dritare/element të vërtetë ose hedhje ElementNotFoundError (ndonjehere ElementAmbiguousError, nëse gjenden disa elementë, domethënë, duhet të sqaroni kriterin e kërkimit). Ky "mbështjellës" tashmë di të bëjë disa veprime me një element ose të marrë të dhëna prej tij.

Python mund të fshehë thirrjen .wrapper_object(), kështu që kodi përfundimtar bëhet më i shkurtër. Ne rekomandojmë përdorimin e tij vetëm për qëllime korrigjimi. Dy rreshtat e ardhshëm bëjnë saktësisht të njëjtën gjë:

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

Ka shumë kritere kërkimi për një specifikim të dritares. Këtu janë vetëm disa shembuj:

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

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

Një listë e të gjitha kritereve të mundshme është në dokumentet e funksionit pywinauto.findwindows.find_elements(…).

Magjia e aksesit sipas atributit dhe çelësit

Python e bën të lehtë krijimin e specifikimeve të dritares dhe njohjen e atributeve të objektit në mënyrë dinamike (nga brenda, metoda është anashkaluar __getattribute__). Natyrisht, të njëjtat kufizime vendosen në emrin e atributit si në emrin e çdo ndryshoreje (nuk mund të futni hapësira, presje ose karaktere të tjera speciale). Për fat të mirë, pywinauto përdor të ashtuquajturin algoritëm kërkimi "përputhja më e mirë" që është rezistent ndaj gabimeve dhe variacioneve të vogla.

app.UntitledNotepad
# то же самое, что
app.window(best_match='UntitledNotepad')

Nëse keni ende nevojë për vargje Unicode (për shembull, për gjuhën ruse), hapësira, etj., mund të përdorni me çelës (sikur të ishte një fjalor i rregullt):

app['Untitled - Notepad']
# то же самое, что
app.window(best_match='Untitled - Notepad')

Pesë rregulla për emrat magjikë

Si të zbuloni emrat standardë magjikë? Ato që i janë caktuar elementit përpara kërkimit. Nëse keni specifikuar një emër që është mjaftueshëm i ngjashëm me standardin, atëherë elementi do të gjendet.

  1. Sipas titullit (teksti, emri): app.Properties.OK.click()
  2. Sipas tekstit dhe sipas llojit të elementit: app.Properties.OKButton.click()
  3. Sipas llojit dhe numrit: app.Properties.Button3.click() (emrat Button0 и Button1 lidhur me elementin e parë të gjetur, Button2 - tek e dyta, dhe pastaj me radhë - kështu ndodhi historikisht)
  4. Sipas tekstit statik (majtas ose lart) dhe sipas llojit: app.OpenDialog.FileNameEdit.set_text("") (i dobishëm për elementët me tekst dinamik)
  5. Sipas llojit dhe tekstit brenda: app.Properties.TabControlSharing.select("General")

Zakonisht zbatohen dy ose tre rregulla në të njëjtën kohë, rrallë më shumë. Për të kontrolluar se cilët emra specifikë janë të disponueshëm për secilin element, mund të përdorni metodën print_control_identifiers(). Mund të printojë një pemë elementësh si në ekran ashtu edhe në një skedar. Për çdo element shtypen emrat e tij standardë magjikë. Ju gjithashtu mund të kopjoni dhe ngjisni specifikime më të detajuara të elementeve fëmijë prej andej. Rezultati në skenar do të duket si ky:

app.Properties.child_window(data-gt-translate-attributes='["title"]' title="Contains:", auto_id="13087", control_type="Edit")

Vetë pema e elementeve është zakonisht një mbulesë këmbësh mjaft e madhe.

>>> app.Properties.print_control_identifiers()

Control Identifiers:

Dialog - 'Windows NT Properties'    (L688, T518, R1065, B1006)
[u'Windows NT PropertiesDialog', u'Dialog', u'Windows NT Properties']
child_window(data-gt-translate-attributes='["title"]' title="Windows NT Properties", control_type="Window")
   |
   | Image - ''    (L717, T589, R749, B622)
   | [u'', u'0', u'Image1', u'Image0', 'Image', u'1']
   | child_window(auto_id="13057", control_type="Image")
   |
   | Image - ''    (L717, T630, R1035, B632)
   | ['Image2', u'2']
   | child_window(auto_id="13095", control_type="Image")
   |
   | Edit - 'Folder name:'    (L790, T596, R1036, B619)
   | [u'3', 'Edit', u'Edit1', u'Edit0']
   | child_window(data-gt-translate-attributes='["title"]' title="Folder name:", auto_id="13156", control_type="Edit")
   |
   | Static - 'Type:'    (L717, T643, R780, B658)
   | [u'Type:Static', u'Static', u'Static1', u'Static0', u'Type:']
   | child_window(data-gt-translate-attributes='["title"]' title="Type:", auto_id="13080", control_type="Text")
   |
   | Edit - 'Type:'    (L790, T643, R1036, B666)
   | [u'4', 'Edit2', u'Type:Edit']
   | child_window(data-gt-translate-attributes='["title"]' title="Type:", auto_id="13059", control_type="Edit")
   |
   | Static - 'Location:'    (L717, T669, R780, B684)
   | [u'Location:Static', u'Location:', u'Static2']
   | child_window(data-gt-translate-attributes='["title"]' title="Location:", auto_id="13089", control_type="Text")
   |
   | Edit - 'Location:'    (L790, T669, R1036, B692)
   | ['Edit3', u'Location:Edit', u'5']
   | child_window(data-gt-translate-attributes='["title"]' title="Location:", auto_id="13065", control_type="Edit")
   |
   | Static - 'Size:'    (L717, T695, R780, B710)
   | [u'Size:Static', u'Size:', u'Static3']
   | child_window(data-gt-translate-attributes='["title"]' title="Size:", auto_id="13081", control_type="Text")
   |
   | Edit - 'Size:'    (L790, T695, R1036, B718)
   | ['Edit4', u'6', u'Size:Edit']
   | child_window(data-gt-translate-attributes='["title"]' title="Size:", auto_id="13064", control_type="Edit")
   |
   | Static - 'Size on disk:'    (L717, T721, R780, B736)
   | [u'Size on disk:', u'Size on disk:Static', u'Static4']
   | child_window(data-gt-translate-attributes='["title"]' title="Size on disk:", auto_id="13107", control_type="Text")
   |
   | Edit - 'Size on disk:'    (L790, T721, R1036, B744)
   | ['Edit5', u'7', u'Size on disk:Edit']
   | child_window(data-gt-translate-attributes='["title"]' title="Size on disk:", auto_id="13106", control_type="Edit")
   |
   | Static - 'Contains:'    (L717, T747, R780, B762)
   | [u'Contains:1', u'Contains:0', u'Contains:Static', u'Static5', u'Contains:']
   | child_window(data-gt-translate-attributes='["title"]' title="Contains:", auto_id="13088", control_type="Text")
   |
   | Edit - 'Contains:'    (L790, T747, R1036, B770)
   | [u'8', 'Edit6', u'Contains:Edit']
   | child_window(data-gt-translate-attributes='["title"]' title="Contains:", auto_id="13087", control_type="Edit")
   |
   | Image - 'Contains:'    (L717, T773, R1035, B775)
   | [u'Contains:Image', 'Image3', u'Contains:2']
   | child_window(data-gt-translate-attributes='["title"]' title="Contains:", auto_id="13096", control_type="Image")
   |
   | Static - 'Created:'    (L717, T786, R780, B801)
   | [u'Created:', u'Created:Static', u'Static6', u'Created:1', u'Created:0']
   | child_window(data-gt-translate-attributes='["title"]' title="Created:", auto_id="13092", control_type="Text")
   |
   | Edit - 'Created:'    (L790, T786, R1036, B809)
   | [u'Created:Edit', 'Edit7', u'9']
   | child_window(data-gt-translate-attributes='["title"]' title="Created:", auto_id="13072", control_type="Edit")
   |
   | Image - 'Created:'    (L717, T812, R1035, B814)
   | [u'Created:Image', 'Image4', u'Created:2']
   | child_window(data-gt-translate-attributes='["title"]' title="Created:", auto_id="13097", control_type="Image")
   |
   | Static - 'Attributes:'    (L717, T825, R780, B840)
   | [u'Attributes:Static', u'Static7', u'Attributes:']
   | child_window(data-gt-translate-attributes='["title"]' title="Attributes:", auto_id="13091", control_type="Text")
   |
   | CheckBox - 'Read-only (Only applies to files in folder)'    (L790, T825, R1035, B841)
   | [u'CheckBox0', u'CheckBox1', 'CheckBox', u'Read-only (Only applies to files in folder)CheckBox', u'Read-only (Only applies to files in folder)']
   | child_window(data-gt-translate-attributes='["title"]' title="Read-only (Only applies to files in folder)", auto_id="13075", control_type="CheckBox")
   |
   | CheckBox - 'Hidden'    (L790, T848, R865, B864)
   | ['CheckBox2', u'HiddenCheckBox', u'Hidden']
   | child_window(data-gt-translate-attributes='["title"]' title="Hidden", auto_id="13076", control_type="CheckBox")
   |
   | Button - 'Advanced...'    (L930, T845, R1035, B868)
   | [u'Advanced...', u'Advanced...Button', 'Button', u'Button1', u'Button0']
   | child_window(data-gt-translate-attributes='["title"]' title="Advanced...", auto_id="13154", control_type="Button")
   |
   | Button - 'OK'    (L814, T968, R889, B991)
   | ['Button2', u'OK', u'OKButton']
   | child_window(data-gt-translate-attributes='["title"]' title="OK", auto_id="1", control_type="Button")
   |
   | Button - 'Cancel'    (L895, T968, R970, B991)
   | ['Button3', u'CancelButton', u'Cancel']
   | child_window(data-gt-translate-attributes='["title"]' title="Cancel", auto_id="2", control_type="Button")
   |
   | Button - 'Apply'    (L976, T968, R1051, B991)
   | ['Button4', u'ApplyButton', u'Apply']
   | child_window(data-gt-translate-attributes='["title"]' title="Apply", 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 - 'General'    (L704, T558, R753, B576)
   |    | [u'GeneralTabItem', 'TabItem', u'General', u'TabItem0', u'TabItem1']
   |    | child_window(data-gt-translate-attributes='["title"]' title="General", control_type="TabItem")
   |    |
   |    | TabItem - 'Sharing'    (L753, T558, R801, B576)
   |    | [u'Sharing', u'SharingTabItem', 'TabItem2']
   |    | child_window(data-gt-translate-attributes='["title"]' title="Sharing", control_type="TabItem")
   |    |
   |    | TabItem - 'Security'    (L801, T558, R851, B576)
   |    | [u'Security', 'TabItem3', u'SecurityTabItem']
   |    | child_window(data-gt-translate-attributes='["title"]' title="Security", control_type="TabItem")
   |    |
   |    | TabItem - 'Previous Versions'    (L851, T558, R947, B576)
   |    | [u'Previous VersionsTabItem', u'Previous Versions', 'TabItem4']
   |    | child_window(data-gt-translate-attributes='["title"]' title="Previous Versions", control_type="TabItem")
   |    |
   |    | TabItem - 'Customize'    (L947, T558, R1007, B576)
   |    | [u'CustomizeTabItem', 'TabItem5', u'Customize']
   |    | child_window(data-gt-translate-attributes='["title"]' title="Customize", control_type="TabItem")
   |
   | TitleBar - 'None'    (L712, T521, R1057, B549)
   | ['TitleBar', u'11']
   |    |
   |    | Menu - 'System'    (L696, T526, R718, B548)
   |    | [u'System0', u'System', u'System1', u'Menu', u'SystemMenu']
   |    | child_window(data-gt-translate-attributes='["title"]' title="System", auto_id="MenuBar", control_type="MenuBar")
   |    |    |
   |    |    | MenuItem - 'System'    (L696, T526, R718, B548)
   |    |    | [u'System2', u'MenuItem', u'SystemMenuItem']
   |    |    | child_window(data-gt-translate-attributes='["title"]' title="System", control_type="MenuItem")
   |    |
   |    | Button - 'Close'    (L1024, T519, R1058, B549)
   |    | [u'CloseButton', u'Close', 'Button5']
   |    | child_window(data-gt-translate-attributes='["title"]' title="Close", control_type="Button")

Në disa raste, printimi i të gjithë pemës mund të jetë i ngadalshëm (për shembull, në iTunes ka deri në tre mijë elementë në një skedë!), por mund të përdorni opsionin depth (thellesi): depth=1 - vetë elementi, depth=2 - vetëm fëmijët e menjëhershëm, e kështu me radhë. Mund të specifikohet gjithashtu në specifikimet gjatë krijimit child_window.

shembuj

Ne po rimbushim vazhdimisht listën e shembujve në depo. Ndër ato të fundit, vlen të përmendet automatizimi i analizuesit të rrjetit WireShark (ky është një shembull i mirë i një aplikacioni Qt5; megjithëse kjo detyrë mund të zgjidhet pa një GUI, sepse ekziston scapy.Sniffer nga paketa Python me gunga). Ekziston gjithashtu një shembull i automatizimit të MS Paint me shiritin e veglave Ribbon.

Një shembull tjetër i shkëlqyer i shkruar nga një student i imi: zvarritja e një skedari nga explorer.exe në faqen e Chrome për Google Drive (do të kalojë në depon kryesore pak më vonë).

Dhe, sigurisht, një shembull i abonimit në ngjarjet e tastierës (çelësat e nxehtë) dhe të miut:
hook_and_listen.py.

Mirënjohje

Falenderime të veçanta për ata që vazhdimisht ndihmojnë në zhvillimin e projektit. Për mua dhe Valentina Ky është një hobi i përhershëm. Dy nga studentët e mi nga UNN kohët e fundit mbrojtën diplomën e tyre bachelor në këtë temë. Alexander dha një kontribut të madh në mbështetjen e MS UI Automation dhe kohët e fundit filloi të krijojë një gjenerator automatik të kodit bazuar në parimin "record-playback" bazuar në vetitë e tekstit (ky është veçoria më komplekse), deri më tani vetëm për backend-in "uia". Ivan po zhvillon një backend të ri për Linux bazuar në AT-SPI (module mouse и keyboard bazuar python-xlib - tashmë në versionet 0.6.x).

Meqenëse kam dhënë një kurs të veçantë për automatizimin në Python për mjaft kohë, disa studentë master bëjnë detyrat e shtëpisë, duke zbatuar veçori të vogla ose shembuj të automatizimit. Disa gjëra kyçe në fazën e kërkimit u zbuluan gjithashtu dikur nga studentët. Edhe pse ndonjëherë ju duhet të monitoroni rreptësisht cilësinë e kodit. Për këtë ndihmojnë shumë analizuesit statikë (QuantifiedCode, Codacy dhe Landscape) dhe testet automatike në cloud (shërbimi AppVeyor) me mbulim kodi prej rreth 95%.

Gjithashtu falënderoj të gjithë ata që lënë komente, ngrenë gabime dhe dërgojnë kërkesa për tërheqje!

Burime Shtesë

Ne ndjekim pyetjet etiketë në StackOverflow (u shfaq së fundmi etiketë në versionin rus të SO) Dhe me fjalë kyçe në Toster... ka Bisedë në gjuhën ruse në Gitter.

Ne përditësojmë çdo muaj vlerësimi i bibliotekave me burim të hapur për testimin e GUI. Për sa i përket numrit të yjeve në GitHub, vetëm Autohotkey (ata kanë një komunitet shumë të madh dhe një histori të gjatë) dhe PyAutoGUI po rriten më shpejt (kryesisht për shkak të popullaritetit të librave nga autori i tij Al Sweigart: "Automate the Boring Stuff with Python” dhe të tjerë).

Burimi: www.habr.com

Bleni një host të besueshëm për faqet me mbrojtje DDoS, serverë VPS VDS 🔥 Bleni hosting të besueshëm të faqeve të internetit me mbrojtje DDoS, servera VPS VDS | ProHoster