Блокнот в 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
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
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()
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
pywinauto
Закрыть блокнот таким образом уже не получится, так как мы внесли изменения и нужно их сохранить.
pywinauto
Закрыть и сохранить
После команды dlg.close() появляется следующее окно:
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
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
Если выполнить код из предыдущей главы один раз, то никаких проблем не возникнет.
Если выполнить его второй раз подряд - появится диалоговое окно с предложение
подвердить перезапись файла.
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 |