18 lipca 2011

Automatyzacja obsługi GUI z wykorzystaniem Sikuli

Automatyzacja obsługi GUI z wykorzystaniem Sikuli

Autorem artykułu jest Kamil Michalak



Coraz częściej podczas używania komputera korzystamy z różnego rodzaju automatów. Stworzenie narzędzia przyspieszającego pracę nie jest trudne, jeżeli wykonywane czynności można opakować w skrypty, problem pojawia się, jeżeli mamy do czynienia z graficznym interfejsem.

Projekt Sikuli jest idealnym sposobem na ułatwienie sobie życia. Pomysł realizowany jest przez grupę zapaleńców z MIT (Massachusetts Institute of Technology). Prace programistyczne przyniosły w rezultacie dość wygodne IDE oraz ciekawe, częściowo graficzne API oparte o język programowania Python (projekt korzysta z javowej implementacji Pythona – Jythona). Samo konstruowanie skryptu dla automatu jest banalnie proste. Proces ten sprowadza się do prostego zaznaczenia na ekranie elementów, które mają zostać użyte i określeniu akcji jakie mają zostać na nich wykonane.

Na chwilę obecną framework pozwala na wykonanie takich akcji, jak kliknięcie elementu, kliknięcie podwójne, czy kliknięcie prawym przyciskiem myszy, pisanie (meulacja wciśnięcia przycisku na klawiaturze), przeciąganie elementów (drag and drop), wykonanie operacji wklejania. Mamy również możliwość wyszukiwania elementów na ekrania, sprawdzanie czy określony przez nas komponent istnieje (jest widoczny), możemy obserwować zmiany danego elementu, czy reagować na jego pojawienie się lub zniknięcie. Pełną listę dostępnych funkcji można przejrzeć na stronie projektu pod adresem http://sikuli.org/trac/wiki/reference-0.10. Wszelkiego rodzaju operacje wykonywane są na rzeczywistych komponentach widocznych na ekranie monitora. Programista może definiować jakie elementy mają zostać wykorzystane przez Sikuli przy pomocy prostych zrzutów ekranu Przykładowy skrypt (http://sikuli.org/documentation.shtml#examples/set-ip-input.sikuli/set-ip-input.html)

oraz określenie w jaki stopniu widoczny element powinien być podobny do screena.
Można więc stwierdzić, że prostego "robota" jest w stanie skonstruować nawet zupełny laik.
Przykład prostego skryptu pochodzący ze strony projektu został zaprezentowany poniżej.

Jak widać nie jest to skomplikowany kod. Jego stworzenie zajmuje bardzo mało czasu. Co więcej Sikuli udostępnia biblioteki, dzięki którym możliwe jest pisanie automatów w Javie. Jest to rozwiązanie o tyle ciekawe, że pozwala na wykorzystanie frameworku na wielu systemach operacyjnych i w różnych projektach. Kolejnym atutem projektu jest możliwość rozbudowania jego funkcjonalności poprzez własne moduły napisane w języku Python (bądź Javie, jeżeli ktoś korzysta z API dla tego języka).

Framework sam w sobie jest dość dobrze zorganizowany i pomysłowy. Oparte o Jython (niestety nie jest on całkowicie kompatybilny z Jythonem, pomimo jego integracji z samym Sikuli, np. na chwilę obecną importowanie własnych modułów pythona jest bardzo utrudnione, a import skryptów frameworka potrafi w ogóle nie działać) narzędzie pozwala na tworzenie skryptów umożliwiających na obsługę graficznego interfejsu użytkownika bez nadzoru człowieka. Można by stwierdzić, że spełnia on swoje zadanie jako pomoc w automatyzacji obsługi GUI różnego rodzaju aplikacji. Niestety rzeczywistość pokazuje, że Sikuli działa czasem stanowczo za wolno. Dzieje się tak, ponieważ algorytm zastosowany do przeszukiwania tzw. screenów jest dość prymitywny i w przypadku dużych rozdzielczości ekranu może wymagać dużej liczby operacji przeszukiwania.
Częściowym rozwiązaniem tego typu problemów jest stosowanie regionów. Korzystanie z obszarów pracy (zarówno zdefiniowanych na sztywno jak i tworzonych dynamicznie) przyspiesza pracę Sikuli poprzez zawężanie pola na ekranie, które ma zostać objęte wyszukiwaniem.
Określanie regionów pracy dla automatu ma znaczenie w przypadku, gdy mamy do czynienia z kilkoma bardzo podobnymi lub identycznymi elementami GUI, jak na przykład przyciski czy pozycje w menu. Określając region możemy zminimalizować bądź całkowicie wykluczyć ryzyko odnalezienia i wykorzystania przez skrypt niewłaściwego elementu interfejsu graficznego. Dość dobrym pomysłem jest również korzystanie w takich przypadkach ze sposobu pokazanego w tutorialu “How to click on a particular check box?“, który pokazuje jak radzić sobie w trudnej sytuacji korzystając z regionów i punktów charakterystycznych rozmieszczonych w obszarze przeszukiwania.
Nieco innym zagadnieniem jest wyszukiwanie wszystkich elementów pasujących do danego wzorca. Zgodnie z dokumentacją, do tego celu najefektywniej jest korzystać z metody findAll() dostarczanej przez Sikuli. Należy jednak przy tym zwrócić uwagę, że stosowanie tej funckcji tworzy strukturę danych o bardzo dużej objętości, przez co zwiększamy ilość pamięci potrzebnej do działania naszego automatu. W niektórych przypadkach może to prowadzić do przyspieszenia pojawienia się znanego w Sikuli buga (memory leak, który nie został jeszcze załatany). Z tego też powodu nie wskazane jest stosowanie konstrukcji typu:

for GUIcomponent in findAll(“some_pattern.png”):
#some instructions
pass

Zdecydowanie lepiej jest skorzystać z dodatkowej zmiennej, którą można później zniszczyć:

elements = findAll(“some_pattern.png”):
for GUIcoponent in elements:
# some instructions
pass
elements.destroy()

Kolejnym czynnikiem wpływającym na trafność rozpoznania screenów przez Sikuli jest osiągnięcie fucusa przez wybrane elementy. W niektórych przypadkach, ustawienie focusu danego elementu może spowodować, że zostanie on rozpoznany jako zupełnie inny komponent, bądź w ogóle nie zostanie uwzględniony w wynikach wyszukiwania. Dzieje się tak najczęściej, jeżeli dany komponent interfejsu graficznego zyskuje dodatkowe obramowanie bądź podświetlenie po uzyskaniu focusa.
Podczas obsługi niektórych komponentów takich, jak np. rozwijane menu ważne jest uwzględnienie czasu animacji danego komponentu. Dla przykładu załóżmy, że mamy przycisk, po którego kliknięciu rozwija się dodatkowe menu. Automat ma za zadanie kliknąć przycisk, a następnie wybrać jedną z pozycji menu. Czasami może się tu pojawić problem w postaci wyjątku FindFailed. Dzieje się tak dlatego, że skrypt stara się odnaleźć elementy menu od razu po kliknięciu naszego przycisku. Jeżeli menu jest rozwijane z wyświetleniem animacji, konieczne jest uwzględnienie czasu jej trwania. Możemy to zrobić prostym poleceniem wait(). Pozwoli nam ono na opóźnienie wykonania kolejnej instrukcji. Złą stroną takiego podejścia jest niestety wydłużenie działania samego skryptu.
Kolejnym pomysłem na ułatwienie sobie życia jest korzystanie z funkcji type(). Pozwala ona na symulację wciśnięcia klawiszy na klawiaturze. Jak łatwo się domyślić można z niej korzystać w najbardziej oczywisty sposób wpisując teksty w różnego rodzaju pola typu text edit. Można również użyć type() do nieco innych zastosowań, a dokładniej obsługi GUI. Korzystając z różnego rodzaju skrótów klawiaturowych możemy wywoływać opcje automatyzowanej aplikacji bez konieczności przeszukiwania ekranu. Czasem może to pomóc nie tylko w przyspieszeniu naszego skryptu, ale także w poprawieniu poprawności jego działania, pozwalając na uniknięcie pomyłek w odnajdywaniu komponentów interfejsu.
Czasem funkcjonalność Sikuli staje się nazbyt ograniczona, bądź niektóre operacje są strasznie powolne. Jeżeli trafiliśmy na właśnie taki przypadek możemy wspomóc się zwykłymi skryptami pythona, które można dołączać do skryptów frameworka.

Reasumując, podczas korzystania z Sikuli ważne jest dokładne poznanie GUI. Ważne jest, żeby nasze skrypty wykonywały się szybko, jednak zawsze należy pamiętać o tym, że niektóre elementy graficzne wymagają opóźnień w odczycie ich stanu.
Framework posiada bardzo brzydki błąd, który powoduje wyciek pamięci, co może uniemożliwić wykonanie bardzo długich, bądź skomplikowanych skryptów. Wymusza to na użytkowniku stosowanie takich konstrukcji językowych, które pozwolą na minimalizację czasu wyonania i zużycia pamięci.

---

K. Michalak


Artykuł pochodzi z serwisu www.Artelis.pl

Brak komentarzy:

Prześlij komentarz