Python-bibliotek — er et open source-projekt til automatisering af desktop GUI-applikationer på Windows. De seneste to år har der været store nye funktioner:
- Understøttelse af MS UI Automation-teknologi. Den tidligere grænseflade er bevaret, og nu understøttes: WinForms, WPF, Qt5, Windows Store (UWP) og meget mere — næsten alt, hvad der findes på Windows.
- Backend/plugin-system (der er i øjeblikket to under overfladen: den standardmæssige
"win32"og den nye"uia"). Vi bevæger os gradvist mod tværplatformsmuligheder. - Win32 hooks til mus og tastatur (hotkeys i stil med pyHook).
Vi vil også give en kort oversigt over, hvad der findes i open source til desktopautomatisering (uden krav om seriøs sammenligning).
Denne artikel er delvist en afkodning af en præsentation fra SQA Days 20-konferencen i Minsk ( og ), delvist den russiske version til pywinauto.
- Primære tilgange
- Primære desktop-tilgængelighedsteknologier
Lad os begynde med et kort overblik over open source inden for dette område. For desktop GUI-applikationer er det lidt mere kompliceret end for web, som har Selenium. Her er de primære tilgange:
Koordinatmetode
Hård kodning af klikpunkter, håber på heldige rammer.
[+] Tværplatform, nem at implementere.
[+] Let at lave 'record-replay' testoptagelse.
[-] Mest ustabil i forhold til ændringer i skærmopløsning, temaer, skrifttyper, vinduesstørrelser osv.
[-] Kræver enormt arbejde til vedligeholdelse, ofte lettere at generere tests fra bunden igen eller teste manuelt.
[-] Automatiserer kun handlinger; der er andre metoder til verifikation og datadetektering.
Værktøjer (tværplatform): , , og mange andre. Som regel inkluderer mere komplekse værktøjer denne funktionalitet (ikke altid tværplatform).
Det skal nævnes, at koordinatmetoden kan supplere de øvrige tilgange. For eksempel kan man klikke på relative koordinater for brugerdefineret grafik (fra det øverste venstre hjørne af vinduet/elementet i stedet for hele skærmen) — dette er typisk ret stabilt, især når man tager hensyn til elementets længde/bredde (så påvirker selv forskellige skærmopløsninger ikke resultatet).
En anden mulighed er at dedikere kun én maskine med stabile indstillinger til testene (ikke cross-platform, men i nogle tilfælde fungerer det).
Genkendelse af referencebilleder
[+] Cross-platform
[+-] Relativt pålidelig (bedre end koordinatmetoden), men kræver stadig nogle tricks.
[-+] Relativt langsom, da den kræver CPU-ressourcer til genkendelsesalgoritmer.
[-] Om tekstgenkendelse (OCR) tales der som regel ikke => tekstdata kan ikke hentes. Så vidt jeg ved, er de eksisterende OCR-løsninger ikke særlig pålidelige til denne type opgaver og anvendes ikke bredt (kommentarer er velkomne, hvis det ikke længere er sådan).
Værktøjer: , (Sikuli-kompatibel, skrevet i ren Python), .
Tilgængelighedsteknologier
[+] Den mest pålidelige metode, da den tillader søgning efter tekst, uanset hvordan den er tegnet af systemet eller rammeværket.
[+] Gør det muligt at udtrække tekstdata => lettere at verificere testresultater.
[+] Som regel den hurtigste, da den næsten ikke bruger CPU-ressourcer.
[-] Det er svært at lave et cross-platform værktøj: absolut alle open-source biblioteker understøtter en eller to tilgængelighedsteknologier. Windows/Linux/MacOS understøttes slet ikke af nogen, undtagen betalte som TestComplete, UFT eller Squish.
[-] Denne teknologi er ikke altid tilgængelig i princippet. For eksempel, test af boot-skræmmen inden i VirtualBox – her kan man ikke undgå billedgenkendelse. Men i mange klassiske tilfælde er tilgang til tilgængelighed stadig anvendelig. Det vil også blive omtalt videre.
Værktøjer: skrevet i C#, skrevet i C# (Selenium-kompatibel), skrevet i C# (Appium-kompatibel), , (kompatibel med LDTP), , skrevet i Ruby, (Linux Desktop Testing Project) og dens Windows-version .
LDTP er måske det eneste cross-platform open-source værktøj (mere præcist et bibliotekssystem) baseret på tilgængelighedsteknologier. Dog er det ikke så populært. Jeg har ikke selv brugt det, men ifølge anmeldelser er dets grænseflade ikke den mest brugervenlige. Hvis der er positive anmeldelser, så del dem gerne i kommentarerne.
Test-backdoor (a.k.a. intern cykel)
Til cross-platform applikationer laver udviklerne ofte en intern mekanisme for at sikre testbarhed. For eksempel opretter de en hjælpende TCP-server i applikationen, som testene forbinder til og sender tekstkommander: hvad der skal trykkes på, hvor data skal hentes osv. Pålideligt, men ikke universelt.
Primære desktop-tilgængelighedsteknologier
Den gode gamle Win32 API
De fleste Windows-applikationer, der er skrevet før WPF og derefter Windows Store, er i en eller anden grad bygget på Win32 API. Det vil sige, MFC, WTL, C++ Builder, Delphi, VB6 — alle disse værktøjer bruger Win32 API. Selv Windows Forms er i vid udstrækning kompatible med Win32 API.
Værktøjer: (ligner VB) og Python wrapper , (eget sprog, har IDispatch COM interface), (Python), (Ruby), (Ruby).
Microsoft UI Automation
Den største fordel: MS UI Automation-teknologien understøtter langt de fleste GUI-applikationer på Windows med få undtagelser. Problemet: det er ikke meget lettere at lære end Win32 API. Ellers ville ingen lave wrappers over den.
Det er faktisk et sæt af custom COM interfaces (primært, UIAutomationCore.dll), samt har en .NET wrapper i form af namespace System.Windows.Automation. Den har i øvrigt en indført fejl, som kan gøre, at nogle UI-elementer kan blive overset. Derfor er det bedre at bruge UIAutomationCore.dll direkte (hvis du har hørt om UiaComWrapper på C#, så er det det).
Varianter af COM interfaces:
(1) Basal IUknown — "the root of all evil". Det laveste niveau, slet ikke brugervenligt.
(2) IDispatch og afledte (for eksempel, Excel.Application), som kan bruges i Python med pakken win32com.client (som er en del af pyWin32). Den mest bekvemme og pæne løsning.
(3) Custom interfaces, som et tredjeparts Python-pakke .
Værktøjer: skrevet i C#, 0.6.0+, skrevet i C#, (de har ikke afsløret kildekoden af C wrappers over UIAutomationCore.dll), til Ruby.
AT-SPI
Selvom næsten alle Linux-distributioner er baseret på X Window System (i Fedora 25 er "x" blevet ændret til Wayland), tillader "x" kun at operere med top-level vinduer og mus/tastatur. For detaljeret analyse af knapper, listebokse osv. findes teknologien AT-SPI. De mest populære vinduesmanagers har en såkaldt AT-SPI registry daemon, som giver applikationer et automatiserbart GUI (minimalt understøttes Qt og GTK).
Værktøjer: .
pyatspi2, efter min mening, indeholder for mange afhængigheder som den samme PyGObject. Selve teknologien er tilgængelig som et almindeligt dynamisk bibliotek libatspi.so. Der er adgang til den . For biblioteket pywinauto planlægger vi at implementere AT-SPI støtte netop sådan: gennem indlæsning af libatspi.so og ctypes-modulet. Der er kun et lille problem med at bruge den nødvendige version, da de er lidt forskellige for GTK+ og Qt-applikationer. En sandsynlig udgivelse af pywinauto 0.7.0 med fuld Linux-support kan forventes i første halvdel af 2018.
Apple Accessibility API
På MacOS findes der et eget automatiseringssprog kaldet AppleScript. For at implementere noget lignende i Python skal man selvfølgelig bruge funktioner fra ObjectiveC. Siden MacOS 10.6 er pakken pyobjc inkluderet i den præinstallerede Python. Dette vil også lette liste over afhængigheder for fremtidig support i pywinauto.
Værktøjer: Udover Apple Script-sproget er det værd at lægge mærke til , også kendt som pyatom. Den er kompatibel med LDTP, men er også et selvstændigt bibliotek. Der findes , skrevet af min studerende. Der er et kendt problem: fleksible timing virker ikke (metoderne waitFor*). Men generelt er det en god løsning.
Hvordan man kommer i gang med pywinauto
Først og fremmest skal man have en GUI-inspektør til objekter (det, der kaldes Spy tool). Den hjælper med at undersøge applikationen indefra: hvordan elementhierarkiet er opbygget, og hvilke egenskaber der er tilgængelige. De mest kendte objektsinspektører er:
- Spy++ — en del af Visual Studio, inklusive Express eller Community Edition. Bruger Win32 API. Også kendt er dens klon AutoIt Window Info.
- Inspect.exe — en del af Windows SDK. Hvis det er installeret, kan man finde det i mappen
C:Program Files (x86)Windows Kitsbinx64. I selve inspektøren skal man vælge tilstanden UI Automation i stedet for MS AA (Active Accessibility, forgængeren til UI Automation).
Når applikationen er blevet gennemgået, vælger man backend, som man vil bruge. Det er tilstrækkeligt at angive backend-navnet, når man opretter objektet Application.
- backend=»win32″ — bruges som standard, fungerer godt med MFC, WTL, VB6 og andre legacy-applikationer.
- backend=»uia» — en ny backend til MS UI Automation: fungerer perfekt med WPF og WinForms; er også god til Delphi og Windows Store-applikationer; arbejder med Qt5 og nogle Java-applikationer. Generelt gælder det, at hvis Inspect.exe kan se elementer og deres egenskaber, så er denne backend passende. Stort set alle browsere understøtter også UI Automation (Mozilla som standard, og Chrome skal have sat en kommandolinjeparameter,
--force-renderer-accessibility, for at se elementerne på siderne i Inspect.exe). Selvfølgelig er der næppe mulighed for konkurrence med Selenium på dette område. Det er blot endnu en måde at arbejde med browseren på (kan være nyttig i tværgående scenarier).
Indgangspunkter for automatisering
Applikationen er tilstrækkeligt undersøgt. Det er tid til at oprette objektet Application og starte det eller tilslutte sig et allerede kørende. Det er ikke blot en klon af standardklassen subprocess.Popen, nemlig det indledende objekt, der begrænser alle dine handlinger inden for procesgrænserne. Dette er meget nyttigt, hvis flere instanser af applikationen kører, og du ikke ønsker at berøre de andre.
from pywinauto.application import Application
app = Application(backend="uia").start('notepad.exe')
# Beskriv vinduet, som vi ønsker at finde i processen Notepad.exe
dlg_spec = app.UntitledNotepad
# vent på at vinduet rent faktisk vises
actionable_dlg = dlg_spec.wait('visible')Hvis du ønsker at styre flere applikationer samtidig, vil klassen hjælpe dig. Desktop. For eksempel er hierarkiet af elementer i lommeregneren på Win10 spredt over flere processer (ikke kun calc.exe). Så uden objektet Desktop kan man ikke komme udenom.
from subprocess import Popen
from pywinauto import Desktop
Popen('calc.exe', shell=True)
dlg = Desktop(backend="uia").Calculator
dlg.wait('visible')Rodobjektet (Applikation eller Desktop) — det er det eneste sted, hvor backend skal angives. Alt andet falder gennemsigtigt ind i koncepterne 'specifikation->wrapper', som vi senere vil se nærmere på.
Specifikationer for vinduer/elementer
Dette er den grundlæggende koncept, som pywinautos interface bygger på. Du kan beskrive vinduet/elementet omtrentligt eller mere detaljeret, selv om det endnu ikke eksisterer eller allerede er lukket. Vinduesspecifikationen (objekt WindowSpecification) gemmer kriterierne for, hvordan man skal finde det egentlige vindue eller element.
Eksempel på en detaljeret vinduesspecifikation:
>>> dlg_spec = app.window(title='Untitled - Notepad')
>>> dlg_spec
>>> dlg_spec.wrapper_object()Selve søgningen efter vinduet finder sted ved at kalde metoden .wrapper_object(). Den returnerer en slags "wrapper" for det egentlige vindue/element eller kaster ElementNotFoundError (nogle gange ElementAmbiguousError, hvis flere elementer er fundet, så det kræves at præcisere søgekriteriet). Denne "wrapper" kan allerede udføre nogle handlinger med elementet eller hente data fra det.
Python kan skjule opkaldet .wrapper_object(), så den endelige kode bliver kortere. Vi anbefaler kun at bruge det til fejlfinding. De næste to linjer gør helt det samme:
dlg_spec.wrapper_object().minimize() # debugging
dlg_spec.minimize() # productionDer er mange søgekriterier for vinduesspecifikationen. Her er bare nogle få eksempler:
# могут иметь несколько уровней
app.window(title_re='.* - Notepad$').window(class_name='Edit')
# можно комбинировать критерии (как AND) и не ограничиваться одним процессом приложения
dlg = Desktop(backend="uia").Calculator
dlg.window(auto_id='num8Button', control_type='Button')Listen over alle mulige kriterier findes i dokumentationen for funktionen .
Magien ved adgang via attribut og nøgle
Python forenkler oprettelsen af vinduesspecifikationer og genkender objektattributter dynamisk (metoden er overskrevet indeni __getattribute__). Selvfølgelig gælder de samme begrænsninger for et attributnavn som for navnet på enhver variabel (man kan ikke indsætte mellemrum, kommaer og andre specialtegn). Heldigvis bruger pywinauto en såkaldt „best match“ søgealgoritme, der er robust over for tastefejl og små variationer.
app.UntitledNotepad
# det samme som
app.window(best_match='UntitledNotepad')Hvis man alligevel har brug for Unicode-strenge (for eksempel til russisk), mellemrum osv., kan man få adgang via nøglen (som om det var et almindeligt ordbog):
app['Untitled - Notepad']
# det samme som
app.window(best_match='Untitled - Notepad')Fem regler for magiske navne
Hvordan kan man finde de reference-magic navne? De, der tildeles elementet før søgning. Hvis du har angivet et navn, der ligner referencen, vil elementet blive fundet.
- Efter overskriften (tekst, navn):
app.Properties.OK.click() - Efter teksten og typen af elementet:
app.Properties.OKButton.click() - Efter typen og nummeret:
app.Properties.Button3.click()(navneButton0ogButton1er knyttet til det første fundne element,Button2— til det andet, og så videre i rækkefølge — sådan har det historisk set været) - Efter statisk tekst (til venstre eller ovenfor) og efter type:
app.OpenDialog.FileNameEdit.set_text("")(nyttigt for elementer med dynamisk tekst) - Efter type og tekst indeni:
app.Properties.TabControlSharing.select("General")
Som regel anvendes to til tre regler samtidig, sjældent flere. For at kontrollere, hvilke specifikke navne der er tilgængelige for hvert element, kan man bruge metoden print_control_identifiers(). Den kan udskrive træet af elementer både på skærmen og i en fil. For hvert element udskrives dets reference-magic navne. Man kan også kopiere mere detaljerede specifikationer for underliggende elementer derfra. Resultatet i scriptet vil se således ud:
app.Properties.child_window(title="Contains:", auto_id="13087", control_type="Edit")Selve elementtræet — er normalt en ret stor tekst.
>>> app.Properties.print_control_identifiers()
Kontrol Identifikatorer:
Dialog - 'Windows NT Egenskaber' (L688, T518, R1065, B1006)
[u'Windows NT EgenskaberDialog', u'Dialog', u'Windows NT Egenskaber']
barn_vindue(titel="Windows NT Egenskaber", kontrol_type="Vindue")
|
| Billede - '' (L717, T589, R749, B622)
| [u'', u'0', u'Billede1', u'Billede0', 'Billede', u'1']
| barn_vindue(auto_id="13057", kontrol_type="Billede")
|
| Billede - '' (L717, T630, R1035, B632)
| ['Billede2', u'2']
| barn_vindue(auto_id="13095", kontrol_type="Billede")
|
| Rediger - 'Mappe navn:' (L790, T596, R1036, B619)
| [u'3', 'Rediger', u'Rediger1', u'Rediger0']
| barn_vindue(titel="Mappe navn:", auto_id="13156", kontrol_type="Rediger")
|
| Statisk - 'Type:' (L717, T643, R780, B658)
| [u'Type:Statisk', u'Statisk', u'Statisk1', u'Statisk0', u'Type:']
| barn_vindue(titel="Type:", auto_id="13080", kontrol_type="Tekst")
|
| Rediger - 'Type:' (L790, T643, R1036, B666)
| [u'4', 'Rediger2', u'Type:Rediger']
| barn_vindue(titel="Type:", auto_id="13059", kontrol_type="Rediger")
|
| Statisk - 'Placering:' (L717, T669, R780, B684)
| [u'Placering:Statisk', u'Placering:', u'Statisk2']
| barn_vindue(titel="Placering:", auto_id="13089", kontrol_type="Tekst")
|
| Rediger - 'Placering:' (L790, T669, R1036, B692)
| ['Rediger3', u'Placering:Rediger', u'5']
| barn_vindue(titel="Placering:", auto_id="13065", kontrol_type="Rediger")
|
| Statisk - 'Størrelse:' (L717, T695, R780, B710)
| [u'Størrelse:Statisk', u'Størrelse:', u'Statisk3']
| barn_vindue(titel="Størrelse:", auto_id="13081", kontrol_type="Tekst")
|
| Rediger - 'Størrelse:' (L790, T695, R1036, B718)
| ['Rediger4', u'6', u'Størrelse:Rediger']
| barn_vindue(titel="Størrelse:", auto_id="13064", kontrol_type="Rediger")
|
| Statisk - 'Størrelse på disk:' (L717, T721, R780, B736)
| [u'Størrelse på disk:', u'Størrelse på disk:Statisk', u'Statisk4']
| barn_vindue(titel="Størrelse på disk:", auto_id="13107", kontrol_type="Tekst")
|
| Rediger - 'Størrelse på disk:' (L790, T721, R1036, B744)
| ['Rediger5', u'7', u'Størrelse på disk:Rediger']
| barn_vindue(titel="Størrelse på disk:", auto_id="13106", kontrol_type="Rediger")
|
| Statisk - 'Indeholder:' (L717, T747, R780, B762)
| [u'Indeholder:1', u'Indeholder:0', u'Indeholder:Statisk', u'Statisk5', u'Indeholder:']
| barn_vindue(titel="Indeholder:", auto_id="13088", kontrol_type="Tekst")
|
| Rediger - 'Indeholder:' (L790, T747, R1036, B770)
| [u'8', 'Rediger6', u'Indeholder:Rediger']
| barn_vindue(titel="Indeholder:", auto_id="13087", kontrol_type="Rediger")
|
| Billede - 'Indeholder:' (L717, T773, R1035, B775)
| [u'Indeholder:Billede', 'Billede3', u'Indeholder:2']
| barn_vindue(titel="Indeholder:", auto_id="13096", kontrol_type="Billede")
|
| Statisk - 'Oprettet:' (L717, T786, R780, B801)
| [u'Oprettet:', u'Oprettet:Statisk', u'Statisk6', u'Oprettet:1', u'Oprettet:0']
| barn_vindue(titel="Oprettet:", auto_id="13092", kontrol_type="Tekst")
|
| Rediger - 'Oprettet:' (L790, T786, R1036, B809)
| [u'Oprettet:Rediger', 'Rediger7', u'9']
| barn_vindue(titel="Oprettet:", auto_id="13072", kontrol_type="Rediger")
|
| Billede - 'Oprettet:' (L717, T812, R1035, B814)
| [u'Oprettet:Billede', 'Billede4', u'Oprettet:2']
| barn_vindue(titel="Oprettet:", auto_id="13097", kontrol_type="Billede")
|
| Statisk - 'Egenskaber:' (L717, T825, R780, B840)
| [u'Egenskaber:Statisk', u'Statisk7', u'Egenskaber:']
| barn_vindue(titel="Egenskaber:", auto_id="13091", kontrol_type="Tekst")
|
| Tjekboks - 'Skrivebeskyttet (Gælder kun for filer i mappen)' (L790, T825, R1035, B841)
| [u'Tjekboks0', u'Tjekboks1', 'Tjekboks', u'Skrivebeskyttet (Gælder kun for filer i mappen)Tjekboks', u'Skrivebeskyttet (Gælder kun for filer i mappen)']
| barn_vindue(titel="Skrivebeskyttet (Gælder kun for filer i mappen)", auto_id="13075", kontrol_type="Tjekboks")
|
| Tjekboks - 'Skjult' (L790, T848, R865, B864)
| ['Tjekboks2', u'SkjultTjekboks', u'Skjult']
| barn_vindue(titel="Skjult", auto_id="13076", kontrol_type="Tjekboks")
|
| Knap - 'Avanceret...' (L930, T845, R1035, B868)
| [u'Avanceret...', u'Avanceret...Knap', 'Knap', u'Knap1', u'Knap0']
| barn_vindue(titel="Avanceret...", auto_id="13154", kontrol_type="Knap")
|
| Knap - 'OK' (L814, T968, R889, B991)
| ['Knap2', u'OK', u'OKKnap']
| barn_vindue(titel="OK", auto_id="1", kontrol_type="Knap")
|
| Knap - 'Annuller' (L895, T968, R970, B991)
| ['Knap3', u'AnnullerKnap', u'Annuller']
| barn_vindue(titel="Annuller", auto_id="2", kontrol_type="Knap")
|
| Knap - 'Anvend' (L976, T968, R1051, B991)
| ['Knap4', u'AnvendKnap', u'Anvend']
| barn_vindue(titel="Anvend", auto_id="12321", kontrol_type="Knap")
|
| FaneKontrol - '' (L702, T556, R1051, B962)
| [u'10', u'FaneKontrolDeling', u'FaneKontrolTidligere Versioner', u'FaneKontrolSikkerhed', u'FaneKontrol', u'FaneKontrolTilpas']
| barn_vindue(auto_id="12320", kontrol_type="Fane")
| |
| | FaneElement - 'Generelt' (L704, T558, R753, B576)
| | [u'GenereltFaneElement', 'FaneElement', u'Generelt', u'FaneElement0', u'FaneElement1']
| | barn_vindue(titel="Generelt", kontrol_type="FaneElement")
| |
| | FaneElement - 'Deling' (L753, T558, R801, B576)
| | [u'Deling', u'DelingFaneElement', 'FaneElement2']
| | barn_vindue(titel="Deling", kontrol_type="FaneElement")
| |
| | FaneElement - 'Sikkerhed' (L801, T558, R851, B576)
| | [u'Sikkerhed', 'FaneElement3', u'SikkerhedFaneElement']
| | barn_vindue(titel="Sikkerhed", kontrol_type="FaneElement")
| |
| | FaneElement - 'Tidligere Versioner' (L851, T558, R947, B576)
| | [u'Tidligere VersionerFaneElement', u'Tidligere Versioner', 'FaneElement4']
| | barn_vindue(titel="Tidligere Versioner", kontrol_type="FaneElement")
| |
| | FaneElement - 'Tilpas' (L947, T558, R1007, B576)
| | [u'TilpasFaneElement', 'FaneElement5', u'Tilpas']
| | barn_vindue(titel="Tilpas", kontrol_type="FaneElement")
|
| TitelBar - 'Ingen' (L712, T521, R1057, B549)
| ['TitelBar', u'11']
| |
| | Menu - 'System' (L696, T526, R718, B548)
| | [u'System0', u'System', u'System1', u'Menu', u'SystemMenu']
| | barn_vindue(titel="System", auto_id="MenuBar", kontrol_type="MenuBar")
| | |
| | | MenuElement - 'System' (L696, T526, R718, B548)
| | | [u'System2', u'MenuElement', u'SystemMenuElement']
| | | barn_vindue(titel="System", kontrol_type="MenuElement")
| |
| | Knap - 'Luk' (L1024, T519, R1058, B549)
| | [u'LukKnap', u'Luk', 'Knap5']
| | barn_vindue(titel="Luk", kontrol_type="Knap")I nogle tilfælde kan udskrivning af hele træet være langsom (for eksempel i iTunes, hvor der på én fane er hele tre tusinde elementer!), men man kan bruge parameteren depth (dybde): depth=1 — selve elementet, depth=2 — kun direkte børn, og så videre. Den kan også angives i specifikationerne ved oprettelse af child_window.
Eksempler
Vi opdaterer konstant . Blandt de nyere er automatiseringen af netværksanalysatoren WireShark (et godt eksempel på en Qt5-applikation; selvom denne opgave også kan løses uden GUI, da der er scapy.Sniffer fra Python-pakken ). Der er også et eksempel på automatisering af MS Paint med dens Ribbon-værktøjslinje.
Et andet fremragende eksempel, skrevet af min studerende: (den vil flytte til hovedarkivet lidt senere).
Og selvfølgelig et eksempel på abonnement på tastatur- (hot keys) og musbegivenheder:
.
Taknemmelighed
En særlig tak til dem, der konstant hjælper med at udvikle projektet. For mig og er det et konstant hobbyprojekt. To af mine studerende fra NNGU har for nylig bestået deres bacheloropgaver om dette emne. har ydet et stort bidrag til støtte for MS UI Automation og har for nylig begyndt at lave en automatisk kodegenerator baseret på 'optagelse-afspilning' principper med tekstlige egenskaber (det er den sværeste funktion), i øjeblikket kun for 'uia' backend. udvikler en ny backend under Linux baseret på AT-SPI (modulerne mouse og keyboard baseret på — allerede i versionerne 0.6.x).
Da jeg har undervist i et specielt kursus i automatisering med Python i ret lang tid, udfører en del af kandidatstuderende deres hjemmearbejde ved at implementere små funktioner eller eksempler på automatisering. Nogle væsentlige ting på forskningsstadiet blev også engang opdaget af studerende. Selvom man nogle gange skal holde nøje øje med kodekvaliteten. Det hjælper meget med statiske analyseværktøjer (QuantifiedCode, Codacy og Landscape) og automatiserede tests i skyen (servicen AppVeyor) med en kode dækning på omkring 95%.
Også tak til alle, der efterlader feedback, rapporterer fejl og sender pull-requests!
Yderligere ressourcer
For spørgsmål holder vi øje med (der er for nylig opstået ) og . Der er .
Vi opdaterer hver måned Når det kommer til antallet af stjerner på GitHub, er det kun Autohotkey og PyAutoGUI, der vokser hurtigere (de har henholdsvis et meget stort fællesskab og en lang historie). PyAutoGUI har også haft fordel af bøgernes popularitet fra forfatteren Al Sweigart: «Automate the Boring Stuff with Python» og andre.
Kilde: habr.com
