Biblioteka Pythona — jest projektem typu open source służącym do automatyzacji aplikacji graficznych na komputerach stacjonarnych WindowsW ciągu ostatnich dwóch lat dodano nowe, ważne funkcje:
- Wsparcie dla technologii MS UI Automation. Interfejs pozostaje ten sam i teraz obsługuje WinForms, WPF, Qt5, Windows Sklep (UWP) i tak dalej - prawie wszystko, co jest na Windows.
- System backendów/wtyczek (obecnie pod maską znajdują się dwa z nich: default
"win32"i nowe"uia"). Następnie płynnie przechodzimy w stronę cross-platformowości. - Haki Win32 dla myszy i klawiatury (klawisze skrótu w duchu pyHook).
Podamy również krótki przegląd tego, co jest dostępne w otwartym kodzie źródłowym do automatyzacji komputerów stacjonarnych (bez udawania poważnego porównania).
Artykuł ten jest częściowym zapisem relacji z konferencji SQA Days 20 w Mińsku ( и ), częściowo wersja rosyjska dla pywinauto.
- Podstawowe podejścia
- Podstawowe technologie dostępności komputerów stacjonarnych
Zacznijmy od krótkiego przeglądu open source w tym obszarze. W przypadku aplikacji GUI na komputery stacjonarne wszystko jest nieco bardziej skomplikowane niż w przypadku sieci, która ma Selenium. Oto główne podejścia:
metoda współrzędnych
Zakoduj na stałe punkty kliknięcia, mamy nadzieję na udane trafienia.
[+] Wieloplatformowy, łatwy do wdrożenia.
[+] Łatwe „nagrywanie i odtwarzanie” nagrań testowych.
[-] Najbardziej niestabilny przy zmianie rozdzielczości ekranu, motywu, czcionek, rozmiarów okien itp.
[-] Wymagane są ogromne wysiłki w zakresie wsparcia; często łatwiej jest zregenerować testy od zera lub przetestować ręcznie.
[-] Automatyzuje tylko działania; istnieją inne metody weryfikacji i odzyskiwania danych.
Narzędzia (wieloplatformowe): , , i wiele innych. Zazwyczaj bardziej złożone narzędzia obejmują tę funkcjonalność (nie zawsze wieloplatformowe).
Warto powiedzieć, że metoda współrzędnych może uzupełniać inne podejścia. Przykładowo w przypadku niestandardowej grafiki można kliknąć na współrzędne względne (od lewego górnego rogu okna/elementu, a nie całego ekranu) - zwykle jest to całkiem niezawodne, szczególnie jeśli weźmie się pod uwagę długość/szerokość cały element (wtedy różne rozdzielczości ekranu nie zaszkodzą).
Inna opcja: przydziel do testów tylko jedną maszynę ze stabilnymi ustawieniami (nie na wielu platformach, ale w niektórych przypadkach jest to dobre).
Rozpoznawanie obrazu referencyjnego
[+] Wieloplatformowe
[+-] Stosunkowo niezawodna (lepsza niż metoda współrzędnych), ale nadal wymaga pewnych sztuczek.
[-+] Stosunkowo wolno, ponieważ Wymaga zasobów procesora dla algorytmów rozpoznawania.
[-] Rozpoznawanie tekstu (OCR) z reguły nie wchodzi w grę => nie można uzyskać danych tekstowych. Z tego co wiem, istniejące rozwiązania OCR nie są zbyt niezawodne w przypadku tego typu zadań i nie są powszechnie stosowane (proszę o komentarze, jeśli jeszcze tak nie jest).
Narzędzia: , (kompatybilny z Sikuli, czysty Python), .
Technologia dostępności
[+] Najbardziej niezawodna metoda, ponieważ umożliwia wyszukiwanie według tekstu, niezależnie od tego, jak jest on renderowany przez system lub framework.
[+] Umożliwia wyodrębnienie danych tekstowych => łatwiejsza weryfikacja wyników testów.
[+] Z reguły najszybciej, bo prawie nie zużywa zasobów procesora.
[-] Trudno jest stworzyć narzędzie wieloplatformowe: absolutnie wszystkie biblioteki typu open source obsługują jedną lub dwie technologie ułatwień dostępu. Windows/Linux/MacOS nie jest w pełni obsługiwany przez nikogo poza płatnymi aplikacjami, takimi jak TestComplete, UFT i Squish.
[-] Taka technologia nie zawsze jest w zasadzie dostępna. Na przykład testowanie ekranu ładowania w VirtualBoxie - nie da się tego zrobić bez rozpoznawania obrazu. Jednak w wielu klasycznych przypadkach podejście dotyczące dostępności nadal ma zastosowanie. Zostanie to omówione dalej.
Narzędzia: w języku C#, w C# (kompatybilny z Selenium), w języku C# (kompatybilny z Appium), , (kompatybilny z LDTP) , w Rubinie, (Linux Projekt testowania pulpitu) i jego Windows wersja .
LDTP jest prawdopodobnie jedynym wieloplatformowym narzędziem typu open source (dokładniej rodziną bibliotek) opartym na technologiach dostępności. Jednak nie jest to zbyt popularne. Sam z niego nie korzystałem, ale według opinii jego interfejs nie jest najwygodniejszy. Jeśli masz pozytywne opinie, podziel się nimi w komentarzach.
Przetestuj tylne drzwi (inaczej rower stacjonarny)
W przypadku aplikacji wieloplatformowych programiści często sami tworzą wewnętrzny mechanizm zapewniający testowalność. Przykładowo tworzą w aplikacji serwer usług TCP, testują połączenie z nim i wysyłają polecenia tekstowe: w co kliknąć, skąd wziąć dane itp. Niezawodny, ale nie uniwersalny.
Podstawowe technologie dostępności komputerów stacjonarnych
Stary, dobry interfejs API Win32
Najbardziej Windows aplikacje napisane przed wydaniem WPF i po nim Windows Sklepy są w taki czy inny sposób zbudowane na API Win32. Mianowicie MFC, WTL, C++ Builder, Delphi, VB6 – wszystkie te narzędzia korzystają z API Win32. Nawet Windows Formularze są w dużej mierze zgodne z API Win32.
Narzędzia: (podobnie jak VB) i opakowanie Pythona , (własny język, istnieje interfejs IDispatch COM), (Pyton) (Rubin) (Rubin).
Automatyzacja interfejsu użytkownika Microsoftu
Główna zaleta: technologia automatyzacji interfejsu użytkownika MS obsługuje zdecydowaną większość aplikacji GUI Windows Z nielicznymi wyjątkami. Problem: nie jest o wiele łatwiejszy do nauczenia niż API Win32. W przeciwnym razie nikt nie tworzyłby dla niego wrapperów.
W rzeczywistości jest to zestaw niestandardowych interfejsów COM (głównie UIAutomationCore.dll), a także ma opakowanie .NET w postaci namespace System.Windows.Automation. Nawiasem mówiąc, ma wprowadzony błąd, przez który niektóre elementy interfejsu użytkownika mogą zostać pominięte. Dlatego lepiej jest używać bezpośrednio UIAutomationCore.dll (jeśli słyszałeś o UiaComWrapper w C#, to właśnie to).
Rodzaje interfejsów COM:
(1) Podstawowe IUznane – „korzeń wszelkiego zła”. Najniższy poziom, nigdy przyjazny dla użytkownika.
(2) IDispatch i instrumenty pochodne (np Excel.Application), którego można używać w Pythonie za pomocą pakietu win32com.client (dołączonego do pyWin32). Najwygodniejsza i najpiękniejsza opcja.
(3) Niestandardowe interfejsy, z którymi może współpracować pakiet Pythona innej firmy .
Narzędzia: w języku C#, 0.6.0 +, w języku C#, (ich kod źródłowy opakowań C dla UIAutomationCore.dll nie został ujawniony), w Rubinie.
AT-SPI
Pomimo tego, że prawie wszystkie osie rodziny Linux Zbudowany na systemie X Window (w Fedorze 25 „X” został zastąpiony przez Wayland), „X” pozwala jedynie na obsługę okien najwyższego poziomu oraz myszy i klawiatury. Do szczegółowej analizy przycisków, list rozwijanych itp. dostępna jest technologia AT-SPI. Najpopularniejsze menedżery okien posiadają tzw. demona rejestru AT-SPI, który zapewnia zautomatyzowany graficzny interfejs użytkownika dla aplikacji (przynajmniej Qt i GTK są obsługiwane).
Narzędzia: .
Moim zdaniem pyatspi2 zawiera zbyt wiele zależności, takich jak PyGObject. Sama technologia jest dostępna jako zwykła biblioteka dynamiczna libatspi.so. Tam jest W bibliotece pywinauto planujemy zaimplementować obsługę AT-SPI w ten sposób: poprzez załadowanie pliku libatspi.so i modułu ctypes. Jedynym drobnym problemem jest użycie odpowiedniej wersji, ponieważ różnią się one nieznacznie dla aplikacji GTK+ i Qt. Prawdopodobna wersja pywinauto 0.7.0 będzie oferować pełną obsługę. Linux można spodziewać się w pierwszej połowie 2018 roku.
Interfejs API ułatwień dostępu Apple
MacOS ma swój własny język automatyzacji, AppleScript. Aby zaimplementować coś takiego w Pythonie, musisz oczywiście użyć funkcji z ObjectiveC. Wygląda na to, że począwszy od systemu MacOS 10.6 pakiet pyobjc jest zawarty w preinstalowanym języku Python. Ułatwi to również wyświetlenie listy zależności do przyszłej obsługi w pywinauto.
Narzędzia: Oprócz języka Apple Script warto zwrócić na niego uwagę , czyli pyatom. Jest interfejsem kompatybilnym z LDTP, ale jest także samodzielną biblioteką. To ma , napisany przez mojego ucznia. Istnieje znany problem: elastyczne czasy nie działają (methods waitFor*). Ale ogólnie nie jest źle.
Jak rozpocząć pracę z pywinauto
Pierwszym krokiem jest uzbrojenie się w inspektora obiektów GUI (tak zwane narzędzie szpiegowskie). Pomoże Ci to przestudiować aplikację od środka: jak zbudowana jest hierarchia elementów, jakie właściwości są dostępne. Najbardziej znani inspektorzy budowy:
- Szpieg++ - dołączone do Visual Studio, w tym Express lub Community Edition. Korzysta z API Win32. Znany jest również jego klon Informacje o oknie AutoIt.
- Sprawdź.exe — jest wliczony w Windows SDK. Jeśli masz go zainstalowanego, to jest on na systemie 64-bitowym. Windows Znajdziesz to w folderze
C:Program Files (x86)Windows Kits<winver>binx64. W samym inspektorze musisz wybrać tryb Automatyzacja interfejsu użytkownika zamiast MS AA (aktywna dostępność, przodek automatyzacji interfejsu użytkownika).
Po dokładnym zbadaniu aplikacji wybieramy backend, z którego będziemy korzystać. Wystarczy podczas tworzenia obiektu Application podać nazwę backendu.
- backend=”win32″ — gdy jest używany domyślnie, dobrze współpracuje z MFC, WTL, VB6 i innymi starszymi aplikacjami.
- backend=”uia” — nowy back-end dla automatyzacji interfejsu użytkownika MS: doskonale współpracuje z WPF i WinForms; dobrze współpracuje również z Delphi i Windows Aplikacje sklepowe; współpracuje z Qt5 i niektórymi aplikacjami Java. I generalnie, jeśli Inspect.exe widzi elementy i ich właściwości, to ten backend jest odpowiedni. Zasadniczo większość przeglądarek obsługuje również automatyzację interfejsu użytkownika (domyślnie Mozilla, a Chrome wymaga przełącznika wiersza poleceń po uruchomieniu).
--force-renderer-accessibilityaby zobaczyć elementy na stronach w Inspect.exe). Oczywiście konkurencja z Selenium w tym obszarze jest prawie niemożliwa. To po prostu inny sposób pracy z przeglądarką (może być przydatny w scenariuszu obejmującym wiele produktów).
Punkty wejścia do automatyzacji
Aplikacja została szczegółowo zbadana. Czas utworzyć obiekt Application i uruchomić go lub dołączyć do już działającego. To nie jest tylko klon standardowej klasy subprocess.Popen, czyli obiekt wejściowy, który ogranicza wszystkie twoje działania do granic procesu. Jest to bardzo przydatne, jeśli uruchomionych jest kilka instancji aplikacji, ale nie chcesz dotykać reszty.
from pywinauto.application import Application
app = Application(backend="uia").start('notepad.exe')
# Опишем окно, которое хотим найти в процессе Notepad.exe
dlg_spec = app.UntitledNotepad
# ждем пока окно реально появится
actionable_dlg = dlg_spec.wait('visible')Jeśli chcesz zarządzać kilkoma aplikacjami jednocześnie, te zajęcia Ci w tym pomogą Desktop. Na przykład w kalkulatorze na Win10 hierarchia elementów jest rozłożona na kilka procesów (nie tylko calc.exe). Więc nie ma obiektu Desktop niewystarczająco.
from subprocess import Popen
from pywinauto import Desktop
Popen('calc.exe', shell=True)
dlg = Desktop(backend="uia").Calculator
dlg.wait('visible')Obiekt główny (Application lub Desktop) to jedyne miejsce, w którym musisz określić backend. Wszystko inne w sposób przejrzysty mieści się w koncepcji „specyfikacja->opakowanie”, która zostanie omówiona później.
Specyfikacje okien/elementów
Jest to podstawowa koncepcja, na której zbudowany jest interfejs pywinauto. Możesz opisać okno/element z grubsza lub bardziej szczegółowo, nawet jeśli jeszcze nie istnieje lub jest już zamknięte. Specyfikacja okna (obiekt Specyfikacja okna) przechowuje kryteria wyszukiwania prawdziwego okna lub elementu.
Przykład szczegółowej specyfikacji okna:
>>> 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>Samo przeszukiwanie okna następuje poprzez wywołanie metody .wrapper_object(). Zwraca pewne „opakowanie” dla prawdziwego okna/elementu lub rzutów ElementNotFoundError (czasami ElementAmbiguousError, jeśli znaleziono kilka elementów, czyli trzeba doprecyzować kryterium wyszukiwania). Ten „opakowanie” już wie, jak wykonać pewne akcje z elementem lub odebrać z niego dane.
Python może ukryć połączenie .wrapper_object(), więc ostateczny kod staje się krótszy. Zalecamy używanie go wyłącznie do celów debugowania. Następne dwie linie robią dokładnie to samo:
dlg_spec.wrapper_object().minimize() # debugging
dlg_spec.minimize() # productionIstnieje wiele kryteriów wyszukiwania specyfikacji okna. Oto tylko kilka przykładów:
# могут иметь несколько уровней
app.window(title_re='.* - Notepad$').window(class_name='Edit')
# можно комбинировать критерии (как AND) и не ограничиваться одним процессом приложения
dlg = Desktop(backend="uia").Calculator
dlg.window(auto_id='num8Button', control_type='Button')Lista wszystkich możliwych kryteriów znajduje się w dokumentacji funkcji .
Magia dostępu według atrybutu i klucza
Python ułatwia tworzenie specyfikacji okien i dynamiczne rozpoznawanie atrybutów obiektów (wewnętrznie metoda jest nadpisywana __getattribute__). Oczywiście na nazwę atrybutu nałożone są te same ograniczenia, co na nazwę dowolnej zmiennej (nie można wstawiać spacji, przecinków ani innych znaków specjalnych). Na szczęście pywinauto korzysta z tak zwanego algorytmu wyszukiwania „najlepszego dopasowania”, który jest odporny na literówki i niewielkie różnice.
app.UntitledNotepad
# то же самое, что
app.window(best_match='UntitledNotepad')Jeśli nadal potrzebujesz ciągów znaków Unicode (na przykład dla języka rosyjskiego), spacji itp., możesz uzyskać dostęp za pomocą klucza (jak gdyby był to zwykły słownik):
app['Untitled - Notepad']
# то же самое, что
app.window(best_match='Untitled - Notepad')Pięć zasad magicznych imion
Jak znaleźć standardowe magiczne imiona? Te, które są przypisane do elementu przed wyszukiwaniem. Jeśli podałeś nazwę wystarczająco zbliżoną do standardu, to element zostanie znaleziony.
- Według tytułu (tekst, imię):
app.Properties.OK.click() - Według tekstu i typu elementu:
app.Properties.OKButton.click() - Według rodzaju i numeru:
app.Properties.Button3.click()(nazwyButton0иButton1powiązany z pierwszym znalezionym elementem,Button2- do drugiego, a potem w kolejności - tak to się działo historycznie) - Według tekstu statycznego (po lewej lub u góry) i według typu:
app.OpenDialog.FileNameEdit.set_text("")(przydatne w przypadku elementów z tekstem dynamicznym) - Według typu i tekstu w środku:
app.Properties.TabControlSharing.select("General")
Zwykle stosuje się dwie lub trzy zasady jednocześnie, rzadko więcej. Aby sprawdzić, jakie konkretne nazwy są dostępne dla każdego elementu, możesz skorzystać z metody print_control_identifiers(). Potrafi wydrukować drzewo elementów zarówno na ekran, jak i do pliku. Dla każdego elementu drukowane są jego standardowe magiczne nazwy. Możesz także skopiować i wkleić stamtąd bardziej szczegółowe specyfikacje elementów podrzędnych. Wynik w skrypcie będzie wyglądał następująco:
app.Properties.child_window(data-gt-translate-attributes='["title"]' title="Contains:", auto_id="13087", control_type="Edit")Samo drzewo żywiołów to zazwyczaj dość duży obrus.
>>> 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")W niektórych przypadkach wydruk całego drzewa może być powolny (np. w iTunes na jednej zakładce znajdują się aż trzy tysiące elementów!), ale można skorzystać z opcji depth (głębokość): depth=1 - sam element, depth=2 - tylko bezpośrednie dzieci i tak dalej. Można to również określić w specyfikacjach podczas tworzenia child_window.
Примеры
Stale uzupełniamy . Wśród najnowszych warto zwrócić uwagę na automatyzację analizatora sieci WireShark (jest to dobry przykład aplikacji Qt5; choć zadanie to można rozwiązać bez GUI, ponieważ istnieje scapy.Sniffer z pakietu Pythona ). Poniżej znajduje się również przykład automatyzacji programu MS Paint z paskiem narzędzi Wstążka.
Kolejny świetny przykład napisany przez mojego ucznia: (nieco później przeniesie się do głównego repozytorium).
I oczywiście przykład subskrypcji zdarzeń klawiatury (skrótów klawiszowych) i myszy:
.
Podziękowanie
Szczególne podziękowania należą się tym, którzy stale pomagają w rozwoju projektu. Dla mnie i To jest trwałe hobby. Dwóch moich studentów z UNN obroniło niedawno stopnie licencjackie na ten temat. wniósł duży wkład w obsługę MS UI Automation i niedawno zaczął tworzyć automatyczny generator kodu oparty na zasadzie „odtwarzania nagrania” w oparciu o właściwości tekstowe (jest to najbardziej złożona funkcja), jak dotąd tylko dla zaplecza „uia”. opracowuje nowe zaplecze dla Linux na podstawie AT-SPI (moduły mouse и keyboard na podstawie - już w wersjach 0.6.x).
Ponieważ od dłuższego czasu prowadzę specjalny kurs dotyczący automatyzacji w Pythonie, niektórzy studenci studiów magisterskich odrabiają prace domowe, wdrażając drobne funkcje lub przykłady automatyzacji. Studenci odkryli kiedyś pewne kluczowe rzeczy na etapie badań. Chociaż czasami trzeba ściśle monitorować jakość kodu. Bardzo pomagają w tym analizatory statyczne (QuantifiedCode, Codacy i Landscape) oraz automatyczne testy w chmurze (usługa AppVeyor) z pokryciem kodu na poziomie około 95%.
Dziękuję także wszystkim, którzy zostawiają recenzje, zgłaszają błędy i wysyłają prośby o ściągnięcie!
Dodatkowe zasoby
Podążamy za pytaniami (ostatnio pojawił się ) I . Jest .
Aktualizujemy co miesiąc . Pod względem liczby gwiazdek na GitHubie szybciej rosną jedynie Autohotkey (mają bardzo dużą społeczność i długą historię) oraz PyAutoGUI (głównie za sprawą popularności książek jej autora Ala Sweigarta: „Automate the Boring Stuff with Python” i inne).
Źródło: www.habr.com
