Renamed docs folder

This commit is contained in:
markm 2006-02-06 23:42:02 +00:00
parent 5ab3a06f26
commit db1d44a7a5
3 changed files with 411 additions and 0 deletions

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

@ -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

@ -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