Renamed docs folder
This commit is contained in:
parent
5ab3a06f26
commit
db1d44a7a5
7
doc_build/about.txt
Normal file
7
doc_build/about.txt
Normal file
@ -0,0 +1,7 @@
|
||||
About pywinauto
|
||||
===============
|
||||
|
||||
Pywinauto is a tool to automate Windows dialogs and controls.
|
||||
|
||||
There are also some tests supplied - but these are more
|
||||
applicable to Localisation UI testing.
|
316
doc_src/HowTo.txt
Normal file
316
doc_src/HowTo.txt
Normal file
@ -0,0 +1,316 @@
|
||||
========
|
||||
How To's
|
||||
========
|
||||
|
||||
How to sepcify an usable Application instance
|
||||
---------------------------------------------
|
||||
An ``Application()`` instance is the point of contact for all work
|
||||
with the app you are automating. So the Application instance needs
|
||||
to be connected to a process. There are two ways of doing this::
|
||||
|
||||
start_(self, cmd_line, timeout = app_start_timeout):
|
||||
|
||||
or ::
|
||||
|
||||
connect_(self, **kwargs):
|
||||
|
||||
|
||||
``start_()`` is used when the application is not running and you
|
||||
need to start it. Use it in the following way::
|
||||
|
||||
app = Application()
|
||||
app.start_(r"c:\path\to\your\application -a -n -y --arguments")
|
||||
|
||||
The timeout parameter is optional, it should only be necessary to use
|
||||
it if the application takes a long time to start up.
|
||||
|
||||
``connect_()`` is used when the application to be automated is already
|
||||
running. To specify a an already runing application you need to specify
|
||||
one of the following:
|
||||
|
||||
:process: the process id of the application, e.g.
|
||||
|
||||
::
|
||||
|
||||
app = Application()
|
||||
app.connect_(process = 2341)
|
||||
|
||||
|
||||
:handle: The windows handle of a window of the application, e.g.
|
||||
|
||||
::
|
||||
|
||||
app = Application()
|
||||
app.connect_(handle = 0x010f0c)
|
||||
|
||||
|
||||
:path: The path of the executable of the process (``GetModuleFileNameEx``
|
||||
is used to find the path of each process and compared against
|
||||
the value passed in) e.g.
|
||||
|
||||
::
|
||||
|
||||
app = Application()
|
||||
app.connect_(path = r"c:\windows\system32\notepad.exe")
|
||||
|
||||
or any combination of the parameters that specify a window, these get
|
||||
passed to the ``findwindows.find_windows()`` function. e.g. ::
|
||||
|
||||
app = Application()
|
||||
app.connect_(title_re = ".*Notepad", class_name = "Notepad")
|
||||
|
||||
|
||||
|
||||
How to sepcify a dialog of the application
|
||||
------------------------------------------
|
||||
Once the application instance knows what application it is connected to
|
||||
a dialog to work on needs to be specified.
|
||||
|
||||
There are many different ways of doing this. The most common will be
|
||||
using item or attribute access to select a dialog based on it's title. e.g ::
|
||||
|
||||
dlg = app.Notepad
|
||||
|
||||
or equivalently ::
|
||||
|
||||
dlg = app['Notepad']
|
||||
|
||||
|
||||
The next easiest method is to ask for the ``top_window_()`` e.g. ::
|
||||
|
||||
dlg = app.top_window_()
|
||||
|
||||
This will return the window that has the highest Z-Order of the top-level
|
||||
windows of the application.
|
||||
|
||||
**Note**: This is currently fairly untested so I am not sure it will
|
||||
return the correct window. It will definitely be a top level window of
|
||||
the application - it just might not be the one highest in the Z-Order.
|
||||
|
||||
If this is not enough control they you can use the same parameters as
|
||||
can be passed to ``findwindows.find_windows()`` e.g. ::
|
||||
|
||||
dlg = app.window_(title_re = "Page Setup", class_name = "#32770")
|
||||
|
||||
Finally to have the most control you can use ::
|
||||
|
||||
dialogs = app.windows_()
|
||||
|
||||
this will return a list of all the visible, enabled, top level windows
|
||||
of the application. You can then use some of the methods in ``handleprops``
|
||||
module select the dialog you want. Once you have the handle you need
|
||||
then use ::
|
||||
|
||||
Application.window_(handle = win)
|
||||
|
||||
|
||||
How to specify a control on a dialog
|
||||
------------------------------------
|
||||
|
||||
There are a number of ways to specify a control, the simplest are::
|
||||
|
||||
app.dlg.control
|
||||
app['dlg']['control']
|
||||
|
||||
|
||||
The 2nd is better for non English OS's where you need to pass unicode
|
||||
strings e.g. app[u'your dlg title'][u'your ctrl title']
|
||||
|
||||
The code builds up multiple identifiers for each control from the following:
|
||||
|
||||
+ title
|
||||
+ friendly class
|
||||
+ title + friendly class
|
||||
|
||||
If the control's text is empty (after removing non char characters) text is
|
||||
not used. Instead we look for the closest control above and to the right fo
|
||||
the contol. And append the friendly class. So the list becomes
|
||||
|
||||
+ friendly class
|
||||
+ closest text + friendly class
|
||||
|
||||
Once a set of identifiers has been created for all controls in the dialog
|
||||
we disambiguate them.
|
||||
|
||||
|
||||
|
||||
|
||||
use the `WindowSpecification.print_control_identifiers()` method
|
||||
|
||||
e.g. ::
|
||||
|
||||
app.YourDialog.print_control_identifiers()
|
||||
|
||||
Sample output::
|
||||
|
||||
Button - Paper (L1075, T394, R1411, B485)
|
||||
'PaperGroupBox' 'Paper' 'GroupBox'
|
||||
Static - Si&ze: (L1087, T420, R1141, B433)
|
||||
'SizeStatic' 'Static' 'Size'
|
||||
ComboBox - (L1159, T418, R1399, B439)
|
||||
'ComboBox' 'SizeComboBox'
|
||||
Static - &Source: (L1087, T454, R1141, B467)
|
||||
'Source' 'Static' 'SourceStatic'
|
||||
ComboBox - (L1159, T449, R1399, B470)
|
||||
'ComboBox' 'SourceComboBox'
|
||||
Button - Orientation (L1075, T493, R1171, B584)
|
||||
'GroupBox' 'Orientation' 'OrientationGroupBox'
|
||||
Button - P&ortrait (L1087, T514, R1165, B534)
|
||||
'Portrait' 'RadioButton' 'PortraitRadioButton'
|
||||
Button - L&andscape (L1087, T548, R1165, B568)
|
||||
'RadioButton' 'LandscapeRadioButton' 'Landscape'
|
||||
Button - Margins (inches) (L1183, T493, R1411, B584)
|
||||
'Marginsinches' 'MarginsinchesGroupBox' 'GroupBox'
|
||||
Static - &Left: (L1195, T519, R1243, B532)
|
||||
'LeftStatic' 'Static' 'Left'
|
||||
Edit - (L1243, T514, R1285, B534)
|
||||
'Edit' 'LeftEdit'
|
||||
Static - &Right: (L1309, T519, R1357, B532)
|
||||
'Right' 'Static' 'RightStatic'
|
||||
Edit - (L1357, T514, R1399, B534)
|
||||
'Edit' 'RightEdit'
|
||||
Static - &Top: (L1195, T550, R1243, B563)
|
||||
'Top' 'Static' 'TopStatic'
|
||||
Edit - (L1243, T548, R1285, B568)
|
||||
'Edit' 'TopEdit'
|
||||
Static - &Bottom: (L1309, T550, R1357, B563)
|
||||
'BottomStatic' 'Static' 'Bottom'
|
||||
Edit - (L1357, T548, R1399, B568)
|
||||
'Edit' 'BottomEdit'
|
||||
Static - &Header: (L1075, T600, R1119, B613)
|
||||
'Header' 'Static' 'HeaderStatic'
|
||||
Edit - (L1147, T599, R1408, B619)
|
||||
'Edit' 'TopEdit'
|
||||
Static - &Footer: (L1075, T631, R1119, B644)
|
||||
'FooterStatic' 'Static' 'Footer'
|
||||
Edit - (L1147, T630, R1408, B650)
|
||||
'Edit' 'FooterEdit'
|
||||
Button - OK (L1348, T664, R1423, B687)
|
||||
'Button' 'OK' 'OKButton'
|
||||
Button - Cancel (L1429, T664, R1504, B687)
|
||||
'Cancel' 'Button' 'CancelButton'
|
||||
Button - &Printer... (L1510, T664, R1585, B687)
|
||||
'Button' 'Printer' 'PrinterButton'
|
||||
Button - Preview (L1423, T394, R1585, B651)
|
||||
'Preview' 'GroupBox' 'PreviewGroupBox'
|
||||
Static - (L1458, T456, R1549, B586)
|
||||
'PreviewStatic' 'Static'
|
||||
Static - (L1549, T464, R1557, B594)
|
||||
'PreviewStatic' 'Static'
|
||||
Static - (L1466, T586, R1557, B594)
|
||||
'Static' 'BottomStatic'
|
||||
|
||||
This exmple has been taken from test_application.py
|
||||
|
||||
**Note** The identifiers printed by this method have been run through
|
||||
the process that makes the identifier unique. So if you have 2 edit boxes,
|
||||
they will both have "Edit" listed in their identifiers. In reality though
|
||||
the first one can be refered to as "Edit", "Edit0", "Edit1" and the 2nd
|
||||
should be refered to as "Edit2"
|
||||
|
||||
**Note** You do not have to be exact!. Say we take an instance from the
|
||||
example above::
|
||||
|
||||
Button - Margins (inches) (L1183, T493, R1411, B584)
|
||||
'Marginsinches' 'MarginsinchesGroupBox' 'GroupBox'
|
||||
|
||||
Let's say that you don't like any of these
|
||||
|
||||
- ``GroupBox`` - too generic, it could be any group box
|
||||
- ``Marginsinches`` and ``MarginsinchesGroupBox`` - these just don'
|
||||
look right, it would be nicer to leave out the 'inches' part
|
||||
|
||||
Well you CAN! The code does a best match on the identifer you use against
|
||||
all the available identifiers in the dialog.
|
||||
|
||||
For example if you break into the debugger you can see how different
|
||||
identifiers can be used::
|
||||
|
||||
(Pdb) print app.PageSetup.Margins.Text()
|
||||
Margins (inches)
|
||||
(Pdb) print app.PageSetup.MarginsGroupBox.Text()
|
||||
Margins (inches)
|
||||
|
||||
|
||||
And this will also cater for typos. Though you still have to be careful
|
||||
as if there are 2 similar identifiers in the dialog the typo you have
|
||||
used might be more similar to another control then the one you were
|
||||
thinking of.
|
||||
|
||||
How to use pywinauto with application languages other than English
|
||||
------------------------------------------------------------------
|
||||
Because Python does not support unicode identifiers in code
|
||||
you cannot use attribute access to reference a control so
|
||||
you would either have to use item access or make an explicit
|
||||
calls to ``window_()``.
|
||||
|
||||
So instead of writing::
|
||||
|
||||
app.dialog_ident.control_ident.Click()
|
||||
|
||||
You would have to write::
|
||||
|
||||
app['dialog_ident']['control_ident'].Click()
|
||||
|
||||
Or use ``window_()`` explictly::
|
||||
|
||||
app.window_(title_re = "NonAsciiCharacters").window_(title = "MoreNonAsciiCharacters").Click()
|
||||
|
||||
To see an example of this see ``examples\MiscExamples.py.GetInfo()``
|
||||
|
||||
|
||||
|
||||
How to deal with controls that do not respond as expected (e.g. OwnerDraw Controls)
|
||||
------------------------------------------------------------------------------------
|
||||
|
||||
Some controls (especially Ownerdrawn controls) do not respond to events as
|
||||
expected. For example if you look at any HLP file and go to the Index Tab (click
|
||||
'Search' button) you will see a listbox. Running Spy or Winspector on this
|
||||
will show you that it is indeed a list box - but it is ownerdrawn. This means
|
||||
that the developer has told Windows that they will override how items are displayed
|
||||
and do it themselves. And in this case they have made it so that strings cannot be
|
||||
retrieved :-(.
|
||||
|
||||
So what problems does this cause? ::
|
||||
|
||||
app.HelpTopics.ListBox.Texts() # 1
|
||||
app.HelpTopics.ListBox.Select("ItemInList") # 2
|
||||
|
||||
|
||||
1. Will return a list of empty strings, all this means is that pywinauto has not
|
||||
been able to get the strings in the listbox
|
||||
|
||||
2. This will fail with an IndexError because the Select(string) method of a ListBox
|
||||
looks for the item in the Texts to know the index of the item that it should select.
|
||||
|
||||
The following workaround will work on this control ::
|
||||
|
||||
app.HelpTopics.ListBox.Select(1)
|
||||
|
||||
This will select the 2nd item in the listbox, because it is not a string lookup
|
||||
it works correctly.
|
||||
|
||||
Unfortunately not even this will always work. The developer can make it so that the
|
||||
control does not respond to standard events like Select. In this case the only way
|
||||
you can select items in the listbox is by using the keyboard simulation of TypeKeys().
|
||||
|
||||
This allows you to send any keystrokes to a control. So to select the 3rd item you
|
||||
would use::
|
||||
|
||||
app.Helptopics.ListBox1.TypeKeys("{HOME}{DOWN 2}{ENTER}")
|
||||
|
||||
|
||||
- ``{HOME}`` will make sure that the first item is highlighted.
|
||||
- ``{DOWN 2}`` will then move the highlight down 2 items
|
||||
- ``{ENTER}`` will select the highlighted item
|
||||
|
||||
If your application made extensive use of a similar control type then you could
|
||||
make using it easier by deriving a new class from ListBox, that could use extra
|
||||
knowledge about your particular application. For example in the WinHelp example
|
||||
evertime an item is highlighted in the list view, it's text is inserted into the
|
||||
Edit control above the list, and you CAN get the text of the item from there e.g. ::
|
||||
|
||||
# print the text of the item currently selected in the list box
|
||||
# (as long as you are not typing into the Edit control!)
|
||||
print app.HelpTopics.Edit.Texts()[1]
|
||||
|
88
doc_src/index.txt
Normal file
88
doc_src/index.txt
Normal file
@ -0,0 +1,88 @@
|
||||
pywinauto
|
||||
(c) Mark Mc Mahon 2006
|
||||
Released under the LGPL licence
|
||||
|
||||
|
||||
What is it
|
||||
----------
|
||||
pywinauto is a set of python modules to automate the Microsoft Windows GUI.
|
||||
At it's simplest it allows you to send mouse and keyboard actions to windows
|
||||
dialogs and controls.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
- Unzip the pywinauto zip file to a folder.
|
||||
- Install the following Python packages
|
||||
- ctypes http://starship.python.net/crew/theller/ctypes/
|
||||
- Sendkeys http://www.rutherfurd.net/python/sendkeys/index.html
|
||||
- *Optional* PIL http://www.pythonware.com/products/pil/index.htm
|
||||
- *Optional* elementtree http://effbot.org/downloads/
|
||||
|
||||
To check you have it installed correctly
|
||||
Run Python ::
|
||||
|
||||
>>> import application
|
||||
>>> app = application.Application()._start("notepad")
|
||||
>>> app.notepad.TypeKeys("%FX")
|
||||
|
||||
|
||||
Where to start
|
||||
--------------
|
||||
Look at the examples provided in test_application.py
|
||||
There are examples in there to work with Notepad and MSPaint.
|
||||
|
||||
|
||||
How does it work
|
||||
----------------
|
||||
A lot is done through attribute access (__getattr__) for each class. For example
|
||||
when you get the attribute of an Application or Dialog object it looks for a
|
||||
dialog or control (respectively).
|
||||
|
||||
::
|
||||
|
||||
myapp.Notepad # looks for a Window/Dialog of your app that has a title 'similar'
|
||||
# to "Notepad"
|
||||
|
||||
myapp.PageSetup.OK # looks first for a dialog with a title like "PageSetup"
|
||||
# then it looks for a control on that dialog with a title
|
||||
# like "OK"
|
||||
|
||||
This attribute resolution is delayed (currently a hard coded amount of time) until
|
||||
it succeeds. So for example if you Select a menu option and then look for the
|
||||
resulting dialog e.g. ::
|
||||
|
||||
app.Notepad.MenuSelect("File->SaveAs")
|
||||
app.SaveAs.ComboBox5.Select("UTF-8")
|
||||
app.SaveAs.edit1.SetText("Example-utf8.txt")
|
||||
app.SaveAs.Save.Click()
|
||||
|
||||
At the 2nd line the SaveAs dialog might not be open by the time this line is
|
||||
executed. So what happens is that we wait until we have a control to resolve
|
||||
before resolving the dialog. At that point if we can't find a SaveAs dialog with
|
||||
a ComboBox5 control then we wait a very short period of time and try again,
|
||||
this is repeated up to a maximum time (currently 1 second!)
|
||||
|
||||
This avoid the user having to use time.sleep or a "WaitForDialog" function.
|
||||
|
||||
|
||||
Some similar tools for comparison
|
||||
---------------------------------
|
||||
* Python tools
|
||||
- Watsup
|
||||
- winGuiAuto
|
||||
|
||||
* Other scripting language tools
|
||||
- Perl Win32::GuiTest
|
||||
- Ruby GuiTest
|
||||
- others?
|
||||
|
||||
* Other free tools
|
||||
- AutoIt
|
||||
- See collection at:
|
||||
|
||||
* Commercial tools
|
||||
- WinRunner
|
||||
- SilkTest
|
||||
- Visual Test
|
||||
- Many Others
|
Loading…
Reference in New Issue
Block a user