Блокнот в PyWinAuto Windows 7

Содержание
Введение
Доступные окна
Закрыть блокнот
print_control_identifiers()
Ввести текст
Закрыть и сохранить
Confirm Save As
Похожие статьи

Введение

Это архивная статья с примерами под Windows 7. Актуальная версия статьи про тестирование Блокнота в Windows 11 - здесь

Я пытался повторить эти действия в Windows 11 и не все из них сработали. В этой статье вы можете изучить простые примеры автоматизации работы с блокнотом Windows 7.

В статье пока не хватает непосредственно тестирования - то есть проверки каких-то сценариев, я планирую добавить обертку на pytest или robot, но пока не дошли руки.

Подготовить окружение можно следующим образом:

python -m venv venv
.\venv\Scripts\activate
python -m pip install pywinauto
New-Item -Path . -Name "notepad_demo.py" -ItemType "file"

Доступные окна

Первое, что я обычно делаю после запуска приложения - проверяю доступные окна. Если в результате вернётся пустой список значит дело плохо и pywinauto не видит доступных окон. В таком случае можно попробовать другой тип бекенда, но не факт, что это поможет

# notepad_demo.py import time from pywinauto.application import Application backend = "win32" app = Application(backend=backend).start("C:\Windows\System32\notepad.exe") time.sleep(2) for w in app.windows(): print(w.window_text())

python notepad_demo.py

Untitled - Notepad M Default IME

Запустить notepad в pywinauto изображение с сайта www.testsetup.ru
Notepad
pywinauto

Если изменить тип бекенда на uia блокнот по-прежнему откроется, но список видимых окон изменится.

# notepad_demo.py import time from pywinauto.application import Application backend = "uia" app = Application(backend=backend).start("C:\Windows\System32\notepad.exe") time.sleep(2) for w in app.windows(): print(w.window_text())

python notepad_demo.py

Запустить notepad в pywinauto изображение с сайта www.testsetup.ru
Notepad
pywinauto

Untitled - Notepad

Осталось только окно с текстом Untitled - Notepad а два непонятных окна M и Default IME уже не видны.

Закрыть блокнот

Теперь, когда мы знаем какое окно доступно для подключения, можно подключиться к нему и закрыть.

Оставим задержку в две секунды, чтобы убедиться в том, что блокнот действительно открылся.

# notepad_demo.py import time from pywinauto.application import Application backend = "uia" app = Application(backend=backend).start("C:\Windows\System32\notepad.exe") dlg = app.window(title="Untitled - Notepad") time.sleep(2) dlg.close()

Запустить notepad в pywinauto изображение с сайта www.testsetup.ru
Notepad
pywinauto

print_control_identifiers()

Чтобы управлять приложением, нужно получить список объектов - контроллеров. Сделать это можно с помощью print_control_identifiers()

print_control_identifiers() выводит на экран список всех контроллеров доступных из текущего состояния приложения а не вообще все контроллеры.

# notepad_demo.py import time from pywinauto.application import Application backend = "uia" app = Application(backend=backend).start("C:\Windows\System32\notepad.exe") dlg = app.window(title="Untitled - Notepad") time.sleep(2) dlg.print_control_identifiers(filename="1_notepad_controls.txt") dlg.close()

notepad .\1_notepad_controls.txt

Control Identifiers: Dialog - 'Untitled - Notepad' (L364, T71, R870, B408) ['Untitled - Notepad', 'Untitled - NotepadDialog', 'Dialog'] child_window(title="Untitled - Notepad", control_type="Window") | | Edit - '' (L368, T113, R866, B404) | ['Edit', 'Edit0', 'Edit1'] | child_window(auto_id="15", control_type="Edit") | | | | ScrollBar - 'Vertical' (L848, T115, R864, B386) | | ['Vertical', 'ScrollBar', 'VerticalScrollBar', 'ScrollBar0', 'ScrollBar1'] | | child_window(title="Vertical", auto_id="NonClientVerticalScrollBar", control_type="ScrollBar") | | | | | | Button - 'Line up' (L848, T115, R864, B131) | | | ['Line up', 'Button', 'Line upButton', 'Button0', 'Button1'] | | | child_window(title="Line up", auto_id="UpButton", control_type="Button") | | | | | | Button - 'Line down' (L848, T370, R864, B386) | | | ['Button2', 'Line down', 'Line downButton'] | | | child_window(title="Line down", auto_id="DownButton", control_type="Button") | | | | ScrollBar - 'Horizontal' (L370, T386, R848, B402) | | ['Horizontal', 'ScrollBar2', 'HorizontalScrollBar'] | | child_window(title="Horizontal", auto_id="NonClientHorizontalScrollBar", control_type="ScrollBar") | | | …

Полную версию вы можете изучить здесь

Ввести текст

Чтобы ввести текст нужно понять какой объект отвечает за область в которой мы обычно печатаем.

В предыдущей главе мы использовали print_control_identifiers() изучим содержимое и попробуем догадаться какой объект нам нужен.

Control Identifiers: Dialog - 'Untitled - Notepad' (L364, T71, R870, B408) ['Untitled - Notepad', 'Untitled - NotepadDialog', 'Dialog'] child_window(title="Untitled - Notepad", control_type="Window") | | Edit - '' (L368, T113, R866, B404) | ['Edit', 'Edit0', 'Edit1'] | child_window(auto_id="15", control_type="Edit") | | | | ScrollBar - 'Vertical' (L848, T115, R864, B386) | | ['Vertical', 'ScrollBar', 'VerticalScrollBar', 'ScrollBar0', 'ScrollBar1'] | | child_window(title="Vertical", auto_id="NonClientVerticalScrollBar", control_type="ScrollBar") | | | | | | Button - 'Line up' (L848, T115, R864, B131) | | | ['Line up', 'Button', 'Line upButton', 'Button0', 'Button1'] | | | child_window(title="Line up", auto_id="UpButton", control_type="Button") | | | | | | Button - 'Line down' (L848, T370, R864, B386) | | | ['Button2', 'Line down', 'Line downButton'] | | | child_window(title="Line down", auto_id="DownButton", control_type="Button")

Подходящим кандидатом выглядит следующий объект:

child_window(auto_id="15", control_type="Edit")

Во-первых, у него есть полоса прокрутки ScrollBar, во-вторых он называется Edit.

# notepad_demo.py import time from pywinauto.application import Application backend = "uia" app = Application(backend=backend).start("C:\Windows\System32\notepad.exe") dlg = app.window(title="Untitled - Notepad") time.sleep(2) text_editor = dlg.child_window( auto_id="15", control_type="Edit" ).wrapper_object() text_editor.type_keys( "Subscribe to t.me/aofeed", with_spaces=True ) dlg.close()

python .\notepad_demo.py

Запустить notepad в pywinauto изображение с сайта www.testsetup.ru
Notepad
pywinauto

Закрыть блокнот таким образом уже не получится, так как мы внесли изменения и нужно их сохранить.

Запустить notepad в pywinauto изображение с сайта www.testsetup.ru
Notepad
pywinauto

Закрыть и сохранить

После команды dlg.close() появляется следующее окно:

Запустить notepad в pywinauto изображение с сайта www.testsetup.ru
Notepad
pywinauto

Чтобы понять как сохранить наши изменения, проверим как pywinauto видит диалоговое окно: как часть текущего объекта, или как новое окно.

Я подозреваю, что как часть текущего, поэтому в этом же скрипте выполним print_control_identifiers() после

# notepad_demo.py import time from pywinauto.application import Application backend = "uia" app = Application(backend=backend).start("C:\Windows\System32\notepad.exe") dlg = app.window(title="Untitled - Notepad") time.sleep(2) text_editor = dlg.child_window( auto_id="15", control_type="Edit" ).wrapper_object() text_editor.type_keys( "Subscribe to t.me/aofeed", with_spaces=True ) dlg.close() dlg.print_control_identifiers(filename="3_notepad_controls_after_close.txt") time.sleep(2) # Здесь мы проверяем не появилось ли второе окно for w in app.windows(): print(w.window_text())

python .\notepad_demo.py

Untitled - Notepad

Никакого второго окна не появилось, значит нужно внимательно изучить контроллеры.

Ниже я покажу ту часть, которая нам пригодится. Полная версия здесь

Control Identifiers: Dialog - 'Untitled - Notepad' (L277, T238, R1603, B928) ['Untitled - Notepad', 'Untitled - NotepadDialog', 'Dialog', 'Dialog0', 'Dialog1'] child_window(title="Untitled - Notepad", control_type="Window") | | Dialog - 'Notepad' (L774, T442, R1140, B571) | ['Notepad', 'Dialog2', 'NotepadDialog'] | child_window(title="Notepad", control_type="Window") | | | | Static - 'Do you want to save changes to Untitled?' (L787, T474, R1021, B496) | | ['Do you want to save changes to Untitled?', 'Static', 'Do you want to save changes to Untitled?Static', 'Static0', 'Static1'] | | child_window(title="Do you want to save changes to Untitled?", auto_id="MainInstruction", control_type="Text") | | | | Static - '' (L0, T0, R0, B0) | | ['Static2'] | | child_window(auto_id="ContentText", control_type="Text") | | | | Button - 'Save' (L898, T535, R961, B559) | | ['SaveButton', 'Save', 'Button', 'Button0', 'Button1'] | | child_window(title="Save", auto_id="CommandButton_6", control_type="Button") | | | | Button - 'Don't Save' (L971, T536, R1054, B558) | | ["Don't SaveButton", 'Button2', "Don't Save"] | | child_window(title="Don't Save", auto_id="CommandButton_7", control_type="Button") | | …

Здесь найти кнопку Save очень просто. Это

child_window(title="Save", auto_id="CommandButton_6", control_type="Button")

Кликнем на неё и сохраним новые контроллеры. Дополнительных окон там не появится.

# notepad_demo.py import time from pywinauto.application import Application backend = "uia" app = Application(backend=backend).start("C:\Windows\System32\notepad.exe") dlg = app.window(title="Untitled - Notepad") time.sleep(2) text_editor = dlg.child_window( auto_id="15", control_type="Edit" ).wrapper_object() text_editor.type_keys( "Subscribe to t.me/aofeed", with_spaces=True ) dlg.close() time.sleep(2) save_button = dlg.child_window( title="Save", auto_id="CommandButton_6", control_type="Button" ).wrapper_object() save_button.click() dlg.print_control_identifiers(filename="4_notepad_controls_after_save.txt")

python .\notepad_demo.py

Запустить notepad в pywinauto изображение с сайта www.testsetup.ru
Notepad
pywinauto

Никакого второго окна не появилось, значит нужно внимательно изучить контроллеры.

Здесь я привожу только интересующие нас контроллеры. Всё остальное заменяю на …

Нажав сюда вы можете перейти изучить полную версию.

Вот так выглядит сокращённое дерево объектов от главного 'Untitled - Notepad' до нужного нам поля для ввода имени файла File name:

child_window(title="Untitled - Notepad", control_type="Window") | | Dialog - 'Save As' (L563, T324, R1203, B836) | ['Dialog2', 'Save AsDialog', 'Save As'] | child_window(title="Save As", control_type="Window") | | | | Pane - '' (L567, T383, R1199, B780) | | ['Pane', 'File name:Pane', 'Pane0', 'Pane1'] … | | | Static - 'File name:' (L577, T725, R692, B746) | | | ['File name:', 'File name:Static', 'Static', 'File name:0', 'File name:1', 'Static0', 'Static1'] | | | child_window(title="File name:", auto_id="SaveDialogLabel", control_type="Text") | | | | | | ComboBox - 'File name:' (L692, T725, R1194, B746) | | | ['ComboBox', 'File name:2', 'File name:ComboBox', 'ComboBox0', 'ComboBox1'] | | | child_window(title="File name:", auto_id="FileNameControlHost", control_type="ComboBox") | | | | | | | | Edit - 'File name:' (L695, T728, R1175, B743) | | | | ['Edit25', 'File name:Edit', 'File name:Edit0', 'File name:Edit1'] | | | | child_window(title="File name:", auto_id="1001", control_type="Edit") … | | | | Button - 'Save' (L994, T794, R1082, B820) | | ['SaveButton', 'Button19', 'Save'] | | child_window(title="Save", auto_id="1", control_type="Button")

Нужно ввести название файла в поле

child_window(title="File name:", auto_id="1001", control_type="Edit")

И нажать на кнопку Save в нижней части окна.

child_window(title="Save", auto_id="1", control_type="Button")

# notepad_demo.py import time from pywinauto.application import Application backend = "uia" app = Application(backend=backend).start("C:\Windows\System32\notepad.exe") dlg = app.window(title="Untitled - Notepad") time.sleep(2) text_editor = dlg.child_window( auto_id="15", control_type="Edit" ).wrapper_object() text_editor.type_keys( "Subscribe to t.me/aofeed", with_spaces=True ) dlg.close() time.sleep(2) save_button = dlg.child_window( title="Save", auto_id="CommandButton_6", control_type="Button" ).wrapper_object() save_button.click() save_as_window = dlg.child_window( title="Save As", control_type="Window" ) file_name_edit = save_as_window.child_window( title="File name:", auto_id="1001", control_type="Edit" ) file_name_edit.type_keys( "demo.txt", with_spaces=True ) save_file_button = save_as_window.child_window( title="Save", auto_id="1", control_type="Button" ) save_file_button.click()

Confirm Save As

Если выполнить код из предыдущей главы один раз, то никаких проблем не возникнет.

Если выполнить его второй раз подряд - появится диалоговое окно с предложение подвердить перезапись файла.

Запустить notepad в pywinauto изображение с сайта www.testsetup.ru
Notepad
pywinauto

Полный список контроллеров можете изучить здесь

Нас интересует следующее:

Control Identifiers: Dialog - 'Untitled - Notepad' (L277, T238, R1603, B928) ['Dialog', 'Untitled - NotepadDialog', 'Untitled - Notepad', 'Dialog0', 'Dialog1'] child_window(title="Untitled - Notepad", control_type="Window") | | Dialog - 'Save As' (L563, T324, R1203, B836) | ['Save AsDialog', 'Dialog2', 'Save As'] | child_window(title="Save As", control_type="Window") | | | | Dialog - 'Confirm Save As' (L774, T445, R1140, B568) | | ['Confirm Save As', 'Confirm Save AsDialog', 'Dialog3'] | | child_window(title="Confirm Save As", control_type="Window") | | | | | | Image - 'MainInstructionIcon' (L787, T477, R819, B509) | | | ['Image', 'MainInstructionIconImage', 'MainInstructionIcon', 'Image0', 'Image1'] | | | child_window(title="MainInstructionIcon", auto_id="MainIcon", control_type="Image") | | | | | | Static - 'demo.txt already exists.\r\nDo you want to replace it?' (L829, T482, R1127, B508) | | | ['demo.txt already exists.\r\nDo you want to replace it?', 'Static', 'demo.txt already exists.\r\nDo you want to replace it?Static', 'Static0', 'Static1'] | | | child_window(title="demo.txt already exists.\r\nDo you want to replace it?", auto_id="ContentText", control_type="Text") | | | | | | Button - 'Yes' (L993, T533, R1055, B555) | | | ['Yes', 'Button', 'YesButton', 'Button0', 'Button1'] | | | child_window(title="Yes", auto_id="CommandButton_6", control_type="Button") | | | | | | Button - 'No' (L1065, T532, R1127, B556) | | | ['No', 'Button2', 'NoButton'] | | | child_window(title="No", auto_id="CommandButton_7", control_type="Button") …

# notepad_demo.py import time from pywinauto.application import Application backend = "uia" app = Application(backend=backend).start("C:\Windows\System32\notepad.exe") dlg = app.window(title="Untitled - Notepad") time.sleep(2) text_editor = dlg.child_window( auto_id="15", control_type="Edit" ).wrapper_object() text_editor.type_keys( "Subscribe to t.me/aofeed", with_spaces=True ) dlg.close() time.sleep(2) save_button = dlg.child_window( title="Save", auto_id="CommandButton_6", control_type="Button" ).wrapper_object() save_button.click() save_as_window = dlg.child_window( title="Save As", control_type="Window" ) file_name_edit = save_as_window.child_window( title="File name:", auto_id="1001", control_type="Edit" ) file_name_edit.type_keys( "demo.txt", with_spaces=True ) save_file_button = save_as_window.child_window( title="Save", auto_id="1", control_type="Button" ) save_file_button.click() time.sleep(2) dlg.print_control_identifiers(filename="5_notepad_controls_after_file_save.txt") # Для разнообразия создадим промежуточный объект confirm_save_as = save_as_window.child_window( title="Confirm Save As", control_type="Window" ) yes_button = confirm_save_as.child_window( title="Yes", auto_id="CommandButton_6", control_type="Button" ) yes_button.click()

Этот скрипт будет работать без ошибок в случае если файл demo.txt существует.

Если файла ещё нет, то окно с просьбой подтвердить перезапись не появится. А так как мы к нему обращаемся - получим ошибку

Traceback (most recent call last): File "Z:\auto\venv\lib\site-packages\pywinauto\application.py", line 250, in __resolve_control ctrl = wait_until_passes( File "Z:\auto\venv\lib\site-packages\pywinauto\timings.py", line 458, in wait_until_passes raise err pywinauto.timings.TimeoutError During handling of the above exception, another exception occurred: Traceback (most recent call last): File ".\notepad_demo.py", line 52, in <module> dlg.print_control_identifiers(filename="5_notepad_controls_after_file_save.txt") File "Z:\auto\venv\lib\site-packages\pywinauto\application.py", line 613, in print_control_identifiers this_ctrl = self.__resolve_control(self.criteria)[-1] File "Z:\auto\venv\lib\site-packages\pywinauto\application.py", line 261, in __resolve_control raise e.original_exception File "Z:\auto\venv\lib\site-packages\pywinauto\timings.py", line 436, in wait_until_passes func_val = func(*args, **kwargs) File "Z:\auto\venv\lib\site-packages\pywinauto\application.py", line 203, in __get_ctrl dialog = self.backend.generic_wrapper_class(findwindows.find_element(**criteria[0])) File "Z:\auto\venv\lib\site-packages\pywinauto\findwindows.py", line 87, in find_element raise ElementNotFoundError(kwargs) pywinauto.findwindows.ElementNotFoundError: {'title': 'Untitled - Notepad', 'backend': 'uia', 'process': 4056}

В нашем случае эта проблема решается проверкой существования файла перед попыткой сохранить.

Следующий скрипт будет работать в обоих случаях.

# notepad_demo.py import os import time from pywinauto.application import Application FILENAME = "demo.txt" backend = "uia" app = Application(backend=backend).start("C:\Windows\System32\notepad.exe") dlg = app.window(title="Untitled - Notepad") time.sleep(2) text_editor = dlg.child_window( auto_id="15", control_type="Edit" ).wrapper_object() text_editor.type_keys( "Subscribe to t.me/aofeed", with_spaces=True ) dlg.close() time.sleep(2) save_button = dlg.child_window( title="Save", auto_id="CommandButton_6", control_type="Button" ).wrapper_object() save_button.click() save_as_window = dlg.child_window( title="Save As", control_type="Window" ) file_name_edit = save_as_window.child_window( title="File name:", auto_id="1001", control_type="Edit" ) file_name_edit.type_keys( FILENAME, with_spaces=True ) save_file_button = save_as_window.child_window( title="Save", auto_id="1", control_type="Button" ) # Проверка if os.path.exists(FILENAME): save_file_button.click() time.sleep(2) # Здесь уже без промежуточного объекта yes_button = save_as_window.child_window( title="Yes", auto_id="CommandButton_6", control_type="Button" ) yes_button.click() else: save_file_button.click()

Не всегда будет способ проверить по какому сценарию пойдёт скрипт с помощью сторонних средств.

В предыдщем примере помогла проверка существования файла с помощью os.path.exists() .

В общем случае нужно уметь проверить, появилось какое-то окно или нет.

Мы можем сделать это с помощью метода exists() следующим образом

# Проверка save_file_button.click() yes_button = save_as_window.child_window( title="Yes", auto_id="CommandButton_6", control_type="Button" ) if yes_button.exists(timeout=2): yes_button.click() else: print("Yes button is not clicked")

Автор статьи: Андрей Олегович

Похожие статьи
pywinauto
Основы
Ошибки
Python

Поиск по сайту

Подпишитесь на Telegram канал @aofeed чтобы следить за выходом новых статей и обновлением старых

Перейти на канал

@aofeed

Задать вопрос в Телеграм-группе

@aofeedchat

Контакты и сотрудничество:
Рекомендую наш хостинг beget.com
Пишите на info@aredel.com если Вы:
1. Хотите написать статью для нашего сайта или перевести статью на свой родной язык.
2. Хотите разместить на сайте рекламу, подходящую по тематике.
3. Реклама на моём сайте имеет максимальный уровень цензуры. Если Вы увидели рекламный блок недопустимый для просмотра детьми школьного возраста, вызывающий шок или вводящий в заблуждение - пожалуйста свяжитесь с нами по электронной почте
4. Нашли на сайте ошибку, неточности, баг и т.д. ... .......
5. Статьи можно расшарить в соцсетях, нажав на иконку сети: