Перейти до основного вмісту

pywinauto: Стережись event'ів, бо вони неочевидні

Передмова

В даному пості мова піде про підводні камені при використанні стороннього програмного забезпечення через засім автоматизації дій 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". Звісно, знаходимо цей прапорець, встановлюємо його і виконуємо такий же пошук, от тільки поставимо затримку, щоб переконатись в тому, що прапорець встановлений.

import time
...
wwo_flag.Check()
time.sleep(1)
edit_box.TypeKeys("Some text{ENTER}", with_spaces=True)


І, зовсім неочікувано, отримуємо той же результат пошуку. Прапорець встановився, а пошук відбувається так, ніби ні. Змінимо код наступним чином, щоб не встановлювати прапорець, а "клікати" на нього:

import time
...
if wwo_flag.GetCheckState() != 1:
    wwo_flag.Click()
time.sleep(1)
edit_box.TypeKeys("Some text{ENTER}", with_spaces=True)

Запускаємо пошук - все працює. Можна тільки здогадуватись (швидше за все, звісно, так і є), що параметри пошуку передвстановлюються наперед на "клік" по прапорцю, а не аналізуються при ініціації пошуку (клік на кнопку Search дає той же ефект, що і Enter в полі вводу рядка).

Висновок

Варто зазначити, що цей випадок ще легко локалізується. Якщо подумати, можна придумати набагато складніші речі з різними контролами, все-таки, в даному випадку працюємо з чорним ящиком, і з цим нічого не зробиш. Не попадайтесь...

Коментарі

  1. А как мы определяем какой из фагов активируем, их ж 4 в окне, почему активруется именно нужный или не все 4!?

    ВідповістиВидалити
  2. @reason89
    Это уже проблема разработчика =) Здесь я не показывал, но можно делать по разному. Например, проитерировать по children'ам окна и определить, что это именно этот флажок. В данном случае он должен быть
    1. type(control) == CheckBox
    2. 'Whole words only' in control.Texts.
    После этого сохраняем флажок в переменную wwo_flags и уже работаем с этой переменной.

    ВідповістиВидалити
  3. Добрый день, Ростислав

    Подскажите, как быть с 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()

    ВідповістиВидалити
  4. Тимур, гляньте в документацию по модулю pywinauto.timings. Там есть интересная функции WaintUntil, WaitUntilPasses. Прелесть в том, что они с интервалом пробуют выполнить некую функцию проверки. Когда функция возвратит True - можно выполнять действие. Также можно указать таймаут, когда функция перестанет пытаться.

    Ну, это кошерный вариант time.sleep.

    ВідповістиВидалити
  5. Ссылка - http://pywinauto.googlecode.com/hg/pywinauto/docs/code/pywinauto.timings.html

    ВідповістиВидалити

Дописати коментар

Популярні дописи з цього блогу

Регулярні вирази в Python: вивчення та оптимізація

Writing a regular expression is more than a skill -- it's an art. Jeffrey Friedl Що це таке? Рано чи піздно майже кожному програмісту в своєму житті доводиться стикатись з регулярними виразами. Термін "Регулярні вирази" є перекладом з англійської словосполучення "Regular expressions" і не є зовсім точним, а для тих, хто перший раз почув цей термін, мабуть, навіть спантеличуючим (я, наприклад, коли вперше почув, ніяк не міг собі второпати по назві, хоча б приблизно, що це, і для чого використовується). Літературний і більш осмислений переклад звучав би, мабуть, як "шаблонні вирази". Але назва вже прижилась, а скажете "шаблонні вирази" - вас просто не зрозуміють :). Звідси: Регулярний вираз -  це рядок, що задає шаблон пошуку під-рядків в рядку. Регулярні вирази використовуються для аналізу текстів на предмет відповідності текстової інформації деякому шаблону. Наприклад , шаблон, що задає слово, яке містить букву "к". Де застосовують

Python: як програмно перемкнути розкладку клавіатури в Windows

Дослідивши дане питання, я побачив, що Python не має засобів "з коробки" для вирішення цієї задачі. Відвоідно, задача повинна вирішуватись для каждої ОС своїм шляхом. Дане рішення було знайдено мною для ОС Windows XP +. Панацея - Win API Для того, щоб виконати завдання необхідно встановити додатково бібліотеку pywin32 , яка надає доступ до функцій Windows API з Python. З цієї бібліотеки нам знадобиться модуль win32api . >>> import win32api Дослідивши його вміст, можна побачити, що для роботы з розкладкою клавіатури є декілька функцій і одне системне повідомлення Windows - WM_INPUTLANGCHANGE : GetKeyboardLayout GetKeyboardLayoutList LoadKeyboardLayout В даному випадку для нас важлива саме остання функція - LoadKeyboardLayout . Дана функція завантажує нову розкладку (якщо вона ще не завантажена) і виконує після цього ще якісь дії; приймає в якості аргументів два: рядок з ідентифікатором розкладки. дію. Більш детально про їхні можливі значення можна почитати в MSDN . О

Python: PEP-8 чи не PEP-8

Пост - не технічний, кому не цікаво - можете далі не читати... PEP-8, хоча й фактично є пропозицією по розширенню Python під номером 8, серед Python програмістів уже став терміном, що позначає правила стилю оформлення коду. Ні, я не збираюсь зараз описувати його тут - про нього можна почитати в першоджерелі . Питання в тому, слідувати цьому стандарту, чи не слідувати? Ітак, стандарт це в більшості випадків добре, оскільки вносить порядок. Наприклад, стандарт USB 2.0 - просто прекрасний стандарт, уявіть собі, якби флешки були не USB, а кожна мала б свій вихід :)... Жахливо, так, були б у нас USB-порти як card-reader'и - 62 в 1.. Реально 62 в 1 Інша справа з PEP-8. Тут все по іншому, адже програма не змінює свою поведінку, якщо ми будемр робити відступ не в 4 пробіла, а 2 (добре, що більшість, все-таки, робить 4), або будемо ставити пробіл перед другою дужкою, чи не будемо і т.д..  Отже, кожен програміст може редагувати свій код як йому хочеться. Мені, наприклад, подобається