Sytuacja kobiet w IT w 2024 roku
30.04.20206 min
Ng Wai Foong

Ng Wai FoongAI EngineerYOOZOO GAMES

Automatyzuj programy z pywinauto

Dowiedz się, jak poprawnie automatyzować akcje i programy Windowsa za pomocą pythonowego modułu o nazwie pywinauto.

Automatyzuj programy z pywinauto

W tym artykule nauczysz się automatyzować programy w Windowsie za pomocą modułu Pythona o nazwie pywinauto. Zgodnie z oficjalną dokumentacją pywinauto to:

… zestaw modułów Pythona do automatyzacji Microsoft Windows GUI. Mówiąc najprościej, umożliwia to wysyłanie akcji myszy i klawiatury do okien dialogowych i kontrolek systemu Windows. Obsługuje on też bardziej złożone działania, takie jak pobieranie danych tekstowych.

Obsługiwane technologie pod maską: Win32 API (backend = "win32"; domyślnie używany), MS UI Automation (backend = "uia"). 

Moduły emulacji urządzeń wprowadzania, mysz i klawiatura działają zarówno w systemie Windows, jak i w Linuxie.

Artykuł jest podzielony na pięć sekcji:

  • Konfiguracja
  • Podstawowe użycie
  • Klawiatura i mysz
  • Przechwytywanie obrazu
  • Podsumowanie

1. Konfiguracja

Moduł ten można łatwo zainstalować za pomocą pip install. Automatycznie zainstaluje to nasz moduł i wszystkie wymagane zależności. Polecam utworzenie nowego środowiska wirtualnego, zanim ruszycie dalej.

pip install -U pywinauto

Po zakończeniu sprawdź, czy masz zainstalowane następujące moduły i zależności za pomocą następującego polecenia:

pip list

Powinniście mieć następujące zależności: 

  • pywinauto
  • pyWin32
  • comtypes
  • six
  • Pillow (chociaż ten moduł jest opcjonalny i wymagany tylko, jeśli chcesz zrobić zrzut ekranu)

Przejdźmy dalej i zacznijmy pisać kod w języku Python.

2. Podstawowe użycie


Pojęcia i definicje 

Zanim przejdziemy dalej, spójrzmy na niektóre istotne dla nas terminy i definicje.

  • Dialog— jest to okno zawierające kilka innych elementów GUI, takich jak przyciski, pola edycji itp. Dialog nie musi być jednak głównym oknem. Okno komunikatu w górnej części formularza głównego to także okno dialogowe. Główny formularz jest również uważany przez pywinauto za okno dialogowe.
  • Control— jest to element GUI na dowolnym poziomie hierarchii. Definicja ta obejmuje okno, przycisk, pole edycji, siatkę, komórkę siatki, pasek itp.


Backend

Backend odnosi się do technologii ułatwień dostępu, które działają na drugim planie. W momencie pisania, mamy tylko takie backendy:

  • Win32 API (backend = "win32")— domyślny backend dla newMFC, VB6, VCL, prostych kontrolek WinForm oraz większości starszych aplikacji.
  • MS UI Automation (backend = "uia")— WinForms, WPF, aplikacje ze sklepu, Qt5, przeglądarki. Chrome wymaga flagi cmd renderer-force-accesability przed uruchomieniem. Niestandardowe kontrolki nie są obsługiwane z powodu ograniczeń biblioteki comtypes.


Importowanie

Pierwszą częścią jest zaimportowanie niezbędnego modułu do Twojego skryptu. Zacznijmy od zaimportowania klasy Application modułu pywinauto. Application jest punktem wyjścia dla każdego procesu automatyzacji. Dla przykładu użyję notatnika.

from pywinauto.application import Application

Jeśli zamierzasz wywoływać podstawowe funkcje związane z interakcją użytkownika (np. mysz i klawiatura) musisz również zaimportować następujący kod:

import pywinauto.mouse as mouse
import pywinauto.keyboard as keyboard


Start oraz connect

Musisz połączyć instancję Application z procesem, aby móc korzystać z automatyzacji. Można to zrobić na dwa sposoby:

  • start()— używany, gdy musisz uruchomić aplikację. start() akceptuje ciąg znaków, który może zawierać argument wiersza poleceń i parametr timeout. Parametr ten jest potrzebny tylko wtedy, gdy uruchomienie programu trwa zbyt długo. Oto przykład uruchomienia notatnika:

app = Application().start(r"c:\windows\system32\notepad.exe")
  • connect()— używany, gdy próbujesz zautomatyzować uruchomiony proces. Możesz przekazać identyfikator procesu, handle lub ścieżkę programu. Polecam użycie ścieżki bezwzględnej, bo identyfikator i uchwyt procesu mogą ulec zmianie w przyszłości po ponownym uruchomieniu programu. Musisz upewnić się, że program jest w pełni uruchomiony i działa, bo w przeciwnym razie wystąpi błąd.

app = Application().connect(path=r"c:\windows\system32\notepad.exe")


Okno dialogowe

Musisz zidentyfikować dostępne okna swojego procesu:

app.windows()


Powinny się pojawić następujące wyniki. Oznacza to, że dla tego procesu istnieje tylko jedno aktywne okno:

[<uiawrapper.UIAWrapper - 'Untitled - Notepad', Dialog, 3713031533299862531>]


Możesz przypisać to za pomocą następującej metody:

dlg = app['Untitled - Notepad']

Klucz musi pasować do nazwy okien. W tym przypadku nazwa to Untitled — Notepad. Jeśli nazwa systemu Windows jest zbyt długa, możesz użyć wyrażenia regularnego, aby ją przechwycić:

app.window(title_re=".*Notepad.*")


Kontrolki

Następnym krokiem będzie identyfikacja wszystkich dostępnych kontrolek przez uruchomienie następującego kodu.

dlg.print_control_identifiers()

Otrzymałem taki wynik:


Domyślnie używany jest backend win32. Należy rozważyć zmianę backendu na uia, jeśli masz problemy z uzyskaniem kontrolek.

app = Application(backend="uia").start(r"c:\windows\system32\notepad.exe")

Jeśli twój program jest bardzo złożony, możesz mieć problemy z załadowaniem wszystkich kontrolek. Kontrolki można przechwycić za pomocą następującej metody:

app.dlg.control #first method
app['dlg']['control'] #second method is preferred as it is more robust for unicode strings

Istnieją różne rodzaje kontrolek, które możesz wykorzystać przy automatyzacji. Najczęstsze to Button, Edit i Menultem. Każdy element sterujący ma własne wywołanie funkcji. Więcej informacji znajduje się w dokumentacji.

Zautomatyzuj wybieranie elementu z menu. Przełączam pasek stanu znajdujący się w menu View.

dlg.menu_select("View -> Status Bar")

Po uruchomieniu powyższego kodu zobaczysz, że pasek stanu zostanie zmieniony w zależności od stanu początkowego. Możesz zrobić to samo dla innych funkcji, takich jak zapis pliku.

dlg.menu_select("File -> Save as")


Możesz kontynuować łączenie kolejnych kroków automatyzacji. Poniższy przykład otwiera okno zamiany i zamyka je.

dlg.menu_select("Edit -> Replace")
dlg.Replace.Cancel.click()


Możesz wpisać tekst w kontrolce edycji. Poniższy przykład pokazuje, jak to zrobić.

dlg.Edit.type_keys('Witaj świecie')

Po uruchomieniu zauważysz, że końcowy tekst pojawiający się w Notatniku to Witaj świecie, w którym brakuje obu spacji w oryginalnym tekście. To normalne, ponieważ moduł ten jest oparty na danych wejściowych z klawiatury. Możesz to łatwo rozwiązać, dodając odpowiednie kody klawiszy w danym ciągu. Więcej informacji tutaj

dlg.Edit.type_keys('Witaj{SPACE}świecie')

Jeśli budujesz rozwiązanie automatyzujące, warto jest wstępnie przetworzyć ciąg wejściowy, zanim przekażemy go do funkcji. Utwórzmy prostą funkcję, aby to zrobić:

def replace_space(text):
    return text.replace(' ', '{SPACE}')


Najnowsza wersja zawiera również wbudowane parametry do mapowania ciągu. Ustaw parametr with_spaces na True. Oto pełna lista parametrów:

type_keys(keys, pause=None, with_spaces=False, with_tabs=False, with_newlines=False, turn_off_numlock=True, set_foreground=True, vk_packet=True)


Pamiętaj, że jeśli w twoim ciągu znajdują się nawiasy klamrowe, które nie są kodowane za pomocą kodów klawiszy, to najprawdopodobniej pojawi się błąd.

3. Klawiatura i myszka

Jeśli nie możesz przechwycić kontrolek, możesz je zautomatyzować za pomocą klawiatury i myszy. Zanim przejdziesz dalej, musisz ustawić focus dla aplikacji. Użyj wbudowanej funkcji send_keys, aby rozpocząć pisanie tekstu w swoim programie. Nie należy mylić tej funkcji ze wspomnianymi wyżej type_keys. type_keys to tylko wrapper do wywołania podstawowej funkcji send_keys.

dlg.set_focus()
keyboard.send_keys('Hello')

Załóżmy, że masz problemy z zamknięciem programu poprzez kliknięcie przycisku zamykania w prawym górnym rogu aplikacji. Możesz łatwo rozwiązać ten problem, pisząc automatyzację, która kliknie za ciebie myszką. Przyjrzyjmy się jednak najpierw kontrolkom, które wcześniej wydrukowaliśmy przy użyciu print_control_identifiers. Zauważysz coś takiego:

TitleBar - ''    (L157, T45, R1872, B87)

Ostatnia część odnosi się do współrzędnych lewej, górnej, prawej i dolnej części okna. Możesz tego użyć do wskazania czy przesunięcia myszy na określoną pozycję. Ponieważ przycisk zamykania jest na poziomie paska tytułu, kliknij myszą w prawym górnym rogu. Zmieniłem nieco pozycję, aby zrekompensować przesunięcie przycisku od krawędzi okna.

dlg.set_focus()
mouse.click(coords=(1850, 60))

Powinien wyskoczyć komunikat z prośbą o zapisanie lub wyjście bez zapisywania.

4. Przechwytywanie obrazu

Można przechwycić zrzut okna za pomocą funkcji capture_as_image. Ta funkcja świetnie się nadaje do debugowania. W przypadku zgłoszenia wyjątku można uruchomić tę funkcję i zapisać wynikowy obraz PIL. Zobaczmy, jak to zrobić. Upewnij się, że utworzyłeś folder log w tym samym katalogu co skrypt:

import datetime;
dlg.set_focus()
debug_image = dlg.capture_as_image()
debug_image.save('log/' + str(int(datetime.datetime.now().timestamp())) + '.png')

Pamiętaj, że kod ten przeniesie aplikację na pierwszy plan i zapisuje zrzut wyglądu aplikacji. Może to zawierać inne niepożądane elementy. Wszelkie inne programy, które się przed nim znajdują, również zostaną przechwycone.

5. Podsumowanie

Podsumujmy to, czego się dowiedzieliśmy. Zaczęliśmy instalacją modułu pywinauto za pomocą pip install. Następnie zbadaliśmy podstawowe użycie tego modułu. Poznaliśmy dostępne backendy, a także istotne terminy. Napisaliśmy również kod do automatyzacji prostych czynności, takich jak wybór z menu i kliknięcie przycisku.

Następnie wypróbowaliśmy automatyzację przy pomocy klawiatury i myszy, który zapewnia większą elastyczność w porównaniu ze zwykłą metodą. Na końcu udało nam się przechwycić wygląd okna programu w celach debugowych.

Źródła

  1. Dokumentacja pywinauto
  2. Repozytorium pywinauto


Oryginał tekstu w języku angielskim możesz przeczytać tutaj

<p>Loading...</p>