Automatizoni GUI desktop me Python + pywinauto: si të bashkëpunoni me MS UI Automation

Biblioteka Python pywinauto — është një projekt open source për automatizimin e aplikacioneve GUI desktop në Windows. Në dy vitet e fundit, u shtuan funksionalitete të mëdha:

  • Mbështetje për teknologjinë MS UI Automation. Interface është i njëjtë, dhe tani mbështeten: WinForms, WPF, Qt5, Windows Store (UWP) dhe të tjera — pothuajse gjithçka që ndodhet në Windows.
  • Sistemi i backend-eve/plugin-eve (në këtë moment janë dy nën kapak: i parazgjedhur "win32" dhe i ri "uia"). Në vazhdim, lëvizim drejt multiplatformës.
  • Hook-et Win32 për miun dhe tastierën (çelësat e nxehtë në stilin e pyHook).

Po ashtu, do të bëjmë një përmbledhje të vogël të asaj që ekziston në open source për automatizimin desktop (pa pretenduar për krahasim serioz).

Ky artikull është pjesërisht një shkrim i fjalimit nga konferenca SQA Days 20 në Minsk (video; dhe slidet;), pjesërisht versioni në gjuhën ruse Udhëzuesi i fillestarëve për pywinauto.

Të fillojmë me një përmbledhje të shkurtër të opensource në këtë fushë. Për aplikacionet GUI në desktop është pak më e komplikuar se sa për web-in, i cili ka Selenium. Këtu janë qasjet kryesore:

Metoda e koordinatave

Hardkodojmë pikat e klikimeve, shpresojmë për goditje të suksesshme.
[+] Kros-platformë, e lehtë për t'u realizuar.
[+] E lehtë për të bërë "record-replay" regjistrimin e testeve.
[-] Më e paqëndrueshme ndaj ndryshimeve në rezolutën e ekranit, tema, fontet, përmasat e dritareve etj.
[-] Kërkohet një përpjekje e madhe për mbështetje, shpesh është më e lehtë të rinovohet testet nga e para ose të testohet manualisht.
[-] Automatizon vetëm veprimet, për verifikimin dhe nxjerrjen e të dhënave ka metoda të tjera.

Veglat (kros-platforme): autopy, PyAutoGUI, PyUserInput dhe shumë të tjera. Zakonisht, veglat më të komplikuara përfshijnë këtë funksionalitet (jo gjithmonë kros-platforme).

Është e rëndësishme të thuhet se metoda koordinative mund të plotësojë qasje të tjera. Për shembull, për grafikën e personalizuar, mund të klikoni mbi koordinata relative (nga maja e majtë e dritares/elementit, jo tërë ekranit) — zakonisht kjo është mjaft e besueshme, sidomos nëse merret parasysh gjatësia/gjerësia e tërë elementit (në atë rast, ndarja e ndryshme e ekranit nuk do të pengojë).

Një alternativë tjetër: të zgjedhim për teste vetëm një makinë me konfigurime stabile (nuk është kross-platform, por në disa raste është e pranueshme).

Njohja e imazheve referuese

[+] Kross-platform
[+-] Relativisht i besueshëm (më mirë se metoda koordinative), por përsëri kërkon shkathtësi.
[-+] Relativisht i ngadaltë, pasi kërkon burime CPU për algoritmet e njohjes.
[-] Në lidhje me njohjen e tekstit (OCR), për zakonisht nuk flitet => nuk mund të nxirren të dhënat tekstuale. Sa më është njohur, zgjidhjet ekzistuese të OCR nuk janë shumë të besueshme për këtë lloj detyrash, dhe nuk përdoren gjerësisht (mirë se vini të komentoni, nëse kjo tashmë nuk është ashtu).

Instrumentet: Sikuli, Lackey (e përputhshme me Sikuli, në Python të pastër), PyAutoGUI.

teknologjitë Accessibility

[+] Metoda më e besueshme, pasi lejon kërkimin përmes tekstit, pa marrë parasysh se si është vizatuar nga sistemi ose frameworku.
[+] Lejon shpëton të dhënat tekstuale => lehtëson verifikimin e rezultateve të testeve.
[+] Në përgjithësi, është më i shpejti, sepse pothuajse nuk shpenzon burime CPU.
[-] E vështirë të krijosh një instrument që funksionon në shumë piattaforma: të gjitha bibliotekat open-source mbështesin një deri në dy teknologji aksesueshmërie. Askush nuk mbështet plotësisht Windows/Linux/MacOS, përveç atyre me pagesë si TestComplete, UFT ose Squish.
[-] Një teknologji e tillë nuk është gjithmonë e disponueshme. Për shembull, testimi i ekranit të ngarkesës brenda VirtualBox-it — këtu nuk mund të shmangesh nga njohja e imazheve. Por në shumë raste klasike, qasja e aksesueshmërisë gjithsesi është e aplikueshme. Ky është subjekti i diskutimit më poshtë.

Instrumentet: TestStack.White në C#, Winium.Desktop në C# (i kompatibil në Selenium), MS WinAppDriver në C# (i kompatibil në Appium), pywinauto, pyatom (i kompatibil me LDTP), Python-UIAutomation-for-Windows, RAutomation në Ruby, LDTP (Projekti i Testimit të Desktopit Linux) dhe versioni i tij për Windows Cobra.

LDTP — ndoshta është instrumenti i vetëm open-source në shumë plataforma (më saktësisht një familje bibliotekash) i bazuar në teknologjitë e aksesueshmërisë. Sidoqoftë, ai nuk është shumë i njohur. Nuk e kam përdorur, por sipas komenteve, ndërfaqja e tij nuk është më e lehta për t'u përdorur. Nëse ka mendime pozitive, do të ishte mirë të ndaheshin në komentet.

Backdoor testues (i njohur si biçikleta e brendshme)

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

Teknologjitë kryesore të aksesit desktop

API i vjetër Win32

Shumica e aplikacioneve Windows, të shkruara para daljes së WPF dhe më pas Windows Store, janë ndërtuar siç do të thuash mbi Win32 API. Konkretisht, MFC, WTL, C++ Builder, Delphi, VB6 — të gjitha këto mjete përdorin Win32 API. Edhe Windows Forms — në masë të madhe është kompatibile me Win32 API.

Instrumentet: AutoIt (përafërsisht si VB) dhe mbështetje për Python pyautoit, AutoHotkey (gjuhë e veçantë, ka ndërfaqen IDispatch COM), pywinauto (Python), RAutomation (Ruby), win32-autogui (Ruby).

Microsoft UI Automation

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

Faktikisht, kjo është një grup ndërfaqesh custom COM (në masë të madhe, UIAutomationCore.dll), si dhe ka një mbështetje .NET në formën e namespace System.Windows.Automation. Ajo, përveç kësaj, ka një defekt të integruar, për shkak të së cilit disa elemente UI mund të përjashtohen. Prandaj, është më mirë të përdorni drejtpërdrejt UIAutomationCore.dll (nëse keni dëgjuar për UiaComWrapper në C#, kjo është ajo).

Llojet e ndërfaqeve COM:

(1) IUknown bazik — «burimi i të gjitha të këqijave». Ndërfaqja më e ulët, aspak e përdoruesit.
(2) IDispatch dhe të afërmit (për shembull, Excel.Application), të cilat mund të përdoren në Python me paketën win32com.client (pjesë e pyWin32). Opsioni më i përshtatshëm dhe më i bukur.
(3) Ndërfaqet e personalizuara, me të cilat punon paketa e jashtme Python comtypes.

Instrumentet: TestStack.White në C#, pywinauto 0.6.0+, Winium.Desktop në C#, Python-UIAutomation-for-Windows (kodi burimor i mbulesave C mbi UIAutomationCore.dll nuk është zbuluar), RAutomation në Ruby.

AT-SPI

Pavarësisht nga fakti se pothuajse të gjitha sistemet Linux janë ndërtuar mbi X Window System (në Fedora 25 «x» u zëvendësua me Wayland), «x» lejon vetëm operimin e dritareve të nivelit të lartë dhe miut/tastierës. Për analizimin e detajuar të butonave, lista e kutive dhe kështu me radhë — ekziston teknologjia AT-SPI. Menaxherët e dritareve më të njohur kanë një demon të quajtur AT-SPI registry, i cili siguron një GUI automatizues për aplikacionet (të paktën janë mbështetur Qt dhe GTK).

Instrumentet: pyatspi2.

pyatspi2, sipas mendimit tim, ka shumë varësi si PyGObject. Teknologjia e vet është e disponueshme si një bibliotekë dinamike konvencionale libatspi.so. Ajo është në dispozicion Manual Referencë. Për bibliotekën pywinauto planifikojmë të implementojmë mbështetje për AT-SPI në këtë mënyrë: përmes ngarkimit të libatspi.so dhe modulit ctypes. Ka një problem të vogël vetëm me përdorimin e versionit të duhur, sepse për aplikacionet GTK+ dhe Qt ato janë pak më ndryshe. Pritet që lëshimi i mundshëm i pywinauto 0.7.0 me mbështetje të plotë për Linux të ndodhi në gjysmën e parë të vitit 2018.

Apple Accessibility API

Në MacOS ka një gjuhë automatizimi të quajtur AppleScript. Për të realizuar diçka të ngjashme në Python, natyrisht, duhet të përdoren funksione nga ObjectiveC. Duke filluar, duket, që nga MacOS 10.6, paketa pyobjc përfshihet në Python e parakaluar. Kjo gjithashtu do ta lehtësojë listën e varësive për mbështetje të ardhshme në pywinauto.

Mjetet: Përveç gjuhës Apple Script, duhet të kushtoni vëmendje për ATOMac, i njohur gjithashtu si pyatom. Ai është kompatibël me ndërfaqen LDTP, por gjithashtu është një bibliotekë e pavarur. Në të ka një shembull automatizimi iTunes në macOs, i shkruar nga studenti im. Ka një problem të njohur: koha fleksibël (metodat waitFor*) nuk funksionon. Por, në përgjithësi, është një gjë e mirë.

Si të filloni të punoni me pywinauto

Së pari, është e rëndësishme të armoheni me një inspektor GUI objekte (ajo që quhet Spy tool). Ai do t'ju ndihmojë të studioni aplikacionin nga brenda: si është e strukturuar hierarkia e elementeve, çfarë pronash janë në dispozicion. Inspektorët më të njohur të objekteve janë:

  • Spy++ — vjen me Visual Studio, duke përfshirë edicionet Express ose Community. Përdor API-në Win32. Një variant i njohur i tij është AutoIt Window Info.
  • Inspect.exe — vjen me Windows SDK. Nëse e keni instaluar, mund ta gjeni në Windows 64-bit në katalogun C:Program Files (x86)Windows Kitsbinx64. Në vetë inspektorin, duhet të zgjidhni modin UI Automation në vend të MS AA (Active Accessibility, paraardhësi i UI Automation).

Pasi të kemi ndriçuar aplikacionin nga brenda, zgjedhim backend-in që do të përdorim. Mjafton të jepni emrin e backend-it kur krijoni objektin Application.

  • backend=»win32″ — për momentin përdoret si parazgjedhje, punon mirë me MFC, WTL, VB6 dhe aplikacione të tjera legacys.
  • backend=»uia» — backend i ri për MS UI Automation: funksionon perfekt me WPF dhe WinForms; gjithashtu është i shkëlqyer për aplikacionet Delphi dhe Windows Store; punon me Qt5 dhe disa aplikacione Java. Në përfundim, nëse Inspect.exe sheh elementët dhe atributet e tyre, atëherë ky backend është i përshtatshëm. Në përgjithësi, shumica e shfletuesve gjithashtu mbështesin UI Automation (Mozilla si standard, ndërsa për Chromium duhet dhënë një çelës komandë gjatë nisjes --force-renderer-accessibility, për të parë elementët në faqet e internetit në Inspect.exe). Natyrisht, konkurenca me Selenium në këtë fushë nuk është e mundur. Thjesht një mënyrë tjetër për të punuar me shfletuesin (mund të jetë e dobishme për skenarë ndërprodukt).

Pikat e hyrjes për automatizimin

Aplikacioni është mjaft i studiuar. Është koha të krijoni një objekt Application dhe ta nisni atë ose të lidheni me atë që është tashmë aktiv. Kjo nuk është thjesht një kopje e klasës standarde subprocess.Popen, por një objekt hyrës, që kufizon të gjitha veprimet tuaja brenda kufijve të procesit. Kjo është shumë e dobishme, nëse ka disa instanca të aplikacionit të aktivizuar, dhe nuk dëshirohet të preken të tjerat.

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

# Të përshkruajmë dritaren që duam të gjejmë në procesin Notepad.exe
dlg_spec = app.UntitledNotepad
# presim derisa dritarja të shfaqet realisht
actionable_dlg = dlg_spec.wait('visible')

Nëse dëshironi të menaxhoni disa aplikacione në të njëjtën kohë, klasa Desktop. Për shembull, në kalkulatorin në Win10, hierarkia e elementeve është e shpërndarë në disa procese (jo vetëm calc.exe). Prandaj, pa objektin Desktop nuk do të bëhet.

nga subprocess import Popen
nga pywinauto import Desktop

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

Objekti rrënjësor (Application ose Desktop) — kjo është e vetmja vend ku duhet të përdorni b backend-in. Çdo gjë tjetër ndryshe i përshtatet transparencës në konceptin "specifikim->reproducim", për të cilin do të flasim më vonë.

Specifikimet e dritareve/elementeve

Kjo është koncepti kryesor mbi të cilin ndodhet ndërfaqja pywinauto. Ju mund të përshkruani dritaren/elementin përafërsisht ose më në detaje, edhe nëse ajo ende nuk ekziston ose është mbyllur tashmë. Specifikimi i dritares (objekti WindowSpecification) ruan kriteret sipas të cilave duhet të kërkoni dritaren ose elementin real.

Shembulli i specifikimit të detajuar të dritares:

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

>>> dlg_spec


>>> dlg_spec.wrapper_object()

Kërkimi i dritares ndodh duke thirrur metodën .wrapper_object(). Ajo kthen një "vrapper" për dritaren/realelementin ose hedh ElementNotFoundError (ndonjëherë ElementAmbiguousError, nëse gjenden disa elemente, domethënë është e nevojshme të saktësohet kriteri i kërkimit). Ky "vrapper" tashmë di të bëjë disa veprime me elementin 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. Rekomandojmë ta përdorni vetëm për debugging. Dy rrjeshtat e mëposhtme bëjnë të njëjtën gjë:

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

Ka shumë kritere kërkimi për specifikimin e dritares. Ja 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')

Lista e të gjithë kritereve të mundshme është në dokumentat e funksionit pywinauto.findwindows.find_elements(…).

Magjia e qasjes përmes atributit dhe çelësit

Python e thjeshton krijimin e specifikimeve të dritares dhe njeh atributet e objektit dinamikisht (brenda është tejkaluar metoda __getattribute__). Sigurisht, emri i atributeve ka të njëjtat kufizime si çdo emër variabli (nuk lejohet të vendosen hapësira, presje dhe simbole të tjera speciale). Për fat të mirë, pywinauto përdor algoritmin e njohur si «best match», i cili është i qëndrueshëm ndaj gabimeve të shkrimit dhe variacioneve të vogla.

app.UntitledNotepad
# e njëjta gjë si
app.window(best_match='UntitledNotepad')

Nëse janë të nevojshme vargje Unicode (për shembull, për gjuhën ruse), hapësirat etj., mund të bëni qasje me çelës (ashtu siç do të ishte një fjalor i zakonshëm):

app['Untitled - Notepad']
# e njëjta gjë si
app.window(best_match='Untitled - Notepad')

Pesë rregulla për emrat magjikë

Si të kuptojmë emrat magjikë referencë? Ato, të cilat i atribuohen elementit para kërkimit. Nëse keni specifikuar një emër që është mjaft i ngjashëm me referencën, atëherë elementi do të gjendet.

  1. Sipërmarrja (teksti, emri): app.Properties.OK.click()
  2. Sipërmarrja sipas tekstit dhe llojit të elementit: app.Properties.OKButton.click()
  3. Sipërmarrja sipas llojit dhe numrit: app.Properties.Button3.click() (emrat Button0 dhe Button1 janë të lidhur me elementin e parë të gjendur, Button2 — me të dytin, dhe më pas sipas rendit — kështu është formuar historikisht)
  4. Sipërmarrja sipas tekstit statik (në majë ose në anë) dhe sipas llojit: app.OpenDialog.FileNameEdit.set_text("") (e dobishme për elementet me tekst dinamik)
  5. Sipërmarrja sipas llojit dhe tekstit brenda: app.Properties.TabControlSharing.select("General")

Zakonisht, dy-tre rregulla përdoren njëkohësisht, rrallë më shumë. Për të kontrolluar se cilat emra konkretë janë të disponueshëm për secilin element, mund të përdorim metodën print_control_identifiers(). Ai mund të printojë pemën e elementeve si në ekran, ashtu edhe në një skedar. Për secilin element printohen emrat e tij referues. Po ashtu, mund të kopjoni nga aty specifikimet më të detajuara të elementeve fëmijë. Rezultati në skript do të duket kështu:

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

E gjithë pema e elementeve — zakonisht është një sasi e madhe informacioni.

>>> app.Properties.print_control_identifiers()

Identifikuesit e Kontrollit:

Dialog - 'Pronësitë e Windows NT'    (L688, T518, R1065, B1006)
[u'Pronësitë e Windows NT', u'Dialog', u'Pronësitë e Windows NT']
child_window(title="Pronësitë e Windows NT", control_type="Window")
   |
   | Imazh - ''    (L717, T589, R749, B622)
   | [u'', u'0', u'Imazh1', u'Imazh0', 'Imazh', u'1']
   | child_window(auto_id="13057", control_type="Image")
   |
   | Imazh - ''    (L717, T630, R1035, B632)
   | ['Imazh2', u'2']
   | child_window(auto_id="13095", control_type="Image")
   |
   | Edit - 'Emri i dosjes:'    (L790, T596, R1036, B619)
   | [u'3', 'Edit', u'Edit1', u'Edit0']
   | child_window(title="Emri i dosjes:", auto_id="13156", control_type="Edit")
   |
   | Statik - 'Lloji:'    (L717, T643, R780, B658)
   | [u'Lloji:Statik', u'Statik', u'Statik1', u'Statik0', u'Lloji:']
   | child_window(title="Lloji:", auto_id="13080", control_type="Text")
   |
   | Edit - 'Lloji:'    (L790, T643, R1036, B666)
   | [u'4', 'Edit2', u'Lloji:Edit']
   | child_window(title="Lloji:", auto_id="13059", control_type="Edit")
   |
   | Statik - 'Vendndodhja:'    (L717, T669, R780, B684)
   | [u'Vendndodhja:Statik', u'Vendndodhja:', u'Statik2']
   | child_window(title="Vendndodhja:", auto_id="13089", control_type="Text")
   |
   | Edit - 'Vendndodhja:'    (L790, T669, R1036, B692)
   | ['Edit3', u'Vendndodhja:Edit', u'5']
   | child_window(title="Vendndodhja:", auto_id="13065", control_type="Edit")
   |
   | Statik - 'Madhësia:'    (L717, T695, R780, B710)
   | [u'Madhësia:Statik', u'Madhësia:', u'Statik3']
   | child_window(title="Madhësia:", auto_id="13081", control_type="Text")
   |
   | Edit - 'Madhësia:'    (L790, T695, R1036, B718)
   | ['Edit4', u'6', u'Madhësia:Edit']
   | child_window(title="Madhësia:", auto_id="13064", control_type="Edit")
   |
   | Statik - 'Madhësia në disk:'    (L717, T721, R780, B736)
   | [u'Madhësia në disk:', u'Madhësia në disk:Statik', u'Statik4']
   | child_window(title="Madhësia në disk:", auto_id="13107", control_type="Text")
   |
   | Edit - 'Madhësia në disk:'    (L790, T721, R1036, B744)
   | ['Edit5', u'7', u'Madhësia në disk:Edit']
   | child_window(title="Madhësia në disk:", auto_id="13106", control_type="Edit")
   |
   | Statik - 'Përmban:'    (L717, T747, R780, B762)
   | [u'Përmban:1', u'Përmban:0', u'Përmban:Statik', u'Statik5', u'Përmban:']
   | child_window(title="Përmban:", auto_id="13088", control_type="Text")
   |
   | Edit - 'Përmban:'    (L790, T747, R1036, B770)
   | [u'8', 'Edit6', u'Përmban:Edit']
   | child_window(title="Përmban:", auto_id="13087", control_type="Edit")
   |
   | Imazh - 'Përmban:'    (L717, T773, R1035, B775)
   | [u'Përmban:Imazh', 'Imazh3', u'Përmban:2']
   | child_window(title="Përmban:", auto_id="13096", control_type="Image")
   |
   | Statik - 'Krijuar:'    (L717, T786, R780, B801)
   | [u'Krijuar:', u'Krijuar:Statik', u'Statik6', u'Krijuar:1', u'Krijuar:0']
   | child_window(title="Krijuar:", auto_id="13092", control_type="Text")
   |
   | Edit - 'Krijuar:'    (L790, T786, R1036, B809)
   | [u'Krijuar:Edit', 'Edit7', u'9']
   | child_window(title="Krijuar:", auto_id="13072", control_type="Edit")
   |
   | Imazh - 'Krijuar:'    (L717, T812, R1035, B814)
   | [u'Krijuar:Imazh', 'Imazh4', u'Krijuar:2']
   | child_window(title="Krijuar:", auto_id="13097", control_type="Image")
   |
   | Statik - 'Atributet:'    (L717, T825, R780, B840)
   | [u'Atributet:Statik', u'Statik7', u'Atributet:']
   | child_window(title="Atributet:", auto_id="13091", control_type="Text")
   |
   | Kontrolli - 'Vetëm për lexim (Përveç skedarëve në dosje)'    (L790, T825, R1035, B841)
   | [u'Kontrolli0', u'Kontrolli1', 'Kontrolli', u'Vetëm për lexim (Përveç skedarëve në dosje)Kontrolli', u'Vetëm për lexim (Përveç skedarëve në dosje)']
   | child_window(title="Vetëm për lexim (Përveç skedarëve në dosje)", auto_id="13075", control_type="CheckBox")
   |
   | Kontrolli - 'I fshehur'    (L790, T848, R865, B864)
   | ['Kontrolli2', u'I fshehurKontrolli', u'I fshehur']
   | child_window(title="I fshehur", auto_id="13076", control_type="CheckBox")
   |
   | Butoni - 'Avancuar...'    (L930, T845, R1035, B868)
   | [u'Avancuar...', u'Avancuar...Butoni', 'Buton', u'Buton1', u'Buton0']
   | child_window(title="Avancuar...", auto_id="13154", control_type="Button")
   |
   | Butoni - 'OK'    (L814, T968, R889, B991)
   | ['Buton2', u'OK', u'OKButoni']
   | child_window(title="OK", auto_id="1", control_type="Button")
   |
   | Butoni - 'Anulo'    (L895, T968, R970, B991)
   | ['Buton3', u'AnuloButoni', u'Anulo']
   | child_window(title="Anulo", auto_id="2", control_type="Button")
   |
   | Butoni - 'Apliko'    (L976, T968, R1051, B991)
   | ['Buton4', u'AplikoButoni', u'Apliko']
   | child_window(title="Apliko", auto_id="12321", control_type="Button")
   |
   | TabControl - ''    (L702, T556, R1051, B962)
   | [u'10', u'TabControlSharing', u'TabControlVersionet e mëparshme', u'TabControlSiguria', u'TabControl', u'TabControlPersonalizo']
   | child_window(auto_id="12320", control_type="Tab")
   |    |
   |    | TabItem - 'General'    (L704, T558, R753, B576)
   |    | [u'TabItemGeneral', 'TabItem', u'General', u'TabItem0', u'TabItem1']
   |    | child_window(title="General", control_type="TabItem")
   |    |
   |    | TabItem - 'Sharing'    (L753, T558, R801, B576)
   |    | [u'Sharing', u'SharingTabItem', 'TabItem2']
   |    | child_window(title="Sharing", control_type="TabItem")
   |    |
   |    | TabItem - 'Siguria'    (L801, T558, R851, B576)
   |    | [u'Siguria', 'TabItem3', u'SiguriaTabItem']
   |    | child_window(title="Siguria", control_type="TabItem")
   |    |
   |    | TabItem - 'Versionet e mëparshme'    (L851, T558, R947, B576)
   |    | [u'Vendndodhjet e mëparshmeTabItem', u'Vendndodhjet e mëparshme', 'TabItem4']
   |    | child_window(title="Versionet e mëparshme", control_type="TabItem")
   |    |
   |    | TabItem - 'Personalizo'    (L947, T558, R1007, B576)
   |    | [u'PersonalizimiTabItem', 'TabItem5', u'Personalizo']
   |    | child_window(title="Personalizo", control_type="TabItem")
   |
   | TitleBar - 'Askush'    (L712, T521, R1057, B549)
   | ['TitleBar', u'11']
   |    |
   |    | Menu - 'Sistemi'    (L696, T526, R718, B548)
   |    | [u'Sistemi0', u'Sistem', u'Sistemi1', u'Menu', u'MenuSistemi']
   |    | child_window(title="Sistemi", auto_id="MenuBar", control_type="MenuBar")
   |    |    |
   |    |    | MenuItem - 'Sistemi'    (L696, T526, R718, B548)
   |    |    | [u'Sistemi2', u'MenuItem', u'MenuSistemItem']
   |    |    | child_window(title="Sistemi", control_type="MenuItem")
   |    |
   |    | Butoni - 'Mbyll'    (L1024, T519, R1058, B549)
   |    | [u'MbyllButoni', u'Mbyll', 'Buton5']
   |    | child_window(title="Mbyll", control_type="Button")

Në disa raste, printimi i gjithë pemës mund të ngadalësohet (p.sh., në iTunes në një skedë ka deri në tri mijë elemente!), por mund të përdorni parametrin depth (thellësia): depth=1 — vetë elementi, depth=2 — vetëm fëmijët e drejtpërdrejtë, dhe kështu me radhë. Të njëjtin parameter mund ta tregoni në specifikimet gjatë krijimit të child_window.

Shembuj

Ne vazhdimisht po e pasurojmë listën e shembujve në repositorin. Nga të rejat, vlen të theksohet automatizimi i analistëve të rrjetit WireShark (ky është një shembull i mirë i aplikacionit Qt5; megjithatë, kjo detyrë mund të kryhet edhe pa GUI, sepse ka scapy.Sniffer nga paketa Python scapy). Gjithashtu, ka një shembull të automatizimit të MS Paint me ribonin e tij.

Një tjetër shembull i shkëlqyer, shkruar nga studenti im: përplasja e një skedari nga explorer.exe në faqen e Chrome për Google Drive (ai do të kalojë në repositorin kryesor pak më vonë).

Dhe, natyrisht, një shembull i abonimit në ngjarjet e tastierës (çelësa të nxehtë) dhe mausit:
hook_and_listen.py.

Faleminderit

Faleminderit të veçantë — atyre që vazhdimisht ndihmojnë në zhvillimin e projektit. Për mua dhe Valentina është një hobie i vazhdueshëm. Dy studentët e mi nga NNGA sapo mbrojtën diplomën e bachelor në këtë temë. Aleksandër ka kontribuar shumë në mbështetje të MS UI Automation dhe së fundmi ka filluar të zhvillojë një gjenerues kodi automatizuar sipas parimit "regjistrim-riprodhim" të bazuar në pronat tekstuale (kjo është karakteristika më e komplikuar), për momentin vetëm për backend-in "uia". Ivan po zhvillon një backend të ri për Linux mbi AT-SPI (modulet mouse dhe keyboard në bazë të python-xlib — tashmë në lëshimet 0.6.x).

Përqërua se unë kam lexuar një kurs special mbi automatizimin me Python për një periudhë relativisht të gjatë, disa nga studentët e magjistraturës krejnë detyra shtëpie, duke realizuar karakteristika të vogla ose shembuj automatizimi. Disa gjëra kyçe gjatë fazës së kërkimit gjithashtu janë zbuluar nga studentët. Megjithatë, ndonjëherë është e nevojshme të kemi kujdes të madh për cilësinë e kodit. Këtu ndihmojnë shumë analizatorët statikë (QuantifiedCode, Codacy dhe Landscape) dhe testet automatike në cloud (shërbimi AppVeyor) me mbulimin e kodit rreth 95%.

Gjithashtu faleminderit të gjithëve që lënë komente, hapën bugs dhe dërgojnë kërkesa për bashkëngjitje!

Burime shtesë

Ne ndjekim pyetjet sipas tags në StackOverflow (recently appeared tag në versionin rus të SO) dhe për fjalë kyçe në Toster. Ekziston një chat në gjuhën ruse në Gitter’ë.

Çdo muaj përditësojmë rendit open-source i bibliotekave për testimin GUI. Vetëm Autohotkey (me një komunitet shumë të madh dhe një histori të gjatë) dhe PyAutoGUI (në pjesë të madhe falë popullaritetit të librave të autorit të saj Al Sweigart: «Automatizoni Gjërat e Mërzitshme me Python» dhe të tjerëve) rriten më shpejt në numrin e yjeve në GitHub.

Burimi: habr.com

Bli një hosting të besueshëm për faqet me mbrojtje DDoS, VPS VDS serverë 🔥 Bli një hosting të besueshëm për faqet me mbrojtje DDoS, VPS VDS serverë | ProHoster