Основы PyWinAuto

Содержание
Введение
Установка
Пример применения
print_control_identifiers()
Объекты во вложенных меню
Пример поиска вложенного объекта
Поиск по ctrl_ids и ввод текста
Поиск по app.windows()
requirements.txt
Common Files
Похожие статьи

Введение

pywinauto - это набор модулей python для автоматизации графического интерфейса Microsoft Windows .

В самом простом варианте он позволяет отправлять действия мыши и клавиатуры в диалоговые окна и элементы управления Windows.

Официальная документация

Установка

python -m pip install pywinauto

Collecting pywinauto Downloading pywinauto-0.6.8-py2.py3-none-any.whl (362 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 362.9/362.9 kB ? eta 0:00:00 Collecting six Using cached six-1.16.0-py2.py3-none-any.whl (11 kB) Collecting comtypes Downloading comtypes-1.2.0-py2.py3-none-any.whl (184 kB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 184.3/184.3 kB ? eta 0:00:00 Collecting pywin32 Downloading pywin32-306-cp311-cp311-win_amd64.whl (9.2 MB) ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.2/9.2 MB 21.8 MB/s eta 0:00:00 Installing collected packages: pywin32, comtypes, six, pywinauto Successfully installed comtypes-1.2.0 pywin32-306 pywinauto-0.6.8 six-1.16.0

Пример

В некоторых случаях нужно перейти в директорию с .exe файлом перед тем как запускать приложение

import os os.chdir("PATH")

from pywinauto.application import Application app = Application(backend='uia').start('notepad.exe') # app = Application(backend='win32').start('')

python pywinauto_tutorial.py

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

from pywinauto.application import Application app = Application(backend='uia').start('notepad.exe') app = Application(backend='uia').connect(title='Untitled - Notepad', timeout=100)

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

print_control_identifiers()

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

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

from pywinauto.application import Application app = Application(backend='uia').start('notepad.exe') app = Application(backend='uia').connect(title='Untitled - Notepad', timeout=100) app.UntitledNotepad.print_control_identifiers()

Control Identifiers: Dialog - 'Untitled - Notepad' (L-1127, T248, R-579, B1034) ['Untitled - Notepad', 'Untitled - NotepadDialog', 'Dialog'] child_window(title="Untitled - Notepad", control_type="Window") | | Edit - 'Text Editor' (L-1118, T311, R-588, B996) | ['Edit', 'Edit0', 'Edit1'] | child_window(title="Text Editor", auto_id="15", control_type="Edit") | | | | ScrollBar - 'Vertical' (L-609, T311, R-588, B975) | | ['VerticalScrollBar', 'Vertical', 'ScrollBar', 'ScrollBar0', 'ScrollBar1'] | | child_window(title="Vertical", auto_id="NonClientVerticalScrollBar", control_type="ScrollBar") | | | | | | Button - 'Line up' (L-609, T311, R-588, B332) | | | ['Button', 'Line up', 'Line upButton', 'Button0', 'Button1'] | | | child_window(title="Line up", auto_id="UpButton", control_type="Button") | | | | | | Button - 'Line down' (L-609, T954, R-588, B975) | | | ['Button2', 'Line downButton', 'Line down'] | | | child_window(title="Line down", auto_id="DownButton", control_type="Button") | | | | ScrollBar - 'Horizontal' (L-1118, T975, R-609, B996) | | ['HorizontalScrollBar', 'ScrollBar2', 'Horizontal'] | | child_window(title="Horizontal", auto_id="NonClientHorizontalScrollBar", control_type="ScrollBar") | | | | | | Button - 'Column left' (L-1118, T975, R-1097, B996) | | | ['Button3', 'Column left', 'Column leftButton'] | | | child_window(title="Column left", auto_id="UpButton", control_type="Button") | | | | | | Button - 'Column right' (L-630, T975, R-609, B996) | | | ['Button4', 'Column rightButton', 'Column right'] | | | child_window(title="Column right", auto_id="DownButton", control_type="Button") | | | | Thumb - '' (L-609, T975, R-588, B996) | | ['Thumb'] | | StatusBar - 'Status Bar' (L-1118, T996, R-588, B1025) | ['StatusBar', 'Status Bar', 'Status BarStatusBar'] | child_window(title="Status Bar", auto_id="1025", control_type="StatusBar") | | | | Static - '' (L0, T0, R0, B0) | | ['Static', 'Static0', 'Static1'] | | …

Подробнее можно изучить control identifiers блокнота в статье «Автотестирование блокнота с pywinauto»

from pywinauto.application import Application app = Application(backend='uia').start('notepad.exe') app = Application(backend='uia').connect( title='Untitled - Notepad', timeout=100 ) # app.UntitledNotepad.print_control_identifiers() text_editor = app.UntitledNotepad.child_window( title="Text Editor", auto_id="15", control_type="Edit" ).wrapper_object() text_editor.type_keys( "Subscribe to t.me/aofeed", with_spaces=True )

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

Часто контроллеров очень много и смотреть на их вывод в терминал неудобно.

Решается эта проблема записью в файл, который нужно указать как аргумент

app.MyApp.print_control_identifiers(filename="controls.txt")

Файл controls.txt будет создан в рабочей директории, то есть скорее всего рядом с запускаемым .exe файлом а не рядом со скриптом.

Объекты во вложенных меню

Когда мы в первый раз использовали print_control_identifiers() был получен список всех контроллеров, доступных из стартового окна.

Чтобы полуить списки контроллеров, которые доступны из подменю, нужно сперва выполнить click_input() на нужное меню и затем сразу же выполнить print_control_identifiers()

Действуя по этому алгоритму можно создать библиотеку из всех доступных контроллеров.

from pywinauto.application import Application app = Application(backend='uia').start('notepad.exe') app = Application(backend='uia').connect( title='Untitled - Notepad', timeout=100 ) # app.UntitledNotepad.print_control_identifiers() text_editor = app.UntitledNotepad.child_window( title="Text Editor", auto_id="15", control_type="Edit" ).wrapper_object() text_editor.type_keys( "Subscribe to t.me/aofeed", with_spaces=True ) file_menu = app.UntitledNotepad.child_window(title="File", control_type="MenuItem").wrapper_object() file_menu.click_input() app.UntitledNotepad.print_control_identifiers()

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

Control Identifiers: Dialog - '*Untitled - Notepad' (L-1512, T253, R641, B1009) ['*Untitled - Notepad', 'Dialog', '*Untitled - NotepadDialog'] child_window(title="*Untitled - Notepad", control_type="Window") | | Menu - 'File' (L-1503, T315, R-1234, B527) | ['File', 'FileMenu', 'Menu', 'Menu0', 'Menu1', 'File0', 'File1'] | child_window(title="File", control_type="Menu") | | | | MenuItem - 'New Ctrl+N' (L-1500, T318, R-1237, B342) | | ['New\tCtrl+N', 'New\tCtrl+NMenuItem', 'MenuItem', 'MenuItem0', 'MenuItem1'] | | child_window(title="New Ctrl+N", auto_id="1", control_type="MenuItem") | | | | MenuItem - 'New Window Ctrl+Shift+N' (L-1500, T342, R-1237, B366) | | ['MenuItem2', 'New Window\tCtrl+Shift+N', 'New Window\tCtrl+Shift+NMenuItem'] | | child_window(title="New Window Ctrl+Shift+N", auto_id="8", control_type="MenuItem") | | | | MenuItem - 'Open... Ctrl+O' (L-1500, T366, R-1237, B390) | | ['Open...\tCtrl+O', 'Open...\tCtrl+OMenuItem', 'MenuItem3'] | | child_window(title="Open... Ctrl+O", auto_id="2", control_type="MenuItem") | | | | MenuItem - 'Save Ctrl+S' (L-1500, T390, R-1237, B414) | | ['Save\tCtrl+SMenuItem', 'Save\tCtrl+S', 'MenuItem4'] | | child_window(title="Save Ctrl+S", auto_id="3", control_type="MenuItem") | | | | MenuItem - 'Save As... Ctrl+Shift+S' (L-1500, T414, R-1237, B438) | | ['MenuItem5', 'Save As...\tCtrl+Shift+S', 'Save As...\tCtrl+Shift+SMenuItem'] | | child_window(title="Save As... Ctrl+Shift+S", auto_id="4", control_type="MenuItem") | | | | MenuItem - 'Page Setup...' (L-1500, T445, R-1237, B469) | | ['Page Setup...MenuItem', 'MenuItem6', 'Page Setup...'] | | child_window(title="Page Setup...", auto_id="5", control_type="MenuItem") | | | | MenuItem - 'Print... Ctrl+P' (L-1500, T469, R-1237, B493) | | ['Print...\tCtrl+P', 'MenuItem7', 'Print...\tCtrl+PMenuItem'] | | child_window(title="Print... Ctrl+P", auto_id="6", control_type="MenuItem") | | | | MenuItem - 'Exit' (L-1500, T500, R-1237, B524) | | ['Exit', 'ExitMenuItem', 'MenuItem8'] | | child_window(title="Exit", auto_id="7", control_type="MenuItem") | | Edit - 'Text Editor' (L-1503, T316, R632, B971) | ['Edit', 'Edit0', 'Edit1'] | child_window(title="Text Editor", auto_id="15", control_type="Edit") | | | | ScrollBar - 'Vertical' (L611, T316, R632, B950) | | ['Vertical', 'ScrollBar', 'VerticalScrollBar', 'ScrollBar0', 'ScrollBar1'] | | child_window(title="Vertical", auto_id="NonClientVerticalScrollBar", control_type="ScrollBar") | | | | | | Button - 'Line up' (L611, T316, R632, B337) | | | ['Line up', 'Line upButton', 'Button', 'Button0', 'Button1'] | | | child_window(title="Line up", auto_id="UpButton", control_type="Button") | | | | | | Button - 'Line down' (L611, T929, R632, B950) | | | ['Line down', 'Line downButton', 'Button2'] | | | child_window(title="Line down", auto_id="DownButton", control_type="Button") | | …

Подробнее можно изучить control identifiers блокнота в статье «Автотестирование блокнота с pywinauto»

Пример поиска вложенного объекта

Откроем новое окно.

Для этого в предыдущем выводе найдём

… | | MenuItem - 'New Window Ctrl+Shift+N' (L-1500, T342, R-1237, B366) | | ['MenuItem2', 'New Window\tCtrl+Shift+N', 'New Window\tCtrl+Shift+NMenuItem'] | | child_window(title="New Window Ctrl+Shift+N", auto_id="8", control_type="MenuItem")

Скопируем строку с clild_window, но вместо New Window Ctrl+Shift+N используем New Window\tCtrl+Shift+N

from pywinauto.application import Application app = Application(backend='uia').start('notepad.exe') app = Application(backend='uia').connect( title='Untitled - Notepad', timeout=100 ) # app.UntitledNotepad.print_control_identifiers() text_editor = app.UntitledNotepad.child_window( title="Text Editor", auto_id="15", control_type="Edit" ).wrapper_object() text_editor.type_keys( "Subscribe to t.me/aofeed", with_spaces=True ) file_menu = app.UntitledNotepad.child_window( title="File", control_type="MenuItem" ).wrapper_object() file_menu.click_input() app.UntitledNotepad.print_control_identifiers() new_window = app.UntitledNotepad.child_window( title="New Window\tCtrl+Shift+N", auto_id="8", control_type="MenuItem" ).wrapper_object() new_window.click_input()

Диалоговое окно

Для простоты не будем открывать новое окно. Откроем блокнот, введём текст и с помощью контроллера close

… | | Button - 'Close' (L573, T254, R633, B291) | | ['CloseButton', 'Close', 'Button7'] | | child_window(title="Close", control_type="Button") …

закроем блокнот. Появится диалоговое окно Save, Don't Save и так далее.

В этот момент нужно распечатать контроллеры этого диалогового окна и использовать нужный.

from pywinauto.application import Application app = Application(backend='uia').start('notepad.exe') app = Application(backend='uia').connect( title='Untitled - Notepad', timeout=100 ) text_editor = app.UntitledNotepad.child_window( title="Text Editor", auto_id="15", control_type="Edit" ).wrapper_object() text_editor.type_keys( "Subscribe to t.me/aofeed", with_spaces=True ) file_menu = app.UntitledNotepad.child_window( title="File", control_type="MenuItem" ).wrapper_object() file_menu.click_input() close = app.UntitledNotepad.child_window( title="Close", control_type="Button" ).wrapper_object() close.click_input() app.UntitledNotepad.print_control_identifiers() dont_save = app.UntitledNotepad.child_window( title="Don't Save", auto_id="CommandButton_7", control_type="Button" ).wrapper_object() dont_save.click_input()

… Control Identifiers: Dialog - '*Untitled - Notepad' (L321, T173, R769, B851) ['Dialog', '*Untitled - Notepad', '*Untitled - NotepadDialog', 'Dialog0', 'Dialog1'] child_window(title="*Untitled - Notepad", control_type="Window") | | Dialog - 'Notepad' (L1042, T568, R1500, B745) | ['Notepad', 'Dialog2', 'NotepadDialog'] | child_window(title="Notepad", control_type="Window") | | | | Static - 'Do you want to save changes to Untitled?' (L1064, T619, R1427, B647) | | ['Static', 'Do you want to save changes to Untitled?', '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' (L1174, T696, R1261, B725) | | ['Button', 'Save', 'SaveButton', 'Button0', 'Button1'] | | child_window(title="Save", auto_id="CommandButton_6", control_type="Button") | | | | Button - 'Don't Save' (L1269, T696, R1383, B725) | | ['Button2', "Don't SaveButton", "Don't Save"] | | child_window(title="Don't Save", auto_id="CommandButton_7", control_type="Button") | | | | Button - 'Cancel' (L1391, T696, R1478, B725) | | ['Cancel', 'Button3', 'CancelButton'] | | child_window(title="Cancel", auto_id="CommandButton_2", control_type="Button") | | | | TitleBar - '' (L1051, T571, R1491, B606) | | ['TitleBar', 'TitleBar0', 'TitleBar1'] | | | | | | Button - 'Close' (L1448, T569, R1492, B606) | | | ['Button4', 'CloseButton', 'Close', 'CloseButton0', 'CloseButton1', 'Close0', 'Close1'] | | | child_window(title="Close", control_type="Button") …

Контроллеры

Существует два набора контроллеров:

Поиск элемента для ввода текста

Допустим нужно ввести текст в такой элемент

ComboBox pywinauto изображение с сайта www.testsetup.ru
Форма ввода
www.testsetup.ru

Примерный порядок действий:

Запустить приложение.

Определить название (title) диалогового окна - в данном примере это App Name.

Распечатать контроллеры для диалогового окна

app = Application(backend='win32').start("path_to_exe") app = app.connect(best_match="App Name", timeout=3) dlg = app.AppName dlg.print_ctrl_ids()

Где-то в выдаче будет похожий результат

Control Identifiers: Dialog - 'App Name' (L735, T375, R1186, B777) ['App NameDialog', 'App Name', 'Dialog'] child_window(title="App Name", class_name="#32770") | … | ComboBox - '' (L819, T518, R1067, B539) | ['ComboBox', '&Destination:ComboBox', 'ComboBox0', 'ComboBox1'] | child_window(title="", class_name="ComboBox") | | | | Edit - '' (L822, T521, R1047, B536) | | ['&Destination:Edit', 'Edit', 'Edit0', 'Edit1'] | | child_window(title="", class_name="Edit") …

Если бы в поле был текст, например abc, то он бы отобразился так:

… | ComboBox - 'abc' (L819, T518, R1067, B539) | ['ComboBox', '&Destination:ComboBox', 'ComboBox0', 'ComboBox1'] | child_window(title="abc", class_name="ComboBox") | | | | Edit - 'abc' (L822, T521, R1047, B536) | | ['&Destination:Edit', 'Edit', 'Edit0', 'Edit1'] | | child_window(title="abc", class_name="Edit") …

Так как искомый элемент содержится внутри ComboBox можно перебрать все ComboBox с помощью found_index

Предположим, что нам повезло и нужный бокс имеет индекс 0.

destination = dlg.child_window(class_name="ComboBox", found_index=0) destination.print_ctrl_ids()

Control Identifiers: ComboBox - 'abc' (L819, T518, R1067, B539) ['ComboBox'] child_window(title="abc", class_name="ComboBox") | | Edit - 'abc' (L822, T521, R1047, B536) | ['Edit'] | child_window(title="abc", class_name="Edit")

Аналогичным способом найдём вложенное поле для ввода. Так как дочерный элемент всего один здесь тоже будет индекс 0.

text_input = destination.child_window(class_name="Edit", found_index=0) text_input.set_text("https://devhops.ru")

Результат:

ComboBox pywinauto изображение с сайта www.testsetup.ru
Текст успешно введён
www.testsetup.ru

Полный код

app = Application(backend='win32').start("path_to_exe") app = app.connect(best_match="App Name", timeout=3) dlg = app.AppName # dlg.print_ctrl_ids() destination = dlg.child_window(class_name="ComboBox", found_index=0) # destination.print_ctrl_ids() text_input = destination.child_window(class_name="Edit", found_index=0) text_input.set_text("https://devhops.ru")

app.windows()

Пример поиска по app.windows(). Подсмотрел здесь

print([w.window_text() for w in app.windows()]) i = 0 for w in app.windows(): i += 1 print(i) print(dir(w)) print(w.children()) if i == 1: wind = w print(dir(wind)) print(wind.children()) children = wind.children() i = 0 for child in children: i += 1 print(dir(child)) print(child.texts) if i == 1: child.click()

requirements.txt

certifi==2024.2.2 charset-normalizer==3.3.2 comtypes==1.4.1 idna==3.7 pywin32==306 pywinauto==0.6.8 requests==2.31.0 six==1.16.0 typing_extensions==4.12.2 urllib3==2.2.1 WMI==1.5.1

Common Files

from pywinauto import Desktop, Application Application().start('explorer.exe "C:\Program Files"') # connect to another process spawned by explorer.exe # Note: make sure the script is running as Administrator! app = Application(backend="uia").connect(path="explorer.exe", title="Program Files") app.ProgramFiles.set_focus() common_files = app.ProgramFiles.ItemsView.get_item('Common Files') common_files.right_click_input() app.ContextMenu.Properties.invoke() # this dialog is open in another process (Desktop object doesn't rely on any process id) Properties = Desktop(backend='uia').Common_Files_Properties Properties.print_control_identifiers() Properties.Cancel.click() Properties.wait_not('visible') # make sure the dialog is closed

Похожие статьи
Автоматизация
pywinauto
Ошибки
Python
Изображение баннера

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

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

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

@aofeed

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

@aofeedchat

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