Windows GUI Automation with Python (based on text properties)
BSD-3-CLAUSE License
Bot releases are visible (Hide)
Published by vasily-v-ryabov almost 5 years ago
allow_magic_lookup
flag for Application and Desktop object. Thanks @pakal!win32_hooks.py
. Thanks @TomRobo237!vk_packet=False
in method type_keys
, default value is vk_packet=True
. Thanks @philmbailey!__init__.py
. Thanks @pakal!remote_memory_block.py
. Thanks @TomRobo237!Published by vasily-v-ryabov over 5 years ago
ctypes.wintypes
more to avoid redundant definitions for Win32 API.EditWrapper.is_editable()
for "uia" backend.InvalidControlType
properly.menu_select()
for one level main menu in WinForms apps.Application
object non-iterable (iterating was hang).GetWindowRect
(method .rectangle()
).dump_tree() / print_control_identifiers()
.Published by vasily-v-ryabov over 5 years ago
kill()
hard (and fast) by default (can be used with param soft=True
optionally).visible_only=False
a default option for method connect()
(useful for minimized apps).down
and up
for .type_keys()
method. See the improved docs for keyboard module for more details. Thanks @badari412 !windows()
to class Desktop
.UnicodeDecodeError/UnicodeEncodeError
in several casesbackend="uia"
.COMError
for runtime_id
property.click()
for some radio buttons.utf-8
encoding while writing dump_tree()
output to file.backend="win32"
.GetWindowRect
call.Published by vasily-v-ryabov about 6 years ago
automation_id
and control_type
propertiesauto_id
and control_type
.iter_children()
and iter_descendants()
.is_checked()
to "win32" check box.Application().connect(...)
works better with timeout
.set_focus()
for "uia" backend including minimized window casemaximize()/minimize()
methods can be chained now.@always_wait_until_passes
and @always_wait_until
..format()
for logging BaseWrapper actions (issue #471).Published by vasily-v-ryabov over 6 years ago
ValueError: NULL COM pointer access
(UIA backend).Multi-threading mode (MTA) for comtypes is enabled by default, if it's not initialized
by another library before importing pywinauto.
Method get_value()
has been added to EditWrapper in UIA backend.
Method scroll()
has been added for all UIA controls which have ScrollPattern implemented.
Added methods is_minimized/is_maximized/is_normal/get_show_state
for UIAWrapper.
Added handling in-place controls inside ListView control and (row, column) indexing
in a grid-like table mode. Examples:
auto_detected_ctrl = list_view.get_item(0).inplace_control()
combo = list_view.get_item(1,1).inplace_control("ComboBox")
combo.select("Item name")
edit = list_view.get_item(3,4).inplace_control("Edit")
edit.type_keys("some text{ENTER}", set_foreground=False)
dt_picker = list_view.get_item(2,0).inplace_control("DateTimePicker")
Published by vasily-v-ryabov over 7 years ago
backend="uia"
.wait/wait_not
methods:
wait('exists')
doesn't work for backend="uia"
. Thanks @maollm!wait/wait_not
take ~ default time (5 sec.) instead of customized timeout like 1 sec.depth
param can used in a WindowSpecification
now. depth=1
means this control,depth=2
means immediate children only and so on (aligned with print_control_identifiers
method). Thanks @dmitrykazanbaev!send_chars
is supposed to send character input (this includes {Enter}
, {Tab}
, {Backspace}
) without Alt/Shift/Ctrl modifiers.send_keystrokes
is for key input (including key combinations with Alt/Shift/Ctrl modifiers).Application().connect(path='your.exe')
uses default timeout Timings.app_connect_timeout
.timeout
and retry_interval
keyword arguments. Thanks @daniil-kukushkin!print_control_identifiers
is more consistent and minimum 2x faster now! Thanks @cetygamer!Application
with your own methods. Thanks @efremovd!work_dir
can be used in Application().start(...)
. Thanks @efremovd!Application
has been enriched with methods is_process_running()
and wait_for_process_exit()
. Thanks @efremovd!timings
uses time.clock()
for Python 2.x and time.perf_counter()
for Python 3.xprint_control_identifiers()
can dump UI elements tree to a file. Thanks @sovrasov!backend="uia"
, extended example for MS Paint. Thanks @ArtemSkrebkov!CalendarWrapper
for backend="win32"
with these methods: get_month_delta
,set_month_delta
and get_month_range
. Thanks @Nikita-K!legacy_properties()
to UIAWrapper
. Thanks @AsyaPronina!backend="win32"
. Thanks @KirillMoizik!TreeViewWrapper
for backend="win32"
argument 4: <type 'exceptions.OverflowError'>: long int too long to convert
).Published by vasily-v-ryabov over 7 years ago
set_focus()
.print_control_identifiers()
gets bytes string on Python 3.x.ensure_visible()
is called inside before the click.taskbar.SystemTrayIcons
localization friendly.Published by vasily-v-ryabov over 7 years ago
win32_hooks
module is well tested and more reliable now. See detailed example.HwndWrapper.set_focus()
fails when used via interpreter. Thanks @mtkennerly!comtypes
prints a lot of warnings at import pywinauto
.is_dialog()
and restore()
are missed for UIA backend.print_control_identifiers()
crashes on some applications with Unicode symbols.python setup.py install
may fail if pyWin32 dependency was installed manually.dlg = app.Custom.Menu
.send_chars()
can now send {ENTER}
to some applications. Thanks @batterseapower!control_type
or auto_id
inApplication.kill()
is also optimized in many cases.Published by vasily-v-ryabov almost 8 years ago
app = Application(backend='uia').start('your_app.exe')
.'win32'
if nothing is specified.UIAutomationCore.dll
through comtypes
(like UiaComWrapper for .NET but in CPython).keyboard
and mouse
can be used out of any window context now. And they work on Linux as well!ElementInfo
and from BaseWrapper
. New backendbackend.register()
. Linux AT SPI and Apple Accessibility API are in the long term plans.click_input
should be usedClickInput
.win32_hooks
module. Keyboard and mouse eventwin32_hooks
module isPublished by vasily-v-ryabov almost 9 years ago
where="check"
possible value to the ListViewWrapper.Click/ClickInput` methods.CheckByClickInput
and UncheckByClickInput
methods for a plain check box.MenuBarClickInput
method of the ToolbarWrapper
.Published by vasily-v-ryabov about 9 years ago
TypeKeys
method again;SetEditText/TypeKeys
methods to take non-string arguments;SetEditText/TypeKeys
.Wait("active")
, raise a SyntaxError when waiting for an incorrect state.MenuItem
method Click
is renamed to ClickInput
while Click = Select
now.SetTransparency
method can make a window transparent in a specified degree (from 0 to 255).found_index
is useful to choose the control in the list of similar ones with same texts.Published by vasily-v-ryabov about 9 years ago
CPUUsage
returns CPU usage as a percent (float number),WaitCPUUsageLower
waits until the connected process' CPU usage is lower thanListViewWrapper
interface is aligned with TreeViewWrapper
. GetItem()
returns a _listview_item
object that looks like a _treeview_element
now.TreeViewWrapper.Select
doesn't work when the control is not in focus.TabControlWrapper.Select
doesn't work in case of TCS_BUTTONS style set.ListViewWrapper
methods Check/UnCheck
are fixed.ClientToScreen
method doesn't return a value (modifying mutable argument is not good practice).Published by vasily-v-ryabov over 9 years ago
DebugMessage
methodactionlogger.enable()
and actionlogger.disable()
.logging
module there's method actionlogger.set_level(level)
.Published by vasily-v-ryabov over 9 years ago
best_match
algorithm allows names like ToolbarFile
$23453
)RemoteMemoryBlock
can now detect memory corruption by checking guard signaturetaskbar
modulesysinfo
module for checking 32-bit or 64-bit OS and Pythonset_foreground
flag in TypeKeys
method for typing into in-place controlscreate_new_console
and wait_for_idle
in Application.start
methodPublished by vasily-v-ryabov over 9 years ago
findbestmatch.GetNonTextControlName()
extra_tests.py
which needs to export a ModifyRegisteredTests()
method.notepad_fast.py
to make it easier to profile (added a method)WrapHandle
to use a cache for classes it has matched - this isSendMessageTimeout
to .001 seconds from .4WaitNot
was raising an error if the control was not found - it shouldListViewWrapper.Deselect()
per Chistophe Keller's suggestion.WaitGuiIdle(self)
so that the control has a chance to processtimings.Timings
class. There arePublished by vasily-v-ryabov over 9 years ago
Added automatic Application data collection which can be used when
running the same test on a different spoken language version. Support
is still preliminary and is expected to change. Please treat as early
Alpha.
If you have a different language version of Windows then you can try
this out by running the notepad_fast.py
example with the langauge
argument e.g.
examples\notepad_fast.py language
This will load the application data from the supplied file
notepad_fast.pkl and use it for finding the right menu items and
controls to select.
app = Application().connect_(title = 'Find')
app.Find.Close.Click()
app.NotePad.MenuSelect("File->Exit")
1st change was to implement static methods start()
and
connect()
. These methods return a new Application instance
so the above code becomes
app = Application.connect(title = 'Find')
app.Find.Close.Click()
app.NotePad.MenuSelect("File->Exit")
I also wanted to make it easier to start working with a simple
application - that may or may not have only one dialog. To make this
situation easier I made window_()
not throw if the application has not
been start()ed
or connect()ed
first. This leads to simpler code
like
app = Application()
app.Find.Close.Click()
app.NotePad.MenuSelect("File->Exit")
What happens here is that when you execute any of Application.window_()
,
Application.__getattr__()
or Application.__getitem__()
when the
application hasn't been connected or started. It looks for the window
that best matches your specification and connects the application to
that process.
This is extra functionality - existing connect_()
and
start_()
methods still exist
HwndWrapper.SetFocus()
so that it would work even if the windowcontrols.common_controls.TabControlWrapper.GetTabState()
TabStates()
as these did not seem to be returning valid values anyway.MenuSelect
would sometimes not work as expected.SetFocus()
before selecting a menu item to ensure thatclipboard.GetData()
Published by vasily-v-ryabov over 9 years ago
application.WindowSpecification.Wait()
and WaitNot()
methods.WaitReady()
,WaitNotEnabled()
, WaitNotVisible()
now use these methods. I was able to alsoWaitNotReady()
, WaitEnabled()
, WaitVisible()
,WaitExists()
, WaitNotExists()
. Please use Wait()
and WaitNot()
as I haveWait*
methods.application.Application.start()
and connect()
static methods. Thesestart_()
and connect_()
as they are from pywinauto.application import Application
notepad = Application.start("notepad")
same_notepad = Application.connect(path = "notepad")
Published by vasily-v-ryabov over 9 years ago
PopupMenuWrapper
for context menu's app.Notepad.Edit.RightClick()
# need to use MenuClick rather then MenuSelect
app.PopupMenu.MenuClick("Select All")
app.Notepad.Edit.RightClick()
app.PopupMenu.MenuClick("Copy")
I could think of merging the RightClick()
and MenuSelect()
into one method
ContextMenuSelect()
if that makes sense to most people.
Added Support for Up-Down controls
Not all top level windows now have a FriendlyClassName of "Dialog".
I changed this because it made it hard to get windows of a particular
class. For example the main Notepad window has a class name of "Notepad".
This was primarily implemented due to work I did getting the System Tray.
Renamed StatusBarWrapper.PartWidths()
to PartRightEdges()
as this
is more correct for what it returns.
Changed HwndWrapper.Text() and SetText() to WindowText() and
SetWindowText() respectively to try and make it clearer that it is
the text returned by GetWindowText and not the text that is visible
on the control. This change also suggested that EditWrapper.SetText()
be changed to SetEditText() (though this is not a hard requirement
EditWrapper.SetText() still exists - but may be deprecated.
Added ClickInput, DoubleClickInput, RightClickInput, PressMouseInput
ReleaseMouseInput to HwndWrapper - these use SendInput rather then
WM_LBUTTONDOWN, WM_RBUTTONUP, etc used by Click, DoubleClick etc.
I also added a MenuClick method that allows you to click on menu
items. This means you can now 'physically' drop menus down.
Some further working with tooltips that need to be cleaned up.
Fixed a bug where coordinates passed to any of the Click operations had
the X and Y coordinates swapped.
Added new MenuItem and Menu classes that are to the most part hidden
but you can get a menu item by doing
app.Notepad.MenuItem("View")
app.Notepad.MenuItem("View->Status Bar")
MenuItems have various actions so for example you can use
MenuItem.IsChecked()
to check if the menu item is checked.
Among other methods there are Click()
and Enabled()
.
Modified the 'best match' algorithm for finding controls.
It now searches a couple of times, and tries to find the best
fit for the text passed to it. The idea here is to make it more
"Select what I want - not that other thing that looks a bit like
what I want!". It is possible this change could mean you need to
use new identifiers in scripts - but in general very little modification
should be necessary.
There was also a change to the algorithm that looked for the closest
text control. It missed some obvious controls in the previous
implementation. It also had a bug for controls above the control
rather than to the left.
Added a new example scripts SaveFromInternetExplorer.py and
SaveFromFirefox.py which show automating downloading of a page
from either of these browsers.
Added yet more unit tests, there are now a total of 134 tests.
Published by vasily-v-ryabov over 9 years ago
DialogWrapper.MenuSelect()
method to notify the parent app.dlg.<Nearby_text><Window_class>
e.g. to reference the "Footer" edit control in the Page Setup dialog
you could use
app.PageSetup.FooterEdit
MoveWindow
method to HwndWrapper
Published by vasily-v-ryabov over 9 years ago
_connect, _start, _window, _control, _write
respectively to
connect_, start_, window_, connect_, write_
If you forget to change _window
, _connect
and _start
then you will probably get the following error:
TypeError: '_DynamicAttributes' object is not callable
pywinauto
is now a package name - you need to import it or its modulesFriendlyClassName
is discovered (and still not really happy!)