Вступление
Sikuli — это API позволяющая писать на Jython сценарии автоматизации опираясь на визуальную составляющую любой программы/сайта и т.д. Особенно приятна для автоматизации Flash.
О Sikuli написано мало статей и большинство из них обзорные. Ещё меньше русскоязычного хелпа, и ещё меньше примеров кода. И отсутствие последнего пожалуй самое трагичное для тестировщика ПО который столкнулся в работе с необходимостью автоматизировать какой либо флэш. Как раз это и подтолкнуло меня написать более ёмкую статью по Sikuli и описать несколько подробнее некоторые особенности использования.
Установка
Для установки и правильной работы Sikuli вам понадобится:
1. Java шестой версии и обязательно 32х разрядная.
2. Дистрибутив
3. Пакет обновлений.
В связи с тем, что Sikuli идет только в 32-разрядной комплектации, то для правильной работы на 64х-битных системах в обязательном порядке ставим Java 6u38 32х (с 7й версией не работает).
Так же необходимо поставить пакет обновлений. Устанавливается легко. Просто копируется из архива (пункт 3) содержимое папки «Sikuli-IDE» в папку с установленной программой «C:Program Files (x86)Sikuli X», кстати не меняйте папку установки назначенную по умолчанию.
Если вдруг что не так, вот ссылочка для почитать.
Функционал и особенности.
Снимок экрана
Применяется для выделения определённой области экрана и последующем созданием паттерна
При нажатии на паттерн появится окно «Настройка шаблонов»
С вкладками всё примитивно и ясно. Единственное отмечу что на вкладке «смещение цели» целью является то место куда переместиться курсор и произведет клик/ввод. Причём место клика может лежать далеко за пределами паттерна.
Теперь о особенностях.
*****.png это название искомого рисунка который должен лежать в папке заранее сохраненного проекта.
similar = схожесть, где 1=100% (лучше не использовать ибо находит не всегда), а например 0,85 (через точку) = 85%, что на мой взгляд самое оптимальное значение.
targetOffset(x,y)) и тут важно что координаты «x» и «y» это смещение цели от центра паттерна.
Выделение области/wait()/Click()
Великолепная функция и что самое главное в крайней степени полезная.
Многие говорят что Sikuli плохо соображает в работе и ошибается ещё здорово. Я же с этим в корне не согласен и считаю, что грамотный подход значительно увеличивает производительность программы.
Эта функция задает активную область экрана.
в которой будет производиться поиск искомого паттерна.
Можно ещё и вот так:
Region(x,y,w,h).find(Pattern("*****.png".similar(0.85)targetOffset(x,y)))
Она крайне полезна если нам нужно чтобы скрипт реагировал на изменения оперативно, не тормозил систему.
Как пример:
из всего нашего большого экрана мы выбираем только небольшую область где каждый раз появляется заветная кнопочка.
Далее описываем что в этой области нам надо искать и что с этим потом делать.
Для того чтобы оптимизировать процесс нам нужно запустить этот скрипт и чтоб он за нас 1 раз совершил клик. После мы переходим на вкладку «Сообщение» где видим координаты клика.
Далее делаем цикл и и делаем клик по координатам (это при условии что кнопка всегда будет появляться в одном и том же месте).
Схожесть в поиске делаем около 70 процентов, быстрее найдёт, ожидание 999…
По итогу мы имеем практически моментальное срабатывание.
Find()
Функция find() ищет паттерн. Но вот зачем? Всё просто. Один раз нашел, а дальше он будет знать где находится найденный объект всегда. Как применить на практике:
Имеем много чего либо…
И нажимать это «что либо» программе придется долго и упорно…
Для того чтобы программа знала куда нажимать, нужно выставить targetOffset()
Повторяем для каждой цели, присваиваем переменные, оптимизируем если поле с множеством находится в одном и том же месте.
Далее мы можем найденные зоны использовать как хотим. Причём клик будет происходить без задержек на поиск и т.д.
Остальной функционал рассматривать не буду он примитивен и прост.
Примеры
Settings.MoveMouseDelay = 0.02
def f5():
while "f5":
click(Pattern("KyddinsmapsD-1.png").targetOffset(-41,0))
sleep(5)
if exists(Pattern("1344731699750.png").similar(0.90),10):
continue
else:
pass
try:
wait(Pattern("CLOSElah.png").similar(0.85),25)
except:
continue
try:
click(Pattern("CLOSElah.png").similar(0.85).targetOffset(1,-6))
dragDrop(Location(1430,388), Location(1431,435)) # 435
break
except:
pass
def send_frend():
click(Location(849,495))
sleep(1)
click(Location(578,695))
sleep(1)
def pre_click_frend():
click(Location(713,300))
click(Location(713,315))
def click_frend():
click(Location(713,332))
click(Location(714,348))
click(Location(714,364))
click(Location(715,380))
click(Location(714,396))
click(Location(713,412))
click(Location(713,427))
click(Location(713,444))
click(Location(713,460))
click(Location(713,474))
click(Location(713,490))
click(Location(715,506))
click(Location(714,522))
click(Location(714,537))
click(Location(713,554))
click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)),
click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)),
click(Location(923,541)), click(Location(923,541))
sleep(1)
click(Location(713,288))
click(Location(713,304))
click(Location(713,320))
click(Location(713,336))
click(Location(713,352))
click(Location(713,368))
click(Location(713,384))
click(Location(713,400))
click(Location(713,416))
click(Location(713,432))
click(Location(713,448))
click(Location(713,464))
click(Location(713,480))
click(Location(713,496))
click(Location(713,512))
click(Location(713,528))
click(Location(713,544))
click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)),
click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)),
click(Location(923,541))
sleep(1)
click(Location(713,300)),
click(Location(713,315))
click(Location(713,332))
click(Location(714,348))
click(Location(714,364))
click(Location(715,380))
click(Location(714,396))
click(Location(713,412))
click(Location(713,427))
click(Location(713,444))
click(Location(713,460))
click(Location(713,474))
click(Location(713,490))
click(Location(715,506))
click(Location(714,522))
click(Location(714,537))
sleep(1)
def send_fb():
click(Location(756,760))
sleep(2)
try:
click(Pattern("0TIIp3BHTb33.png").targetOffset(-2,1))
except:
pass
def off (event):
popup("The program is completed.nCreator of this miracle: Grumo Van Blum.nIf you have questions about using this program, please contact us by e-mail: grumovanblum@gmail.comnGood Luck 8c)")
exit()
popup("If you want to stop the script, press: 'Ctrl' + 'Alt' + 'Space'")
Env.addHotkey(Key.SPACE, KeyModifier.ALT+KeyModifier.CTRL, off)
click(Pattern("1344836792562.png").similar(0.88))
n = 0
n = int(input("How many cycles you want to run?"))
while n > 0:
f5()
while "cycle":
if n == 0:
break
else:
send_frend()
if exists(Pattern("Jd.png").similar(0.96),1):
popup("Friends come to an end :)")
n=0
continue
else:
pre_click_frend()
sleep(1)
if exists(Pattern("Youcanontysd.png").similar(0.95),1):
f5()
send_frend()
pre_click_frend()
else:
pass
click_frend()
send_fb()
n -= 1
if n == 0:
break
else:
sleep(3)
И видео о том как он работает (снимал в режиме отладки).
Используемые полезности
Alt+Shift+c остановит исполнение скрипта и вернёт IDE.
Выставляем время перемещения курсора:
Settings.MoveMouseDelay = 0.02
Присваиваем сочетаниям клавиш какое либо событие:
def off (event):
exit()
Env.addHotkey(Key.SPACE, KeyModifier.ALT+KeyModifier.CTRL, off)
Вызов инициируемого диалогового окна:
n = int(input("А сколько раз будем проверять?"))
Бездействие в 1 сек:
sleep(1)
Если у вас после вставки скопированного текста код паттерна или региона заменился на картинку просто нажмите 1 или 2 раза сочетание клавиш Ctrl+z.
Это ещё далеко не всё и будет обязательно ещё. Но на данном этапе считаю, что основную идею я выразил.
Буду рад фидбэку.
Если есть вопросы по Sikuli, то пишите.
Следующим разом скорее всего расскажу как Sikuli научить распознавать текст и записывать лог ошибок в отдельный файл.
Спасибо за внимание.
Автор: Gulsom