PyWinAuto Basics

Contents
Introduction
Install
Example
print_control_identifiers()
Nested objects
Search for nested object
Search with ctrl_ids and enter text
Search with app.windows()
requirements.txt
Related Articles

Introduction

pywinauto is a set of modules python for automating the graphical interface Microsoft Windows .

In its simplest form, it allows you to send mouse and keyboard actions to Windows dialog boxes and controls.

Official documentation

Install

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

Example

In some cases, you need to go to the directory with the .exe file before launching the application

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

Start notepad with pywinauto image from website www.aredel.com
Notepad

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

Start notepad with pywinauto image from website www.aredel.com
Notepad

print_control_identifiers()

To control the application, you need to get a list of controller objects. To do this you can use print_control_identifiers()

print_control_identifiers() displays a list of all controllers available from the current state of the application and not all controllers at all.

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'] | | …

You can learn more about notepad control identifiers in the article «Autotesting a notebook with 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 )

Start notepad with pywinauto image from website www.aredel.com
Notepad

There are often a lot of controllers and it is inconvenient to look at their output to the terminal.

This problem is solved by writing to a file that needs to be specified as an argument

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

File controls.txt will be created in the working directory, that is, most likely next to with the .exe file being run and not next to the script.

Objects in nested menus

When was the first time we used print_control_identifiers() a list of all controllers available from the start window was obtained.

To get the lists of controllers that are available from the submenu, you must first run click_input() to the desired menu and then immediately execute print_control_identifiers()

Using this algorithm, you can create a library from all available controllers.

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()

Start notepad with pywinauto image from website www.aredel.com
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") | | …

Additionally, you can find the control identifiers Notepad in the article «Autotesting a notebook with pywinauto»

Example of searching for a nested object

Let's open a new window.

To do this, in the previous output we will find

… | | 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")

Copy the line with clild_window, but instead of New Window Ctrl+Shift+N we will use 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()

The dialog box

For simplicity, we will not open a new window. Open the notepad, enter the text and use the close controller

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

Let's close the notebook. The Save, Don't Save dialog box appears and so on.

At this point, you need to print the controllers of this dialog box and use the one you need.

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") …

Controllers

There are two sets of controllers:

Searching for a text input element

Let's say you need to enter text into similar element

ComboBox pywinauto image from website www.aredel.com
Input Box
www.aredel.com

Approximate procedure:

Launch the application.

Define the title of the dialog box - in this example, it is the App Name.

Print controllers for the dialog box

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

Somewhere in the output there will be a similar result

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") …

If there was text in the field, for example abc, it would be displayed like this:

… | 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") …

Since the desired element is contained inside the ComboBox, you can iterate through all ComboBox using found_index

Let's assume that we are lucky and the desired box has an index of 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")

In a similar way, we will find a nested input field. Since there is only one child element, there will also be an index of 0.

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

Result:

ComboBox pywinauto image from website www.aredel.com
Successful input
www.aredel.com

Full code

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://aredel.com")

app.windows()

An example of a search by app.windows(). I spied on it here

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

Related Articles
Automation
pywinauto
Python
Banner Image

Search on this site

Subscribe to @aofeed channel for updates

Visit Channel

@aofeed

Feedback and Questions in Telegram

@aofeedchat