Передмова
В даному пості мова піде про підводні камені при використанні стороннього програмного забезпечення через засім автоматизації дій pywinauto, а саме про події (events). Як відомо, GUI-бібліотеки будуться в стилі подійного (event-driven) програмування. Таким чином, будь-яка дія додатку є відповіддю на дію користувача. Тобто всередині існує певний цикл, який ходить по черзі подій, що формується діями користувача і запускає реакції на ці дії. Таким чином, отримуємо відповідь на конкретну подію. Розібрались.
Суть проблеми
Проблеми виникають, коли розробник не знає, як саме обробляються події в програмі, дії якої автоматизуються через pywinauto. Розглянемо це діло на більш конкретному прикладі - Adobe Reader 9. Цей прекрасний софт дозволяє нам відкривати PDF-документи, а ще він має прекрасну форму пошуку, з якою можна погратись. Виглядає вона так, як показано на рис. 1.
![]() |
Рис 1. Форма пошуку Adobe Reader. |
Отже, з того, що нам цікаво, ми бачимо, що форма має текстове поле вводу пошукового рядка, прапорці конфігурації параметрів пошуку, і, власне, кнопку "Search", яка виконує пошук (також шукати можна, натиснувши Enter, коли фокус встановлений в поле вводу рядка пошуку). Отже, знайти текст досить просто. Шукаємо для поля вводу:
edit_box.TypeKeys("Some text{ENTER}", with_spaces=True)
Ну, як і очікувалось, текст знайдений, все ок. Інша справа, коли ми хочемо пошукати тільки цілі слова. Для цього нам потрібно встановити прапорець "Whole words only". Звісно, знаходимо цей прапорець, встановлюємо його і виконуємо такий же пошук, от тільки поставимо затримку, щоб переконатись в тому, що прапорець встановлений.
...
wwo_flag.Check()
time.sleep(1)
edit_box.TypeKeys("Some text{ENTER}", with_spaces=True)
import time
edit_box.TypeKeys("Some text{ENTER}", with_spaces=True)
І, зовсім неочікувано, отримуємо той же результат пошуку. Прапорець встановився, а пошук відбувається так, ніби ні. Змінимо код наступним чином, щоб не встановлювати прапорець, а "клікати" на нього:
...
if wwo_flag.GetCheckState() != 1:
wwo_flag.Click()
time.sleep(1)
edit_box.TypeKeys("Some text{ENTER}", with_spaces=True)
edit_box.TypeKeys("Some text{ENTER}", with_spaces=True)
Запускаємо пошук - все працює. Можна тільки здогадуватись (швидше за все, звісно, так і є), що параметри пошуку передвстановлюються наперед на "клік" по прапорцю, а не аналізуються при ініціації пошуку (клік на кнопку Search дає той же ефект, що і Enter в полі вводу рядка).
Висновок
Варто зазначити, що цей випадок ще легко локалізується. Якщо подумати, можна придумати набагато складніші речі з різними контролами, все-таки, в даному випадку працюємо з чорним ящиком, і з цим нічого не зробиш. Не попадайтесь...
А как мы определяем какой из фагов активируем, их ж 4 в окне, почему активруется именно нужный или не все 4!?
ВідповістиВидалити@reason89
ВідповістиВидалитиЭто уже проблема разработчика =) Здесь я не показывал, но можно делать по разному. Например, проитерировать по children'ам окна и определить, что это именно этот флажок. В данном случае он должен быть
1. type(control) == CheckBox
2. 'Whole words only' in control.Texts.
После этого сохраняем флажок в переменную wwo_flags и уже работаем с этой переменной.
Добрый день, Ростислав
ВідповістиВидалитиПодскажите, как быть с pywinauto. Пишу скрипт, который автоматически инсталлирует FSViewer.
И проблема в следующем: чтобы закончить установку, нужно ткнуть несколько раз далее в инсталляторе.
А как сделать так, чтобы при переходе, скрипт дождался завершение предыдущего шага, а потом продолжил следующий.
На данный момент есть такой вариант
app.Dlg.Wait("exists enabled visible ready")
app.Dlg.Button1.Click()
app.Dlg.Wait("exists enabled visible ready")
app.Dlg.Button2.Click()
Но если, будет тормоз в установке, то все идет к чертям. Использовать time.sleep как-то некошерно.
Думал, про такой вариант, но не работает
app.Dlg.Wait("exists enabled visible ready")
app.Dlg.Button1.Click()
app.Dlg.WaitNot("exists enabled visible ready", 2, 2)
app.Dlg.Wait("exists enabled visible ready")
app.Dlg.Button2.Click()
Тимур, гляньте в документацию по модулю pywinauto.timings. Там есть интересная функции WaintUntil, WaitUntilPasses. Прелесть в том, что они с интервалом пробуют выполнить некую функцию проверки. Когда функция возвратит True - можно выполнять действие. Также можно указать таймаут, когда функция перестанет пытаться.
ВідповістиВидалитиНу, это кошерный вариант time.sleep.
Ссылка - http://pywinauto.googlecode.com/hg/pywinauto/docs/code/pywinauto.timings.html
ВідповістиВидалити