made a copy
This commit is contained in:
parent
0210e5352c
commit
75c90bc145
36
MakeBackup.bat
Normal file
36
MakeBackup.bat
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
@echo off
|
||||||
|
if (%1)==() goto VersionNotGiven
|
||||||
|
|
||||||
|
md previousReleases\%1
|
||||||
|
|
||||||
|
if not exist previousReleases\%1 goto FolderNotCreated
|
||||||
|
|
||||||
|
md previousReleases\%1\DlgCheck2
|
||||||
|
if not exist previousReleases\%1 goto FolderTwoNotCreated
|
||||||
|
|
||||||
|
copy *.py previousReleases\%1
|
||||||
|
copy dlgCheck2 previousReleases\%1\DlgCheck2
|
||||||
|
|
||||||
|
|
||||||
|
goto finished
|
||||||
|
|
||||||
|
:FolderNotCreated
|
||||||
|
echo.
|
||||||
|
echo Could not create the folder "previousReleases\%1"
|
||||||
|
echo.
|
||||||
|
goto finished
|
||||||
|
|
||||||
|
:FolderTwoNotCreated
|
||||||
|
echo.
|
||||||
|
echo Could not create the folder "previousReleases\%1\DlgCheck2"
|
||||||
|
echo.
|
||||||
|
goto finished
|
||||||
|
|
||||||
|
|
||||||
|
VersionNotGiven
|
||||||
|
echo.
|
||||||
|
echo please specify the version of the backup
|
||||||
|
echo.
|
||||||
|
goto finished
|
||||||
|
|
||||||
|
:finished
|
19
Readme.txt
Normal file
19
Readme.txt
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Unzip the Zip file
|
||||||
|
|
||||||
|
Copy (though you could move also) Python24.dll, dlgchecks2.dll and move PythonConfig.py to the folder that contains your executable (Silktest directory).
|
||||||
|
Modify PythonConfig.py to point to where you have unzipped the other files (this folder should contain all the *.py files and the Python24.zip file.
|
||||||
|
|
||||||
|
If your machine does not have the appropriate Visual Studio 7 DLLs you will also need these in the executable directory. (Included in the Zip in RequiredDLLs).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
There is also a little sample FindDialog.exe application (it does not use the DLL - but the underlying Python libraries). What it does is perform most of the tests (it doesn't do AllControls or AsianHotkeyTests) and highlights controls with errors with a red rectangle.
|
||||||
|
|
||||||
|
Mostly you need to run this dialog with a regular expresion as the dialog title e.g.
|
||||||
|
FindDialog "^Explorer" (the ^ is to make sure you don't match the Command prompt window you are in!)
|
||||||
|
|
||||||
|
But you could use other regular expressions also e.g. - the first windows with 'Document' in the title,
|
||||||
|
".*[D]ocument.*" (again the [ and ] are to make sure that the current command prompt window is not found!)
|
||||||
|
|
||||||
|
Note that this file needs Python24.dll - so if you have moved it out - this program will not work!.
|
0
basic/controls/controlactions.py
Normal file
0
basic/controls/controlactions.py
Normal file
89
doc_src/dev_notes.txt
Normal file
89
doc_src/dev_notes.txt
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
====================
|
||||||
|
ATTRIBUTE RESOLUTION
|
||||||
|
====================
|
||||||
|
|
||||||
|
TWO LEVELS
|
||||||
|
* application.member (Python resolves)
|
||||||
|
an attribute of application object
|
||||||
|
* application.dialog
|
||||||
|
a dialog reference
|
||||||
|
|
||||||
|
THREE LEVELS
|
||||||
|
* application.member.attr (Python resolves)
|
||||||
|
another attribute of the previous member
|
||||||
|
* application.dialog.member
|
||||||
|
a member of the dialog object
|
||||||
|
* application.dialog.control
|
||||||
|
a control on the dialog
|
||||||
|
|
||||||
|
FOUR LEVELS (leaving out Python resolved)
|
||||||
|
* application.dialog.member.member
|
||||||
|
* application.dialog.control.member
|
||||||
|
|
||||||
|
|
||||||
|
DELAYED RESOLUTION FOR SUCCESS
|
||||||
|
Taking the example
|
||||||
|
app.dlg.control.action()
|
||||||
|
If we leave out syntax and programming errors there are still a number of reasons why it could fail.
|
||||||
|
|
||||||
|
dlg might not be found
|
||||||
|
control might not be found
|
||||||
|
either dlg or control may be disabled
|
||||||
|
|
||||||
|
dialog and control may be found but on the wrong dialog (e.g. in Notepad you can bring up 2 "Page Setup" dialogs both with an OK button)
|
||||||
|
|
||||||
|
One solution would just be to add a "sleep" before trying to find each new dialog (to ensure that it is there and ready) - but this will mean lots of unnecessary waiting.
|
||||||
|
|
||||||
|
So the solution I have tried is:
|
||||||
|
- perform the complete attribute access resolution at the latest possible time
|
||||||
|
- if it fails then wait and try again
|
||||||
|
- after a specified timeout fail raising the original exception.
|
||||||
|
|
||||||
|
This means that in the normal case you don't have unnecessary waits - and in the failure case - you still get an exception with the error.
|
||||||
|
|
||||||
|
Also waiting to do resolution as late as possible stops errors where an earlier part of the path succeedes - but finds the wrong item.
|
||||||
|
|
||||||
|
So for example if finds the page setup dialog in Notepad
|
||||||
|
# open the Printer setup dialog (which has "Page Setup" as title)
|
||||||
|
app.PageSetup.Printer.Click()
|
||||||
|
|
||||||
|
# if this runs too quickly it actually finds the current page setup dialog
|
||||||
|
# before the next dialog opens, but that dialog does not have a Properties
|
||||||
|
# button - so an error is raised.
|
||||||
|
# because we re-run the resolution from the start we find the new pagesetup dialog.
|
||||||
|
app.PageSetup.Properties.Click()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
==================
|
||||||
|
WRITING TO DIALOGS
|
||||||
|
==================
|
||||||
|
We need a way of making sure that the dialog is active without having to access a control on it.
|
||||||
|
e.g.
|
||||||
|
app.MainWin.MenuSelect("Something That->Loads a Dialog")
|
||||||
|
app.Dlg._write("dlg.xml")
|
||||||
|
|
||||||
|
or a harder problem:
|
||||||
|
app.PageSetup.Printer.Click()
|
||||||
|
app.PageSetup._write("pagesetup.xml")
|
||||||
|
|
||||||
|
In this second example it is very hard to be sure that the correct Page Setup dialog is shown.
|
||||||
|
|
||||||
|
The only way to be realy sure is to check for the existance of certain control(s) (ID, Class, text, whatever) - but it would be nice to not have to deal with those :-(
|
||||||
|
|
||||||
|
Another less declarative (more magic?) is to scan the list of available windows/controls and if they haven't changed then accept that the correct one is shown.
|
||||||
|
|
||||||
|
When testing and having XML files then we should use those to make sure that we have the correct dialog up (by using Class/ID)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
175
pywinauto/DemiTest.py
Normal file
175
pywinauto/DemiTest.py
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
"""
|
||||||
|
First of all, thanks Thomas for ctypes..
|
||||||
|
|
||||||
|
Hacked a bit into Henks Venster dialog class, using a different
|
||||||
|
c-source as basis. see -> Microsoft Knowledge Base Article - 141201.
|
||||||
|
|
||||||
|
http://support.microsoft.com/default.aspx?scid=kb;en-us;141201
|
||||||
|
|
||||||
|
Out came a bit more bare bone dialog class. Though I don"t
|
||||||
|
intend to start a win32 dialog discussion here I thought this source
|
||||||
|
could come in quite handy.
|
||||||
|
|
||||||
|
|
||||||
|
J\\xfcrgen
|
||||||
|
|
||||||
|
* had to reformat it tabs to spaces on the fly, using notepad,
|
||||||
|
so I hope theres no error in there.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ctypes import windll, WINFUNCTYPE, byref, c_int as BOOL, \
|
||||||
|
c_ulong as HWND, c_uint as UINT, c_long as LPARAM, \
|
||||||
|
c_ushort as WORD
|
||||||
|
|
||||||
|
WPARAM = UINT
|
||||||
|
|
||||||
|
def LOWORD(dword): return dword & 0x0000ffff
|
||||||
|
def HIWORD(dword): return dword >> 16
|
||||||
|
|
||||||
|
|
||||||
|
DIALOGPROC = WINFUNCTYPE(BOOL, HWND, UINT, WPARAM, LPARAM)
|
||||||
|
|
||||||
|
|
||||||
|
_user32 = windll.user32
|
||||||
|
|
||||||
|
WM_CLOSE = 16
|
||||||
|
WS_CHILD = 1073741824
|
||||||
|
WS_VISIBLE = 268435456
|
||||||
|
#**************************************************
|
||||||
|
|
||||||
|
#**************************************************
|
||||||
|
class dialog(object):
|
||||||
|
|
||||||
|
def __init__(self, title, exstyle, style, x, y, w, h):
|
||||||
|
self._items = 0
|
||||||
|
self._id = 0
|
||||||
|
|
||||||
|
p = [
|
||||||
|
1, # dlgversion
|
||||||
|
0xFFFF, # signature
|
||||||
|
0, # LOWORD(helpId)
|
||||||
|
0, # HIWORD(helpId)
|
||||||
|
LOWORD(exstyle), # extendedstyle
|
||||||
|
HIWORD(exstyle), # ...
|
||||||
|
LOWORD(style), # style
|
||||||
|
HIWORD(style), # ...
|
||||||
|
0, # number of items
|
||||||
|
x, # x
|
||||||
|
y, # x
|
||||||
|
w, # w
|
||||||
|
h, # h
|
||||||
|
0, # menu
|
||||||
|
0, # class
|
||||||
|
]
|
||||||
|
|
||||||
|
title = map(ord, unicode(title)) + [0]
|
||||||
|
p += title
|
||||||
|
if len(p) % 2:
|
||||||
|
p.append(0)
|
||||||
|
|
||||||
|
#p.append(18)
|
||||||
|
#p.append(700)
|
||||||
|
#p.append(11)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self.p = p
|
||||||
|
|
||||||
|
def _dlgitem(self, classname, title, exstyle, style, x, y, w, h):
|
||||||
|
self._items += 1
|
||||||
|
self._id += 1
|
||||||
|
|
||||||
|
style |= WS_CHILD | WS_VISIBLE
|
||||||
|
p = [
|
||||||
|
0, # LOWORD(helpId)
|
||||||
|
0, # HIWORD(helpId)
|
||||||
|
LOWORD(exstyle), # extendedstyle
|
||||||
|
HIWORD(exstyle), # ...
|
||||||
|
LOWORD(style), # style
|
||||||
|
HIWORD(style), # ...
|
||||||
|
x, # x
|
||||||
|
y, # y
|
||||||
|
w, # w
|
||||||
|
h, # h
|
||||||
|
LOWORD(self._id), # controlId
|
||||||
|
HIWORD(self._id) # ...
|
||||||
|
]
|
||||||
|
|
||||||
|
classname = map(ord, unicode(classname)) + [0]
|
||||||
|
p += classname
|
||||||
|
title = map(ord, unicode(title)) + [0]
|
||||||
|
p += title
|
||||||
|
|
||||||
|
if len(p) % 2:
|
||||||
|
p.append(0)
|
||||||
|
else:
|
||||||
|
p += [0, 0]
|
||||||
|
self.p += p
|
||||||
|
|
||||||
|
|
||||||
|
def run_modal(self, hwnd=0):
|
||||||
|
self.p[8] = self._items
|
||||||
|
|
||||||
|
self._p_template = (WORD * len(self.p))(*self.p)
|
||||||
|
self._p_dialogproc = DIALOGPROC(self.dialogproc)
|
||||||
|
|
||||||
|
result = _user32.DialogBoxIndirectParamA(
|
||||||
|
0, byref(self._p_template), hwnd, self._p_dialogproc, 0
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def dialogproc(self, hwnd, message, wparam, lparam):
|
||||||
|
if message==WM_CLOSE:
|
||||||
|
_user32.EndDialog(hwnd, 0)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
#************************************************
|
||||||
|
#d = dialog("dialog sample", 0, 0, 30, 30, 160, 100)
|
||||||
|
#d._dlgitem("button", "button1", 0, 0, 5, 5, 30, 20)
|
||||||
|
#d._dlgitem("button", "button2", 0, 0, 5, 30, 30, 20)
|
||||||
|
#d._dlgitem("edit", "edit box", 0, 0, 40, 0, 50, 60)
|
||||||
|
#d._dlgitem("combobox", "",0, 0, 100, 0, 50, 65)
|
||||||
|
#
|
||||||
|
|
||||||
|
import sys
|
||||||
|
if len(sys.argv) < 2 :
|
||||||
|
print "Please specify the XML file to read"
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
import PyDlgCheckerWrapper
|
||||||
|
PyDlgCheckerWrapper.InitDialogFromFile(sys.argv[1])
|
||||||
|
dlg = PyDlgCheckerWrapper.TestInfo['Dialog']
|
||||||
|
|
||||||
|
d = None
|
||||||
|
|
||||||
|
for i, ctrl in enumerate(dlg.AllControls()):
|
||||||
|
print i
|
||||||
|
if i == 0:
|
||||||
|
d = dialog(
|
||||||
|
ctrl.Text,
|
||||||
|
0,#ctrl.ExStyle,
|
||||||
|
0,#ctrl.Style,
|
||||||
|
ctrl.Rectangle.left, # x
|
||||||
|
ctrl.Rectangle.top, # y
|
||||||
|
ctrl.Rectangle.right - ctrl.Rectangle.left, # cx
|
||||||
|
ctrl.Rectangle.bottom - ctrl.Rectangle.top, # cy
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
d._dlgitem(
|
||||||
|
ctrl.Class,
|
||||||
|
ctrl.Text,
|
||||||
|
ctrl.ExStyle,
|
||||||
|
ctrl.Style,
|
||||||
|
ctrl.Rectangle.left, # x
|
||||||
|
ctrl.Rectangle.top, # y
|
||||||
|
ctrl.Rectangle.right - ctrl.Rectangle.left, # cx
|
||||||
|
ctrl.Rectangle.bottom - ctrl.Rectangle.top, # cy
|
||||||
|
)
|
||||||
|
|
||||||
|
print d
|
||||||
|
d.run_modal()
|
||||||
|
|
356
pywinauto/DrawDialogFromXML.py
Normal file
356
pywinauto/DrawDialogFromXML.py
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
r"""Converts XML files captured by DLGChecks to a close aproximation
|
||||||
|
of the original dialog
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
DrawDialogFromXML.py (XML filepaths or directories containing XML files)
|
||||||
|
|
||||||
|
e.g.
|
||||||
|
DrawDialogFromXML.py jpn c:\temp PropertiesDialog.xml
|
||||||
|
|
||||||
|
|
||||||
|
You can specify more then one file or more than one directory
|
||||||
|
Wildcard characters are not currently handled (?*).
|
||||||
|
|
||||||
|
Note that there will be differences between dialogs drawn this way
|
||||||
|
and the original dialog:
|
||||||
|
- Anything drawn directly to the dialog will not be drawn (usually
|
||||||
|
something done in the OnPaint handler of the dialog.
|
||||||
|
- OwnerDrawn controls will not be handled 100% correctly,
|
||||||
|
but as close as possible
|
||||||
|
- Images, Bitmaps and Icons are currently not drawn
|
||||||
|
"""
|
||||||
|
|
||||||
|
import pywin.mfc.dialog
|
||||||
|
import XMLHelpers
|
||||||
|
import win32defines
|
||||||
|
import win32functions
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
# ignore the warning in the pywin/mfc/dialog module line number 32
|
||||||
|
import warnings
|
||||||
|
warnings.filterwarnings("ignore", module = ".*dialog", lineno = 32)
|
||||||
|
|
||||||
|
# some constants for fixing Dialog units
|
||||||
|
rectSizerX = .675 #.675
|
||||||
|
rectSizerY = .62 #.62
|
||||||
|
titleBarHeight = 10
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
def Main():
|
||||||
|
|
||||||
|
# check that the parameters are correct
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print __doc__
|
||||||
|
print "*** Please specify the language and the " \
|
||||||
|
"XML files or folders with XML files to read"
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
xmlFiles = []
|
||||||
|
|
||||||
|
# get the full list of XML files to try
|
||||||
|
for arg in sys.argv[1:]:
|
||||||
|
# if the current argument is a directory
|
||||||
|
|
||||||
|
if os.path.isdir(arg):
|
||||||
|
|
||||||
|
# for all the files in this directory
|
||||||
|
for root, dirs, files in os.walk(arg):
|
||||||
|
for f in files:
|
||||||
|
|
||||||
|
# if it is an XML file
|
||||||
|
if os.path.splitext(f)[1].lower() == ".xml":
|
||||||
|
# add it to our list
|
||||||
|
xmlFiles.append(os.path.join(root, f))
|
||||||
|
|
||||||
|
# if the argument is a file then just append it
|
||||||
|
elif os.path.isfile(arg):
|
||||||
|
xmlFiles.append(arg)
|
||||||
|
|
||||||
|
|
||||||
|
for xmlFile in xmlFiles:
|
||||||
|
print xmlFile
|
||||||
|
|
||||||
|
try:
|
||||||
|
# try and parse the XML file
|
||||||
|
# could easily fail if structure of XML is not exactly what
|
||||||
|
# we are expecting - for example if it is an XML file not
|
||||||
|
# containing Dialog Information or from a later version of
|
||||||
|
# XML dialog
|
||||||
|
ctrls = ParseXML(xmlFile)
|
||||||
|
|
||||||
|
# convert the XML information into a dialog template understood by
|
||||||
|
# Win32ui
|
||||||
|
template = MakeOLDDlgTemplate(ctrls)
|
||||||
|
|
||||||
|
#try:
|
||||||
|
# construct the dialog
|
||||||
|
dlg = XmlDialog(template)
|
||||||
|
#except DeprecationWarning, e:
|
||||||
|
# pass
|
||||||
|
|
||||||
|
# give the dialog the original control information
|
||||||
|
dlg.DlgControls = ctrls
|
||||||
|
|
||||||
|
|
||||||
|
# show the dialog
|
||||||
|
ret = dlg.DoModal()
|
||||||
|
if ret == -1:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
except (AttributeError, XMLHelpers.XMLParsingError):
|
||||||
|
print "**Ignored** %s"% xmlFile
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
def ParseXML(filename):
|
||||||
|
"Parse the Old style XML file to a dictionary"
|
||||||
|
|
||||||
|
ctrls = XMLHelpers.ReadPropertiesFromFile(filename)
|
||||||
|
return ctrls
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
def ScaleToDialogUnitsAndConvert(rect, isDialogControl = False):
|
||||||
|
bottomOffset = 0
|
||||||
|
if isDialogControl:
|
||||||
|
bottomOffset = titleBarHeight
|
||||||
|
|
||||||
|
|
||||||
|
left = int(rect.left * rectSizerX) # x
|
||||||
|
top = int(rect.top * rectSizerY) -titleBarHeight # y
|
||||||
|
right =int((rect.right - rect.left)* rectSizerX) # cx
|
||||||
|
bottom = int(((rect.bottom - rect.top) * rectSizerY) - bottomOffset) # cy
|
||||||
|
|
||||||
|
return (left, top, right, bottom)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
# For Old XML files - converts from Dictionary to template
|
||||||
|
# ===========================================================================
|
||||||
|
def MakeOLDDlgTemplate(controls):
|
||||||
|
template = []
|
||||||
|
|
||||||
|
#pprint(controls)
|
||||||
|
for i, ctrl in enumerate(controls):
|
||||||
|
|
||||||
|
title = ctrl['Texts'][0].encode('mbcs')
|
||||||
|
|
||||||
|
# if it is the Dialog control
|
||||||
|
if i == 0:
|
||||||
|
|
||||||
|
# remove WS CHILD style if it exists
|
||||||
|
style = ctrl["Style"]
|
||||||
|
if style & win32defines.WS_CHILD == win32defines.WS_CHILD:
|
||||||
|
style = style ^ win32defines.WS_CHILD
|
||||||
|
|
||||||
|
# Start off the template with the dilaog information
|
||||||
|
template = [[
|
||||||
|
str(title),
|
||||||
|
ScaleToDialogUnitsAndConvert(ctrl["Rectangle"], True),
|
||||||
|
style,
|
||||||
|
int(ctrl["ExStyle"]),
|
||||||
|
(8, ctrl["Fonts"][0].lfFaceName, )
|
||||||
|
#(ctrl["Font").lfHeight, ctrl["Font").lfFaceName, )
|
||||||
|
|
||||||
|
], ]
|
||||||
|
|
||||||
|
#For each of the other controls
|
||||||
|
else:
|
||||||
|
|
||||||
|
# do not create ComboLBoxes and Edit controls that are just part of
|
||||||
|
# ComboBox controls - they will be created automatically by the ComboBox
|
||||||
|
# control itself
|
||||||
|
if ctrl["Class"] == "ComboLBox" and int(ctrl["ControlID"]) == 1000 or \
|
||||||
|
ctrl["Class"] == "Edit" and int(ctrl["ControlID"]) == 1001:
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
if not ctrl['IsVisible']:
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
# remove the OwnderDraw style from ComboBoxes
|
||||||
|
style = int(ctrl["Style"])
|
||||||
|
if ctrl["Class"] == "ComboBox" and style & 0x10:
|
||||||
|
style = style ^ 0x10
|
||||||
|
|
||||||
|
# change controls we don't know how to deal with by
|
||||||
|
# converting them to a static and changing the title to contain
|
||||||
|
# the old class name
|
||||||
|
if ctrl["Class"] not in (
|
||||||
|
"#32770",
|
||||||
|
"Button",
|
||||||
|
"ComboBox",
|
||||||
|
"ComboLBox",
|
||||||
|
"Edit",
|
||||||
|
"ListBox",
|
||||||
|
"ScrollBar",
|
||||||
|
"Static",
|
||||||
|
|
||||||
|
"ComboBoxEx32",
|
||||||
|
"msctls_statusbar32",
|
||||||
|
"msctls_updown32",
|
||||||
|
"SysHeader32",
|
||||||
|
"SysListView32",
|
||||||
|
"SysTabControl32",
|
||||||
|
"SysTreeView32",
|
||||||
|
"ToolbarWindow32",
|
||||||
|
#"SSDemoParent",
|
||||||
|
#"GraphCtl", # test
|
||||||
|
#"CHECKLIST_ACLUI", # test
|
||||||
|
#"rctrl_renwnd32",
|
||||||
|
#"RichEdit20W",
|
||||||
|
):
|
||||||
|
|
||||||
|
title = u"Was previously: " + ctrl["Class"]
|
||||||
|
ctrl["Class"] = "Static"
|
||||||
|
#continue
|
||||||
|
|
||||||
|
# don't bother doing dialogs or Tab Controls
|
||||||
|
if ctrl["Class"] in ("#32770", "SysTabControl32"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# ensure that for drop down combo boxes that the size of the drop down is enough to
|
||||||
|
# clearly see many items
|
||||||
|
if ctrl["Class"] in ("ComboBox",) and \
|
||||||
|
(ctrl['Style'] & win32defines.CBS_DROPDOWNLIST == win32defines.CBS_DROPDOWNLIST or \
|
||||||
|
ctrl['Style'] & win32defines.CBS_DROPDOWN == win32defines.CBS_DROPDOWN):
|
||||||
|
#not ctrl['Style'] & win32defines.CBS_SIMPLE:#& win32defines.CBS_DROPDOWNLIST or ctrl['Style'] & win32defines.CBS_DROPDOWN):
|
||||||
|
ctrl["Rectangle"].bottom += 200
|
||||||
|
|
||||||
|
|
||||||
|
item = [
|
||||||
|
str(ctrl["Class"]),
|
||||||
|
str(title),
|
||||||
|
int(ctrl["ControlID"]),
|
||||||
|
ScaleToDialogUnitsAndConvert(ctrl["Rectangle"] - controls[0]['Rectangle']),
|
||||||
|
style,
|
||||||
|
int(ctrl["ExStyle"]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# append the information needed for the template
|
||||||
|
template.append(item)
|
||||||
|
|
||||||
|
#pprint(template)
|
||||||
|
|
||||||
|
return template
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
class XmlDialog (pywin.mfc.dialog.Dialog):
|
||||||
|
def OnInitDialog(self):
|
||||||
|
|
||||||
|
# loop over all the controls in the original dialog
|
||||||
|
for x in self.DlgControls:
|
||||||
|
|
||||||
|
# if it is a combobox
|
||||||
|
if x["Class"] == "ComboBox":
|
||||||
|
|
||||||
|
try:
|
||||||
|
# get the control
|
||||||
|
ctrl = self.GetDlgItem(int(x['ControlID']))
|
||||||
|
|
||||||
|
#ctrl.SetExtendedUI(1)
|
||||||
|
|
||||||
|
# add each of the text items
|
||||||
|
for subTitle in x["Texts"][1:]:
|
||||||
|
ctrl.AddString(subTitle.encode('mbcs'))
|
||||||
|
|
||||||
|
|
||||||
|
# win32functions.SendMessage(
|
||||||
|
# ctrl.GetSafeHwnd(),
|
||||||
|
# win32defines.CB_ADDSTRING,
|
||||||
|
# 0,
|
||||||
|
# subTitle )
|
||||||
|
|
||||||
|
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
elif x["Class"] == "ListBox":
|
||||||
|
try:
|
||||||
|
ctrl = self.GetDlgItem(int(x['ControlID']))
|
||||||
|
|
||||||
|
for subTitle in x["Texts"][1:]:
|
||||||
|
win32functions.SendMessage(
|
||||||
|
ctrl.GetSafeHwnd(),
|
||||||
|
win32defines.LB_ADDSTRING,
|
||||||
|
0,
|
||||||
|
subTitle.encode('mbcs') )
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# if x.has_key('Image'):
|
||||||
|
# #print "hasImage"
|
||||||
|
# ctrl = self.GetDlgItem(int(x['ControlID']))
|
||||||
|
# bmp = x['Image']
|
||||||
|
#
|
||||||
|
# from PIL import ImageWin
|
||||||
|
# dib = ImageWin.Dib(bmp)
|
||||||
|
#
|
||||||
|
# hDC = ctrl.GetDC()
|
||||||
|
# #hDC.DrawText("Should have an image", (0, 0, 100, 199), 0)
|
||||||
|
# #print dib
|
||||||
|
#
|
||||||
|
# dib.expose(hDC.GetHandleAttrib())
|
||||||
|
#
|
||||||
|
# #dib.draw(hDC.GetHandleOutput(), (0, 0, 990, 990))
|
||||||
|
|
||||||
|
def OnPaint(self):
|
||||||
|
# loop over all the controls in the original dialog
|
||||||
|
for x in self.DlgControls:
|
||||||
|
|
||||||
|
if x.has_key('Image'):
|
||||||
|
|
||||||
|
try:
|
||||||
|
ctrl = self.GetDlgItem(int(x['ControlID']))
|
||||||
|
|
||||||
|
bmp = x['Image']
|
||||||
|
|
||||||
|
from PIL import ImageWin
|
||||||
|
dib = ImageWin.Dib(bmp)
|
||||||
|
|
||||||
|
hdc, paint_struct = ctrl.BeginPaint()
|
||||||
|
dib.expose(hdc.GetHandleAttrib())
|
||||||
|
|
||||||
|
ctrl.EndPaint(paint_struct)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
#return True
|
||||||
|
|
||||||
|
|
||||||
|
# dib = ImageWin.Dib (bmp)
|
||||||
|
# scaled_size = tuple ([scale * i for i in bmp.size])
|
||||||
|
# x = (printer_size[0] - scaled_size[0]) / 2
|
||||||
|
# y = (printer_size[1] - scaled_size[1]) / 2
|
||||||
|
# dib.draw (hDC.GetHandleOutput (), (x, y) + scaled_size)
|
||||||
|
|
||||||
|
|
||||||
|
# ===========================================================================
|
||||||
|
if __name__ == "__main__":
|
||||||
|
Main()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
361
pywinauto/FindDialog.py
Normal file
361
pywinauto/FindDialog.py
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
# pylint: disable-msg=W0611
|
||||||
|
"""Explanation of script"""
|
||||||
|
from pprint import pprint
|
||||||
|
import re
|
||||||
|
|
||||||
|
from ctypes import *
|
||||||
|
from ctypes.wintypes import *
|
||||||
|
|
||||||
|
from win32functions import *
|
||||||
|
from win32defines import *
|
||||||
|
from win32structures import *
|
||||||
|
import controls
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
__revision__ = "0.0.1"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# import all the necessary files that we want to end
|
||||||
|
# up in the Py2exe zip.
|
||||||
|
|
||||||
|
|
||||||
|
import PyDlgCheckerWrapper
|
||||||
|
|
||||||
|
#
|
||||||
|
#def FindWindow(
|
||||||
|
# start_window = None,
|
||||||
|
# match_title = None,
|
||||||
|
# match_class = None,
|
||||||
|
# toplevel_only = False,
|
||||||
|
# recurse_children = True,
|
||||||
|
# this_thread_only = None,
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# if start_window == None:
|
||||||
|
# start_window = GetDesktopWindow()
|
||||||
|
#
|
||||||
|
# if recurse_children:
|
||||||
|
# # get the 1st child of the start window
|
||||||
|
# win_to_test = GetWindow (start_window, GW_CHILD)
|
||||||
|
#
|
||||||
|
# wrapped = WrapHandle(win_to_test)
|
||||||
|
#
|
||||||
|
# if match_title:
|
||||||
|
# if re.match(match_title, wrapped.Text)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
totalWindowCount = 0
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def FindDialog(titleToFind, caseSensitive = False, testClass = None, startWin = None):
|
||||||
|
"""Find a dialog based on the title
|
||||||
|
|
||||||
|
|
||||||
|
Returns the dialog that has a title that matches the regular
|
||||||
|
expression in titleToFind.
|
||||||
|
If caseSensitive == True then it performs a case sensitive match
|
||||||
|
If startWin == None then it starts searching from the desktop window
|
||||||
|
otherwise it searches the child windows of the specified window."""
|
||||||
|
|
||||||
|
if caseSensitive:
|
||||||
|
|
||||||
|
flags = re.IGNORECASE
|
||||||
|
else:
|
||||||
|
flags = 0
|
||||||
|
|
||||||
|
titleRe = re.compile(titleToFind, flags)
|
||||||
|
|
||||||
|
# If the startWin is NULL then we are just starting and we
|
||||||
|
# should start with the Desktop window and look from there
|
||||||
|
if startWin == None:
|
||||||
|
startWin = GetDesktopWindow()
|
||||||
|
|
||||||
|
# get the 1st child of the start window
|
||||||
|
winToTest = GetWindow (startWin, GW_CHILD)
|
||||||
|
|
||||||
|
# Now Iterate through all the children of the startwindow
|
||||||
|
# (ie ALL windows, dialogs, controls ) then if the HWND is a dialog
|
||||||
|
# get the Title and compare it to what we are looking for
|
||||||
|
# it makes a check first to make sure that the window has at
|
||||||
|
# least 1 child window
|
||||||
|
while winToTest:
|
||||||
|
global totalWindowCount
|
||||||
|
totalWindowCount += 1
|
||||||
|
|
||||||
|
# get the Title of the Window and if the Title the same as
|
||||||
|
# what we want if So then return it
|
||||||
|
title = controls.WrapHandle(winToTest).Text
|
||||||
|
|
||||||
|
# Check the title to see if it is the same as the title we
|
||||||
|
# are looking for - if it is then return the handle
|
||||||
|
found = titleRe.search(title)
|
||||||
|
if found:
|
||||||
|
|
||||||
|
if testClass:
|
||||||
|
if testClass == controls.WrapHandle(winToTest).Class:
|
||||||
|
return winToTest
|
||||||
|
else:
|
||||||
|
return winToTest
|
||||||
|
|
||||||
|
|
||||||
|
# Now Check through the children of the present window
|
||||||
|
# this is recursive through all the children of the window
|
||||||
|
# It calls FindDialog with the title and the new window
|
||||||
|
# this will keep going recursively until the window is found
|
||||||
|
# or we reach the end of the children
|
||||||
|
tempWin = FindDialog (titleToFind, caseSensitive, testClass, winToTest)
|
||||||
|
|
||||||
|
if tempWin != None:
|
||||||
|
return tempWin
|
||||||
|
|
||||||
|
# So the last one wasnt it just continue with the next
|
||||||
|
# which will be the next window at the present level
|
||||||
|
winToTest = GetWindow (winToTest, GW_HWNDNEXT)
|
||||||
|
|
||||||
|
# we have gotten to here so we didnt find the window
|
||||||
|
# in the current depth of the tree return NULL to say
|
||||||
|
# that we didnt get it and continue at the previous depth
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def DrawOutline(rect, colour = None, fill = BS_NULL):
|
||||||
|
# create the brush
|
||||||
|
|
||||||
|
colours = {
|
||||||
|
"green" : 0x00ff00,
|
||||||
|
"blue" : 0xff0000,
|
||||||
|
"red" : 0x0000ff,
|
||||||
|
}
|
||||||
|
|
||||||
|
if colour not in colours:
|
||||||
|
colour = 'green'
|
||||||
|
|
||||||
|
colour = colours[colour]
|
||||||
|
|
||||||
|
brush = LOGBRUSH()
|
||||||
|
brush.lbStyle = fill
|
||||||
|
brush.lbHatch = HS_DIAGCROSS
|
||||||
|
|
||||||
|
hBrush = CreateBrushIndirect(byref(brush))
|
||||||
|
|
||||||
|
hPen = CreatePen(PS_SOLID, 2, colour)
|
||||||
|
|
||||||
|
# get the Device Context
|
||||||
|
dc = CreateDC(u"DISPLAY", None, None, None )
|
||||||
|
|
||||||
|
SelectObject(dc, hBrush)
|
||||||
|
SelectObject(dc, hPen)
|
||||||
|
|
||||||
|
Rectangle(dc, rect.left, rect.top, rect.right, rect.bottom)
|
||||||
|
|
||||||
|
# Delete the brush and pen we created
|
||||||
|
DeleteObject(hBrush)
|
||||||
|
DeleteObject(hPen)
|
||||||
|
|
||||||
|
# delete the Display context that we created
|
||||||
|
DeleteDC(dc)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#============================================================================
|
||||||
|
def PopulateCommandLineParser():
|
||||||
|
from optparse import OptionParser
|
||||||
|
|
||||||
|
parser = OptionParser()
|
||||||
|
|
||||||
|
parser.add_option("-w", "--window", dest="windowTitle", metavar="WINDOW", #action = "store",
|
||||||
|
help="The window title to find and test")
|
||||||
|
|
||||||
|
parser.add_option("-f", "--xml", dest="xmlFileName",# metavar="FILE",
|
||||||
|
help="save the window information to this XML file")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
parser.add_option("-r", "--refwin", dest="refWindow", #metavar="FILE",
|
||||||
|
action = "append",
|
||||||
|
help="save the domino report to file")
|
||||||
|
|
||||||
|
|
||||||
|
parser.add_option("-x", "--refxml", dest="refXML", #metavar="USER",
|
||||||
|
help="username to access Domino DTS")
|
||||||
|
|
||||||
|
|
||||||
|
# parser.add_option("-p", "--pwd", dest="password", metavar="PASSWORD",
|
||||||
|
# help="password for user to access Domino DTS")
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# parser.add_option("--opendays", dest="open_days_report", metavar="FILE",
|
||||||
|
# help="get number of days bug is open and"\
|
||||||
|
# " write output to file")
|
||||||
|
#
|
||||||
|
# parser.add_option("--clarify", dest="clarify_report", metavar="FILE",
|
||||||
|
# help="get number of days each bug has been" \
|
||||||
|
# "open and write output to file")
|
||||||
|
#
|
||||||
|
# parser.add_option("--opengraph", dest="opengraph_report", metavar="FILE",
|
||||||
|
# help="write a table that can be used to graph " \
|
||||||
|
# "the historical open/closed bug graph")
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import sys
|
||||||
|
import PyDlgCheckerWrapper
|
||||||
|
|
||||||
|
PyDlgCheckerWrapper.InitializeModules(".")
|
||||||
|
|
||||||
|
|
||||||
|
optParser = PopulateCommandLineParser()
|
||||||
|
options, args = optParser.parse_args()
|
||||||
|
|
||||||
|
for arg in sys.argv:
|
||||||
|
if arg.lower().lstrip("/-") in ("?", "h", "help"):
|
||||||
|
optParser.print_help()
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if not (options.windowTitle or options.xmlFileName):
|
||||||
|
if args:
|
||||||
|
arg = args[0]
|
||||||
|
if arg.lower().endswith(".xml"):
|
||||||
|
options.xmlFileName = arg
|
||||||
|
else:
|
||||||
|
options.windowTitle = arg
|
||||||
|
else:
|
||||||
|
|
||||||
|
optParser.print_help()
|
||||||
|
|
||||||
|
print
|
||||||
|
print "*ERROR* Please specify either Window title or XML FileName"
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
if options.windowTitle and options.xmlFileName:
|
||||||
|
optParser.print_help()
|
||||||
|
|
||||||
|
print
|
||||||
|
print "*ERROR* Please specify only one of Window title or XML FileName"
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if options.windowTitle:
|
||||||
|
# find the dialog
|
||||||
|
handle = FindDialog ("^" + options.windowTitle.decode("mbcs"))
|
||||||
|
|
||||||
|
if not handle:
|
||||||
|
print "Could not find dialog"
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
PyDlgCheckerWrapper.InitDialogFromWindow(handle)
|
||||||
|
|
||||||
|
outputXML = PyDlgCheckerWrapper.TestInfo['Controls'][0].Text
|
||||||
|
outputXML = outputXML.replace("\\", '_')
|
||||||
|
outputXML = outputXML.replace("*", '_')
|
||||||
|
outputXML = outputXML.replace("?", '_')
|
||||||
|
outputXML = outputXML.replace(">", '_')
|
||||||
|
outputXML = outputXML.replace("<", '_')
|
||||||
|
outputXML = outputXML.replace("/", '_')
|
||||||
|
outputXML = outputXML.replace("|", '_')
|
||||||
|
outputXML = outputXML.replace("\"", '_')
|
||||||
|
outputXML = outputXML.replace(":", '_')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
elif options.xmlFileName:
|
||||||
|
|
||||||
|
|
||||||
|
PyDlgCheckerWrapper.InitDialogFromFile(options.xmlFileName)
|
||||||
|
|
||||||
|
outputXML = options.xmlFileName
|
||||||
|
|
||||||
|
print outputXML
|
||||||
|
|
||||||
|
# write out the XML file
|
||||||
|
PyDlgCheckerWrapper.WriteDialogToFile (outputXML + ".xml")
|
||||||
|
|
||||||
|
|
||||||
|
if options.refWindow:
|
||||||
|
handle = FindDialog ("^" + options.refWindow)
|
||||||
|
PyDlgCheckerWrapper.AddReferenceFromWindow(handle)
|
||||||
|
|
||||||
|
elif options.refXML:
|
||||||
|
PyDlgCheckerWrapper.AddReferenceFromFile(options.refXML)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for i in range(0, PyDlgCheckerWrapper.GetRegisteredTestCount()):
|
||||||
|
tst = PyDlgCheckerWrapper.GetRegisteredTestName(i)
|
||||||
|
if tst not in ("AllControls", "AsianHotkey", "Overlapping"):
|
||||||
|
print `tst`
|
||||||
|
PyDlgCheckerWrapper.AddTest(tst)
|
||||||
|
#print tst
|
||||||
|
|
||||||
|
PyDlgCheckerWrapper.RunTests(True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (ctrls, info, bType, inRef) in PyDlgCheckerWrapper.TestInfo['Bugs']:
|
||||||
|
print "BugType:", bType,
|
||||||
|
|
||||||
|
for i in info:
|
||||||
|
print i, info[i],
|
||||||
|
print
|
||||||
|
|
||||||
|
|
||||||
|
for i, ctrl in enumerate(ctrls):
|
||||||
|
print '\t"%s" "%s" (%d %d %d %d) Vis: %d'% (
|
||||||
|
ctrl.Text,
|
||||||
|
ctrl.FriendlyClassName,
|
||||||
|
ctrl.Rectangle.left,
|
||||||
|
ctrl.Rectangle.top,
|
||||||
|
ctrl.Rectangle.right,
|
||||||
|
ctrl.Rectangle.bottom,
|
||||||
|
ctrl.IsVisible,)
|
||||||
|
|
||||||
|
try:
|
||||||
|
|
||||||
|
dlgRect = PyDlgCheckerWrapper.TestInfo['Controls'][0].handle.Rectangle
|
||||||
|
|
||||||
|
DrawOutline(ctrl.Rectangle + dlgRect, "red")
|
||||||
|
except AttributeError, e:
|
||||||
|
#print e
|
||||||
|
pass
|
||||||
|
|
||||||
|
print
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
71
pywinauto/InfoManager.py
Normal file
71
pywinauto/InfoManager.py
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# get imported by all
|
||||||
|
# each class and test register themselves with this
|
||||||
|
import re
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
class ClassRegistry(object):
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
def __init__(self):
|
||||||
|
self.defaultClass = None
|
||||||
|
self.registry = {}
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
def AddClass(self, className, classObj):
|
||||||
|
self.registry[className] = classObj
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
def GetClass(self, className):
|
||||||
|
# see if any registered class matches the class name passed in
|
||||||
|
for cls in self.registry:
|
||||||
|
if re.match(cls, className):
|
||||||
|
return self.registry[cls]
|
||||||
|
|
||||||
|
# Ok if we got here then none matched so we need to
|
||||||
|
# return the default class if it has been set
|
||||||
|
if self.defaultClass:
|
||||||
|
return self.defaultClass
|
||||||
|
|
||||||
|
else:
|
||||||
|
# oops No default class - raise an exception
|
||||||
|
raise unicode("Nothing registered with name '%s'"% className)
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------
|
||||||
|
def RegisteredClasses(self):
|
||||||
|
return self.registry.keys()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# the following functions are so that when the class is imported many times
|
||||||
|
# the variables windowClassRegistry, testFuncRegistry are not re-set
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def WindowClassRegistry():
|
||||||
|
global windowClassRegistry
|
||||||
|
|
||||||
|
try:
|
||||||
|
return windowClassRegistry
|
||||||
|
except:
|
||||||
|
windowClassRegistry = ClassRegistry()
|
||||||
|
return windowClassRegistry
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def TestFuncRegistry():
|
||||||
|
global testFuncRegistry
|
||||||
|
|
||||||
|
try:
|
||||||
|
return testFuncRegistry
|
||||||
|
except:
|
||||||
|
testFuncRegistry = ClassRegistry()
|
||||||
|
return testFuncRegistry
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def Config():
|
||||||
|
global testConfiguration
|
||||||
|
|
||||||
|
try:
|
||||||
|
return testConfiguration
|
||||||
|
except:
|
||||||
|
testConfiguration = {}
|
||||||
|
return testConfiguration
|
||||||
|
|
407
pywinauto/XMLHelpers.py
Normal file
407
pywinauto/XMLHelpers.py
Normal file
@ -0,0 +1,407 @@
|
|||||||
|
# how should we read in the XML file
|
||||||
|
# NOT USING MS Components (requirement on machine)
|
||||||
|
# maybe using built in XML
|
||||||
|
# maybe using elementtree
|
||||||
|
# others?
|
||||||
|
|
||||||
|
import elementtree
|
||||||
|
from elementtree.ElementTree import Element, SubElement, ElementTree
|
||||||
|
import xml.parsers.expat
|
||||||
|
import ctypes
|
||||||
|
import re
|
||||||
|
import PIL.Image
|
||||||
|
|
||||||
|
|
||||||
|
from win32structures import RECT, LOGFONTW
|
||||||
|
|
||||||
|
|
||||||
|
class XMLParsingError(RuntimeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
charReplacements = (
|
||||||
|
("\\\\" , "\\"),
|
||||||
|
("\\t" , "\t"),
|
||||||
|
("\\r" , "\r"),
|
||||||
|
("\\n" , "\n"),
|
||||||
|
("\\x12" , "\x12"),
|
||||||
|
)
|
||||||
|
|
||||||
|
#todo - make the dialog reading function not actually know about the
|
||||||
|
# types of each element (so that we can read the control properties
|
||||||
|
# without having to know each and every element type)
|
||||||
|
# probably need to store info on what type things are.
|
||||||
|
#
|
||||||
|
# This has mostly been done:
|
||||||
|
# - if it is a ctypes struct then there is a __type__ field
|
||||||
|
# which says what kind of stuct it is
|
||||||
|
# - If it is an image then a "_IMG" is appeded to the the element tag
|
||||||
|
# - if it is a long then _LONG is appended to attribute name
|
||||||
|
# everything else is considered a string!
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def SetNodeProps(element, name, value):
|
||||||
|
|
||||||
|
|
||||||
|
# if it is a ctypes structure
|
||||||
|
if isinstance(value, ctypes.Structure):
|
||||||
|
|
||||||
|
# create an element for the structure
|
||||||
|
structElem = SubElement(element, name)
|
||||||
|
clsModule = value.__class__.__module__
|
||||||
|
clsName = value.__class__.__name__
|
||||||
|
structElem.set("__type__", "%s" % clsName)
|
||||||
|
|
||||||
|
# iterate over the fields in the structure
|
||||||
|
for propName in value._fields_:
|
||||||
|
propName = propName[0]
|
||||||
|
itemVal = getattr(value, propName)
|
||||||
|
|
||||||
|
if isinstance(itemVal, (int, long)):
|
||||||
|
propName += "_LONG"
|
||||||
|
itemVal = unicode(itemVal)
|
||||||
|
|
||||||
|
structElem.set(propName, EscapeSpecials(itemVal))
|
||||||
|
|
||||||
|
elif isinstance(value, PIL.Image.Image):
|
||||||
|
try:
|
||||||
|
imageData = value.tostring().encode("bz2").encode("base64")
|
||||||
|
SetNodeProps(
|
||||||
|
element,
|
||||||
|
name + "_IMG",
|
||||||
|
{"mode": value.mode, "size":value.size, "data":imageData})
|
||||||
|
|
||||||
|
# a system error is raised from time to time when we try to grab
|
||||||
|
# the image of a control that has 0 height or width
|
||||||
|
except SystemError, e:
|
||||||
|
pass
|
||||||
|
#print ";;;" * 20
|
||||||
|
#print value.mode, value.size
|
||||||
|
#imageData = ""
|
||||||
|
|
||||||
|
|
||||||
|
elif isinstance(value, (list, tuple)):
|
||||||
|
# add the element to hold the values
|
||||||
|
#listElem = SubElement(element, name)
|
||||||
|
|
||||||
|
# remove the s at the end (if there)
|
||||||
|
#name = name.rstrip('s')
|
||||||
|
|
||||||
|
for i, attrVal in enumerate(value):
|
||||||
|
SetNodeProps(element, "%s_%05d"%(name, i), attrVal)
|
||||||
|
|
||||||
|
elif isinstance(value, dict):
|
||||||
|
dictElem = SubElement(element, name)
|
||||||
|
|
||||||
|
for n, val in value.items():
|
||||||
|
SetNodeProps(dictElem, n, val)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if isinstance(value, (int, long)):
|
||||||
|
name += "_LONG"
|
||||||
|
|
||||||
|
element.set(name, EscapeSpecials(value))
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def WriteDialogToFile(fileName, props):
|
||||||
|
|
||||||
|
# build a tree structure
|
||||||
|
root = Element("DIALOG")
|
||||||
|
root.set("_version_", "2.0")
|
||||||
|
for ctrl in props:
|
||||||
|
ctrlElem = SubElement(root, "CONTROL")
|
||||||
|
for name, value in sorted(ctrl.items()):
|
||||||
|
SetNodeProps(ctrlElem, name, value)
|
||||||
|
|
||||||
|
# wrap it in an ElementTree instance, and save as XML
|
||||||
|
tree = ElementTree(root)
|
||||||
|
tree.write(fileName, encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def EscapeSpecials(inStr):
|
||||||
|
"Ensure that some characters are escaped before writing to XML"
|
||||||
|
|
||||||
|
# make sure input is a unicode string or convert
|
||||||
|
inStr = unicode(inStr)
|
||||||
|
|
||||||
|
# unicode_escape encoding seems to be broken
|
||||||
|
#escaped = escaped.encode("unicode_escape")
|
||||||
|
|
||||||
|
for (replacement, char) in charReplacements:
|
||||||
|
inStr = inStr.replace(char, replacement)
|
||||||
|
|
||||||
|
return inStr
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def UnEscapeSpecials(inStr):
|
||||||
|
"Replace escaped characters with real character"
|
||||||
|
|
||||||
|
|
||||||
|
#bits = inStr.split("\\\\")
|
||||||
|
#for i, bit in enumerate(bits):
|
||||||
|
# for (replacement, char) in charReplacements[1:]:
|
||||||
|
# inStr = inStr.replace(replacement, char)
|
||||||
|
|
||||||
|
inStr = inStr.decode("unicode_escape")
|
||||||
|
|
||||||
|
return unicode(inStr)
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def XMLToStruct(element, structType = None):
|
||||||
|
"""Convert an ElementTree to a ctypes Struct
|
||||||
|
|
||||||
|
If structType is not specified then element['__type__']
|
||||||
|
will be used for the ctypes struct type"""
|
||||||
|
|
||||||
|
# handle if we are passed in an element or a dictionary
|
||||||
|
if isinstance(element, elementtree.ElementTree._ElementInterface):
|
||||||
|
attribs = element.attrib
|
||||||
|
else:
|
||||||
|
attribs = element
|
||||||
|
|
||||||
|
# if the type has not been passed in
|
||||||
|
if not structType:
|
||||||
|
# get the type and create an instance of the type
|
||||||
|
struct = globals()[attribs["__type__"]]()
|
||||||
|
else:
|
||||||
|
# create an instance of the type
|
||||||
|
struct = globals()[structType]()
|
||||||
|
|
||||||
|
# get the attribute and set them upper case
|
||||||
|
structAttribs = dict([(at.upper(), at) for at in dir(struct)])
|
||||||
|
|
||||||
|
# for each of the attributes in the element
|
||||||
|
for propName in attribs:
|
||||||
|
|
||||||
|
# get teh value
|
||||||
|
val = attribs[propName]
|
||||||
|
|
||||||
|
# if the value ends with "_long"
|
||||||
|
if propName.endswith("_LONG"):
|
||||||
|
# get an long attribute out of the value
|
||||||
|
val = long(val)
|
||||||
|
propName = propName[:-5]
|
||||||
|
|
||||||
|
# if the value is a string
|
||||||
|
elif isinstance(val, basestring):
|
||||||
|
# make sure it if Unicode
|
||||||
|
val = unicode(val)
|
||||||
|
|
||||||
|
# now we can have all upper case attribute name
|
||||||
|
# but structure name will not be upper case
|
||||||
|
if propName.upper() in structAttribs:
|
||||||
|
propName = structAttribs[propName.upper()]
|
||||||
|
|
||||||
|
# set the appropriate attribute of the Struct
|
||||||
|
setattr(struct, propName, val)
|
||||||
|
|
||||||
|
# reutrn the struct
|
||||||
|
return struct
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def OLD_XMLToTitles(element):
|
||||||
|
|
||||||
|
# get all the attribute names
|
||||||
|
titleNames = element.keys()
|
||||||
|
|
||||||
|
# sort them to make sure we get them in the right order
|
||||||
|
titleNames.sort()
|
||||||
|
|
||||||
|
# build up the array
|
||||||
|
titles = []
|
||||||
|
for name in titleNames:
|
||||||
|
val = element[name]
|
||||||
|
val = val.replace('\\n', '\n')
|
||||||
|
val = val.replace('\\x12', '\x12')
|
||||||
|
val = val.replace('\\\\', '\\')
|
||||||
|
|
||||||
|
titles.append(unicode(val))
|
||||||
|
|
||||||
|
return titles
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def ExtractProperties(properties, propName, propValue):
|
||||||
|
|
||||||
|
# get the base property name and number if it in the form
|
||||||
|
# "PROPNAME_00001" = ('PROPNAME', 1)
|
||||||
|
propName, reqdIndex = SplitNumber(propName)
|
||||||
|
|
||||||
|
# if there is no required index, and the property
|
||||||
|
# was not already set - then just set it
|
||||||
|
|
||||||
|
# if this is an indexed member of a list
|
||||||
|
if reqdIndex == None:
|
||||||
|
# Have we hit a property with this name already
|
||||||
|
if propName in properties:
|
||||||
|
# try to append current value to the property
|
||||||
|
try:
|
||||||
|
properties[propName].append(propValue)
|
||||||
|
|
||||||
|
# if that fails then we need to make sure that
|
||||||
|
# the curruen property is a list and then
|
||||||
|
# append it
|
||||||
|
except:
|
||||||
|
newVal = [properties[propName], propValue]
|
||||||
|
properties[propName] = newVal
|
||||||
|
# No index, no previous property with that name
|
||||||
|
# - just set the property
|
||||||
|
else:
|
||||||
|
properties[propName] = propValue
|
||||||
|
|
||||||
|
# OK - so it HAS an index
|
||||||
|
else:
|
||||||
|
|
||||||
|
# make sure that the property is a list
|
||||||
|
properties.setdefault(propName, [])
|
||||||
|
|
||||||
|
# make sure that the list has enough elements
|
||||||
|
while 1:
|
||||||
|
if len(properties[propName]) <= reqdIndex:
|
||||||
|
properties[propName].append('')
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
# put our value in at the right index
|
||||||
|
properties[propName][reqdIndex] = propValue
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def GetAttributes(element):
|
||||||
|
properties = {}
|
||||||
|
|
||||||
|
# get all the attributes
|
||||||
|
for attribName, val in element.attrib.items():
|
||||||
|
|
||||||
|
# if it is 'Long' element convert it to an long
|
||||||
|
if attribName.endswith("_LONG"):
|
||||||
|
val = long(val)
|
||||||
|
attribName = attribName[:-5]
|
||||||
|
|
||||||
|
else:
|
||||||
|
# otherwise it is a string - make sure we get it as a unicode string
|
||||||
|
val = UnEscapeSpecials(val)
|
||||||
|
|
||||||
|
ExtractProperties(properties, attribName, val)
|
||||||
|
|
||||||
|
return properties
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
number = re.compile(r"^(.*)_(\d{5})$")
|
||||||
|
def SplitNumber(propName):
|
||||||
|
found = number.search(propName)
|
||||||
|
|
||||||
|
if not found:
|
||||||
|
return propName, None
|
||||||
|
|
||||||
|
return found.group(1), int(found.group(2))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def ReadXMLStructure(controlElement):
|
||||||
|
|
||||||
|
# get the attributes for the current element
|
||||||
|
properties = GetAttributes(controlElement)
|
||||||
|
|
||||||
|
for elem in controlElement:
|
||||||
|
# if it is a ctypes structure
|
||||||
|
if "__type__" in elem.attrib:
|
||||||
|
# create a new instance of the correct type
|
||||||
|
|
||||||
|
# grab the data
|
||||||
|
propVal = XMLToStruct(elem)
|
||||||
|
|
||||||
|
elif elem.tag.endswith("_IMG"):
|
||||||
|
elem.tag = elem.tag[:-4]
|
||||||
|
|
||||||
|
# get image Attribs
|
||||||
|
img = GetAttributes(elem)
|
||||||
|
data = img['data'].decode('base64').decode('bz2')
|
||||||
|
propVal = PIL.Image.fromstring(img['mode'], img['size'], data)
|
||||||
|
|
||||||
|
else:
|
||||||
|
propVal = ReadXMLStructure(elem)
|
||||||
|
|
||||||
|
ExtractProperties(properties, elem.tag, propVal)
|
||||||
|
|
||||||
|
return properties
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def ReadPropertiesFromFile(filename):
|
||||||
|
"""Return an list of controls from XML file filename"""
|
||||||
|
# parse the file
|
||||||
|
|
||||||
|
parsed = ElementTree().parse(filename)
|
||||||
|
|
||||||
|
# Return the list that has been stored under 'CONTROL'
|
||||||
|
props = ReadXMLStructure(parsed)['CONTROL']
|
||||||
|
|
||||||
|
# it is an old XML so let's fix it up a little
|
||||||
|
if not parsed.attrib.has_key("_version_"):
|
||||||
|
|
||||||
|
# find each of the control elements
|
||||||
|
for ctrlProp in props:
|
||||||
|
|
||||||
|
ctrlProp['Fonts'] = [XMLToStruct(ctrlProp['FONT'], "LOGFONTW"), ]
|
||||||
|
|
||||||
|
ctrlProp['Rectangle'] = XMLToStruct(ctrlProp["RECTANGLE"], "RECT")
|
||||||
|
|
||||||
|
ctrlProp['ClientRects'] = [XMLToStruct(ctrlProp["CLIENTRECT"], "RECT"),]
|
||||||
|
|
||||||
|
ctrlProp['Texts'] = OLD_XMLToTitles(ctrlProp["TITLES"])
|
||||||
|
|
||||||
|
ctrlProp['Class'] = ctrlProp['CLASS']
|
||||||
|
ctrlProp['ContextHelpID'] = ctrlProp['HELPID']
|
||||||
|
ctrlProp['ControlID'] = ctrlProp['CTRLID']
|
||||||
|
ctrlProp['ExStyle'] = ctrlProp['EXSTYLE']
|
||||||
|
ctrlProp['FriendlyClassName'] = ctrlProp['FRIENDLYCLASS']
|
||||||
|
ctrlProp['IsUnicode'] = ctrlProp['ISUNICODE']
|
||||||
|
ctrlProp['IsVisible'] = ctrlProp['ISVISIBLE']
|
||||||
|
ctrlProp['Style'] = ctrlProp['STYLE']
|
||||||
|
ctrlProp['UserData'] = ctrlProp['USERDATA']
|
||||||
|
|
||||||
|
for propName in [
|
||||||
|
'CLASS',
|
||||||
|
'CLIENTRECT',
|
||||||
|
'CTRLID',
|
||||||
|
'EXSTYLE',
|
||||||
|
'FONT',
|
||||||
|
'FRIENDLYCLASS',
|
||||||
|
'HELPID',
|
||||||
|
'ISUNICODE',
|
||||||
|
'ISVISIBLE',
|
||||||
|
'RECTANGLE',
|
||||||
|
'STYLE',
|
||||||
|
'TITLES',
|
||||||
|
'USERDATA',
|
||||||
|
]:
|
||||||
|
del(ctrlProp[propName])
|
||||||
|
|
||||||
|
return props
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
if __name__ == "__main__":
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
props = ReadPropertiesFromFile(sys.argv[1])
|
||||||
|
WriteDialogToFile(sys.argv[1] + "__", props)
|
||||||
|
|
||||||
|
import pprint
|
||||||
|
pprint.pprint(props)
|
||||||
|
|
||||||
|
|
1090
pywinauto/application.py
Normal file
1090
pywinauto/application.py
Normal file
File diff suppressed because it is too large
Load Diff
635
pywinauto/controlactions.py
Normal file
635
pywinauto/controlactions.py
Normal file
@ -0,0 +1,635 @@
|
|||||||
|
#ControlActions
|
||||||
|
import time
|
||||||
|
|
||||||
|
from win32defines import *
|
||||||
|
import win32functions
|
||||||
|
import SendKeys
|
||||||
|
|
||||||
|
|
||||||
|
delay_after_click = .05
|
||||||
|
|
||||||
|
class ControlNotEnabled(RuntimeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ControlNotVisible(RuntimeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def verify_actionable(ctrl):
|
||||||
|
verify_enabled(ctrl)
|
||||||
|
verify_visible(ctrl)
|
||||||
|
|
||||||
|
def verify_enabled(ctrl):
|
||||||
|
if not ctrl.FriendlyClassName == "Dialog":
|
||||||
|
if not ctrl.Parent.IsEnabled:
|
||||||
|
raise ControlNotEnabled()
|
||||||
|
|
||||||
|
if not ctrl.IsEnabled:
|
||||||
|
raise ControlNotEnabled()
|
||||||
|
|
||||||
|
def verify_visible(ctrl):
|
||||||
|
if not ctrl.IsVisible or not ctrl.Parent.IsVisible:
|
||||||
|
raise ControlNotVisible()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mouse_flags = {
|
||||||
|
"left": MK_LBUTTON,
|
||||||
|
"right": MK_RBUTTON,
|
||||||
|
"middle": MK_MBUTTON,
|
||||||
|
"shift": MK_SHIFT,
|
||||||
|
"control": MK_CONTROL,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def calc_flags_and_coords(pressed, coords):
|
||||||
|
flags = 0
|
||||||
|
|
||||||
|
for key in pressed.split():
|
||||||
|
flags |= mouse_flags[key.lower()]
|
||||||
|
|
||||||
|
click_point = coords[0] << 16 | coords[1]
|
||||||
|
|
||||||
|
return flags, click_point
|
||||||
|
|
||||||
|
|
||||||
|
def perform_click(ctrl, button = "left", pressed = "", coords = (0, 0), double = False, down = True, up = True):
|
||||||
|
verify_enabled(ctrl)
|
||||||
|
|
||||||
|
msgs = []
|
||||||
|
if not double:
|
||||||
|
if button.lower() == "left":
|
||||||
|
if down:
|
||||||
|
msgs.append(WM_LBUTTONDOWN)
|
||||||
|
if up:
|
||||||
|
msgs.append(WM_LBUTTONUP)
|
||||||
|
|
||||||
|
elif button.lower() == "middle":
|
||||||
|
if down:
|
||||||
|
msgs.append(WM_MBUTTONDOWN)
|
||||||
|
if up:
|
||||||
|
msgs.append(WM_MBUTTONUP)
|
||||||
|
|
||||||
|
elif button.lower() == "right":
|
||||||
|
if down:
|
||||||
|
msgs.append(WM_RBUTTONDOWN)
|
||||||
|
if up:
|
||||||
|
msgs.append(WM_RBUTTONUP)
|
||||||
|
|
||||||
|
else:
|
||||||
|
if button.lower() == "left":
|
||||||
|
msgs = (WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, WM_LBUTTONUP)
|
||||||
|
elif button.lower() == "middle":
|
||||||
|
msgs = (WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MBUTTONDBLCLK, WM_MBUTTONUP)
|
||||||
|
elif button.lower() == "right":
|
||||||
|
msgs = (WM_RBUTTONDOWN, WM_RBUTTONUP, WM_RBUTTONDBLCLK, WM_RBUTTONUP)
|
||||||
|
|
||||||
|
|
||||||
|
flags, click_point = calc_flags_and_coords(pressed, coords)
|
||||||
|
|
||||||
|
for msg in msgs:
|
||||||
|
ctrl.PostMessage(msg, flags, click_point)
|
||||||
|
|
||||||
|
#ctrl.PostMessage(msg, 1, click_point)
|
||||||
|
time.sleep(delay_after_click)
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def click_action(ctrl, button = "left", pressed = "", coords = (0, 0), double = False):
|
||||||
|
perform_click(ctrl, button, pressed, coords, double)
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def doubleclick_action(ctrl, button = "left", pressed = "", coords = (0, 0), double = True):
|
||||||
|
perform_click(ctrl, button, pressed, coords, double)
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def rightclick_action(ctrl, button = "right", pressed = "", coords = (0, 0), double = True):
|
||||||
|
perform_click(ctrl, button, pressed, coords, double)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def check_button_action(ctrl, select = True):
|
||||||
|
ctrl.SendMessage(BM_SETCHECK, 1)
|
||||||
|
|
||||||
|
def uncheck_button_action(ctrl, select = True):
|
||||||
|
ctrl.SendMessage(BM_SETCHECK, 0)
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def press_mouse_action(ctrl, button = "left", pressed = "", coords = (0, 0)):
|
||||||
|
flags, click_point = calc_flags_and_coords(pressed, coords)
|
||||||
|
|
||||||
|
perform_click(ctrl, button, pressed, coords, up = False)
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def release_mouse_action(ctrl, button = "left", pressed = "", coords = (0, 0)):
|
||||||
|
flags, click_point = calc_flags_and_coords(pressed, coords)
|
||||||
|
perform_click(ctrl, button, pressed, coords, down = False)
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def move_mouse_action(ctrl, pressed = "left", coords = (0, 0)):
|
||||||
|
flags, click_point = calc_flags_and_coords(pressed, coords)
|
||||||
|
ctrl.PostMessage(WM_MOUSEMOVE, flags, click_point)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def settext_action(ctrl, text, append = False):
|
||||||
|
if append:
|
||||||
|
text = ctrl.Text + text
|
||||||
|
|
||||||
|
text = c_wchar_p(unicode(text))
|
||||||
|
ctrl.PostMessage(WM_SETTEXT, 0, text)
|
||||||
|
|
||||||
|
def typekeys_action(
|
||||||
|
ctrl,
|
||||||
|
keys,
|
||||||
|
pause = 0.05,
|
||||||
|
with_spaces = False,
|
||||||
|
with_tabs = False,
|
||||||
|
with_newlines = False,
|
||||||
|
turn_off_numlock = True):
|
||||||
|
|
||||||
|
verify_enabled(ctrl)
|
||||||
|
|
||||||
|
win32functions.AttachThreadInput(win32functions.GetCurrentThreadId(), ctrl.Process(), 1)
|
||||||
|
win32functions.SetForegroundWindow(ctrl)
|
||||||
|
SendKeys.SendKeys(keys, pause, with_spaces, with_tabs, with_newlines, turn_off_numlock)
|
||||||
|
win32functions.AttachThreadInput(win32functions.GetCurrentThreadId(), ctrl.Process(), 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def combobox_select(ctrl, item):
|
||||||
|
if isinstance(item, (int, long)):
|
||||||
|
index = item
|
||||||
|
else:
|
||||||
|
index = ctrl.Texts.index(item)
|
||||||
|
|
||||||
|
ctrl.PostMessage(CB_SETCURSEL, index, 0)
|
||||||
|
ctrl.PostMessage(CBN_SELCHANGE)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def listbox_select(ctrl, item):
|
||||||
|
if isinstance(item, (int, long)):
|
||||||
|
index = item
|
||||||
|
else:
|
||||||
|
index = ctrl.Texts.index(item)
|
||||||
|
|
||||||
|
ctrl.PostMessage(LB_SETCURSEL, index, 0)
|
||||||
|
ctrl.PostMessage(LBN_SELCHANGE)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def set_edit_text(ctrl, text, pos_start = -1, pos_end = -1):
|
||||||
|
set_edit_selection(ctrl, pos_start, pos_end)
|
||||||
|
|
||||||
|
text = c_wchar_p(unicode(text))
|
||||||
|
ctrl.SendMessage(EM_REPLACESEL, True, text)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def set_edit_selection(ctrl, start = 0, end = -1):
|
||||||
|
|
||||||
|
# if we have been asked to select a string
|
||||||
|
if isinstance(start, basestring):
|
||||||
|
string_to_select = start
|
||||||
|
#
|
||||||
|
start = ctrl.texts[1].index(string_to_select)
|
||||||
|
end = start + len(string_to_select)
|
||||||
|
|
||||||
|
ctrl.PostMessage(EM_SETSEL, start, end)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def write_debug_text(ctrl, text):
|
||||||
|
dc = win32functions.CreateDC(u"DISPLAY", None, None, None )
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
|
||||||
|
if not dc:
|
||||||
|
raise ctypes.WinError()
|
||||||
|
|
||||||
|
rect = ctrl.Rectangle
|
||||||
|
#rect.left = 0
|
||||||
|
#rect.top = 0
|
||||||
|
|
||||||
|
#ret = win32functions.TextOut(dc, rect.left, rect.top, unicode(text), len(text))
|
||||||
|
|
||||||
|
ret = win32functions.DrawText(dc, unicode(text), len(text), ctypes.byref(rect), DT_SINGLELINE)
|
||||||
|
|
||||||
|
if not ret:
|
||||||
|
raise ctypes.WinError()
|
||||||
|
|
||||||
|
|
||||||
|
def menupick_action(menuitem):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def select_tab_action(ctrl, tab):
|
||||||
|
if isinstance(tab, basestring):
|
||||||
|
# find the string in the tab control
|
||||||
|
import findbestmatch
|
||||||
|
bestText = findbestmatch.find_best_match(tab, ctrl.Texts, ctrl.Texts)
|
||||||
|
tab = ctrl.Texts.index(bestText) - 1
|
||||||
|
|
||||||
|
ctrl.SendMessage(TCM_SETCURFOCUS, tab)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def draw_outline(ctrl, colour = 'green', thickness = 2, fill = BS_NULL, rect = None):
|
||||||
|
#====================================================================
|
||||||
|
|
||||||
|
colours = {
|
||||||
|
"green" : 0x00ff00,
|
||||||
|
"blue" : 0xff0000,
|
||||||
|
"red" : 0x0000ff,
|
||||||
|
}
|
||||||
|
|
||||||
|
# if it's a known colour
|
||||||
|
if colour in colours:
|
||||||
|
colour = colours[colour]
|
||||||
|
|
||||||
|
if not rect:
|
||||||
|
rect = ctrl.Rectangle
|
||||||
|
|
||||||
|
# create the pen(outline)
|
||||||
|
hPen = CreatePen(PS_SOLID, 2, colour)
|
||||||
|
|
||||||
|
# create the brush (inside)
|
||||||
|
brush = LOGBRUSH()
|
||||||
|
brush.lbStyle = fill
|
||||||
|
brush.lbHatch = HS_DIAGCROSS
|
||||||
|
hBrush = CreateBrushIndirect(byref(brush))
|
||||||
|
|
||||||
|
# get the Device Context
|
||||||
|
dc = CreateDC(u"DISPLAY", None, None, None )
|
||||||
|
|
||||||
|
# push our objects into it
|
||||||
|
SelectObject(dc, hBrush)
|
||||||
|
SelectObject(dc, hPen)
|
||||||
|
|
||||||
|
win32functions.Rectangle(dc, rect.left, rect.top, rect.right, rect.bottom)
|
||||||
|
|
||||||
|
# Delete the brush and pen we created
|
||||||
|
DeleteObject(hBrush)
|
||||||
|
DeleteObject(hPen)
|
||||||
|
|
||||||
|
# delete the Display context that we created
|
||||||
|
DeleteDC(dc)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
######ANYWIN
|
||||||
|
#CaptureBitmap
|
||||||
|
#GetAppId
|
||||||
|
#GetCaption
|
||||||
|
#GetChildren
|
||||||
|
#GetClass
|
||||||
|
#GetHandle
|
||||||
|
#GetNativeClass
|
||||||
|
#GetParent
|
||||||
|
#IsEnabled
|
||||||
|
#IsVisible
|
||||||
|
#TypeKeys
|
||||||
|
#Click
|
||||||
|
#DoubleClick
|
||||||
|
|
||||||
|
#GetHelpText
|
||||||
|
|
||||||
|
|
||||||
|
#ClearTrap
|
||||||
|
#Exists
|
||||||
|
#GenerateDecl
|
||||||
|
#GetArrayProperty
|
||||||
|
#GetBitmapCRC
|
||||||
|
#GetContents
|
||||||
|
#GetEverything
|
||||||
|
#GetIDGetIndex
|
||||||
|
#GetInputLanguage
|
||||||
|
#GetManyProperties
|
||||||
|
#GetName
|
||||||
|
#GetProperty
|
||||||
|
#GetPropertyList
|
||||||
|
#GetRect
|
||||||
|
#GetTag
|
||||||
|
#InvokeMethods
|
||||||
|
#IsActive
|
||||||
|
#IsArrayProperty
|
||||||
|
#IsDefined
|
||||||
|
#IsOfClass
|
||||||
|
#InvokeJava
|
||||||
|
#MenuSelect
|
||||||
|
#MoveMouse
|
||||||
|
#MultiClick
|
||||||
|
#PopupSelect
|
||||||
|
#PressKeys
|
||||||
|
#PressMouse
|
||||||
|
#ReleaseKeys
|
||||||
|
#ReleaseMouse
|
||||||
|
#ScrollIntoView
|
||||||
|
#SetArrayProperty
|
||||||
|
#SetInputLanguage
|
||||||
|
#SetProperty
|
||||||
|
#SetTrap
|
||||||
|
#VerifyActive
|
||||||
|
#VerifyBitmap
|
||||||
|
#VerifyEnabled
|
||||||
|
#VerifyEverything
|
||||||
|
#VerifyText
|
||||||
|
#VerifyProperties
|
||||||
|
#WaitBitmap
|
||||||
|
#Properties
|
||||||
|
#
|
||||||
|
#bActive
|
||||||
|
#AppId
|
||||||
|
#sCaption
|
||||||
|
#lwChildren
|
||||||
|
#Class
|
||||||
|
#bEnabled
|
||||||
|
#bExists
|
||||||
|
#sID
|
||||||
|
#iIndex
|
||||||
|
#sName
|
||||||
|
#wParent
|
||||||
|
#Rect
|
||||||
|
#hWnd
|
||||||
|
#WndTag
|
||||||
|
#
|
||||||
|
#
|
||||||
|
######CONTROL
|
||||||
|
#GetPriorStatic
|
||||||
|
#HasFocus
|
||||||
|
#SetFocus
|
||||||
|
#VerifyFocus
|
||||||
|
#
|
||||||
|
######BUTTON
|
||||||
|
#Click
|
||||||
|
#IsIndeterminate
|
||||||
|
#IsPressed
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#####CHECKBOX
|
||||||
|
#Check
|
||||||
|
#GetState
|
||||||
|
#IsChecked
|
||||||
|
#SetState
|
||||||
|
#Toggle
|
||||||
|
#Uncheck
|
||||||
|
#VerifyValue
|
||||||
|
#
|
||||||
|
#bChecked
|
||||||
|
#bValue
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#####MENUITEM
|
||||||
|
#Check
|
||||||
|
#IsChecked
|
||||||
|
#Pick
|
||||||
|
#Uncheck
|
||||||
|
#VerifyChecked
|
||||||
|
#
|
||||||
|
#bChecked
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#####COMBOBOX
|
||||||
|
#ClearText
|
||||||
|
#FindItem
|
||||||
|
#GetContents
|
||||||
|
#GetItemCount
|
||||||
|
#GetItemText
|
||||||
|
#GetSelIndex
|
||||||
|
#GetSelText
|
||||||
|
#GetText
|
||||||
|
#Select
|
||||||
|
#SetText
|
||||||
|
#VerifyContents
|
||||||
|
#VerifyText
|
||||||
|
#VerifyValue
|
||||||
|
#
|
||||||
|
#lsContents
|
||||||
|
#iItemCount
|
||||||
|
#iValue
|
||||||
|
#sValue
|
||||||
|
#
|
||||||
|
#####LISTBOX
|
||||||
|
#BeginDrag
|
||||||
|
#DoubleSelect
|
||||||
|
#EndDrag
|
||||||
|
#ExtendSelect
|
||||||
|
#FindItem
|
||||||
|
#GetContents
|
||||||
|
#GetItemCount
|
||||||
|
#GetItemText
|
||||||
|
#GetMultiSelIndex
|
||||||
|
#GetMultiSelText
|
||||||
|
#GetSelIndex
|
||||||
|
#GetSelText
|
||||||
|
#IsExtendSel
|
||||||
|
#IsMultiSel
|
||||||
|
#MultiSelect
|
||||||
|
#MultiUnselect
|
||||||
|
#Select
|
||||||
|
#SelectList
|
||||||
|
#SelectRange
|
||||||
|
#VerifyContents
|
||||||
|
#VerifyValue
|
||||||
|
#
|
||||||
|
#lsContents
|
||||||
|
#bIsExtend
|
||||||
|
#bIsMulti
|
||||||
|
#iItemCount
|
||||||
|
#iValue
|
||||||
|
#liValue
|
||||||
|
#lsValue
|
||||||
|
#sValue
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#####EDIT
|
||||||
|
#ClearText
|
||||||
|
#GetContents
|
||||||
|
#GetFontName
|
||||||
|
#GetFontSize
|
||||||
|
#GetMultiSelText
|
||||||
|
#GetMultiText
|
||||||
|
#GetPosition
|
||||||
|
#GetSelRange
|
||||||
|
#GetSelText
|
||||||
|
#GetText
|
||||||
|
#IsBold
|
||||||
|
#IsItalic
|
||||||
|
#IsMultiText
|
||||||
|
#IsRichText
|
||||||
|
#IsUnderline
|
||||||
|
#SetMultiText
|
||||||
|
#SetPosition
|
||||||
|
#SetSelRange
|
||||||
|
#SetText
|
||||||
|
#VerifyPosition
|
||||||
|
#VerifySelRange
|
||||||
|
#VerifySelText
|
||||||
|
#VerifyValue
|
||||||
|
#
|
||||||
|
#bIsMulti
|
||||||
|
#lsValue
|
||||||
|
#sValue
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#####LISTVIEW
|
||||||
|
#BeginDrag
|
||||||
|
#DoubleSelect
|
||||||
|
#EndDrag
|
||||||
|
#ExposeItem
|
||||||
|
#ExtendSelect
|
||||||
|
#FindItem
|
||||||
|
#GetColumnCount
|
||||||
|
#GetColumnName
|
||||||
|
#GetContents
|
||||||
|
#GetItemImageState
|
||||||
|
#GetItemImageIndex
|
||||||
|
#GetItemRect
|
||||||
|
#GetItemText
|
||||||
|
#GetMultiSelIndex
|
||||||
|
#GetMultiSelText
|
||||||
|
#GetSelIndex
|
||||||
|
#GetSelText
|
||||||
|
#GetView
|
||||||
|
#method
|
||||||
|
#(ListView)
|
||||||
|
#IsExtendSel
|
||||||
|
#IsMultiSel
|
||||||
|
#MultiSelect
|
||||||
|
#MultiUnselect
|
||||||
|
#PressItem
|
||||||
|
#ReleaseItem
|
||||||
|
#Select
|
||||||
|
#SelectList
|
||||||
|
#SelectRange
|
||||||
|
#VerifyContents
|
||||||
|
#VerifyValue
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#####TREEVIEW
|
||||||
|
#BeginDrag
|
||||||
|
#Collapse
|
||||||
|
#DoubleSelect
|
||||||
|
#EndDrag
|
||||||
|
#Expand
|
||||||
|
#ExposeItem
|
||||||
|
#ExtendSelect
|
||||||
|
#FindItem
|
||||||
|
#GetContents
|
||||||
|
#GetItemCount
|
||||||
|
#GetItemImageIndex
|
||||||
|
#GetItemImageState
|
||||||
|
#GetItemLevel
|
||||||
|
#GetItemRect
|
||||||
|
#GetItemText
|
||||||
|
#GetSelIndex
|
||||||
|
#GetSelText
|
||||||
|
#GetSubItemCount
|
||||||
|
#GetSubItems
|
||||||
|
#IsItemEditable
|
||||||
|
#IsItemExpandable
|
||||||
|
#IsItemExpanded
|
||||||
|
#MultiSelect
|
||||||
|
#MultiUnselect
|
||||||
|
#PressItem
|
||||||
|
#ReleaseItem
|
||||||
|
#Select
|
||||||
|
#SelectList
|
||||||
|
#VerifyContents
|
||||||
|
#VerifyValue
|
||||||
|
#
|
||||||
|
#####Static
|
||||||
|
#GetText
|
||||||
|
#VerifyValue
|
||||||
|
|
||||||
|
|
||||||
|
standard_action_funcs = dict(
|
||||||
|
Click = click_action,
|
||||||
|
RightClick = rightclick_action,
|
||||||
|
DoubleClick = doubleclick_action,
|
||||||
|
TypeKeys = typekeys_action,
|
||||||
|
SetText = settext_action,
|
||||||
|
ReleaseMouse = release_mouse_action,
|
||||||
|
MoveMouse = move_mouse_action,
|
||||||
|
PressMouse = press_mouse_action,
|
||||||
|
DebugMessage = write_debug_text,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class_specific_actions = {
|
||||||
|
|
||||||
|
'ComboBox' : dict(
|
||||||
|
Select = combobox_select,
|
||||||
|
),
|
||||||
|
|
||||||
|
'ListBox' : dict(
|
||||||
|
Select = listbox_select,
|
||||||
|
),
|
||||||
|
|
||||||
|
'Edit' : dict(
|
||||||
|
Select = set_edit_selection,
|
||||||
|
SetText = set_edit_text,
|
||||||
|
),
|
||||||
|
|
||||||
|
'CheckBox' : dict(
|
||||||
|
Check = check_button_action,
|
||||||
|
UnCheck = uncheck_button_action,
|
||||||
|
),
|
||||||
|
|
||||||
|
'Button' : dict(
|
||||||
|
Check = check_button_action,
|
||||||
|
UnCheck = uncheck_button_action,
|
||||||
|
),
|
||||||
|
"TabControl" : dict(
|
||||||
|
Select = select_tab_action
|
||||||
|
),
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#=========================================================================
|
||||||
|
def deferred_func(func):
|
||||||
|
def func_wrapper(ctrl, *args, **kwargs):
|
||||||
|
|
||||||
|
return func(ctrl._, *args, **kwargs)
|
||||||
|
|
||||||
|
return func_wrapper
|
||||||
|
|
||||||
|
#=========================================================================
|
||||||
|
def identity(func):
|
||||||
|
return func
|
||||||
|
|
||||||
|
|
||||||
|
#=========================================================================
|
||||||
|
def add_actions(to_obj, deferred = False):
|
||||||
|
# add common actions:
|
||||||
|
import application
|
||||||
|
|
||||||
|
if hasattr(to_obj, "_"):
|
||||||
|
defer_action = deferred_func
|
||||||
|
ctrl = to_obj._
|
||||||
|
|
||||||
|
else:
|
||||||
|
defer_action = identity
|
||||||
|
ctrl = to_obj
|
||||||
|
|
||||||
|
# for each of the standard actions
|
||||||
|
for action_name in standard_action_funcs:
|
||||||
|
# add it to the control class
|
||||||
|
setattr (to_obj.__class__, action_name, defer_action(standard_action_funcs[action_name]))
|
||||||
|
|
||||||
|
if class_specific_actions.has_key(ctrl.FriendlyClassName):
|
||||||
|
actions = class_specific_actions[ctrl.FriendlyClassName]
|
||||||
|
|
||||||
|
for action_name, action_func in actions.items():
|
||||||
|
setattr (to_obj.__class__, action_name, defer_action(action_func))
|
||||||
|
|
||||||
|
return ctrl
|
177
pywinauto/controlproperties.py
Normal file
177
pywinauto/controlproperties.py
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
# Control Properties
|
||||||
|
from win32structures import RECT, LOGFONTW
|
||||||
|
|
||||||
|
from pprint import pprint
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class ControlProps(dict):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, props = {}):
|
||||||
|
# default to having menuItems for all things
|
||||||
|
self.MenuItems = []
|
||||||
|
|
||||||
|
self.update(props)
|
||||||
|
#for x in props:
|
||||||
|
#self[x] = props[x]
|
||||||
|
|
||||||
|
if hasattr(props, "handle"):
|
||||||
|
self.__dict__['handle'] = props.handle
|
||||||
|
else:
|
||||||
|
self.__dict__['handle'] = None
|
||||||
|
|
||||||
|
self.__dict__['ref'] = None
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
# handles attribute access for dictionary items and
|
||||||
|
# for plurals (e.g. if self.Fonts = [4, 2] then self.Font = 4)
|
||||||
|
def __getattr__(self, key):
|
||||||
|
|
||||||
|
# if the key is not in the dictionary but the plural is
|
||||||
|
if key not in self and key + "s" in self:
|
||||||
|
|
||||||
|
# try to get the first element of the possible list item
|
||||||
|
try:
|
||||||
|
return self[key + "s"][0]
|
||||||
|
except TypeError, e:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if key in self:
|
||||||
|
return self[key]
|
||||||
|
|
||||||
|
return self.__dict__[key]
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __setattr__(self, key, value):
|
||||||
|
if key in self.__dict__:
|
||||||
|
self.__dict__[key] = value
|
||||||
|
else:
|
||||||
|
self[key] = value
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def HasStyle(self, flag):
|
||||||
|
return self.Style & flag == flag
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def HasExStyle(self, flag):
|
||||||
|
return self.ExStyle & flag == flag
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def GetMenuBlocks(ctrls):
|
||||||
|
allMenuBlocks = []
|
||||||
|
for ctrl in ctrls:
|
||||||
|
if ctrl.has_key('MenuItems'):
|
||||||
|
|
||||||
|
# we need to get all the separate menu blocks!
|
||||||
|
menuBlocks = MenuBlockAsControls(ctrl.MenuItems)
|
||||||
|
allMenuBlocks.extend(menuBlocks)
|
||||||
|
|
||||||
|
return allMenuBlocks
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def MenuBlockAsControls(menuItems, parentage = []):
|
||||||
|
|
||||||
|
blocks = []
|
||||||
|
|
||||||
|
curBlock = []
|
||||||
|
for item in menuItems:
|
||||||
|
|
||||||
|
# do a bit of conversion first :-)
|
||||||
|
itemAsCtrl = MenuItemAsControl(item)
|
||||||
|
|
||||||
|
# update the FriendlyClassName to contain the 'path' to
|
||||||
|
# this particular item
|
||||||
|
itemPath = "%s->%s" % ("->".join(parentage), item['Text'])
|
||||||
|
itemAsCtrl.FriendlyClassName = "MenuItem %s" %itemPath
|
||||||
|
|
||||||
|
#append the item to the current menu block
|
||||||
|
curBlock.append(itemAsCtrl)
|
||||||
|
|
||||||
|
# If the item has a sub menu
|
||||||
|
if item.has_key('MenuItems'):
|
||||||
|
|
||||||
|
# add the current item the path
|
||||||
|
parentage.append(item['Text'])
|
||||||
|
|
||||||
|
# Get the block for the SubMenu, and add it to the list of
|
||||||
|
# blocks we have found
|
||||||
|
blocks.extend(MenuBlockAsControls(item['MenuItems'], parentage))
|
||||||
|
|
||||||
|
# and seeing as we are dong with that sub menu remove the current
|
||||||
|
# item from the path
|
||||||
|
del(parentage[-1])
|
||||||
|
|
||||||
|
# add the current block to the list of blocks
|
||||||
|
blocks.append(curBlock)
|
||||||
|
|
||||||
|
return blocks
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def MenuItemAsControl(menuItem):
|
||||||
|
itemAsCtrl = ControlProps()
|
||||||
|
|
||||||
|
itemAsCtrl["Texts"] = [menuItem['Text'], ]
|
||||||
|
itemAsCtrl["ControlID"] = menuItem['ID']
|
||||||
|
itemAsCtrl["Type"] = menuItem['Type']
|
||||||
|
itemAsCtrl["State"] = menuItem['State']
|
||||||
|
|
||||||
|
itemAsCtrl["Class"] = "MenuItem"
|
||||||
|
itemAsCtrl["FriendlyClassName"] = "MenuItem"
|
||||||
|
|
||||||
|
# as most of these don't matter - just set them up with default stuff
|
||||||
|
itemAsCtrl["Rectangle"] = RECT(0, 0, 999, 999)
|
||||||
|
itemAsCtrl["Fonts"] = [LOGFONTW(), ]
|
||||||
|
itemAsCtrl["ClientRects"] = [RECT(0, 0, 999, 999), ]
|
||||||
|
itemAsCtrl["ContextHelpID"] = 0
|
||||||
|
itemAsCtrl["UserData"] = 0
|
||||||
|
itemAsCtrl["Style"] = 0
|
||||||
|
itemAsCtrl["ExStyle"] = 0
|
||||||
|
itemAsCtrl["IsVisible"] = 1
|
||||||
|
|
||||||
|
return itemAsCtrl
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def SetReferenceControls(controls, refControls):
|
||||||
|
|
||||||
|
# numbers of controls must be the same (though in future I could imagine
|
||||||
|
# relaxing this constraint)
|
||||||
|
|
||||||
|
if len(controls) != len(refControls):
|
||||||
|
#print len(controls), len(refControls)
|
||||||
|
#pprint(controls)
|
||||||
|
#print "==" * 20
|
||||||
|
#pprint(refControls)
|
||||||
|
raise "Numbers of controls on ref. dialog does not match Loc. dialog"
|
||||||
|
|
||||||
|
# set the controls
|
||||||
|
for i, ctrl in enumerate(controls):
|
||||||
|
ctrl.ref = refControls[i]
|
||||||
|
|
||||||
|
toRet = 1
|
||||||
|
allIDsSameFlag = 2
|
||||||
|
allClassesSameFlag = 4
|
||||||
|
|
||||||
|
# find if all the control id's match
|
||||||
|
if [ctrl.ControlID for ctrl in controls] == \
|
||||||
|
[ctrl.ControlID for ctrl in refControls]:
|
||||||
|
|
||||||
|
toRet += allIDsSameFlag
|
||||||
|
|
||||||
|
# check if the control classes match
|
||||||
|
if [ctrl.Class for ctrl in controls] == \
|
||||||
|
[ctrl.Class for ctrl in refControls]:
|
||||||
|
|
||||||
|
toRet += allClassesSameFlag
|
||||||
|
|
||||||
|
return toRet
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
517
pywinauto/controls/HwndWrapper.py
Normal file
517
pywinauto/controls/HwndWrapper.py
Normal file
@ -0,0 +1,517 @@
|
|||||||
|
# pylint: disable-msg=W0611
|
||||||
|
import re
|
||||||
|
from pprint import pprint
|
||||||
|
import PIL.ImageGrab
|
||||||
|
from ctypes import *
|
||||||
|
|
||||||
|
from win32defines import *
|
||||||
|
from win32functions import *
|
||||||
|
from win32structures import *
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def WrapHandle(hwnd, isDialog = False):
|
||||||
|
|
||||||
|
default_wrapper = HwndWrapper(hwnd)
|
||||||
|
|
||||||
|
for wrapper_name in HwndWrappers:
|
||||||
|
if re.search(wrapper_name, default_wrapper.Class):
|
||||||
|
return HwndWrappers[wrapper_name](hwnd)
|
||||||
|
|
||||||
|
default_wrapper._NeedsImageProp = True
|
||||||
|
|
||||||
|
return default_wrapper
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class HwndWrapper(object):
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
|
||||||
|
self.handle = hwnd
|
||||||
|
|
||||||
|
# make it so that ctypes conversion happens correctly
|
||||||
|
self._as_parameter_ = self.handle
|
||||||
|
|
||||||
|
# specify whether we need to grab an image of ourselves
|
||||||
|
# when asked for properties
|
||||||
|
self._NeedsImageProp = False
|
||||||
|
|
||||||
|
# set the friendly class name to default to
|
||||||
|
# the class name
|
||||||
|
self.FriendlyClassName = self.Class
|
||||||
|
self._extra_texts = []
|
||||||
|
self._extra_clientrects = []
|
||||||
|
self._extra_props = {}
|
||||||
|
|
||||||
|
self._extra_props['MenuItems'] = self.MenuItems
|
||||||
|
|
||||||
|
# if it is a main window
|
||||||
|
if self.IsDialog:
|
||||||
|
self.FriendlyClassName = "Dialog"
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_is_dialog(self):
|
||||||
|
if (self.HasStyle(WS_OVERLAPPED) or self.HasStyle(WS_CAPTION)) and not self.HasStyle(WS_CHILD):
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
IsDialog = property (get_is_dialog, doc = "Whether the window is a dialog or not")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
# define the Menu Property
|
||||||
|
def get_menuitems(self):
|
||||||
|
if self.IsDialog:
|
||||||
|
return GetMenuItems(GetMenu(self))
|
||||||
|
else:
|
||||||
|
return []
|
||||||
|
MenuItems = property (get_menuitems, doc = "Return the menu items for the dialog")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_text(self):
|
||||||
|
# Had to use GetWindowText and GetWindowTextLength rather than
|
||||||
|
# using WM_GETTEXT as we want it to fail for controls that dont
|
||||||
|
# have 'usable' first text e.g. Edit controls and Comboboxes.
|
||||||
|
|
||||||
|
length = GetWindowTextLength(self.handle,)
|
||||||
|
|
||||||
|
text = ''
|
||||||
|
if length:
|
||||||
|
length += 1
|
||||||
|
|
||||||
|
buffer = (c_wchar * length)()
|
||||||
|
|
||||||
|
ret = GetWindowText(self, byref(buffer), length)
|
||||||
|
|
||||||
|
if ret:
|
||||||
|
text = buffer.value
|
||||||
|
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# # get the number of characters required
|
||||||
|
# bufferSize = self.SendMessage (WM_GETTEXTLENGTH)
|
||||||
|
#
|
||||||
|
# text = ''
|
||||||
|
# # allocate the buffer size
|
||||||
|
# if bufferSize:
|
||||||
|
# bufferSize += 1
|
||||||
|
# # allocate the buffer
|
||||||
|
# title = (c_wchar * bufferSize)()
|
||||||
|
#
|
||||||
|
# # retrieve the text
|
||||||
|
# self.SendMessage (WM_GETTEXT, bufferSize, title)
|
||||||
|
#
|
||||||
|
# # get the unicode object from the ctypes instance
|
||||||
|
# text = title.value
|
||||||
|
#
|
||||||
|
# return text
|
||||||
|
Text = property (get_text, doc = "Main text of the control")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_texts(self):
|
||||||
|
texts = [self.Text, ]
|
||||||
|
texts.extend(self._extra_texts)
|
||||||
|
return texts
|
||||||
|
Texts = property (get_texts, doc = "All text items of the control")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_Class(self):
|
||||||
|
# get the className
|
||||||
|
className = (c_wchar * 257)()
|
||||||
|
GetClassName (self, byref(className), 256)
|
||||||
|
return className.value
|
||||||
|
Class = property (get_Class, doc = "Class Name of the window")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_parent(self):
|
||||||
|
return HwndWrapper(GetParent(self))
|
||||||
|
Parent = property (get_parent, doc = "Parent window of window")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_Style(self):
|
||||||
|
return GetWindowLong (self, GWL_STYLE)
|
||||||
|
Style = property (get_Style, doc = "Style of window")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_ExStyle(self):
|
||||||
|
return GetWindowLong (self.handle, GWL_EXSTYLE)
|
||||||
|
ExStyle = property (get_ExStyle, doc = "Extended Style of window")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_ControlID(self):
|
||||||
|
return GetWindowLong (self.handle, GWL_ID)
|
||||||
|
ControlID = property (get_ControlID, doc = "The ID of the window")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_userdata(self):
|
||||||
|
return GetWindowLong (self.handle, GWL_USERDATA)
|
||||||
|
UserData = property (
|
||||||
|
get_userdata, doc = "Extra data associted with the window")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_ContextHelpID(self):
|
||||||
|
return GetWindowContextHelpId (self.handle)
|
||||||
|
ContextHelpID = property (
|
||||||
|
get_ContextHelpID, doc = "The Context Help ID of the window")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_IsVisible(self):
|
||||||
|
return IsWindowVisible(self.handle)
|
||||||
|
IsVisible = property (get_IsVisible, doc = "Whether the window is visible or not")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_IsUnicode(self):
|
||||||
|
return IsWindowUnicode(self.handle)
|
||||||
|
IsUnicode = property (get_IsUnicode, doc = "Whether the window is unicode or not")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_isenabled(self):
|
||||||
|
return IsWindowEnabled(self.handle)
|
||||||
|
IsEnabled = property (get_isenabled, doc = "Whether the window is enabled or not")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_clientrect(self):
|
||||||
|
"Returns the client rectangle of the control"
|
||||||
|
clientRect = RECT()
|
||||||
|
GetClientRect(self.handle, byref(clientRect))
|
||||||
|
return clientRect
|
||||||
|
ClientRect = property (get_clientrect, doc = "ClientRect of window")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_clientrects(self):
|
||||||
|
clientrects = [self.ClientRect, ]
|
||||||
|
clientrects.extend(self._extra_clientrects)
|
||||||
|
return clientrects
|
||||||
|
ClientRects = property (
|
||||||
|
get_clientrects, doc = "All client rectanbgles of the control")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_rectangle(self):
|
||||||
|
# get the full rectangle
|
||||||
|
rect = RECT()
|
||||||
|
GetWindowRect(self.handle, byref(rect))
|
||||||
|
return rect
|
||||||
|
Rectangle = property (get_rectangle, doc = "Rectangle of window")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_children(self):
|
||||||
|
|
||||||
|
# this will be filled in the callback function
|
||||||
|
childWindows = []
|
||||||
|
|
||||||
|
# callback function for EnumChildWindows
|
||||||
|
def enumChildProc(hWnd, LPARAM):
|
||||||
|
win = WrapHandle(hWnd)
|
||||||
|
|
||||||
|
# append it to our list
|
||||||
|
childWindows.append(win)
|
||||||
|
|
||||||
|
# return true to keep going
|
||||||
|
return True
|
||||||
|
|
||||||
|
# define the child proc type
|
||||||
|
EnumChildProc = WINFUNCTYPE(c_int, HWND, LPARAM)
|
||||||
|
proc = EnumChildProc(enumChildProc)
|
||||||
|
|
||||||
|
# loop over all the children (callback called for each)
|
||||||
|
EnumChildWindows(self.handle, proc, 0)
|
||||||
|
|
||||||
|
return childWindows
|
||||||
|
Children = property (get_children, doc = "The list of children")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_Font(self):
|
||||||
|
# set the font
|
||||||
|
fontHandle = self.SendMessage (WM_GETFONT)
|
||||||
|
|
||||||
|
# if the fondUsed is 0 then the control is using the
|
||||||
|
# system font
|
||||||
|
if not fontHandle:
|
||||||
|
fontHandle = GetStockObject(SYSTEM_FONT);
|
||||||
|
|
||||||
|
# Get the Logfont structure of the font of the control
|
||||||
|
font = LOGFONTW()
|
||||||
|
ret = GetObject(fontHandle, sizeof(font), byref(font))
|
||||||
|
|
||||||
|
# The function could not get the font - this is probably
|
||||||
|
# because the control does not have associated Font/Text
|
||||||
|
# So we should make sure the elements of the font are zeroed.
|
||||||
|
if not ret:
|
||||||
|
font = LOGFONTW()
|
||||||
|
|
||||||
|
# if it is a main window
|
||||||
|
if (self.HasStyle(WS_OVERLAPPED) or self.HasStyle(WS_CAPTION)) and not self.HasStyle(WS_CHILD):
|
||||||
|
|
||||||
|
if "MS Shell Dlg" in font.lfFaceName or font.lfFaceName == "System":
|
||||||
|
# these are not usually the fonts actaully used in for
|
||||||
|
# title bars so we need to get the default title bar font
|
||||||
|
|
||||||
|
# get the title font based on the system metrics rather
|
||||||
|
# than the font of the control itself
|
||||||
|
SPI_GETNONCLIENTMETRICS = 41 # set the message number
|
||||||
|
ncms = NONCLIENTMETRICSW()
|
||||||
|
ncms.cbSize = sizeof(ncms)
|
||||||
|
SystemParametersInfo(
|
||||||
|
SPI_GETNONCLIENTMETRICS,
|
||||||
|
sizeof(ncms),
|
||||||
|
byref(ncms),
|
||||||
|
0)
|
||||||
|
|
||||||
|
# with either of the following 2 flags set the font of the
|
||||||
|
# dialog isthe small one (but there is normally no difference!
|
||||||
|
if self.HasStyle(WS_EX_TOOLWINDOW) or \
|
||||||
|
self.HasStyle(WS_EX_PALETTEWINDOW):
|
||||||
|
|
||||||
|
font = ncms.lfSmCaptionFont
|
||||||
|
else:
|
||||||
|
font = ncms.lfCaptionFont
|
||||||
|
|
||||||
|
return font
|
||||||
|
Font = property (get_Font, doc = "The font of the window")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_Fonts(self):
|
||||||
|
return [self.Font, ]
|
||||||
|
Fonts = property (get_Fonts, doc = "All fonts of the control")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def IsChild(self, parent):
|
||||||
|
"Return whether the window is a child of parent."
|
||||||
|
|
||||||
|
# Call the IsChild API funciton and convert the result
|
||||||
|
# to True/False
|
||||||
|
return IsChild(self.handle, parentHwnd) != 0
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def HasStyle(self, Style):
|
||||||
|
if self.Style & Style == Style:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def HasExStyle(self, Style):
|
||||||
|
if self.ExStyle & Style == Style:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def SendMessage(self, message, wparam = 0 , lparam = 0):
|
||||||
|
return SendMessage(self.handle, message, wparam, lparam)
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def PostMessage(self, message, wparam = 0 , lparam = 0):
|
||||||
|
return PostMessage(self.handle, message, wparam, lparam)
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def GetProperties(self):
|
||||||
|
props = self._extra_props
|
||||||
|
|
||||||
|
# get the className
|
||||||
|
props['Class'] = self.Class
|
||||||
|
|
||||||
|
# set up the friendlyclass defaulting
|
||||||
|
# to the class Name
|
||||||
|
props['FriendlyClassName'] = self.FriendlyClassName
|
||||||
|
|
||||||
|
props['Texts'] = self.Texts
|
||||||
|
props['Style'] = self.Style
|
||||||
|
props['ExStyle'] = self.ExStyle
|
||||||
|
props['ControlID'] = self.ControlID
|
||||||
|
props['UserData'] = self.UserData
|
||||||
|
props['ContextHelpID'] = self.ContextHelpID
|
||||||
|
|
||||||
|
props['Fonts'] = self.Fonts
|
||||||
|
props['ClientRects'] = self.ClientRects
|
||||||
|
|
||||||
|
props['Rectangle'] = self.Rectangle
|
||||||
|
|
||||||
|
props['IsVisible'] = self.IsVisible
|
||||||
|
props['IsUnicode'] = self.IsUnicode
|
||||||
|
props['IsEnabled'] = self.IsEnabled
|
||||||
|
|
||||||
|
#props['MenuItems'] = []
|
||||||
|
|
||||||
|
if self._NeedsImageProp:
|
||||||
|
props['Image'] = self.CaptureAsImage()
|
||||||
|
|
||||||
|
#return the properties
|
||||||
|
return props
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def CaptureAsImage(self):
|
||||||
|
|
||||||
|
if not (self.Rectangle.width() and self.Rectangle.height()):
|
||||||
|
return None
|
||||||
|
|
||||||
|
# get the control rectangle in a way that PIL likes it
|
||||||
|
box = (
|
||||||
|
self.Rectangle.left,
|
||||||
|
self.Rectangle.top,
|
||||||
|
self.Rectangle.right,
|
||||||
|
self.Rectangle.bottom)
|
||||||
|
|
||||||
|
# grab the image and get raw data as a string
|
||||||
|
image = PIL.ImageGrab.grab(box)
|
||||||
|
|
||||||
|
return image
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def __eq__(self, other):
|
||||||
|
if isinstance(other, HwndWrapper):
|
||||||
|
return self.handle == other.handle
|
||||||
|
else:
|
||||||
|
return self.handle == other
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_process_id(self):
|
||||||
|
"ID of process that controls this window"
|
||||||
|
process_id = c_int()
|
||||||
|
GetWindowThreadProcessId(self, byref(process_id))
|
||||||
|
|
||||||
|
return process_id.value
|
||||||
|
ProcessID = property (get_process_id, doc = get_process_id.__doc__)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def GetMenuItems(menuHandle):#, indent = ""):
|
||||||
|
|
||||||
|
# If it doesn't have a real menu just return
|
||||||
|
if not IsMenu(menuHandle):
|
||||||
|
return []
|
||||||
|
|
||||||
|
items = []
|
||||||
|
|
||||||
|
itemCount = GetMenuItemCount(menuHandle)
|
||||||
|
|
||||||
|
# for each menu item
|
||||||
|
for i in range(0, itemCount):
|
||||||
|
|
||||||
|
itemProp = {} #Controls_Standard.ControlProps()
|
||||||
|
|
||||||
|
# get the information on the menu Item
|
||||||
|
menuInfo = MENUITEMINFOW()
|
||||||
|
menuInfo.cbSize = sizeof (menuInfo)
|
||||||
|
menuInfo.fMask = \
|
||||||
|
MIIM_CHECKMARKS | \
|
||||||
|
MIIM_ID | \
|
||||||
|
MIIM_STATE | \
|
||||||
|
MIIM_SUBMENU | \
|
||||||
|
MIIM_TYPE
|
||||||
|
#MIIM_FTYPE | \
|
||||||
|
#MIIM_STRING
|
||||||
|
#MIIM_DATA | \
|
||||||
|
|
||||||
|
|
||||||
|
ret = GetMenuItemInfo (menuHandle, i, True, byref(menuInfo))
|
||||||
|
if not ret:
|
||||||
|
raise WinError()
|
||||||
|
|
||||||
|
itemProp['State'] = menuInfo.fState
|
||||||
|
itemProp['Type'] = menuInfo.fType
|
||||||
|
itemProp['ID'] = menuInfo.wID
|
||||||
|
#itemProp.handle = menuHandle
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# if there is text
|
||||||
|
if menuInfo.cch:
|
||||||
|
# allocate a buffer
|
||||||
|
bufferSize = menuInfo.cch+1
|
||||||
|
text = (c_wchar * bufferSize)()
|
||||||
|
|
||||||
|
# update the structure and get the text info
|
||||||
|
menuInfo.dwTypeData = addressof(text)
|
||||||
|
menuInfo.cch = bufferSize
|
||||||
|
GetMenuItemInfo (menuHandle, i, True, byref(menuInfo))
|
||||||
|
itemProp['Text'] = text.value
|
||||||
|
else:
|
||||||
|
itemProp['Text'] = ""
|
||||||
|
|
||||||
|
|
||||||
|
# if it's a sub menu then get it's items
|
||||||
|
if menuInfo.hSubMenu:
|
||||||
|
#indent += " "
|
||||||
|
subMenuItems = GetMenuItems(menuInfo.hSubMenu)#, indent)
|
||||||
|
itemProp['MenuItems'] = subMenuItems
|
||||||
|
#indent = indent[1:-2]
|
||||||
|
|
||||||
|
items.append(itemProp)
|
||||||
|
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class dummy_control(dict):
|
||||||
|
pass
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def GetDialogPropsFromHandle(hwnd):
|
||||||
|
# wrap the dialog handle and start a new list for the
|
||||||
|
# controls on the dialog
|
||||||
|
controls = [WrapHandle(hwnd, True), ]
|
||||||
|
|
||||||
|
# add all the children of the dialog
|
||||||
|
controls.extend(controls[0].Children)
|
||||||
|
|
||||||
|
props = []
|
||||||
|
|
||||||
|
# Add each control to the properties for this dialog
|
||||||
|
for ctrl in controls:
|
||||||
|
# Get properties for each control and wrap them in
|
||||||
|
# dummy_control so that we can assign handle
|
||||||
|
ctrlProps = dummy_control(ctrl.GetProperties())
|
||||||
|
|
||||||
|
# assign the handle
|
||||||
|
ctrlProps.handle = ctrl.handle
|
||||||
|
|
||||||
|
# offset the rectangle from the dialog rectangle
|
||||||
|
ctrlProps['Rectangle'] -= controls[0].Rectangle
|
||||||
|
|
||||||
|
props.append(ctrlProps)
|
||||||
|
|
||||||
|
return props
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
HwndWrappers = {}
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def Main():
|
||||||
|
|
||||||
|
from FindDialog import FindDialog
|
||||||
|
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
handle = GetDesktopWindow()
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
handle = int(eval(sys.argv[1]))
|
||||||
|
|
||||||
|
except:
|
||||||
|
|
||||||
|
handle = FindDialog("^" + sys.argv[1], testClass = "#32770")
|
||||||
|
|
||||||
|
if not handle:
|
||||||
|
print "dialog not found"
|
||||||
|
sys.exit()
|
||||||
|
#print handle
|
||||||
|
#pprint (HwndWrapper(handle).GetProperties())
|
||||||
|
|
||||||
|
|
||||||
|
#pprint(GetDialogPropsFromHandle(handle))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
Main()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,753 @@
|
|||||||
|
# pylint: disable-msg=W0611
|
||||||
|
"Handles Static, Button, Edit, ListBox, ComboBox, ComboLBox"
|
||||||
|
from ctypes import *
|
||||||
|
from ctypes.wintypes import *
|
||||||
|
|
||||||
|
from APIFuncs import *
|
||||||
|
from APIDefines import *
|
||||||
|
from APIStructures import *
|
||||||
|
|
||||||
|
from XMLHelpers import *
|
||||||
|
|
||||||
|
__revision__ = "0.0.0.1"
|
||||||
|
|
||||||
|
|
||||||
|
from InfoManager import WindowClassRegistry
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def GetMenuItems(menuHandle):#, indent = ""):
|
||||||
|
items = []
|
||||||
|
|
||||||
|
# check that it is a menu
|
||||||
|
if IsMenu(menuHandle):
|
||||||
|
|
||||||
|
itemCount = GetMenuItemCount(menuHandle)
|
||||||
|
|
||||||
|
# for each menu item
|
||||||
|
for i in range(0, itemCount):
|
||||||
|
|
||||||
|
itemProp = {}
|
||||||
|
|
||||||
|
# get the information on the menu Item
|
||||||
|
menuInfo = MENUITEMINFOW()
|
||||||
|
menuInfo.cbSize = sizeof (menuInfo)
|
||||||
|
menuInfo.fMask = \
|
||||||
|
MIIM_CHECKMARKS | \
|
||||||
|
MIIM_ID | \
|
||||||
|
MIIM_STATE | \
|
||||||
|
MIIM_SUBMENU | \
|
||||||
|
MIIM_TYPE
|
||||||
|
#MIIM_FTYPE | \
|
||||||
|
#MIIM_STRING
|
||||||
|
#MIIM_DATA | \
|
||||||
|
|
||||||
|
|
||||||
|
ret = GetMenuItemInfo (menuHandle, i, True, byref(menuInfo))
|
||||||
|
if not ret:
|
||||||
|
raise WinError()
|
||||||
|
|
||||||
|
itemProp['State'] = menuInfo.fState
|
||||||
|
itemProp['Type'] = menuInfo.fType
|
||||||
|
itemProp['ID'] = menuInfo.wID
|
||||||
|
|
||||||
|
|
||||||
|
# if there is text
|
||||||
|
if menuInfo.cch:
|
||||||
|
# allocate a buffer
|
||||||
|
bufferSize = menuInfo.cch+1
|
||||||
|
text = (c_wchar * bufferSize)()
|
||||||
|
|
||||||
|
# update the structure and get the text info
|
||||||
|
menuInfo.dwTypeData = addressof(text)
|
||||||
|
menuInfo.cch = bufferSize
|
||||||
|
GetMenuItemInfo (menuHandle, i, True, byref(menuInfo))
|
||||||
|
itemProp['Text'] = text.value
|
||||||
|
else:
|
||||||
|
itemProp['Text'] = ""
|
||||||
|
|
||||||
|
|
||||||
|
# if it's a sub menu then get it's items
|
||||||
|
if menuInfo.hSubMenu:
|
||||||
|
#indent += " "
|
||||||
|
subMenuItems = GetMenuItems(menuInfo.hSubMenu)#, indent)
|
||||||
|
itemProp['MenuItems'] = subMenuItems
|
||||||
|
#indent = indent[1:-2]
|
||||||
|
|
||||||
|
items.append(itemProp)
|
||||||
|
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def DefaultWindowHwndReader(hwnd, dialogRect):
|
||||||
|
|
||||||
|
props = {}
|
||||||
|
|
||||||
|
# get the className
|
||||||
|
props['Class'] = GetClass(hwnd)
|
||||||
|
|
||||||
|
# set up the friendlyclass defaulting
|
||||||
|
# to the class Name
|
||||||
|
props['FriendlyClassName'] = props['Class']
|
||||||
|
|
||||||
|
# get the title
|
||||||
|
bufferSize = SendMessage (hwnd, WM_GETTEXTLENGTH, 0, 0)
|
||||||
|
if bufferSize:
|
||||||
|
bufferSize += 1
|
||||||
|
title = (c_wchar * bufferSize)()
|
||||||
|
SendMessage (hwnd, WM_GETTEXT, bufferSize, title)
|
||||||
|
|
||||||
|
props['Titles'] = [title.value, ]
|
||||||
|
else:
|
||||||
|
props['Titles'] = ['', ]
|
||||||
|
|
||||||
|
|
||||||
|
props['Style'] = GetWindowLong (hwnd, GWL_STYLE)
|
||||||
|
props['ExStyle'] = GetWindowLong (hwnd, GWL_EXSTYLE)
|
||||||
|
props['ControlID'] = GetWindowLong (hwnd, GWL_ID)
|
||||||
|
props['UserData'] = GetWindowLong (hwnd, GWL_USERDATA)
|
||||||
|
props['ContextHelpID'] = GetWindowContextHelpId (hwnd)
|
||||||
|
|
||||||
|
|
||||||
|
# set the font
|
||||||
|
fontHandle = SendMessage (hwnd, WM_GETFONT, 0, 0)
|
||||||
|
|
||||||
|
# if the fondUsed is 0 then the control is using the system font
|
||||||
|
if not fontHandle:
|
||||||
|
fontHandle = GetStockObject(SYSTEM_FONT);
|
||||||
|
|
||||||
|
# Get the Logfont structure of the font of the control
|
||||||
|
font = LOGFONTW()
|
||||||
|
ret = GetObject(fontHandle, sizeof(font), byref(font))
|
||||||
|
|
||||||
|
# The function could not get the font - this is probably because
|
||||||
|
# the control does not have associated Font/Text
|
||||||
|
# So we should make sure the elements of the font are zeroed.
|
||||||
|
if not ret:
|
||||||
|
font = LOGFONTW()
|
||||||
|
|
||||||
|
props['Fonts'] = [font,]
|
||||||
|
|
||||||
|
# get the client rectangle
|
||||||
|
clientRect = RECT()
|
||||||
|
GetClientRect(hwnd, byref(clientRect))
|
||||||
|
props['ClientRect'] = clientRect
|
||||||
|
|
||||||
|
# get the full rectangle
|
||||||
|
rect = RECT()
|
||||||
|
GetWindowRect(hwnd, byref(rect))
|
||||||
|
|
||||||
|
if dialogRect:
|
||||||
|
# offset it's rect depending on it's parents
|
||||||
|
rect.left -= dialogRect.left
|
||||||
|
rect.top -= dialogRect.top
|
||||||
|
rect.right -= dialogRect.left
|
||||||
|
rect.bottom -= dialogRect.top
|
||||||
|
|
||||||
|
#props['DialogRect'] = dialogRect
|
||||||
|
|
||||||
|
props['Rectangles'] = [rect,]
|
||||||
|
|
||||||
|
props['IsVisible'] = IsWindowVisible(hwnd)
|
||||||
|
props['IsUnicode'] = IsWindowUnicode(hwnd)
|
||||||
|
|
||||||
|
return props
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def GetChildWindows(dialog, dlgRect = None):
|
||||||
|
|
||||||
|
# this will be filled in the callback function
|
||||||
|
childWindows = []
|
||||||
|
|
||||||
|
# callback function for EnumChildWindows
|
||||||
|
def enumChildProc(hWnd, LPARAM):
|
||||||
|
win = Window(hWnd, dlgRect)
|
||||||
|
|
||||||
|
# construct an instance of the appropriate type
|
||||||
|
win = WindowClassRegistry().GetClass(win.Class)(hWnd, dlgRect)
|
||||||
|
|
||||||
|
# append it to our list
|
||||||
|
childWindows.append(win)
|
||||||
|
|
||||||
|
# return true to keep going
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# define the child proc type
|
||||||
|
EnumChildProc = WINFUNCTYPE(c_int, HWND, LPARAM)
|
||||||
|
proc = EnumChildProc(enumChildProc)
|
||||||
|
|
||||||
|
# loop over all the children (callback called for each)
|
||||||
|
EnumChildWindows(dialog, proc, 0)
|
||||||
|
|
||||||
|
return childWindows
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def RemoveNonCurrentTabControls(dialog, childWindows):
|
||||||
|
|
||||||
|
# find out if there is a tab control and get it if there is one
|
||||||
|
tab = None
|
||||||
|
for child in childWindows:
|
||||||
|
if child.Class == "SysTabControl32":
|
||||||
|
tab = child
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
|
# make a copy of childWindows
|
||||||
|
firstTabChildren = list(childWindows)
|
||||||
|
if tab:
|
||||||
|
|
||||||
|
# get the parent of the tab control
|
||||||
|
tabParent = GetParent(tab.handle)
|
||||||
|
|
||||||
|
# find the control with that hwnd
|
||||||
|
tabParent = [c for c in childWindows if \
|
||||||
|
c.handle == tabParent][0]
|
||||||
|
|
||||||
|
# get the index of the parent
|
||||||
|
parentIdx = childWindows.index(tabParent) + 1
|
||||||
|
|
||||||
|
passedFirstTab = False
|
||||||
|
for child in childWindows[parentIdx:]:
|
||||||
|
|
||||||
|
# if the current control is a dialog
|
||||||
|
if child.Class == "#32770":
|
||||||
|
|
||||||
|
# if this is the first tab
|
||||||
|
if not passedFirstTab :
|
||||||
|
# then just skip it
|
||||||
|
passedFirstTab = True
|
||||||
|
else:
|
||||||
|
# Ok so this is NOT the first tab
|
||||||
|
# remove the dialog control itself
|
||||||
|
firstTabChildren.remove(child)
|
||||||
|
|
||||||
|
# then remove all the children of that dialog
|
||||||
|
for x in GetChildWindows(child.handle):
|
||||||
|
firstTabChildren.remove(x)
|
||||||
|
|
||||||
|
return firstTabChildren
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def GetClass(hwnd):
|
||||||
|
# get the className
|
||||||
|
className = (c_wchar * 257)()
|
||||||
|
GetClassName (hwnd, byref(className), 256)
|
||||||
|
return className.value
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def GetTitle(hwnd):
|
||||||
|
# get the title
|
||||||
|
bufferSize = SendMessage (hwnd, WM_GETTEXTLENGTH, 0, 0)
|
||||||
|
title = (c_wchar * bufferSize)()
|
||||||
|
|
||||||
|
if bufferSize:
|
||||||
|
bufferSize += 1
|
||||||
|
SendMessage (hwnd, WM_GETTEXT, bufferSize, title)
|
||||||
|
|
||||||
|
|
||||||
|
return title.value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class Window(object):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwndOrXML, dlgRect = None):
|
||||||
|
|
||||||
|
self.ref = None
|
||||||
|
|
||||||
|
if isinstance(hwndOrXML, (int, long)):
|
||||||
|
self.handle = hwndOrXML
|
||||||
|
self.properties = DefaultWindowHwndReader(hwndOrXML, dlgRect)
|
||||||
|
|
||||||
|
# this is only for use when we are running based on the dialog
|
||||||
|
# itself - no reason to write it to the XML file so don't
|
||||||
|
# add it to the properties
|
||||||
|
self.actualRect = RECT()
|
||||||
|
GetWindowRect(hwndOrXML, byref(self.actualRect))
|
||||||
|
|
||||||
|
self.properties["MenuItems"] = GetMenuItems(GetMenu(hwndOrXML))
|
||||||
|
#print self.properties["MenuItems"]
|
||||||
|
|
||||||
|
#self.GetContextMenu()
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.properties = {}
|
||||||
|
self.properties['ClientRect'] = XMLToRect(hwndOrXML.find("CLIENTRECT"))
|
||||||
|
self.properties['Rectangles'] = []
|
||||||
|
rectangles = hwndOrXML.find("RECTANGLES")
|
||||||
|
for rect in rectangles:
|
||||||
|
self.properties['Rectangles'].append(XMLToRect(rect))
|
||||||
|
|
||||||
|
self.properties['Fonts'] = []
|
||||||
|
fonts = hwndOrXML.find("FONTS")
|
||||||
|
for font in fonts:
|
||||||
|
self.properties['Fonts'].append(XMLToFont(font))
|
||||||
|
|
||||||
|
self.properties['Titles'] = XMLToTitles(hwndOrXML.find("TITLES"))
|
||||||
|
|
||||||
|
|
||||||
|
self.properties["MenuItems"] = XMLToMenuItems(hwndOrXML.find("MENUITEMS"))
|
||||||
|
#print self.properties["MenuItems"]
|
||||||
|
|
||||||
|
|
||||||
|
# get all the attributes
|
||||||
|
for propName in hwndOrXML.attrib:
|
||||||
|
|
||||||
|
val = hwndOrXML.attrib[propName]
|
||||||
|
|
||||||
|
if propName.endswith("_LONG"):
|
||||||
|
val = long(val)
|
||||||
|
propName = propName[:-5]
|
||||||
|
else:
|
||||||
|
val = unicode(val)
|
||||||
|
|
||||||
|
self.properties[propName] = val
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if name in self.properties:
|
||||||
|
return self.properties[name]
|
||||||
|
else:
|
||||||
|
raise AttributeError("'%s' has no attribute '%s'"% \
|
||||||
|
(self.__class__.__name__, name))
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def GetTitle(self):
|
||||||
|
return self.Titles[0]
|
||||||
|
Title = property(GetTitle)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def GetRectangle(self):
|
||||||
|
return self.Rectangles[0]
|
||||||
|
Rectangle = property(GetRectangle)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def GetFont(self):
|
||||||
|
return self.Fonts[0]
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def SetFont(self, font):
|
||||||
|
self.Fonts[0] = font
|
||||||
|
|
||||||
|
Font = property(GetFont, SetFont)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def Parent(self):
|
||||||
|
# do a preliminary construction to a Window
|
||||||
|
parent = Window(GetParent(self.handle))
|
||||||
|
|
||||||
|
# reconstruct it to the correct type
|
||||||
|
return WindowClassRegistry().GetClass(parent.Class)(parent.handle)
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def Style(self, flag = None):
|
||||||
|
style = self.properties['Style']
|
||||||
|
if flag:
|
||||||
|
return style & flag == flag
|
||||||
|
else:
|
||||||
|
return style
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def ExStyle(self, flag = None):
|
||||||
|
exstyle = self.properties['ExStyle']
|
||||||
|
if flag:
|
||||||
|
return exstyle & flag == flag
|
||||||
|
else:
|
||||||
|
return exstyle
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def GetContextMenu(self):
|
||||||
|
rect = self.Rectangle
|
||||||
|
|
||||||
|
# set the position of the context menu to be 2 pixels in from
|
||||||
|
# the control edge
|
||||||
|
pos = c_long ((rect.top+ 2 << 16) | (rect.left + 2))
|
||||||
|
|
||||||
|
# get the top window before trying to bring up a context menu
|
||||||
|
oldTopWin = FindWindow(0, 0)
|
||||||
|
|
||||||
|
# send the message but time-out after 10 mili seconds
|
||||||
|
res = DWORD()
|
||||||
|
SendMessageTimeout (
|
||||||
|
self.handle,
|
||||||
|
WM_CONTEXTMENU,
|
||||||
|
self.handle,
|
||||||
|
pos,
|
||||||
|
0,
|
||||||
|
100, # time out in miliseconds
|
||||||
|
byref(res)) # result
|
||||||
|
|
||||||
|
# get the top window
|
||||||
|
popMenuWin = FindWindow(0, 0)
|
||||||
|
|
||||||
|
# if no context menu has opened try right clicking the control
|
||||||
|
# if oldTopWin == popMenuWin:
|
||||||
|
# SendMessageTimeout (
|
||||||
|
# self.handle,
|
||||||
|
# WM_RBUTTONDOWN,
|
||||||
|
# 0,
|
||||||
|
# pos,
|
||||||
|
# 0,
|
||||||
|
# 100, # time out in miliseconds
|
||||||
|
# byref(res)) # result
|
||||||
|
#
|
||||||
|
# SendMessageTimeout (
|
||||||
|
# self.handle,
|
||||||
|
# WM_RBUTTONUP,
|
||||||
|
# 2,
|
||||||
|
# pos,
|
||||||
|
# 0,
|
||||||
|
# 100, # time out in miliseconds
|
||||||
|
# byref(res)) # result
|
||||||
|
#
|
||||||
|
# # wait another .1 of a second to allow it to display
|
||||||
|
# import time
|
||||||
|
# time.sleep(.1)
|
||||||
|
#
|
||||||
|
# # get the top window
|
||||||
|
# popMenuWin = FindWindow(0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
# if we still haven't opened a popup menu
|
||||||
|
if oldTopWin == popMenuWin:
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# get the MenuBar info from the PopupWindow which will
|
||||||
|
# give you the Menu Handle for the menu itself
|
||||||
|
mbi = MENUBARINFO()
|
||||||
|
mbi.cbSize = sizeof(MENUBARINFO)
|
||||||
|
ret = GetMenuBarInfo(popMenuWin, OBJID_CLIENT, 0, byref(mbi))
|
||||||
|
|
||||||
|
if ret:
|
||||||
|
#print ret, mbi.rcBar.left, mbi.hMenu
|
||||||
|
GetMenuItems(mbi.hMenu)
|
||||||
|
self.properties["ContextMenu"] = GetMenuItems(mbi.hMenu)
|
||||||
|
|
||||||
|
|
||||||
|
# make sure the popup goes away!
|
||||||
|
SendMessage (self.handle, WM_CANCELMODE, 0, 0)
|
||||||
|
SendMessage (popMenuWin, WM_CANCELMODE, 0, 0)
|
||||||
|
|
||||||
|
# if it's still open - then close it.
|
||||||
|
if IsWindowVisible(popMenuWin):
|
||||||
|
SendMessage (popMenuWin, WM_CLOSE, 0, 0)
|
||||||
|
#SendMessage (popMenuWin, WM_DESTROY, 0, 0)
|
||||||
|
#SendMessage (popMenuWin, WM_NCDESTROY , 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __cmp__(self, other):
|
||||||
|
return cmp(self.handle, other.handle)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.handle)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
# def __str__(self):
|
||||||
|
# return "%8d %-15s\t'%s'" % (self.handle,
|
||||||
|
# "'%s'"% self.riendlyClassName,
|
||||||
|
# self.Title)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class DialogWindow(Window):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwndOrXML):
|
||||||
|
|
||||||
|
self.children = []
|
||||||
|
|
||||||
|
if isinstance(hwndOrXML, (int, long)):
|
||||||
|
# read the properties for the dialog itself
|
||||||
|
# Get the dialog Rectangle first - to get the control offset
|
||||||
|
|
||||||
|
if not IsWindow(hwndOrXML):
|
||||||
|
raise "The window handle passed is not valid"
|
||||||
|
|
||||||
|
|
||||||
|
rect = RECT()
|
||||||
|
GetWindowRect(hwndOrXML, byref(rect))
|
||||||
|
|
||||||
|
Window.__init__(self, hwndOrXML, rect)
|
||||||
|
|
||||||
|
# Get the font of the dialog - dialog fonts are different from
|
||||||
|
# normal fonts in that the fonts "MS Shell Dlg", "MS Shell Dlg 2"
|
||||||
|
# and "System" actally map to the default system font - which is
|
||||||
|
# Windows 2000/XP is usually Tahoma
|
||||||
|
|
||||||
|
font = self.Font
|
||||||
|
if "MS Shell Dlg" in font.lfFaceName or font.lfFaceName == "System":
|
||||||
|
# these are not usually the fonts actaully used in for
|
||||||
|
# title bars so we need to get the default title bar font
|
||||||
|
|
||||||
|
# get the title font based on the system metrics rather
|
||||||
|
# than the font of the control itself
|
||||||
|
SPI_GETNONCLIENTMETRICS = 41
|
||||||
|
ncms = NONCLIENTMETRICSW()
|
||||||
|
ncms.cbSize = sizeof(ncms)
|
||||||
|
SystemParametersInfo(
|
||||||
|
SPI_GETNONCLIENTMETRICS,
|
||||||
|
sizeof(ncms),
|
||||||
|
byref(ncms),
|
||||||
|
0)
|
||||||
|
|
||||||
|
# with either of the following 2 flags set the font of the
|
||||||
|
# dialog isthe small one (but there is normally no difference!
|
||||||
|
if self.Style(WS_EX_TOOLWINDOW) or self.Style(WS_EX_PALETTEWINDOW):
|
||||||
|
self.properties['Fonts'][0] = ncms.lfSmCaptionFont
|
||||||
|
else:
|
||||||
|
self.properties['Fonts'][0] = ncms.lfCaptionFont
|
||||||
|
|
||||||
|
self.properties['FriendlyClassName'] = "Dialog"
|
||||||
|
|
||||||
|
self.children = GetChildWindows(self.handle, rect)
|
||||||
|
self.children.insert(0, self)
|
||||||
|
self.children = RemoveNonCurrentTabControls(self.handle, self.children)
|
||||||
|
|
||||||
|
else:
|
||||||
|
dialogElemReached = False
|
||||||
|
for ctrl in hwndOrXML.findall("CONTROL"):
|
||||||
|
|
||||||
|
# if this is the first time through the dialog
|
||||||
|
if not dialogElemReached:
|
||||||
|
# initialise the Dialog itself
|
||||||
|
Window.__init__(self, ctrl)
|
||||||
|
dialogElemReached = True
|
||||||
|
|
||||||
|
# otherwise contruct each control normally
|
||||||
|
else:
|
||||||
|
# get the class for the control with that window class
|
||||||
|
Klass = WindowClassRegistry().GetClass(ctrl.attrib["Class"])
|
||||||
|
|
||||||
|
# construct the object and append it
|
||||||
|
self.children.append(Klass(ctrl, self.Rectangle))
|
||||||
|
self.children.insert(0, self)
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def AllControls(self):
|
||||||
|
return self.children
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def AddReference(self, ref):
|
||||||
|
|
||||||
|
|
||||||
|
#print "x"*20, ref.AllControls()
|
||||||
|
if len(self.AllControls()) != len(ref.AllControls()):
|
||||||
|
print len(self.AllControls()), len(ref.AllControls())
|
||||||
|
raise "Numbers of controls on ref. dialog does not match Loc. dialog"
|
||||||
|
|
||||||
|
|
||||||
|
allIDsMatched = True
|
||||||
|
allClassesMatched = True
|
||||||
|
for idx, ctrl in enumerate(self.AllControls()):
|
||||||
|
refCtrl = ref.AllControls()[idx]
|
||||||
|
ctrl.ref = refCtrl
|
||||||
|
|
||||||
|
if ctrl.ControlID != refCtrl.ControlID:
|
||||||
|
allIDsMatched = False
|
||||||
|
|
||||||
|
if ctrl.Class != refCtrl.Class:
|
||||||
|
allClassesMatched = False
|
||||||
|
|
||||||
|
toRet = 1
|
||||||
|
|
||||||
|
allIDsSameFlag = 2
|
||||||
|
allClassesSameFlag = 4
|
||||||
|
|
||||||
|
if allIDsMatched:
|
||||||
|
toRet += allIDsSameFlag
|
||||||
|
|
||||||
|
if allClassesMatched:
|
||||||
|
toRet += allClassesSameFlag
|
||||||
|
|
||||||
|
return toRet
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def MenuBlocks(self):
|
||||||
|
allMenuBlocks = []
|
||||||
|
for win in self.children:
|
||||||
|
if win.MenuItems:
|
||||||
|
# we need to get all the separate menu blocks!
|
||||||
|
menuBlocks = GetMenuItemsAsCtrlBocks(win.MenuItems)
|
||||||
|
allMenuBlocks.extend(menuBlocks)
|
||||||
|
|
||||||
|
return allMenuBlocks
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DummyCtrl(dict):
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if name not in self:
|
||||||
|
if name + "s" in self:
|
||||||
|
return self[name + "s"][0]
|
||||||
|
return self[name]
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def GetMenuItemsAsCtrlBocks(menuItems):
|
||||||
|
blocks = []
|
||||||
|
|
||||||
|
curBlock = []
|
||||||
|
for item in menuItems:
|
||||||
|
# do a bit of conversion first :-)
|
||||||
|
itemAsCtrl = DummyCtrl()
|
||||||
|
|
||||||
|
itemAsCtrl["Titles"] = [item['Text'], ]
|
||||||
|
itemAsCtrl["ControlID"] = item['ID']
|
||||||
|
itemAsCtrl["Type"] = item['Type']
|
||||||
|
itemAsCtrl["State"] = item['State']
|
||||||
|
|
||||||
|
itemAsCtrl["Class"] = "MenuItem"
|
||||||
|
itemAsCtrl["FriendlyClassName"] = "MenuItem"
|
||||||
|
|
||||||
|
|
||||||
|
itemAsCtrl["Rectangles"] = [RECT(), ]
|
||||||
|
itemAsCtrl["Fonts"] = [LOGFONTW(), ]
|
||||||
|
itemAsCtrl["ClientRect"] = RECT()
|
||||||
|
itemAsCtrl["ContextHelpID"] = 0
|
||||||
|
itemAsCtrl["UserData"] = 0
|
||||||
|
itemAsCtrl["Style"] = 0
|
||||||
|
itemAsCtrl["ExStyle"] = 0
|
||||||
|
itemAsCtrl["IsVisible"] = 1
|
||||||
|
itemAsCtrl.ref = 0
|
||||||
|
|
||||||
|
curBlock.append(itemAsCtrl)
|
||||||
|
|
||||||
|
if item.has_key('MenuItems'):
|
||||||
|
blocks.extend(GetMenuItemsAsCtrlBocks(item['MenuItems']))
|
||||||
|
|
||||||
|
blocks.append(curBlock)
|
||||||
|
|
||||||
|
return blocks
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class ListBox(Window):
|
||||||
|
GetCount = LB_GETCOUNT
|
||||||
|
GetItemTextLen = LB_GETTEXTLEN
|
||||||
|
GetItemText = LB_GETTEXT
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwndOrXML, dlgRect):
|
||||||
|
Window.__init__(self, hwndOrXML, dlgRect)
|
||||||
|
|
||||||
|
if isinstance(hwndOrXML, (int, long)):
|
||||||
|
self.__GetItems()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __GetItems(self):
|
||||||
|
# find out how many text items are in the combobox
|
||||||
|
numItems = SendMessage(self.handle, self.GetCount, 0, 0)
|
||||||
|
|
||||||
|
# get the text for each item in the combobox
|
||||||
|
for i in range(0, numItems):
|
||||||
|
textLen = SendMessage (self.handle, self.GetItemTextLen, i, 0)
|
||||||
|
text = create_unicode_buffer(textLen+1)
|
||||||
|
SendMessage (self.handle, self.GetItemText, i, byref(text))
|
||||||
|
|
||||||
|
self.Titles.append(text.value)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class ComboBox(ListBox):
|
||||||
|
GetCount = CB_GETCOUNT
|
||||||
|
GetItemTextLen = CB_GETLBTEXTLEN
|
||||||
|
GetItemText = CB_GETLBTEXT
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwndOrXML, dlgRect):
|
||||||
|
ListBox.__init__(self, hwndOrXML, dlgRect)
|
||||||
|
|
||||||
|
if isinstance(hwndOrXML, (int, long)):
|
||||||
|
droppedRect = RECT()
|
||||||
|
|
||||||
|
SendMessage(
|
||||||
|
self.handle,
|
||||||
|
CB_GETDROPPEDCONTROLRECT,
|
||||||
|
0,
|
||||||
|
byref(droppedRect))
|
||||||
|
|
||||||
|
|
||||||
|
self.properties['DroppedRect'] = droppedRect
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
# get the dropped Rect form
|
||||||
|
droppedRect = XMLToRect(hwndOrXML.find("DROPPEDRECT"))
|
||||||
|
self.properties['DroppedRect'] = droppedRect
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class Button(Window):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwndOrXML, dlgRect):
|
||||||
|
Window.__init__(self, hwndOrXML, dlgRect)
|
||||||
|
|
||||||
|
if isinstance(hwndOrXML, (int, long)):
|
||||||
|
#state = SendMessage(self.handle, BM_GETSTATE, 0, 0)
|
||||||
|
|
||||||
|
style = self.Style()
|
||||||
|
# get the least significant bit
|
||||||
|
styleLSB = style & 0xF
|
||||||
|
|
||||||
|
if styleLSB == BS_3STATE or styleLSB == BS_AUTO3STATE or \
|
||||||
|
styleLSB == BS_AUTOCHECKBOX or \
|
||||||
|
styleLSB == BS_CHECKBOX:
|
||||||
|
self.properties['FriendlyClassName'] = "CheckBox"
|
||||||
|
elif styleLSB == BS_RADIOBUTTON or styleLSB == BS_AUTORADIOBUTTON:
|
||||||
|
self.properties['FriendlyClassName'] = "RadioButton"
|
||||||
|
elif styleLSB == BS_GROUPBOX:
|
||||||
|
self.properties['FriendlyClassName'] = "GroupBox"
|
||||||
|
|
||||||
|
if style & BS_PUSHLIKE:
|
||||||
|
self.properties['FriendlyClassName'] = "Button";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
# todo - replace importing controlTypes with the registeredClasses functionality
|
||||||
|
WindowClassRegistry().defaultClass = Window
|
||||||
|
#WindowClassRegistry().AddClass("#32770", DialogWindow)
|
||||||
|
|
||||||
|
WindowClassRegistry().AddClass("ComboBox", ComboBox)
|
||||||
|
WindowClassRegistry().AddClass("WindowsForms\d*\.COMBOBOX\..*", ComboBox)
|
||||||
|
|
||||||
|
WindowClassRegistry().AddClass("ListBox", ListBox)
|
||||||
|
WindowClassRegistry().AddClass("WindowsForms\d*\.LISTBOX\..*", ListBox)
|
||||||
|
|
||||||
|
WindowClassRegistry().AddClass("Button", Button)
|
||||||
|
WindowClassRegistry().AddClass("WindowsForms\d*\.BUTTON\..*", Button)
|
||||||
|
|
5
pywinauto/controls/__init__.py
Normal file
5
pywinauto/controls/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from HwndWrapper import WrapHandle, GetDialogPropsFromHandle
|
||||||
|
|
||||||
|
import win32_controls
|
||||||
|
import common_controls
|
||||||
|
|
812
pywinauto/controls/common_controls.py
Normal file
812
pywinauto/controls/common_controls.py
Normal file
@ -0,0 +1,812 @@
|
|||||||
|
from win32functions import *
|
||||||
|
from win32defines import *
|
||||||
|
from win32structures import *
|
||||||
|
|
||||||
|
import HwndWrapper
|
||||||
|
|
||||||
|
class AccessDenied(RuntimeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class RemoteMemoryBlock(object):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, handle, size = 8192):
|
||||||
|
self.memAddress = 0
|
||||||
|
self.fileMap = 0
|
||||||
|
|
||||||
|
# work with either a HwndWrapper or a real hwnd
|
||||||
|
#try:
|
||||||
|
# handle = handle.hwnd
|
||||||
|
#except:
|
||||||
|
# pass
|
||||||
|
|
||||||
|
|
||||||
|
processID = c_long()
|
||||||
|
GetWindowThreadProcessId(handle, byref(processID))
|
||||||
|
|
||||||
|
self.process = OpenProcess(
|
||||||
|
PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
|
||||||
|
0,
|
||||||
|
processID)
|
||||||
|
|
||||||
|
if not self.process:
|
||||||
|
# TODO - work in a way that cannot be denied (or fail gracefully
|
||||||
|
#raise WinError()
|
||||||
|
#raise "could not communicate with process (%d)"% (processID.value)
|
||||||
|
raise AccessDenied(str(WinError()) + "process: %d", processID.value)
|
||||||
|
|
||||||
|
if GetVersion() < 0x80000000:
|
||||||
|
self.memAddress = VirtualAllocEx(
|
||||||
|
self.process, # remote process
|
||||||
|
0, # let Valloc decide where
|
||||||
|
size, # how much to allocate
|
||||||
|
MEM_RESERVE | MEM_COMMIT, # allocation type
|
||||||
|
PAGE_READWRITE # protection
|
||||||
|
)
|
||||||
|
|
||||||
|
if not self.memAddress:
|
||||||
|
raise WinError()
|
||||||
|
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise "Win9x allocation not supported"
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __del__(self):
|
||||||
|
# Free the memory in the remote process's address space
|
||||||
|
address = ctypes.c_int(self.memAddress)
|
||||||
|
VirtualFreeEx(self.process, byref(address), 0, MEM_RELEASE)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def Address(self):
|
||||||
|
return self.memAddress
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def Write(self, data):
|
||||||
|
# write the data from this process into the memory allocated
|
||||||
|
# from the other process
|
||||||
|
ret = WriteProcessMemory(
|
||||||
|
self.process,
|
||||||
|
self.memAddress,
|
||||||
|
pointer(data),
|
||||||
|
sizeof(data),
|
||||||
|
0);
|
||||||
|
|
||||||
|
if not ret:
|
||||||
|
raise WinError()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def Read(self, data, address = None):
|
||||||
|
if not address:
|
||||||
|
address = self.memAddress
|
||||||
|
|
||||||
|
ret = ReadProcessMemory(self.process, address, byref(data), sizeof(data), 0)
|
||||||
|
|
||||||
|
# disabled as it often returns an error - but seems to work fine anyway!!
|
||||||
|
if not ret:
|
||||||
|
raise WinError()
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class ListViewWrapper(HwndWrapper.HwndWrapper):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(ListViewWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
self.FriendlyClassName = "ListView"
|
||||||
|
|
||||||
|
# set up a memory block in the remote application
|
||||||
|
self.remoteMem = RemoteMemoryBlock(self)
|
||||||
|
|
||||||
|
self._get_column_info()
|
||||||
|
|
||||||
|
self._extra_texts = self.get_extra_texts()
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_extra_texts(self):
|
||||||
|
|
||||||
|
colcount = len(self._get_column_info())
|
||||||
|
|
||||||
|
if not colcount:
|
||||||
|
colcount = 1
|
||||||
|
|
||||||
|
itemCount = self.SendMessage(LVM_GETITEMCOUNT)
|
||||||
|
|
||||||
|
texts = []
|
||||||
|
|
||||||
|
# now get the item values...
|
||||||
|
# for each of the rows
|
||||||
|
for nRowIndex in range(0, itemCount):
|
||||||
|
|
||||||
|
# and each of the columns for that row
|
||||||
|
for nColIndex in range(0, colcount):
|
||||||
|
|
||||||
|
# set up the item structure to get the text
|
||||||
|
item = LVITEMW()
|
||||||
|
item.iSubItem = nColIndex
|
||||||
|
item.pszText = self.remoteMem.Address() + sizeof(item) + 1
|
||||||
|
item.cchTextMax = 2000
|
||||||
|
item.mask = LVIF_TEXT
|
||||||
|
|
||||||
|
# Write the local LVITEM structure to the remote memory block
|
||||||
|
ret = self.remoteMem.Write(item)
|
||||||
|
|
||||||
|
# get the text for the requested item
|
||||||
|
retval = self.SendMessage(
|
||||||
|
LVM_GETITEMTEXTW,
|
||||||
|
nRowIndex,
|
||||||
|
self.remoteMem.Address())
|
||||||
|
|
||||||
|
# if it succeeded
|
||||||
|
if retval:
|
||||||
|
|
||||||
|
# Read the remote text string
|
||||||
|
charData = (c_wchar*2000)()
|
||||||
|
ret = self.remoteMem.Read(charData, item.pszText)
|
||||||
|
|
||||||
|
# and add it to the titles
|
||||||
|
texts.append(charData.value)
|
||||||
|
else:
|
||||||
|
texts.append('')
|
||||||
|
|
||||||
|
return texts
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def _get_column_info(self):
|
||||||
|
cols = []
|
||||||
|
|
||||||
|
# Get each ListView columns text data
|
||||||
|
nIndex = 0
|
||||||
|
while True:
|
||||||
|
col = LVCOLUMNW()
|
||||||
|
col.mask = LVCF_WIDTH | LVCF_FMT
|
||||||
|
|
||||||
|
# put the information in the memory that the
|
||||||
|
# other process can read/write
|
||||||
|
self.remoteMem.Write(col)
|
||||||
|
|
||||||
|
# ask the other process to update the information
|
||||||
|
retval = self.SendMessage(
|
||||||
|
LVM_GETCOLUMNW,
|
||||||
|
nIndex,
|
||||||
|
self.remoteMem.Address())
|
||||||
|
|
||||||
|
if not retval:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
col = self.remoteMem.Read(col)
|
||||||
|
|
||||||
|
cols.append(col)
|
||||||
|
|
||||||
|
nIndex += 1
|
||||||
|
|
||||||
|
|
||||||
|
if cols:
|
||||||
|
self._extra_props['ColumnWidths'] = [col.cx for col in cols]
|
||||||
|
self._extra_props['ColumnCount'] = len(cols)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self._extra_props['ColumnWidths'] = [999, ]
|
||||||
|
self._extra_props['ColumnCount'] = 1
|
||||||
|
|
||||||
|
return cols
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# commented out as we can get these strings from the header
|
||||||
|
# col = remoteMem.Read(col)
|
||||||
|
#
|
||||||
|
# charData = (c_wchar* 2000)()
|
||||||
|
#
|
||||||
|
# ret = remoteMem.Read(charData, col.pszText)
|
||||||
|
#
|
||||||
|
# self.Titles.append(charData.value)
|
||||||
|
# else:
|
||||||
|
# break
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def GetTreeViewElements(curElem, handle, remoteMem, items = None):
|
||||||
|
|
||||||
|
if items == None:
|
||||||
|
items = []
|
||||||
|
|
||||||
|
item = TVITEMW()
|
||||||
|
item.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_CHILDREN #| TVIF_STATE |
|
||||||
|
item.pszText = remoteMem.Address() + sizeof(item) + 1
|
||||||
|
item.cchTextMax = 2000
|
||||||
|
item.hItem = curElem
|
||||||
|
|
||||||
|
# Write the local LVITEM structure to the remote memory block
|
||||||
|
ret = remoteMem.Write(item)
|
||||||
|
|
||||||
|
# get this entry
|
||||||
|
retval = SendMessage(
|
||||||
|
handle,
|
||||||
|
TVM_GETITEMW,
|
||||||
|
0,
|
||||||
|
remoteMem.Address())
|
||||||
|
|
||||||
|
|
||||||
|
retval = 1
|
||||||
|
if retval:
|
||||||
|
ret = remoteMem.Read(item)
|
||||||
|
|
||||||
|
# Read the remote text string
|
||||||
|
charData = (c_wchar*2000)()
|
||||||
|
ret = remoteMem.Read(charData, item.pszText)
|
||||||
|
|
||||||
|
items.append(charData.value)
|
||||||
|
|
||||||
|
if item.cChildren not in (0,1):
|
||||||
|
print "##### not dealing with that TVN_GETDISPINFO stuff yet"
|
||||||
|
pass
|
||||||
|
|
||||||
|
if item.cChildren == 1:
|
||||||
|
# call this function again for each child handle
|
||||||
|
childElem = SendMessage(
|
||||||
|
handle,
|
||||||
|
TVM_GETNEXTITEM,
|
||||||
|
TVGN_CHILD,
|
||||||
|
curElem)
|
||||||
|
|
||||||
|
if childElem:
|
||||||
|
GetTreeViewElements(childElem, handle, remoteMem, items)
|
||||||
|
|
||||||
|
# get the next element
|
||||||
|
nextElem = SendMessage(
|
||||||
|
handle,
|
||||||
|
TVM_GETNEXTITEM,
|
||||||
|
TVGN_NEXT,
|
||||||
|
curElem)
|
||||||
|
if nextElem:
|
||||||
|
GetTreeViewElements(nextElem, handle, remoteMem, items)
|
||||||
|
|
||||||
|
return items
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class TreeViewWrapper(HwndWrapper.HwndWrapper):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(TreeViewWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
self.FriendlyClassName = "TreeView"
|
||||||
|
|
||||||
|
self._extra_text = []
|
||||||
|
|
||||||
|
remoteMem = RemoteMemoryBlock(self)
|
||||||
|
|
||||||
|
# get the root item:
|
||||||
|
rootElem = self.SendMessage(
|
||||||
|
TVM_GETNEXTITEM,
|
||||||
|
TVGN_ROOT)
|
||||||
|
|
||||||
|
self._extra_texts = GetTreeViewElements(rootElem, self, remoteMem)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class HeaderWrapper(HwndWrapper.HwndWrapper):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(HeaderWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
self.FriendlyClassName = "Header"
|
||||||
|
|
||||||
|
self._fill_header_info()
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def _fill_header_info(self):
|
||||||
|
|
||||||
|
remoteMem = RemoteMemoryBlock(self)
|
||||||
|
|
||||||
|
# get the number of items in the header...
|
||||||
|
itemCount = self.SendMessage(HDM_GETITEMCOUNT)
|
||||||
|
|
||||||
|
for nIndex in range(0, itemCount):
|
||||||
|
|
||||||
|
# get the column rect
|
||||||
|
r = RECT()
|
||||||
|
remoteMem.Write(r)
|
||||||
|
retval = self.SendMessage(
|
||||||
|
HDM_GETITEMRECT,
|
||||||
|
nIndex,
|
||||||
|
remoteMem.Address())
|
||||||
|
|
||||||
|
r = remoteMem.Read(r)
|
||||||
|
self._extra_clientrects.append(r)
|
||||||
|
|
||||||
|
|
||||||
|
item = HDITEMW()
|
||||||
|
item.mask = HDI_FORMAT | HDI_WIDTH | HDI_TEXT #| HDI_ORDER
|
||||||
|
item.cchTextMax = 2000
|
||||||
|
|
||||||
|
# set up the pointer to the text
|
||||||
|
# it should be at the
|
||||||
|
item.pszText = remoteMem.Address() + sizeof(HDITEMW) + 1
|
||||||
|
|
||||||
|
# put the information in the memory that the
|
||||||
|
# other process can read/write
|
||||||
|
remoteMem.Write(item)
|
||||||
|
|
||||||
|
# ask the other process to update the information
|
||||||
|
retval = self.SendMessage(
|
||||||
|
HDM_GETITEMW,
|
||||||
|
nIndex,
|
||||||
|
remoteMem.Address())
|
||||||
|
|
||||||
|
if retval:
|
||||||
|
item = remoteMem.Read(item)
|
||||||
|
|
||||||
|
# Read the remote text string
|
||||||
|
charData = (c_wchar*2000)()
|
||||||
|
remoteMem.Read(charData, item.pszText)
|
||||||
|
self._extra_texts.append(charData.value)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class StatusBarWrapper(HwndWrapper.HwndWrapper):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(StatusBarWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
self.FriendlyClassName = "StatusBar"
|
||||||
|
|
||||||
|
self._fill_statusbar_info()
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def _fill_statusbar_info(self):
|
||||||
|
|
||||||
|
remoteMem = RemoteMemoryBlock(self)
|
||||||
|
|
||||||
|
|
||||||
|
# get the borders for each of the areas there can be a border.
|
||||||
|
borders = (c_int*3)()
|
||||||
|
remoteMem.Write(borders)
|
||||||
|
numParts = self.SendMessage(
|
||||||
|
SB_GETBORDERS,
|
||||||
|
0,
|
||||||
|
remoteMem.Address()
|
||||||
|
)
|
||||||
|
borders = remoteMem.Read(borders)
|
||||||
|
self._extra_props['HorizBorderWidth'] = borders[0]
|
||||||
|
self._extra_props['VertBorderWidth'] = borders[1]
|
||||||
|
self._extra_props['InterBorderWidth'] = borders[2]
|
||||||
|
|
||||||
|
# get the number of parts for this status bar
|
||||||
|
parts = (c_int*99)()
|
||||||
|
remoteMem.Write(parts)
|
||||||
|
numParts = self.SendMessage(
|
||||||
|
SB_GETPARTS,
|
||||||
|
99,
|
||||||
|
remoteMem.Address()
|
||||||
|
)
|
||||||
|
|
||||||
|
for p in range(0, numParts):
|
||||||
|
|
||||||
|
# get the rectangle for this item
|
||||||
|
r = RECT()
|
||||||
|
remoteMem.Write(r)
|
||||||
|
numParts = self.SendMessage(
|
||||||
|
SB_GETRECT,
|
||||||
|
p,
|
||||||
|
remoteMem.Address()
|
||||||
|
)
|
||||||
|
|
||||||
|
r = remoteMem.Read(r)
|
||||||
|
self._extra_clientrects.append(r)
|
||||||
|
|
||||||
|
|
||||||
|
# get the text for this item
|
||||||
|
text = (c_wchar*2000)()
|
||||||
|
remoteMem.Write(text)
|
||||||
|
numParts = self.SendMessage(
|
||||||
|
SB_GETTEXTW,
|
||||||
|
p,
|
||||||
|
remoteMem.Address()
|
||||||
|
)
|
||||||
|
|
||||||
|
text = remoteMem.Read(text)
|
||||||
|
self._extra_texts.append(text.value)
|
||||||
|
|
||||||
|
# remove the first title if we got other titles and the
|
||||||
|
# 2nd is the same
|
||||||
|
#if len (self._extra_texts) >= 1 and self.Text == self._extra_texts[0]:
|
||||||
|
# props["Titles"][0] = ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class TabControlWrapper(HwndWrapper.HwndWrapper):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(TabControlWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
self.FriendlyClassName = "TabControl"
|
||||||
|
|
||||||
|
self._fill_tabcontrol_info()
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def _fill_tabcontrol_info(self):
|
||||||
|
tooltipHandle = self.SendMessage(TCM_GETTOOLTIPS)
|
||||||
|
|
||||||
|
itemCount = self.SendMessage(TCM_GETITEMCOUNT)
|
||||||
|
self._extra_props['TabCount'] = itemCount
|
||||||
|
|
||||||
|
remoteMem = RemoteMemoryBlock(self)
|
||||||
|
|
||||||
|
for i in range(0, itemCount):
|
||||||
|
|
||||||
|
rect = RECT()
|
||||||
|
remoteMem.Write(rect)
|
||||||
|
|
||||||
|
self.SendMessage(TCM_GETITEMRECT, i, remoteMem.Address())
|
||||||
|
|
||||||
|
remoteMem.Read(rect)
|
||||||
|
|
||||||
|
self._extra_clientrects.append(rect)
|
||||||
|
|
||||||
|
|
||||||
|
item = TCITEMW()
|
||||||
|
item.mask = TCIF_STATE | TCIF_TEXT
|
||||||
|
item.cchTextMax = 1999
|
||||||
|
item.pszText = remoteMem.Address() + sizeof(TCITEMW)
|
||||||
|
remoteMem.Write(item)
|
||||||
|
|
||||||
|
self.SendMessage(TCM_GETITEMW, i, remoteMem.Address())
|
||||||
|
|
||||||
|
remoteMem.Read(item)
|
||||||
|
|
||||||
|
|
||||||
|
self._extra_props.setdefault('TabState', []).append(item.dwState)
|
||||||
|
|
||||||
|
text = (c_wchar*2000)()
|
||||||
|
text = remoteMem.Read(text, remoteMem.Address() + sizeof(TCITEMW))
|
||||||
|
self._extra_texts.append(text.value)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class ToolbarWrapper(HwndWrapper.HwndWrapper):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(ToolbarWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
self.FriendlyClassName = "Toolbar"
|
||||||
|
|
||||||
|
self._fill_toolbar_info()
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def _fill_toolbar_info(self):
|
||||||
|
|
||||||
|
buttonCount = self.SendMessage(TB_BUTTONCOUNT)
|
||||||
|
self._extra_props['ButtonCount'] = buttonCount
|
||||||
|
|
||||||
|
remoteMem = RemoteMemoryBlock(self)
|
||||||
|
|
||||||
|
for i in range(0, buttonCount):
|
||||||
|
|
||||||
|
button = TBBUTTON()
|
||||||
|
|
||||||
|
remoteMem.Write(button)
|
||||||
|
|
||||||
|
self.SendMessage(TB_GETBUTTON, i, remoteMem.Address())
|
||||||
|
|
||||||
|
remoteMem.Read(button)
|
||||||
|
|
||||||
|
buttonInfo = TBBUTTONINFOW()
|
||||||
|
buttonInfo.cbSize = sizeof(TBBUTTONINFOW)
|
||||||
|
buttonInfo.dwMask = TBIF_TEXT | TBIF_COMMAND | TBIF_SIZE | TBIF_COMMAND | TBIF_STYLE | TBIF_STATE
|
||||||
|
buttonInfo.pszText = remoteMem.Address() + sizeof(TBBUTTONINFOW)
|
||||||
|
buttonInfo.cchText = 2000
|
||||||
|
|
||||||
|
remoteMem.Write(buttonInfo)
|
||||||
|
|
||||||
|
self.SendMessage(TB_GETBUTTONINFOW, button.idCommand, remoteMem.Address())
|
||||||
|
|
||||||
|
remoteMem.Read(buttonInfo)
|
||||||
|
|
||||||
|
text = (c_wchar * 1999)()
|
||||||
|
remoteMem.Read(text, remoteMem.Address() + sizeof(TBBUTTONINFOW))
|
||||||
|
|
||||||
|
# PrintCtypesStruct(buttonInfo, ('pszText', 'cchText', 'cbSize', 'dwMask', 'lParam', 'iImage'))
|
||||||
|
|
||||||
|
extendedStyle = self.SendMessage(TB_GETEXTENDEDSTYLE)
|
||||||
|
|
||||||
|
self._extra_props.setdefault('Buttons', []).append(
|
||||||
|
dict(
|
||||||
|
iBitMap = button.iBitmap,
|
||||||
|
idCommand = button.idCommand,
|
||||||
|
fsState = button.fsState,
|
||||||
|
fsStyle = button.fsStyle,
|
||||||
|
cx = buttonInfo.cx,
|
||||||
|
ExStyle = extendedStyle
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# if button.fsStyle & TBSTYLE_DROPDOWN == TBSTYLE_DROPDOWN and \
|
||||||
|
# extendedStyle & TBSTYLE_EX_DRAWDDARROWS != TBSTYLE_EX_DRAWDDARROWS:
|
||||||
|
# props['Buttons'][-1]["DROPDOWNMENU"] = 1
|
||||||
|
#
|
||||||
|
# self.SendMessage(WM_COMMAND, button.idCommand)
|
||||||
|
#
|
||||||
|
# print "Pressing", text.value
|
||||||
|
# handle.SendMessage(TB_PRESSBUTTON, button.idCommand, 1)
|
||||||
|
# handle.SendMessage(TB_PRESSBUTTON, button.idCommand, 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
self._extra_texts.append(text.value)
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class RebarWrapper(HwndWrapper.HwndWrapper):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(RebarWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
self.FriendlyClassName = "ReBar"
|
||||||
|
|
||||||
|
self._fill_rebar_info()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def _fill_rebar_info(self):
|
||||||
|
bandCount = self.SendMessage(RB_GETBANDCOUNT)
|
||||||
|
|
||||||
|
#print bandCount
|
||||||
|
self._extra_props['BandCount'] = bandCount
|
||||||
|
|
||||||
|
remoteMem = RemoteMemoryBlock(self)
|
||||||
|
|
||||||
|
for i in range(0, bandCount):
|
||||||
|
|
||||||
|
bandInfo = REBARBANDINFOW()
|
||||||
|
|
||||||
|
bandInfo.cbSize = sizeof(bandInfo)
|
||||||
|
bandInfo.fMask = RBBIM_ID | RBBIM_TEXT | RBBIM_SIZE | RBBIM_IDEALSIZE | RBBIM_CHILDSIZE | RBBIM_COLORS | RBBIM_STYLE | RBBIM_HEADERSIZE | RBBIM_CHILD
|
||||||
|
bandInfo.pszText = remoteMem.Address() + sizeof(bandInfo)
|
||||||
|
bandInfo.cchText = 2000
|
||||||
|
|
||||||
|
remoteMem.Write(bandInfo)
|
||||||
|
|
||||||
|
self.SendMessage(RB_GETBANDINFOW, i, remoteMem.Address())
|
||||||
|
|
||||||
|
remoteMem.Read(bandInfo)
|
||||||
|
|
||||||
|
text = (c_wchar * 1999)()
|
||||||
|
remoteMem.Read(text, remoteMem.Address() + sizeof(bandInfo))
|
||||||
|
|
||||||
|
self._extra_texts.append(text.value)
|
||||||
|
|
||||||
|
#child = HwndWrapper(bandInfo.hwndChild)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class ToolTipsWrapper(HwndWrapper.HwndWrapper):
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(ToolTipsWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
self.FriendlyClassName = "ToolTips"
|
||||||
|
|
||||||
|
self.PlayWithToolTipControls()
|
||||||
|
|
||||||
|
|
||||||
|
def PlayWithToolTipControls(self):
|
||||||
|
|
||||||
|
# get the number of tooltips associated with the control
|
||||||
|
count = self.SendMessage(TTM_GETTOOLCOUNT, 0, 0)
|
||||||
|
|
||||||
|
# find the owner window of the tooltip
|
||||||
|
#parent = Window(GetWindow (winToTest, GW_OWNER))
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
remoteMem = RemoteMemoryBlock(self)
|
||||||
|
except AccessDenied:
|
||||||
|
return
|
||||||
|
|
||||||
|
for i in range(0, count):
|
||||||
|
|
||||||
|
for i2 in range(0, count):
|
||||||
|
tipInfo = TOOLINFOW()
|
||||||
|
tipInfo.cbSize = sizeof(TOOLINFOW)
|
||||||
|
tipInfo.lpszText = remoteMem.Address() + sizeof(tipInfo) +1
|
||||||
|
tipInfo.uId = i2
|
||||||
|
#tipInfo.uFlags = 0xff
|
||||||
|
|
||||||
|
remoteMem.Write(tipInfo)
|
||||||
|
|
||||||
|
ret = self.SendMessage(TTM_ENUMTOOLSW, i, remoteMem.Address())
|
||||||
|
|
||||||
|
if not ret:
|
||||||
|
print WinError()
|
||||||
|
sys.exit()
|
||||||
|
else:
|
||||||
|
remoteMem.Read(tipInfo)
|
||||||
|
|
||||||
|
|
||||||
|
#print i, i2, tipInfo.lpszText
|
||||||
|
#print "-" * 10
|
||||||
|
#PrintCtypesStruct(tipInfo)
|
||||||
|
|
||||||
|
|
||||||
|
if tipInfo.lpszText in (LPSTR_TEXTCALLBACKW, 0, -1):
|
||||||
|
#print "CALLBACK CALLBACK CALLBACK"
|
||||||
|
pass
|
||||||
|
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
text = (c_wchar*2000)()
|
||||||
|
remoteMem.Read(text, tipInfo.lpszText)
|
||||||
|
|
||||||
|
#print "TTT"* 10, `text.value`, i, i2
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#SendMessage(y.hwnd, WM_NOTIFY, )
|
||||||
|
|
||||||
|
|
||||||
|
#n = (c_wchar* 2000)()
|
||||||
|
#ret = ReadProcessMemory(process, y.lpszText, byref(n), sizeof(n), 0)
|
||||||
|
|
||||||
|
#print y.uFlags, Window(y.hwnd).Class, Window(y.hwnd).Title, y.uId, y.hinst, repr(n.value)
|
||||||
|
#curTool += 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def PrintCtypesStruct(struct, exceptList = []):
|
||||||
|
for f in struct._fields_:
|
||||||
|
name = f[0]
|
||||||
|
if name in exceptList:
|
||||||
|
continue
|
||||||
|
print "%20s "% name, getattr(struct, name)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
HwndWrapper.HwndWrappers["SysListView32"] = ListViewWrapper
|
||||||
|
HwndWrapper.HwndWrappers[r"WindowsForms\d*\.SysListView32\..*"] = ListViewWrapper
|
||||||
|
|
||||||
|
HwndWrapper.HwndWrappers["SysTreeView32"] = TreeViewWrapper
|
||||||
|
HwndWrapper.HwndWrappers[r"WindowsForms\d*\.SysTreeView32\..*"] = TreeViewWrapper
|
||||||
|
|
||||||
|
HwndWrapper.HwndWrappers["SysHeader32"] = HeaderWrapper
|
||||||
|
|
||||||
|
HwndWrapper.HwndWrappers["msctls_statusbar32"] = StatusBarWrapper
|
||||||
|
HwndWrapper.HwndWrappers["HSStatusBar"] = StatusBarWrapper
|
||||||
|
HwndWrapper.HwndWrappers[r"WindowsForms\d*\.msctls_statusbar32\..*"] = StatusBarWrapper
|
||||||
|
|
||||||
|
HwndWrapper.HwndWrappers["SysTabControl32"] = TabControlWrapper
|
||||||
|
|
||||||
|
HwndWrapper.HwndWrappers["ToolbarWindow32"] = ToolbarWrapper
|
||||||
|
##HwndWrapper.HwndWrappers["Afx:00400000:8:00010011:00000010:00000000"] = ToolbarWrapper
|
||||||
|
|
||||||
|
HwndWrapper.HwndWrappers["ReBarWindow32"] = RebarWrapper
|
||||||
|
|
||||||
|
#HwndWrapper.HwndWrappers["tooltips_class32"] = ToolTipsWrapper
|
||||||
|
#
|
||||||
|
##HwndWrapper.HwndWrappers["ComboBoxEx32"] = ComboBoxEx
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##
|
||||||
|
##
|
||||||
|
###====================================================================
|
||||||
|
##class ComboBoxEx(Controls_Standard.ComboBox):
|
||||||
|
## #----------------------------------------------------------------
|
||||||
|
## def __init__(self, hwndOrXML):
|
||||||
|
# Window.__init__(self, hwndOrXML)
|
||||||
|
##
|
||||||
|
# if isinstance(hwndOrXML, (int, long)):
|
||||||
|
## comboCntrl = SendMessage(
|
||||||
|
## hwndOrXML,
|
||||||
|
## CBEM_GETCOMBOCONTROL,
|
||||||
|
## 0,
|
||||||
|
## 0)
|
||||||
|
##
|
||||||
|
## print "--"*20, comboCntrl
|
||||||
|
## Controls_Standard.ComboBox.__init__(self, comboCntrl)
|
||||||
|
## print self.DroppedRect
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## droppedRect = RECT()
|
||||||
|
##
|
||||||
|
## SendMessage(
|
||||||
|
## self,
|
||||||
|
## CB_GETDROPPEDCONTROLRECT,
|
||||||
|
## 0,
|
||||||
|
## byref(droppedRect))
|
||||||
|
##
|
||||||
|
## props['DroppedRect'] = droppedRect
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# # find out how many text items are in the combobox
|
||||||
|
# numItems = SendMessage(
|
||||||
|
# self,
|
||||||
|
# CB_GETCOUNT,
|
||||||
|
# 0,
|
||||||
|
# 0)
|
||||||
|
#
|
||||||
|
# print "*"*20, numItems
|
||||||
|
## remoteMem = RemoteMemoryBlock(self)
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## # get the text for each item in the combobox
|
||||||
|
## while True:
|
||||||
|
## item = COMBOBOXEXITEMW()
|
||||||
|
##
|
||||||
|
## item.mask = CBEIF_TEXT
|
||||||
|
## item.cchTextMax = 4000
|
||||||
|
## item.pszText = remoteMem.Address() + sizeof(item) + 1
|
||||||
|
##
|
||||||
|
## remoteMem.Write(item)
|
||||||
|
##
|
||||||
|
## retval = SendMessage (
|
||||||
|
## self,
|
||||||
|
## CBEM_GETITEMW,
|
||||||
|
## 0,
|
||||||
|
## remoteMem.Address()
|
||||||
|
## )
|
||||||
|
##
|
||||||
|
## if retval:
|
||||||
|
## item = remoteMem.Read(item)
|
||||||
|
##
|
||||||
|
## # Read the remote text string
|
||||||
|
## charData = (c_wchar*4000)()
|
||||||
|
## remoteMem.Read(charData, item.pszText)
|
||||||
|
## self.Titles.append(charData.value)
|
||||||
|
## else:
|
||||||
|
## break
|
||||||
|
##
|
||||||
|
#
|
||||||
|
# else:
|
||||||
|
#
|
||||||
|
# # get the dropped Rect form
|
||||||
|
# droppedRect = XMLToRect(hwndOrXML.find("DROPPEDRECT"))
|
||||||
|
# props['DroppedRect'] = droppedRect
|
||||||
|
|
||||||
|
|
||||||
|
|
228
pywinauto/controls/win32_controls.py
Normal file
228
pywinauto/controls/win32_controls.py
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
from HwndWrapper import HwndWrapper, HwndWrappers
|
||||||
|
|
||||||
|
from ctypes import *
|
||||||
|
|
||||||
|
from win32defines import *
|
||||||
|
from win32functions import *
|
||||||
|
from win32structures import *
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class ButtonWrapper(HwndWrapper):
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(ButtonWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
# default to Button for FriendlyClassName
|
||||||
|
self.FriendlyClassName = "Button"
|
||||||
|
self._set_FriendlyClassName()
|
||||||
|
|
||||||
|
self._set_if_needs_image()
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def _set_if_needs_image(self):
|
||||||
|
|
||||||
|
if self.IsVisible and (\
|
||||||
|
self.HasStyle(BS_BITMAP) or \
|
||||||
|
self.HasStyle(BS_ICON) or \
|
||||||
|
self.HasStyle(BS_OWNERDRAW)):
|
||||||
|
|
||||||
|
self._NeedsImageProp = True
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def _set_FriendlyClassName(self):
|
||||||
|
|
||||||
|
|
||||||
|
# get the least significant bit
|
||||||
|
StyleLSB = self.Style & 0xF
|
||||||
|
|
||||||
|
if StyleLSB == BS_3STATE or StyleLSB == BS_AUTO3STATE or \
|
||||||
|
StyleLSB == BS_AUTOCHECKBOX or \
|
||||||
|
StyleLSB == BS_CHECKBOX:
|
||||||
|
self.FriendlyClassName = "CheckBox"
|
||||||
|
|
||||||
|
elif StyleLSB == BS_RADIOBUTTON or StyleLSB == BS_AUTORADIOBUTTON:
|
||||||
|
self.FriendlyClassName = "RadioButton"
|
||||||
|
|
||||||
|
elif StyleLSB == BS_GROUPBOX:
|
||||||
|
self.FriendlyClassName = "GroupBox"
|
||||||
|
|
||||||
|
if self.Style & BS_PUSHLIKE:
|
||||||
|
self.FriendlyClassName = "Button"
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def get_multiple_text_items(wrapper, count_msg, item_len_msg, item_get_msg):
|
||||||
|
texts = []
|
||||||
|
|
||||||
|
# find out how many text items are in the combobox
|
||||||
|
numItems = wrapper.SendMessage(count_msg)
|
||||||
|
|
||||||
|
# get the text for each item in the combobox
|
||||||
|
for i in range(0, numItems):
|
||||||
|
textLen = wrapper.SendMessage (item_len_msg, i, 0)
|
||||||
|
|
||||||
|
text = create_unicode_buffer(textLen+1)
|
||||||
|
|
||||||
|
wrapper.SendMessage (item_get_msg, i, byref(text))
|
||||||
|
|
||||||
|
texts.append(text.value)
|
||||||
|
|
||||||
|
return texts
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class ComboBoxWrapper(HwndWrapper):
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(ComboBoxWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
# default to ComboBox for FriendlyClassName
|
||||||
|
self.FriendlyClassName = "ComboBox"
|
||||||
|
|
||||||
|
self._extra_texts = get_multiple_text_items(self, CB_GETCOUNT, CB_GETLBTEXTLEN, CB_GETLBTEXT)
|
||||||
|
self._extra_props['DroppedRect'] = self.get_droppedrect()
|
||||||
|
|
||||||
|
# get selected item
|
||||||
|
self._extra_props['SelectedItem'] = self.get_selected_index()
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_selected_index(self):
|
||||||
|
return self.SendMessage(CB_GETCURSEL)
|
||||||
|
CurrentlySelected = property(get_selected_index, doc = "The currently selected index of the combobox")
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_droppedrect(self):
|
||||||
|
|
||||||
|
droppedRect = RECT()
|
||||||
|
|
||||||
|
self.SendMessage(
|
||||||
|
CB_GETDROPPEDCONTROLRECT,
|
||||||
|
0,
|
||||||
|
byref(droppedRect))
|
||||||
|
|
||||||
|
# we need to offset the dropped rect from the control
|
||||||
|
droppedRect -= self.Rectangle
|
||||||
|
|
||||||
|
return droppedRect
|
||||||
|
DroppedRect = property(get_droppedrect, doc = "The dropped rectangle of the combobox")
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class ListBoxWrapper(HwndWrapper):
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(ListBoxWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
# default to LisBox for FriendlyClassName
|
||||||
|
self.FriendlyClassName = "ListBox"
|
||||||
|
|
||||||
|
self._extra_texts = get_multiple_text_items(self, LB_GETCOUNT, LB_GETTEXTLEN, LB_GETTEXT)
|
||||||
|
|
||||||
|
# get selected item
|
||||||
|
self._extra_props['SelectedItem'] = self.get_selected_index()
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_selected_index(self):
|
||||||
|
return self.SendMessage(LB_GETCURSEL)
|
||||||
|
CurrentlySelected = property(get_selected_index, doc = "The currently selected index of the listbox")
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class EditWrapper(HwndWrapper):
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(EditWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
# default to Edit for FriendlyClassName
|
||||||
|
self.FriendlyClassName = "Edit"
|
||||||
|
|
||||||
|
# find out how many text items are in the combobox
|
||||||
|
numItems = self.SendMessage(EM_GETLINECOUNT)
|
||||||
|
|
||||||
|
# get the text for each item in the combobox
|
||||||
|
for i in range(0, numItems):
|
||||||
|
textLen = self.SendMessage (EM_LINELENGTH, i, 0)
|
||||||
|
|
||||||
|
text = create_unicode_buffer(textLen+1)
|
||||||
|
|
||||||
|
# set the length - which is required
|
||||||
|
text[0] = unichr(textLen)
|
||||||
|
|
||||||
|
self.SendMessage (EM_GETLINE, i, byref(text))
|
||||||
|
|
||||||
|
self._extra_texts.append(text.value)
|
||||||
|
|
||||||
|
self._extra_texts = ["\n".join(self._extra_texts), ]
|
||||||
|
|
||||||
|
# get selected item
|
||||||
|
self._extra_props['SelectionIndices'] = self.get_selectionindices()
|
||||||
|
|
||||||
|
#-----------------------------------------------------------
|
||||||
|
def get_selectionindices(self):
|
||||||
|
start = c_int()
|
||||||
|
end = c_int()
|
||||||
|
self.SendMessage(EM_GETSEL, byref(start), byref(end))
|
||||||
|
|
||||||
|
return (start.value, end.value)
|
||||||
|
SelectionIndices = property(get_selectionindices, doc = "The start and end indices of the current selection")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class StaticWrapper(HwndWrapper):
|
||||||
|
def __init__(self, hwnd):
|
||||||
|
super(StaticWrapper, self).__init__(hwnd)
|
||||||
|
|
||||||
|
# default to Edit for FriendlyClassName
|
||||||
|
self.FriendlyClassName = "Static"
|
||||||
|
|
||||||
|
# if the control is visible - and it shows an image
|
||||||
|
if self.IsVisible and (
|
||||||
|
self.HasStyle(SS_ICON) or \
|
||||||
|
self.HasStyle(SS_BITMAP) or \
|
||||||
|
self.HasStyle(SS_CENTERIMAGE) or \
|
||||||
|
self.HasStyle(SS_OWNERDRAW)):
|
||||||
|
|
||||||
|
self._NeedsImageProp = True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# the main reason for this is just to make sure that
|
||||||
|
# a Dialog is a known class - and we don't need to take
|
||||||
|
# an image of it (as an unknown control class)
|
||||||
|
class DialogWrapper(HwndWrapper):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
HwndWrappers["ComboBox"] = ComboBoxWrapper
|
||||||
|
HwndWrappers[r"WindowsForms\d*\.COMBOBOX\..*"] = ComboBoxWrapper
|
||||||
|
HwndWrappers["TComboBox"] = ComboBoxWrapper
|
||||||
|
|
||||||
|
HwndWrappers["ListBox"] = ListBoxWrapper
|
||||||
|
HwndWrappers[r"WindowsForms\d*\.LISTBOX\..*"] = ListBoxWrapper
|
||||||
|
HwndWrappers["TListBox"] = ListBoxWrapper
|
||||||
|
|
||||||
|
HwndWrappers["Button"] = ButtonWrapper
|
||||||
|
HwndWrappers[r"WindowsForms\d*\.BUTTON\..*"] = ButtonWrapper
|
||||||
|
HwndWrappers["TButton"] = ButtonWrapper
|
||||||
|
#HwndWrappers["TCheckBox"] = ButtonWrapper
|
||||||
|
#HwndWrappers["TRadioButton"] = ButtonWrapper
|
||||||
|
|
||||||
|
HwndWrappers["Static"] = StaticWrapper
|
||||||
|
HwndWrappers["TPanel"] = StaticWrapper
|
||||||
|
|
||||||
|
HwndWrappers["Edit"] = EditWrapper
|
||||||
|
HwndWrappers["TEdit"] = EditWrapper
|
||||||
|
HwndWrappers["TMemo"] = EditWrapper
|
||||||
|
|
||||||
|
|
||||||
|
HwndWrappers["#32770"] = DialogWrapper
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# 'CheckBox': PCheckBox,
|
||||||
|
# 'TCheckBox': PCheckBox,
|
||||||
|
#
|
84
pywinauto/examples/watsup_ex1.py
Normal file
84
pywinauto/examples/watsup_ex1.py
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
#encoding=latin1
|
||||||
|
import sys
|
||||||
|
from time import sleep
|
||||||
|
import os
|
||||||
|
import os.path
|
||||||
|
|
||||||
|
from application import getsinglewindow, Application, WindowNotFoundError
|
||||||
|
|
||||||
|
|
||||||
|
def do_test_1():
|
||||||
|
app = Application()._start(r"c:\windows\Notepad")
|
||||||
|
notepadWindow = app.Notepad
|
||||||
|
|
||||||
|
notepadWindow.Edit1.SetText(u"Hello, ägain!", 0, -1)
|
||||||
|
sleep(0.8)
|
||||||
|
notepadWindow.Edit.SetText("\r\nYou still there?")
|
||||||
|
|
||||||
|
sleep(0.2)
|
||||||
|
notepadWindow.Edit.SetText("\r\nGoing Bye Bye now!!")
|
||||||
|
|
||||||
|
sleep(1)
|
||||||
|
notepadWindow.MenuSelect("File->Exit")
|
||||||
|
|
||||||
|
app.Notepad.No.Click()
|
||||||
|
|
||||||
|
|
||||||
|
def do_test_2():
|
||||||
|
|
||||||
|
FILENAME='atestfile.txt'
|
||||||
|
|
||||||
|
if os.path.exists(FILENAME):
|
||||||
|
os.remove(FILENAME)
|
||||||
|
|
||||||
|
app = Application()
|
||||||
|
try:
|
||||||
|
app._connect(title ='Simple Form')
|
||||||
|
except WindowNotFoundError:
|
||||||
|
app._start(r'examples\simple.exe')
|
||||||
|
|
||||||
|
form = app.SimpleForm
|
||||||
|
|
||||||
|
form.Edit.SetText(FILENAME)
|
||||||
|
sleep(.6)
|
||||||
|
|
||||||
|
print 'clicking button to create file'
|
||||||
|
form.CreateFile.Click()
|
||||||
|
|
||||||
|
# now check that the file is there
|
||||||
|
if os.path.exists(FILENAME):
|
||||||
|
print 'file %s is present' %FILENAME
|
||||||
|
else:
|
||||||
|
print "file %s isn't there" % FILENAME
|
||||||
|
|
||||||
|
form.MenuSelect("File->Exit")
|
||||||
|
|
||||||
|
|
||||||
|
def do_test_3():
|
||||||
|
app = Application()
|
||||||
|
try:
|
||||||
|
app._connect(title ='Performance Form 2')
|
||||||
|
except WindowNotFoundError:
|
||||||
|
app._start(r'examples\perform2.exe')
|
||||||
|
|
||||||
|
app.PerformanceForm1.Clickme.Click()
|
||||||
|
waited = 0
|
||||||
|
while not app.PerformacneForm1.Edit1._.Texts and not waited >= 1:
|
||||||
|
print 'waiting'
|
||||||
|
sleep(.1)
|
||||||
|
waited += .1
|
||||||
|
|
||||||
|
print `app.PerformacneForm1.Edit1._.Texts`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def Main():
|
||||||
|
for test_num in sys.argv[1:]:
|
||||||
|
globals()['do_test_%s'%test_num] ()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
Main()
|
184
pywinauto/findbestmatch.py
Normal file
184
pywinauto/findbestmatch.py
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
import re
|
||||||
|
import difflib
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class MatchError(IndexError):
|
||||||
|
def __init__(self, msg = '', items = [], tofind = ''):
|
||||||
|
Exception.__init__(self, msg)
|
||||||
|
|
||||||
|
self.items = items
|
||||||
|
self.tofind = tofind
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Could not find '%s' in '%s'"% (self.tofind, self.items)
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def clean_text(text):
|
||||||
|
|
||||||
|
# remove anything after the first tab
|
||||||
|
text_before_tab = re.sub(r"\t.*", "", text)
|
||||||
|
|
||||||
|
# remove any whitespace or non alphanumeric characters
|
||||||
|
return re.sub(r"[^\w ]|\s+", "", text_before_tab).lower()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def build_unique_index_map(items):
|
||||||
|
mapped_items = {}
|
||||||
|
|
||||||
|
#counters = {}
|
||||||
|
for i, text in enumerate(items):
|
||||||
|
text = clean_text(text)
|
||||||
|
|
||||||
|
# no duplicates so just store it without modification
|
||||||
|
if text not in mapped_items:
|
||||||
|
mapped_items[text] = i
|
||||||
|
|
||||||
|
# else this item appears multiple times
|
||||||
|
else:
|
||||||
|
# find unique text
|
||||||
|
unique_text = text
|
||||||
|
counter = 2
|
||||||
|
while unique_text in mapped_items:
|
||||||
|
unique_text = text + str(counter)
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
mapped_items[unique_text] = i
|
||||||
|
|
||||||
|
if not mapped_items.has_key(text + "0"):
|
||||||
|
mapped_items[text + "0"] = mapped_items[text]
|
||||||
|
mapped_items[text + "1"] = mapped_items[text]
|
||||||
|
|
||||||
|
return mapped_items
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def find_best_match(search_text, item_texts, items):
|
||||||
|
search_text = clean_text(search_text)
|
||||||
|
|
||||||
|
# Clean each item, make it unique and map to
|
||||||
|
# to the item index
|
||||||
|
item_index_map = build_unique_index_map(item_texts)
|
||||||
|
|
||||||
|
# find the list of best matches
|
||||||
|
matches = difflib.get_close_matches (search_text, item_index_map.keys())
|
||||||
|
|
||||||
|
# best match is the first one - so get the index stored
|
||||||
|
# for that match text
|
||||||
|
try:
|
||||||
|
best_index = item_index_map[matches[0]]
|
||||||
|
except IndexError:
|
||||||
|
raise MatchError(items = item_texts, tofind = search_text)
|
||||||
|
|
||||||
|
|
||||||
|
return items[best_index]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def get_control_names(control):
|
||||||
|
names = []
|
||||||
|
|
||||||
|
# if it has a reference control - then use that
|
||||||
|
if hasattr(control, 'ref') and control.ref:
|
||||||
|
control = control.ref
|
||||||
|
|
||||||
|
# Add the control based on it's friendly class name
|
||||||
|
names.append(control.FriendlyClassName)
|
||||||
|
|
||||||
|
# if it has some character text then add it base on that
|
||||||
|
# and based on that with friendly class name appended
|
||||||
|
if clean_text(control.Text):
|
||||||
|
names.append(control.Text)
|
||||||
|
names.append(control.Text + control.FriendlyClassName)
|
||||||
|
|
||||||
|
# return the names (either 1 or 3 strings)
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def junk_func(char):
|
||||||
|
if char in ':"/ \t\n\r][{}=-\\|!@#$%^&*,.<>?/()':
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def clean_text2(text): # doesn't change text to lowercase
|
||||||
|
|
||||||
|
# remove anything after the first tab
|
||||||
|
text_before_tab = re.sub(r"\t.*", "", text)
|
||||||
|
|
||||||
|
# remove any whitespace or non alphanumeric characters
|
||||||
|
return re.sub(r"\W", "", text_before_tab)
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def find_best_control_match(search_text, controls):
|
||||||
|
|
||||||
|
|
||||||
|
name_control_map = {}
|
||||||
|
|
||||||
|
# collect all the possible names for all controls
|
||||||
|
# and build a list of them
|
||||||
|
for c in controls:
|
||||||
|
ctrl_names = get_control_names(c)
|
||||||
|
ctrl_names = [clean_text2(n) for n in ctrl_names]
|
||||||
|
|
||||||
|
# remove duplicates
|
||||||
|
ctrl_names = list(set(ctrl_names))
|
||||||
|
|
||||||
|
# for each of the names
|
||||||
|
for n in ctrl_names:
|
||||||
|
|
||||||
|
# if its not there already then just add it
|
||||||
|
if not name_control_map.has_key(n):
|
||||||
|
|
||||||
|
name_control_map[n] = c
|
||||||
|
|
||||||
|
# else this item appears multiple times
|
||||||
|
else:
|
||||||
|
# find unique name
|
||||||
|
unique_text = n
|
||||||
|
counter = 2
|
||||||
|
while unique_text in name_control_map:
|
||||||
|
unique_text = n + str(counter)
|
||||||
|
counter += 1
|
||||||
|
|
||||||
|
# add it with that unique text
|
||||||
|
name_control_map[unique_text] = c
|
||||||
|
|
||||||
|
# and if this was the first time that we noticied that
|
||||||
|
# it was a duplicated name then add new items based on the
|
||||||
|
# duplicated name but add '0' and '1'
|
||||||
|
if not name_control_map.has_key(n + "0"):
|
||||||
|
name_control_map[n + "0"] = name_control_map[n]
|
||||||
|
name_control_map[n + "1"] = name_control_map[n]
|
||||||
|
|
||||||
|
|
||||||
|
# now time to figre out the matching
|
||||||
|
ratio_calc = difflib.SequenceMatcher()
|
||||||
|
ratio_calc.set_seq1(clean_text2(search_text))
|
||||||
|
|
||||||
|
best_ratio = 0
|
||||||
|
best_control = None
|
||||||
|
|
||||||
|
for name, control in name_control_map.items():
|
||||||
|
ratio_calc.set_seq2(name)
|
||||||
|
|
||||||
|
if ratio_calc.ratio() > best_ratio:
|
||||||
|
best_ratio = ratio_calc.quick_ratio()
|
||||||
|
best_control = control
|
||||||
|
|
||||||
|
if best_ratio < .5:
|
||||||
|
raise MatchError(items = name_control_map.keys(), tofind = search_text)
|
||||||
|
|
||||||
|
return best_control
|
||||||
|
|
||||||
|
|
39
pywinauto/tests/__init__.py
Normal file
39
pywinauto/tests/__init__.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
__all__ = ['registered_tests']
|
||||||
|
|
||||||
|
|
||||||
|
test_names = (
|
||||||
|
"AllControls",
|
||||||
|
"AsianHotkey",
|
||||||
|
"ComboBoxDroppedHeight",
|
||||||
|
"CompareToRefFont",
|
||||||
|
"LeadTrailSpaces",
|
||||||
|
"MiscValues",
|
||||||
|
"Missalignment",
|
||||||
|
"MissingExtraString",
|
||||||
|
"Overlapping",
|
||||||
|
"RepeatedHotkey",
|
||||||
|
"Translation",
|
||||||
|
"Truncation",
|
||||||
|
# "menux",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def __init_tests():
|
||||||
|
"Initialize each test by loading it and then register it"
|
||||||
|
initialized = {}
|
||||||
|
|
||||||
|
for test_name in test_names:
|
||||||
|
# get a reference to the package
|
||||||
|
package = __import__("tests."+ test_name.lower()) #
|
||||||
|
|
||||||
|
# get the reference to the module
|
||||||
|
test_module = getattr(package, test_name.lower())
|
||||||
|
|
||||||
|
# class name is the test name + "Test"
|
||||||
|
test_class = getattr(test_module, test_name + "Test")
|
||||||
|
|
||||||
|
initialized[test_name] = test_class
|
||||||
|
|
||||||
|
return initialized
|
||||||
|
|
||||||
|
registered = __init_tests()
|
79
pywinauto/tests/_menux.py
Normal file
79
pywinauto/tests/_menux.py
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
"tests a set of controls for repeated hotkey errors"
|
||||||
|
|
||||||
|
__revision__ = "0.0.1"
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#from Test_AsianHotkey import AsianHotkeyFormatIncorrect
|
||||||
|
#from win32structures import RECT, LOGFONTW
|
||||||
|
#
|
||||||
|
#class DummyCtrl(dict):
|
||||||
|
# def __getattr__(self, name):
|
||||||
|
# if name not in self:
|
||||||
|
# if name + "s" in self:
|
||||||
|
# return self[name + "s"][0]
|
||||||
|
# return self[name]
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
##-----------------------------------------------------------------------------
|
||||||
|
#def MenuRepeatedHotkeyTest(windows):
|
||||||
|
# "Return the repeated hotkey errors"
|
||||||
|
# bugs = []
|
||||||
|
#
|
||||||
|
# from InfoManager import TestFuncRegistry
|
||||||
|
#
|
||||||
|
# for win in windows:
|
||||||
|
# if win.MenuItems:
|
||||||
|
# # we need to get all the separate menu blocks!
|
||||||
|
# menuBlocks = GetMenuItemsAsCtrlBocks(win.MenuItems)
|
||||||
|
#
|
||||||
|
# for menuBlock in menuBlocks:
|
||||||
|
#
|
||||||
|
# for test in TestFuncRegistry().RegisteredClasses():
|
||||||
|
#
|
||||||
|
# TestFunc = TestFuncRegistry().GetClass(test)
|
||||||
|
#
|
||||||
|
# if hasattr(TestFunc, "TestsMenus") and TestFunc.TestsMenus:
|
||||||
|
#
|
||||||
|
# testBugs = TestFunc(menuBlock)
|
||||||
|
# bugs.extend(testBugs)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
##
|
||||||
|
## if AsianHotkeyFormatIncorrect(item['Text']):
|
||||||
|
## bugs.append(
|
||||||
|
## (
|
||||||
|
## [win,],
|
||||||
|
## {
|
||||||
|
## "MenuItem": item['Text'],
|
||||||
|
## },
|
||||||
|
## "MenuAsianHotkeyFormat",
|
||||||
|
## 0)
|
||||||
|
## )
|
||||||
|
##
|
||||||
|
##
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
## bugs.append((
|
||||||
|
## controls,
|
||||||
|
## {
|
||||||
|
## "RepeatedHotkey" : char,
|
||||||
|
## "CharsUsedInDialog" : SetAsString(hotkeys),
|
||||||
|
## "AllCharsInDialog" : SetAsString(allChars),
|
||||||
|
## "AvailableInControls" : ctrlsAvailableChars,
|
||||||
|
## },
|
||||||
|
## "RepeatedHotkey",
|
||||||
|
## isInRef)
|
||||||
|
#
|
||||||
|
# return bugs
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#import tests
|
||||||
|
#tests.register("MenuRepeatedHotkey", AsiMenuRepeatedHotkeyTest)
|
18
pywinauto/tests/allcontrols.py
Normal file
18
pywinauto/tests/allcontrols.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def AllControlsTest(windows):
|
||||||
|
"Returns just one bug for each control"
|
||||||
|
|
||||||
|
bugs = []
|
||||||
|
for win in windows:
|
||||||
|
bugs.append((
|
||||||
|
[win,],
|
||||||
|
{},
|
||||||
|
"AllControls",
|
||||||
|
0
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
return bugs
|
||||||
|
|
125
pywinauto/tests/asianhotkey.py
Normal file
125
pywinauto/tests/asianhotkey.py
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
from repeatedhotkey import ImplementsHotkey, GetHotkey
|
||||||
|
|
||||||
|
#asianHotkeyRE = re.compile ("\(&.\)((\t.*)|(\\t.*)|(\(.*\))|:|(\.\.\.)|>|<|(\n.*)\s)*$")
|
||||||
|
|
||||||
|
asianHotkeyRE = re.compile (r"""
|
||||||
|
\(&.\) # the hotkey
|
||||||
|
(
|
||||||
|
(\t.*)| # tab, and then anything
|
||||||
|
#(\\t.*)| # escaped tab, and then anything
|
||||||
|
(\(.*\) # anything in brackets
|
||||||
|
)|
|
||||||
|
\s*| # any whitespace
|
||||||
|
:| # colon
|
||||||
|
(\.\.\.)| # elipsis
|
||||||
|
>| # greater than sign
|
||||||
|
<| # less than sign
|
||||||
|
(\n.*) # newline, and then anything
|
||||||
|
\s)*$""", re.VERBOSE)
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def AsianHotkeyTest(windows):
|
||||||
|
"Return the repeated hotkey errors"
|
||||||
|
|
||||||
|
bugs = []
|
||||||
|
|
||||||
|
for win in windows:
|
||||||
|
# skip it if it doesn't implement hotkey functionality
|
||||||
|
if not ImplementsHotkey(win):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if AsianHotkeyFormatIncorrect(win.Text):
|
||||||
|
|
||||||
|
bugs.append((
|
||||||
|
[win,],
|
||||||
|
{},
|
||||||
|
"AsianHotkeyFormat",
|
||||||
|
0)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
return bugs
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def AsianHotkeyFormatIncorrect(text):
|
||||||
|
# get the hotkey
|
||||||
|
pos, char = GetHotkey(text)
|
||||||
|
|
||||||
|
# if it has a hotkey then check that it is correct Asian format
|
||||||
|
if char:
|
||||||
|
#print text, char
|
||||||
|
found = asianHotkeyRE.search(text)
|
||||||
|
if not found:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#void CAsianHotkeyFormatTest::operator()(ControlPair control)
|
||||||
|
#{
|
||||||
|
#
|
||||||
|
# // get the 1st title
|
||||||
|
# std::string title = control.loc->Titles()[0];
|
||||||
|
#
|
||||||
|
# // get the hotkey position
|
||||||
|
# size_t hotkeyPos = GetHotkeyPos(title);
|
||||||
|
# if (hotkeyPos == -1)
|
||||||
|
# return;
|
||||||
|
#
|
||||||
|
# // if it uses this '&' as a hoktey
|
||||||
|
# if (!GetHotkeyCtrlData(control.loc))
|
||||||
|
# return;
|
||||||
|
#
|
||||||
|
# // if we have the reference check to make sure that the SAME
|
||||||
|
# // hotkey character is used!
|
||||||
|
# if (control.ref) {
|
||||||
|
# char refHotkey = Hotkey(control.ref->Titles()[0]);
|
||||||
|
# char locHotkey = Hotkey(control.loc->Titles()[0]);
|
||||||
|
#
|
||||||
|
# if (refHotkey != locHotkey)
|
||||||
|
# m_Bugs.push_back(new CAsianHotkeyFormatBug(control, "AsianHotkeyDiffRef"));
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# // ignore strings like "&X:"
|
||||||
|
# if (title.length() <= 3)
|
||||||
|
# return;
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# // so if we have reached here:
|
||||||
|
# // the control REALLY has a hotkey
|
||||||
|
# // and the lenght of the control is greater than 3
|
||||||
|
#
|
||||||
|
# if (hotkeyPos - 2 >= 0 && // at least 4th character ".(&..."
|
||||||
|
# hotkeyPos + 1 <= title.length()-1 && // at most 2nd last character "...(&H)"
|
||||||
|
# title[hotkeyPos-2] == '(' &&
|
||||||
|
# title[hotkeyPos+1] == ')' &&
|
||||||
|
# hotkeyPos +1 == title.find_last_not_of("\n\t :")
|
||||||
|
# )
|
||||||
|
# {
|
||||||
|
# // OK So we know now that the format "..(&X).." is correct and that it is the
|
||||||
|
# // last non space character in the title
|
||||||
|
# ; // SO NO BUG!
|
||||||
|
# }
|
||||||
|
# else /// HEY - a bug!
|
||||||
|
# {
|
||||||
|
# m_Bugs.push_back(new CAsianHotkeyFormatBug(control, "AsianHotkeyFormat"));
|
||||||
|
#
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#}
|
||||||
|
#
|
||||||
|
|
||||||
|
AsianHotkeyTest.TestsMenus = True
|
22
pywinauto/tests/comboboxdroppedheight.py
Normal file
22
pywinauto/tests/comboboxdroppedheight.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
def ComboBoxDroppedHeightTest(windows):
|
||||||
|
bugs = []
|
||||||
|
for win in windows:
|
||||||
|
if not win.ref:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if win.Class != "ComboBox" or win.ref.Class != "ComboBox":
|
||||||
|
continue
|
||||||
|
|
||||||
|
if win.DroppedRect.height() != win.ref.DroppedRect.height():
|
||||||
|
#print win.DroppedRect.height(), win.DroppedRect.height()
|
||||||
|
|
||||||
|
bugs.append((
|
||||||
|
[win, ],
|
||||||
|
{},
|
||||||
|
"ComboBoxDroppedHeight",
|
||||||
|
0,)
|
||||||
|
)
|
||||||
|
|
||||||
|
return bugs
|
||||||
|
|
30
pywinauto/tests/comparetoreffont.py
Normal file
30
pywinauto/tests/comparetoreffont.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import win32structures
|
||||||
|
|
||||||
|
def CompareToRefFontTest(windows):
|
||||||
|
bugs = []
|
||||||
|
for win in windows:
|
||||||
|
if not win.ref:
|
||||||
|
continue
|
||||||
|
|
||||||
|
diffs = {}
|
||||||
|
|
||||||
|
for f in win32structures.LOGFONTW._fields_:
|
||||||
|
|
||||||
|
loc = getattr(win.Font, f[0])
|
||||||
|
ref = getattr(win.ref.Font, f[0])
|
||||||
|
if loc != ref:
|
||||||
|
diffs.append(f[0], loc, ref)
|
||||||
|
|
||||||
|
for diff, locVal, refVal in diffs.items():
|
||||||
|
bugs.append((
|
||||||
|
[win, ],
|
||||||
|
{
|
||||||
|
"ValueType": diff,
|
||||||
|
"Ref": unicode(refVal),
|
||||||
|
"Loc": unicode(locVal),
|
||||||
|
},
|
||||||
|
"CompareToRefFont",
|
||||||
|
0,)
|
||||||
|
)
|
||||||
|
return bugs
|
||||||
|
|
54
pywinauto/tests/leadtrailspaces.py
Normal file
54
pywinauto/tests/leadtrailspaces.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import win32structures
|
||||||
|
|
||||||
|
def LeadTrailSpacesTest(windows):
|
||||||
|
bugs = []
|
||||||
|
for win in windows:
|
||||||
|
if not win.ref:
|
||||||
|
continue
|
||||||
|
|
||||||
|
locLeadSpaces = GetLeadSpaces(win.Text)
|
||||||
|
locTrailSpaces = GetTrailSpaces(win.Text)
|
||||||
|
|
||||||
|
refLeadSpaces = GetLeadSpaces(win.ref.Text)
|
||||||
|
refTrailSpaces = GetTrailSpaces(win.ref.Text)
|
||||||
|
|
||||||
|
diffs = []
|
||||||
|
if locLeadSpaces != refLeadSpaces:
|
||||||
|
diffs.append("Leading", locLeadSpaces, locTrailSpaces)
|
||||||
|
|
||||||
|
if locTrailSpaces != refTrailSpaces:
|
||||||
|
diffs.append("Trailing", locTrailSpaces, refTrailSpaces)
|
||||||
|
|
||||||
|
for diff, loc, ref in diffs:
|
||||||
|
bugs.append((
|
||||||
|
[win, ],
|
||||||
|
{
|
||||||
|
"Lead-Trail": diff,
|
||||||
|
"Ref": ref,
|
||||||
|
"Loc": loc,
|
||||||
|
},
|
||||||
|
"LeadTrailSpaces",
|
||||||
|
0,)
|
||||||
|
)
|
||||||
|
return bugs
|
||||||
|
|
||||||
|
|
||||||
|
def GetLeadSpaces(title):
|
||||||
|
spaces = ''
|
||||||
|
|
||||||
|
for i in range(0, len(title)):
|
||||||
|
if not title[i].isspace():
|
||||||
|
break
|
||||||
|
|
||||||
|
spaces += title[i]
|
||||||
|
|
||||||
|
return spaces
|
||||||
|
|
||||||
|
|
||||||
|
def GetTrailSpaces(title):
|
||||||
|
rev = "".join(reversed(title))
|
||||||
|
spaces = GetLeadSpaces(rev)
|
||||||
|
return "".join(reversed(spaces))
|
||||||
|
|
||||||
|
|
||||||
|
LeadTrailSpacesTest.TestsMenus = True
|
45
pywinauto/tests/miscvalues.py
Normal file
45
pywinauto/tests/miscvalues.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
def MiscValuesTest(windows):
|
||||||
|
bugs = []
|
||||||
|
for win in windows:
|
||||||
|
if not win.ref:
|
||||||
|
continue
|
||||||
|
|
||||||
|
diffs = {}
|
||||||
|
|
||||||
|
if win.Class != win.ref.Class:
|
||||||
|
diffs["Class"] = (win.Class, win.ref.Class)
|
||||||
|
|
||||||
|
|
||||||
|
if win.Style != win.ref.Style:
|
||||||
|
diffs["Style"] = (win.Style, win.ref.Style())
|
||||||
|
|
||||||
|
if win.ExStyle != win.ref.ExStyle:
|
||||||
|
diffs["ExStyle"] = (win.ExStyle, win.ref.ExStyle)
|
||||||
|
|
||||||
|
if win.ContextHelpID != win.ref.ContextHelpID:
|
||||||
|
diffs["HelpID"] = (win.ContextHelpID, win.ref.ContextHelpID)
|
||||||
|
|
||||||
|
if win.ControlID != win.ref.ControlID:
|
||||||
|
diffs["ControlID"] = (win.ControlID, win.ref.ControlID)
|
||||||
|
|
||||||
|
if win.IsVisible != win.ref.IsVisible:
|
||||||
|
diffs["Visibility"] = (win.IsVisible, win.ref.IsVisible)
|
||||||
|
|
||||||
|
if win.UserData != win.ref.UserData:
|
||||||
|
diffs["UserData"] = (win.UserData, win.ref.UserData)
|
||||||
|
|
||||||
|
|
||||||
|
for diff, vals in diffs.items():
|
||||||
|
bugs.append((
|
||||||
|
[win, ],
|
||||||
|
{
|
||||||
|
"ValueType": diff,
|
||||||
|
"Ref": unicode(vals[1]),
|
||||||
|
"Loc": unicode(vals[0]),
|
||||||
|
},
|
||||||
|
"MiscValues",
|
||||||
|
0,)
|
||||||
|
)
|
||||||
|
return bugs
|
||||||
|
|
51
pywinauto/tests/missalignment.py
Normal file
51
pywinauto/tests/missalignment.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
from win32structures import RECT
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def MissalignmentTest(windows):
|
||||||
|
|
||||||
|
refAlignments = {}
|
||||||
|
|
||||||
|
#find the controls alligned along each axis
|
||||||
|
for win in windows:
|
||||||
|
if not win.ref:
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
for side in ("top", "left", "right", "bottom"):
|
||||||
|
sideValue = getattr(win.ref.Rectangle, side)
|
||||||
|
|
||||||
|
# make sure that the side dictionary has been created
|
||||||
|
sideAlignments = refAlignments.setdefault(side, {})
|
||||||
|
|
||||||
|
# make sure that the array of controls for this
|
||||||
|
# alignment line has been created and add the current window
|
||||||
|
sideAlignments.setdefault(sideValue, []).append(win)
|
||||||
|
|
||||||
|
bugs = []
|
||||||
|
for side in refAlignments:
|
||||||
|
for alignment in refAlignments[side]:
|
||||||
|
controls = refAlignments[side][alignment]
|
||||||
|
sides = [getattr(c.Rectangle, side) for c in controls]
|
||||||
|
sides = set(sides)
|
||||||
|
|
||||||
|
if len(sides) > 1:
|
||||||
|
|
||||||
|
overAllRect = RECT()
|
||||||
|
overAllRect.left = min([c.Rectangle.left for c in controls])
|
||||||
|
overAllRect.top = min([c.Rectangle.top for c in controls])
|
||||||
|
overAllRect.right = max([c.Rectangle.right for c in controls])
|
||||||
|
overAllRect.bottom = max([c.Rectangle.bottom for c in controls])
|
||||||
|
|
||||||
|
|
||||||
|
bugs.append((
|
||||||
|
controls,
|
||||||
|
{
|
||||||
|
"AlignmentType": side.upper(),
|
||||||
|
"AlignmentRect": overAllRect
|
||||||
|
},
|
||||||
|
"Missalignment",
|
||||||
|
0)
|
||||||
|
)
|
||||||
|
|
||||||
|
return bugs
|
||||||
|
|
44
pywinauto/tests/missingextrastring.py
Normal file
44
pywinauto/tests/missingextrastring.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
CharsToCheck = (
|
||||||
|
">",
|
||||||
|
">>",
|
||||||
|
"<",
|
||||||
|
"<<",
|
||||||
|
"&",
|
||||||
|
"&&",
|
||||||
|
"...",
|
||||||
|
":",
|
||||||
|
"@",
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def MissingExtraStringTest(windows):
|
||||||
|
bugs = []
|
||||||
|
for win in windows:
|
||||||
|
if not win.ref:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for char in CharsToCheck:
|
||||||
|
missingExtra = ''
|
||||||
|
|
||||||
|
if win.Text.count(char) > win.ref.Text.count(char):
|
||||||
|
missingExtra = "ExtraCharacters"
|
||||||
|
elif win.Text.count(char) < win.ref.Text.count(char):
|
||||||
|
missingExtra = "MissingCharacters"
|
||||||
|
|
||||||
|
if missingExtra:
|
||||||
|
bugs.append((
|
||||||
|
[win,],
|
||||||
|
{
|
||||||
|
"MissingOrExtra": missingExtra,
|
||||||
|
"MissingOrExtraText": char
|
||||||
|
},
|
||||||
|
"MissingExtraString",
|
||||||
|
0))
|
||||||
|
|
||||||
|
return bugs
|
||||||
|
|
||||||
|
|
||||||
|
MissingExtraStringTest.TestsMenus = True
|
||||||
|
|
104
pywinauto/tests/overlapping.py
Normal file
104
pywinauto/tests/overlapping.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
"test for overlapping controls"
|
||||||
|
|
||||||
|
__revision__ = "0.0.1"
|
||||||
|
|
||||||
|
import win32structures
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def OverlappingTest(windows):
|
||||||
|
"Return the repeated hotkey errors"
|
||||||
|
|
||||||
|
bugs = []
|
||||||
|
|
||||||
|
for i, first in enumerate(windows[:-1]):
|
||||||
|
for second in windows[i+1:]:
|
||||||
|
|
||||||
|
# if the reference controls are available
|
||||||
|
if first.ref and second.ref:
|
||||||
|
|
||||||
|
if first.ref.Rectangle == second.ref.Rectangle and \
|
||||||
|
not first.Rectangle == second.Rectangle:
|
||||||
|
|
||||||
|
bugs.append(([first, second], {}, "NotExactOverlap", 0))
|
||||||
|
|
||||||
|
elif ContainedInOther(first.ref.Rectangle, second.ref.Rectangle) and \
|
||||||
|
not ContainedInOther(first.Rectangle, second.Rectangle):
|
||||||
|
|
||||||
|
bugs.append(([first, second], {}, "NotContainedOverlap", 0))
|
||||||
|
|
||||||
|
|
||||||
|
if Overlapped(first.Rectangle, second.Rectangle) and \
|
||||||
|
not ContainedInOther(first.Rectangle, second.Rectangle) and \
|
||||||
|
not first.Rectangle == second.Rectangle:
|
||||||
|
|
||||||
|
ovlRect = OverlapRect(first.Rectangle, second.Rectangle)
|
||||||
|
|
||||||
|
isInRef = -1
|
||||||
|
if first.ref and second.ref:
|
||||||
|
isInRef = 0
|
||||||
|
if Overlapped(first.ref.Rectangle, second.ref.Rectangle):
|
||||||
|
isInRef = 1
|
||||||
|
|
||||||
|
bugs.append(([first, second], {"OverlappedRect":ovlRect}, "Overlapping", isInRef))
|
||||||
|
|
||||||
|
return bugs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
def ContainedInOther(rect1, rect2):
|
||||||
|
# check if rect2 is inside rect1
|
||||||
|
|
||||||
|
if rect1.left >= rect2.left and \
|
||||||
|
rect1.top >= rect2.top and \
|
||||||
|
rect1.right <= rect2.right and \
|
||||||
|
rect1.bottom <= rect2.bottom:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# check if rect1 is inside rect2
|
||||||
|
elif rect2.left >= rect1.left and \
|
||||||
|
rect2.top >= rect1.top and \
|
||||||
|
rect2.right <= rect1.right and \
|
||||||
|
rect2.bottom <= rect1.bottom:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# no previous return - so must not be included
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def Overlapped(rect1, rect2):
|
||||||
|
ovlRect = OverlapRect(rect1, rect2)
|
||||||
|
|
||||||
|
# if it is actually a bug
|
||||||
|
if ovlRect.left < ovlRect.right and ovlRect.top < ovlRect.bottom:
|
||||||
|
# make sure that the rectangle is the 'right way around :-)'
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# Case 1: L2 between L1 and R1 -> max(L1, L2) < min(R1, R2)
|
||||||
|
#
|
||||||
|
# L1 R1
|
||||||
|
# ---------------
|
||||||
|
# L2 R2
|
||||||
|
# --------------
|
||||||
|
#
|
||||||
|
# Case 2: R2 outside L1 and R1 -> NOT max(L1, L2) < min(R1, R2)
|
||||||
|
#
|
||||||
|
# L1 R1
|
||||||
|
# -------------
|
||||||
|
# L2 R2
|
||||||
|
# ------------
|
||||||
|
#
|
||||||
|
def OverlapRect (rect1, rect2):
|
||||||
|
"check whether the 2 rectangles are actually overlapped"
|
||||||
|
|
||||||
|
ovlRect = win32structures.RECT()
|
||||||
|
|
||||||
|
ovlRect.left = max(rect1.left, rect2.left)
|
||||||
|
ovlRect.right = min(rect1.right, rect2.right)
|
||||||
|
ovlRect.top = max(rect1.top, rect2.top)
|
||||||
|
ovlRect.bottom = min(rect1.bottom, rect2.bottom)
|
||||||
|
|
||||||
|
return ovlRect
|
||||||
|
|
172
pywinauto/tests/repeatedhotkey.py
Normal file
172
pywinauto/tests/repeatedhotkey.py
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
"tests a set of controls for repeated hotkey errors"
|
||||||
|
|
||||||
|
__revision__ = "0.0.1"
|
||||||
|
|
||||||
|
from win32defines import SS_NOPREFIX
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def RepeatedHotkeyTest(windows):
|
||||||
|
"Return the repeated hotkey errors"
|
||||||
|
|
||||||
|
hotkeyControls, allChars, hotkeys = CollectDialogInfo(windows)
|
||||||
|
|
||||||
|
#if windows[0].Class == "MenuItem":
|
||||||
|
# print "=-" * 20
|
||||||
|
# import pprint
|
||||||
|
# pprint.pprint (windows)
|
||||||
|
|
||||||
|
# get the available characters in the dialog
|
||||||
|
dlgAvailable = allChars.difference(hotkeys)
|
||||||
|
|
||||||
|
# remove some characters that aren't good choices for hotkeys
|
||||||
|
dlgAvailable.difference_update(set("-& _"))
|
||||||
|
|
||||||
|
|
||||||
|
bugs = []
|
||||||
|
# for each hotkey
|
||||||
|
for char, controls in hotkeyControls.items():
|
||||||
|
|
||||||
|
# if there is more than one control associated then it is a bug
|
||||||
|
if len(controls) > 1:
|
||||||
|
|
||||||
|
ctrlsAvailableChars = ""
|
||||||
|
|
||||||
|
# build up the available characters for each control
|
||||||
|
for c in controls:
|
||||||
|
controlChars = ""
|
||||||
|
controlChars = set(c.Text.lower())
|
||||||
|
|
||||||
|
controlAvailableChars = controlChars.intersection(dlgAvailable)
|
||||||
|
controlAvailableChars = "<%s>" % SetAsString(controlAvailableChars)
|
||||||
|
|
||||||
|
ctrlsAvailableChars += controlAvailableChars
|
||||||
|
|
||||||
|
refCtrls = [ctrl.ref for ctrl in controls if ctrl.ref]
|
||||||
|
refHotkeyControls, refAllChars, refHotkeys = CollectDialogInfo(refCtrls)
|
||||||
|
|
||||||
|
isInRef = -1
|
||||||
|
if len(refHotkeys) > 1:
|
||||||
|
isInRef = 1
|
||||||
|
else:
|
||||||
|
isInRef = 0
|
||||||
|
|
||||||
|
bugs.append((
|
||||||
|
controls,
|
||||||
|
{
|
||||||
|
"RepeatedHotkey" : char,
|
||||||
|
"CharsUsedInDialog" : SetAsString(hotkeys),
|
||||||
|
"AllCharsInDialog" : SetAsString(allChars),
|
||||||
|
"AvailableInControls" : ctrlsAvailableChars,
|
||||||
|
},
|
||||||
|
"RepeatedHotkey",
|
||||||
|
isInRef)
|
||||||
|
)
|
||||||
|
|
||||||
|
# # What is the algorithm to try and do all that is necessary to find
|
||||||
|
# # if it is possible to get a fix character if none of the current
|
||||||
|
# # characters are free
|
||||||
|
# for bug in bugs:
|
||||||
|
# for c, chars in bug.bugData:
|
||||||
|
# # control has no possibilities
|
||||||
|
# if not chars:
|
||||||
|
# # check if there are any other hotkey controls
|
||||||
|
# # in the dialog that could be used
|
||||||
|
# others = set(c.Title.lower()).intersection(unUsedChars)
|
||||||
|
|
||||||
|
|
||||||
|
return bugs
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def CollectDialogInfo(windows):
|
||||||
|
hotkeyControls = {}
|
||||||
|
allChars = ''
|
||||||
|
|
||||||
|
for win in windows:
|
||||||
|
# skip it if it doesn't implement hotkey functionality
|
||||||
|
if not ImplementsHotkey(win):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# get the hotkey
|
||||||
|
pos, char = GetHotkey(win.Text)
|
||||||
|
|
||||||
|
# if no hotkey for this control
|
||||||
|
# then continue
|
||||||
|
if not char:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# store hotkey with list of used hotkeys
|
||||||
|
# map this hotkey to the list of controls that have it
|
||||||
|
hotkeyControls.setdefault(char.lower(), []).append(win)
|
||||||
|
|
||||||
|
|
||||||
|
# Add the title of this control to the list of available
|
||||||
|
# characters for the dialog
|
||||||
|
allChars += win.Text.lower()
|
||||||
|
|
||||||
|
|
||||||
|
allChars = set(allChars)
|
||||||
|
hotkeys = set(hotkeyControls.keys())
|
||||||
|
|
||||||
|
return hotkeyControls, allChars, hotkeys
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
# get hokey position
|
||||||
|
def GetHotkey(text):
|
||||||
|
"Return the position and character of the hotkey"
|
||||||
|
|
||||||
|
# find the last & character that is not followed
|
||||||
|
# by & or by the end of the string
|
||||||
|
|
||||||
|
curEnd = len(text) + 1
|
||||||
|
text = text.replace("&&", "__")
|
||||||
|
while True:
|
||||||
|
pos = text.rfind("&", 0, curEnd)
|
||||||
|
|
||||||
|
# One found was at the end of the text or
|
||||||
|
# no (more) & were found
|
||||||
|
# so return the None value
|
||||||
|
if pos == -1 or pos == len(text):
|
||||||
|
return (-1, '')
|
||||||
|
|
||||||
|
# One found but was prededed by non valid hotkey character
|
||||||
|
# so continue, as that can't be a shortcut
|
||||||
|
if text[pos - 1] == '&':
|
||||||
|
curEnd = pos - 2
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 2 ampersands in a row - so skip
|
||||||
|
# the 1st one and continue
|
||||||
|
return (pos, text[pos+1])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def SetAsString(s):
|
||||||
|
return "".join(sorted(s))
|
||||||
|
|
||||||
|
|
||||||
|
#=============================================================================
|
||||||
|
def ImplementsHotkey(win):
|
||||||
|
"checks whether a control interprets & character to be a hotkey"
|
||||||
|
|
||||||
|
# buttons always implement hotkey
|
||||||
|
if win.Class == "Button":
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Statics do if they don't have SS_NOPREFIX style
|
||||||
|
elif win.Class == "Static" and not win.HasStyle(SS_NOPREFIX):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if win.Class == "MenuItem" and win.State != "2048":
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Most controls don't - so just return false if
|
||||||
|
# neither of the above condition hold
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
RepeatedHotkeyTest.TestsMenus = True
|
||||||
|
|
75
pywinauto/tests/translation.py
Normal file
75
pywinauto/tests/translation.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import re
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
def TranslationTest(windows):
|
||||||
|
"Returns just one bug for each control"
|
||||||
|
|
||||||
|
bugs = []
|
||||||
|
for win in windows:
|
||||||
|
if not win.ref:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# get if any items are untranslated
|
||||||
|
untranTitles, untranIndices = GetUntranslations(win)
|
||||||
|
|
||||||
|
if untranTitles:
|
||||||
|
indicesAsString = ",".join([unicode(x) for x in untranIndices])
|
||||||
|
|
||||||
|
bugs.append((
|
||||||
|
[win,],
|
||||||
|
{
|
||||||
|
"StringIndices": indicesAsString,
|
||||||
|
"Strings": ('"%s"' % '","'.join(untranTitles))
|
||||||
|
},
|
||||||
|
"Translation",
|
||||||
|
0)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
return bugs
|
||||||
|
|
||||||
|
def GetUntranslations(win):
|
||||||
|
# remove ampersands and other non translatable bits from the string
|
||||||
|
|
||||||
|
nonTransChars = re.compile(
|
||||||
|
"""(\&(?!\&)| # ampersand not followed by an ampersand
|
||||||
|
\.\.\.$| # elipsis ...
|
||||||
|
^\s*| # leading whitespace
|
||||||
|
\s*$| # trailing whitespace
|
||||||
|
\s*:\s*$ # trailing colon (with/without whitespace)
|
||||||
|
)* # repeated as often as necessary
|
||||||
|
""", re.X)
|
||||||
|
|
||||||
|
|
||||||
|
# clean each of the loc titles for comparison
|
||||||
|
cleanedLocTitles = []
|
||||||
|
for title in win.Texts:
|
||||||
|
cleanedLocTitles.append(nonTransChars.sub("", title))
|
||||||
|
|
||||||
|
# clean each of the ref titles for comparison
|
||||||
|
cleanedRefTitles = []
|
||||||
|
for title in win.ref.Texts:
|
||||||
|
cleanedRefTitles.append(nonTransChars.sub("", title))
|
||||||
|
|
||||||
|
untranslatedTitles = []
|
||||||
|
untranslatedIndices = []
|
||||||
|
|
||||||
|
# loop over each of the cleaned loc title
|
||||||
|
for index, title in enumerate(cleanedLocTitles):
|
||||||
|
|
||||||
|
# if the title is empty just skip it
|
||||||
|
if not title:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# if that title is in the cleaned Ref Titles
|
||||||
|
if title in cleanedRefTitles:
|
||||||
|
# add this as one of the bugs
|
||||||
|
untranslatedTitles.append(title)
|
||||||
|
untranslatedIndices.append(index)
|
||||||
|
|
||||||
|
# return all the untranslated titles and thier indices
|
||||||
|
return untranslatedTitles, untranslatedIndices
|
||||||
|
|
||||||
|
|
||||||
|
TranslationTest.TestsMenus = True
|
||||||
|
|
426
pywinauto/tests/truncation.py
Normal file
426
pywinauto/tests/truncation.py
Normal file
@ -0,0 +1,426 @@
|
|||||||
|
# pylint: disable-msg=W0611
|
||||||
|
|
||||||
|
"""info on truncation test"""
|
||||||
|
|
||||||
|
|
||||||
|
__revision__ = "0.0.1"
|
||||||
|
|
||||||
|
|
||||||
|
from win32defines import *
|
||||||
|
from win32functions import *
|
||||||
|
from ctypes import byref
|
||||||
|
from win32structures import RECT
|
||||||
|
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def TruncationTest(windows):
|
||||||
|
"Actually do the test"
|
||||||
|
|
||||||
|
truncations = []
|
||||||
|
|
||||||
|
# for each of the windows in the dialog
|
||||||
|
for win in windows:
|
||||||
|
|
||||||
|
truncIdxs, truncStrings = FindTruncations(win)
|
||||||
|
|
||||||
|
isInRef = -1
|
||||||
|
|
||||||
|
# if there were any truncations for this control
|
||||||
|
if truncIdxs:
|
||||||
|
|
||||||
|
# now that we know there was at least one truncation
|
||||||
|
# check if the reference control has truncations
|
||||||
|
if win.ref:
|
||||||
|
isInRef = 0
|
||||||
|
refTruncIdxs, refTruncStrings = FindTruncations(win.ref)
|
||||||
|
|
||||||
|
if refTruncIdxs:
|
||||||
|
isInRef = 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
truncIdxs = ",".join([unicode(x) for x in truncIdxs])
|
||||||
|
truncStrings = '"%s"' % ",".join([unicode(x) for x in truncStrings])
|
||||||
|
truncations.append((
|
||||||
|
[win,],
|
||||||
|
{
|
||||||
|
"StringIndices": truncIdxs,
|
||||||
|
"Strings": truncStrings,
|
||||||
|
},
|
||||||
|
u"Truncation",
|
||||||
|
isInRef)
|
||||||
|
)
|
||||||
|
|
||||||
|
# return all the truncations
|
||||||
|
return truncations
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def FindTruncations(ctrl):
|
||||||
|
|
||||||
|
truncIdxs = []
|
||||||
|
truncStrings = []
|
||||||
|
|
||||||
|
# for each of the titles this dialog
|
||||||
|
for idx, (text, rect, font, flags) in enumerate(GetTruncationInfo(ctrl)):
|
||||||
|
|
||||||
|
# skip if there is no text
|
||||||
|
if not text:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# get the minimum rectangle
|
||||||
|
minRect = GetMinimumRect(text, font, rect, flags)
|
||||||
|
|
||||||
|
# if the min rectangle is bigger than the rectangle of the
|
||||||
|
# object
|
||||||
|
if minRect.right > rect.right or \
|
||||||
|
minRect.bottom > rect.bottom:
|
||||||
|
|
||||||
|
# append the index and the rectangle to list of bug items
|
||||||
|
truncIdxs.append(idx)
|
||||||
|
truncStrings.append(text)
|
||||||
|
|
||||||
|
return truncIdxs, truncStrings
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def GetMinimumRect(text, font, usableRect, drawFlags):
|
||||||
|
isTruncated = False
|
||||||
|
|
||||||
|
# try to create the font
|
||||||
|
# create a Display DC (compatible to the screen)
|
||||||
|
txtDC = CreateDC(u"DISPLAY", None, None, None )
|
||||||
|
|
||||||
|
hFontGUI = CreateFontIndirect(byref(font))
|
||||||
|
|
||||||
|
# Maybe we could not get the font or we got the system font
|
||||||
|
if not hFontGUI:
|
||||||
|
|
||||||
|
# So just get the default system font
|
||||||
|
hFontGUI = GetStockObject(DEFAULT_GUI_FONT)
|
||||||
|
|
||||||
|
# if we still don't have a font!
|
||||||
|
# ----- ie, we're on an antiquated OS, like NT 3.51
|
||||||
|
if not hFontGUI:
|
||||||
|
|
||||||
|
# ----- On Asian platforms, ANSI font won't show.
|
||||||
|
if GetSystemMetrics(SM_DBCSENABLED):
|
||||||
|
# ----- was...(SYSTEM_FONT)
|
||||||
|
hFontGUI = GetStockObject(SYSTEM_FONT)
|
||||||
|
else:
|
||||||
|
# ----- was...(SYSTEM_FONT)
|
||||||
|
hFontGUI = GetStockObject(ANSI_VAR_FONT)
|
||||||
|
|
||||||
|
|
||||||
|
# put our font into the Device Context
|
||||||
|
SelectObject (txtDC, hFontGUI)
|
||||||
|
|
||||||
|
|
||||||
|
modifiedRect = RECT(usableRect)
|
||||||
|
# Now write the text to our DC with our font to get the
|
||||||
|
# rectangle that the text needs to fit in
|
||||||
|
DrawText (txtDC, # The DC
|
||||||
|
unicode(text), # The Title of the control
|
||||||
|
-1, # -1 because sTitle is NULL terminated
|
||||||
|
byref(modifiedRect), # The Rectangle to be calculated to
|
||||||
|
#truncCtrlData.drawTextFormat |
|
||||||
|
DT_CALCRECT | drawFlags)
|
||||||
|
|
||||||
|
#elif modifiedRect.right == usableRect.right and \
|
||||||
|
# modifiedRect.bottom == usableRect.bottom:
|
||||||
|
# print "Oh so you thought you were perfect!!!"
|
||||||
|
|
||||||
|
|
||||||
|
# Delete the font we created
|
||||||
|
DeleteObject(hFontGUI)
|
||||||
|
|
||||||
|
# delete the Display context that we created
|
||||||
|
DeleteDC(txtDC)
|
||||||
|
|
||||||
|
return modifiedRect
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def ButtonTruncInfo(win):
|
||||||
|
"Return truncation data for buttons"
|
||||||
|
lineFormat = DT_SINGLELINE
|
||||||
|
|
||||||
|
widthAdj = 0
|
||||||
|
heightAdj = 0
|
||||||
|
|
||||||
|
# get the last byte of the style
|
||||||
|
buttonStyle = win.Style & 0xF
|
||||||
|
|
||||||
|
if win.HasStyle(BS_MULTILINE):
|
||||||
|
lineFormat = DT_WORDBREAK
|
||||||
|
|
||||||
|
if buttonStyle == BS_PUSHBUTTON:
|
||||||
|
heightAdj = 4
|
||||||
|
widthAdj = 5
|
||||||
|
|
||||||
|
elif win.HasStyle(BS_PUSHLIKE):
|
||||||
|
widthAdj = 3
|
||||||
|
heightAdj = 3 # 3
|
||||||
|
if win.HasStyle(BS_MULTILINE):
|
||||||
|
widthAdj = 9
|
||||||
|
heightAdj = 2 # 3
|
||||||
|
|
||||||
|
elif buttonStyle == BS_CHECKBOX or buttonStyle == BS_AUTOCHECKBOX:
|
||||||
|
widthAdj = 18
|
||||||
|
|
||||||
|
elif buttonStyle == BS_RADIOBUTTON or buttonStyle == BS_AUTORADIOBUTTON:
|
||||||
|
widthAdj = 19
|
||||||
|
|
||||||
|
elif buttonStyle == BS_GROUPBOX:
|
||||||
|
heightAdj = 4
|
||||||
|
widthAdj = 9
|
||||||
|
lineFormat = DT_SINGLELINE
|
||||||
|
|
||||||
|
if win.HasStyle(BS_BITMAP) or win.HasStyle(BS_ICON):
|
||||||
|
heightAdj = -9000
|
||||||
|
widthAdj = -9000
|
||||||
|
lineFormat = DT_WORDBREAK
|
||||||
|
|
||||||
|
newRect = win.ClientRects[0]
|
||||||
|
newRect.right -= widthAdj
|
||||||
|
newRect.bottom -= heightAdj
|
||||||
|
|
||||||
|
return [(win.Text, newRect, win.Font, lineFormat), ]
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def ComboBoxTruncInfo(win):
|
||||||
|
"Return truncation data for comboboxes"
|
||||||
|
# canot wrap and never had a hotkey
|
||||||
|
lineFormat = DT_SINGLELINE | DT_NOPREFIX
|
||||||
|
|
||||||
|
if win.HasStyle(CBS_DROPDOWN) or win.HasStyle(CBS_DROPDOWNLIST):
|
||||||
|
widthAdj = 2#5
|
||||||
|
else:
|
||||||
|
widthAdj = 3
|
||||||
|
|
||||||
|
truncData = []
|
||||||
|
for title in win.Texts:
|
||||||
|
newRect = win.ClientRects[0]
|
||||||
|
newRect.right -= widthAdj
|
||||||
|
truncData.append((title, newRect, win.Font, lineFormat))
|
||||||
|
#print title, newRect, win.Font, lineFormat
|
||||||
|
|
||||||
|
return truncData
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def ComboLBoxTruncInfo(win):
|
||||||
|
"Return truncation data for comboboxes"
|
||||||
|
# canot wrap and never had a hotkey
|
||||||
|
lineFormat = DT_SINGLELINE | DT_NOPREFIX
|
||||||
|
|
||||||
|
truncData = []
|
||||||
|
for title in win.Texts:
|
||||||
|
newRect = win.ClientRects[0]
|
||||||
|
newRect.right -= 5
|
||||||
|
truncData.append((title, newRect, win.Font, lineFormat))
|
||||||
|
|
||||||
|
return truncData
|
||||||
|
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def ListBoxTruncInfo(win):
|
||||||
|
"Return truncation data for listboxes"
|
||||||
|
# canot wrap and never had a hotkey
|
||||||
|
lineFormat = DT_SINGLELINE | DT_NOPREFIX
|
||||||
|
|
||||||
|
truncData = []
|
||||||
|
for title in win.Texts:
|
||||||
|
newRect = win.ClientRects[0]
|
||||||
|
newRect.right -= 2
|
||||||
|
newRect.bottom -= 1
|
||||||
|
truncData.append((title, newRect, win.Font, lineFormat))
|
||||||
|
|
||||||
|
return truncData
|
||||||
|
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def StaticTruncInfo(win):
|
||||||
|
"Return truncation data for static controls"
|
||||||
|
lineFormat = DT_WORDBREAK
|
||||||
|
|
||||||
|
if win.HasStyle(SS_CENTERIMAGE) or \
|
||||||
|
win.HasStyle(SS_SIMPLE) or \
|
||||||
|
win.HasStyle(SS_LEFTNOWORDWRAP):
|
||||||
|
lineFormat = DT_SINGLELINE
|
||||||
|
|
||||||
|
if win.HasStyle(SS_NOPREFIX):
|
||||||
|
lineFormat |= DT_NOPREFIX
|
||||||
|
|
||||||
|
return [(win.Text, win.ClientRects[0], win.Font, lineFormat), ]
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def EditTruncInfo(win):
|
||||||
|
"Return truncation data for static controls"
|
||||||
|
lineFormat = DT_WORDBREAK | DT_NOPREFIX
|
||||||
|
|
||||||
|
if not win.HasStyle(ES_MULTILINE):
|
||||||
|
lineFormat |= DT_SINGLELINE
|
||||||
|
|
||||||
|
return [(win.Text, win.ClientRects[0], win.Font, lineFormat), ]
|
||||||
|
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def DialogTruncInfo(win):
|
||||||
|
"Return truncation data for dialog type windows"
|
||||||
|
# move it down more into range
|
||||||
|
|
||||||
|
newRect = win.ClientRects[0]
|
||||||
|
|
||||||
|
newRect.top += 5
|
||||||
|
newRect.left += 5
|
||||||
|
newRect.right -= 5
|
||||||
|
|
||||||
|
|
||||||
|
if win.HasStyle(WS_THICKFRAME):
|
||||||
|
newRect.top += 1
|
||||||
|
newRect.left += 1
|
||||||
|
newRect.right -= 1
|
||||||
|
|
||||||
|
# if it has the system menu but is a small caption
|
||||||
|
# then the only button it can have is the close button
|
||||||
|
if win.HasStyle(WS_SYSMENU) and (win.HasExStyle(WS_EX_PALETTEWINDOW) or
|
||||||
|
win.HasExStyle(WS_EX_TOOLWINDOW)):
|
||||||
|
newRect.right -= 15
|
||||||
|
|
||||||
|
|
||||||
|
# all the rest only need to be considered if there is a system menu.
|
||||||
|
elif win.HasStyle(WS_SYSMENU):
|
||||||
|
buttons = []
|
||||||
|
# account for the close button
|
||||||
|
newRect.right -= 18
|
||||||
|
buttons.append('close')
|
||||||
|
|
||||||
|
# account for Icon if it is not disabled
|
||||||
|
if not win.HasExStyle(WS_EX_DLGMODALFRAME):
|
||||||
|
newRect.left += 19 # icon
|
||||||
|
|
||||||
|
|
||||||
|
# account for context sensitive help if set
|
||||||
|
if win.HasExStyle(WS_EX_CONTEXTHELP) and not ( \
|
||||||
|
win.HasStyle(WS_MAXIMIZEBOX) and win.HasStyle(WS_MINIMIZEBOX)):
|
||||||
|
|
||||||
|
newRect.right -= 17
|
||||||
|
|
||||||
|
# there is a bigger gap if the minimize box is there
|
||||||
|
if win.HasStyle(WS_MINIMIZEBOX) or win.HasStyle(WS_MAXIMIZEBOX) or \
|
||||||
|
win.HasStyle(WS_GROUP):
|
||||||
|
newRect.right -= 3
|
||||||
|
|
||||||
|
buttons.append('help')
|
||||||
|
|
||||||
|
|
||||||
|
# account for Maximize button (but skip if WS_GROUP is set
|
||||||
|
if win.HasStyle(WS_MINIMIZEBOX) or win.HasStyle(WS_MAXIMIZEBOX) or \
|
||||||
|
win.HasStyle(WS_GROUP):
|
||||||
|
|
||||||
|
newRect.right -= 32
|
||||||
|
buttons.append('min')
|
||||||
|
buttons.append('max')
|
||||||
|
|
||||||
|
if buttons:
|
||||||
|
# space between first button and dialog edge
|
||||||
|
diff = 5
|
||||||
|
|
||||||
|
# space for each button
|
||||||
|
diff += len(buttons) * 16
|
||||||
|
|
||||||
|
# space between close and next button
|
||||||
|
if len(buttons) > 1:
|
||||||
|
diff += 2
|
||||||
|
|
||||||
|
# extra space between help and min buttons
|
||||||
|
if 'min' in buttons and 'help' in buttons:
|
||||||
|
diff += 4
|
||||||
|
|
||||||
|
return [(win.Text, newRect, win.Font, DT_SINGLELINE), ]
|
||||||
|
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def StatusBarTruncInfo(win):
|
||||||
|
truncInfo = WindowTruncInfo(win)
|
||||||
|
for i, (title, rect, font, flag) in enumerate(truncInfo):
|
||||||
|
|
||||||
|
rect.bottom -= win.VertBorderWidth
|
||||||
|
if i == 0:
|
||||||
|
rect.right -= win.HorizBorderWidth
|
||||||
|
else:
|
||||||
|
rect.right -= win.InterBorderWidth
|
||||||
|
|
||||||
|
return truncInfo
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def HeaderTruncInfo(win):
|
||||||
|
truncInfo = WindowTruncInfo(win)
|
||||||
|
|
||||||
|
for i, (title, rect, font, flag) in enumerate(truncInfo):
|
||||||
|
|
||||||
|
rect.right -= 12
|
||||||
|
|
||||||
|
return truncInfo
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def WindowTruncInfo(win):
|
||||||
|
matchedItems = []
|
||||||
|
|
||||||
|
for i, title in enumerate(win.Texts):
|
||||||
|
|
||||||
|
# Use the client rects for rectangles
|
||||||
|
if i < len(win.ClientRects):
|
||||||
|
rect = win.ClientRects[i]
|
||||||
|
else:
|
||||||
|
# until we run out then just use the first 'main' client rectangle
|
||||||
|
rect = win.ClientRects[0]
|
||||||
|
|
||||||
|
# if we have fewer fonts than titles
|
||||||
|
if len(win.Fonts)-1 < i:
|
||||||
|
font = win.Font
|
||||||
|
else:
|
||||||
|
font = win.Fonts[i]
|
||||||
|
|
||||||
|
# add the item
|
||||||
|
matchedItems.append((win.Texts[i], rect, font, DT_SINGLELINE))
|
||||||
|
|
||||||
|
return matchedItems
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
TruncInfo = {
|
||||||
|
"#32770" : DialogTruncInfo,
|
||||||
|
"ComboBox" : ComboBoxTruncInfo,
|
||||||
|
"ComboLBox" : ComboLBoxTruncInfo,
|
||||||
|
"ListBox" : ListBoxTruncInfo,
|
||||||
|
"Button" : ButtonTruncInfo,
|
||||||
|
"Edit": EditTruncInfo,
|
||||||
|
"Static" : StaticTruncInfo,
|
||||||
|
|
||||||
|
# "msctls_statusbar32" : StatusBarTruncInfo,
|
||||||
|
# "HSStatusBar" : StatusBarTruncInfo,
|
||||||
|
# "SysHeader32" : HeaderTruncInfo,
|
||||||
|
|
||||||
|
# "SysListView32" : ListViewTruncInfo,
|
||||||
|
#"SysTreeView32" :
|
||||||
|
}
|
||||||
|
|
||||||
|
#==============================================================================
|
||||||
|
def GetTruncationInfo(win):
|
||||||
|
"helper function to hide non special windows"
|
||||||
|
if win.Class in TruncInfo:
|
||||||
|
return TruncInfo[win.Class](win)
|
||||||
|
else:
|
||||||
|
|
||||||
|
return WindowTruncInfo(win)
|
||||||
|
|
||||||
|
|
||||||
|
|
11875
pywinauto/win32defines.py
Normal file
11875
pywinauto/win32defines.py
Normal file
File diff suppressed because it is too large
Load Diff
99
pywinauto/win32functions.py
Normal file
99
pywinauto/win32functions.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import ctypes
|
||||||
|
from ctypes import *
|
||||||
|
|
||||||
|
import win32structures
|
||||||
|
from win32structures import *
|
||||||
|
|
||||||
|
CreateBrushIndirect = ctypes.windll.gdi32.CreateBrushIndirect
|
||||||
|
CreateDC = ctypes.windll.gdi32.CreateDCW
|
||||||
|
CreateFontIndirect = ctypes.windll.gdi32.CreateFontIndirectW
|
||||||
|
CreatePen = ctypes.windll.gdi32.CreatePen
|
||||||
|
DeleteDC = ctypes.windll.gdi32.DeleteDC
|
||||||
|
DeleteObject = ctypes.windll.gdi32.DeleteObject
|
||||||
|
DrawText = ctypes.windll.user32.DrawTextW
|
||||||
|
TextOut = ctypes.windll.gdi32.TextOutW
|
||||||
|
EnableWindow = ctypes.windll.user32.EnableWindow
|
||||||
|
EnumChildWindows = ctypes.windll.user32.EnumChildWindows
|
||||||
|
EnumDesktopWindows = ctypes.windll.user32.EnumDesktopWindows
|
||||||
|
EnumWindows = ctypes.windll.user32.EnumWindows
|
||||||
|
GetClassName = ctypes.windll.user32.GetClassNameW
|
||||||
|
GetClientRect = ctypes.windll.user32.GetClientRect
|
||||||
|
GetDC = ctypes.windll.user32.GetDC
|
||||||
|
GetDesktopWindow = ctypes.windll.user32.GetDesktopWindow
|
||||||
|
GetMenu = ctypes.windll.user32.GetMenu
|
||||||
|
GetMenuItemCount = ctypes.windll.user32.GetMenuItemCount
|
||||||
|
GetMenuItemInfo = ctypes.windll.user32.GetMenuItemInfoW
|
||||||
|
GetObject = ctypes.windll.gdi32.GetObjectW
|
||||||
|
GetParent = ctypes.windll.user32.GetParent
|
||||||
|
GetStockObject = ctypes.windll.gdi32.GetStockObject
|
||||||
|
GetSystemMenu = ctypes.windll.user32.GetSystemMenu
|
||||||
|
GetSystemMetrics = ctypes.windll.user32.GetSystemMetrics
|
||||||
|
GetTextMetrics = ctypes.windll.gdi32.GetTextMetricsW
|
||||||
|
GetVersion = ctypes.windll.kernel32.GetVersion
|
||||||
|
GetWindow = ctypes.windll.user32.GetWindow
|
||||||
|
ShowWindow = ctypes.windll.user32.ShowWindow
|
||||||
|
GetWindowContextHelpId = ctypes.windll.user32.GetWindowContextHelpId
|
||||||
|
GetWindowLong = ctypes.windll.user32.GetWindowLongW
|
||||||
|
GetWindowRect = ctypes.windll.user32.GetWindowRect
|
||||||
|
GetWindowText = ctypes.windll.user32.GetWindowTextW
|
||||||
|
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
|
||||||
|
GetCurrentThreadId = ctypes.windll.Kernel32.GetCurrentThreadId
|
||||||
|
GetWindowThreadProcessId = ctypes.windll.user32.GetWindowThreadProcessId
|
||||||
|
AttachThreadInput = ctypes.windll.user32.AttachThreadInput
|
||||||
|
IsChild = ctypes.windll.user32.IsChild
|
||||||
|
IsMenu = ctypes.windll.user32.IsMenu
|
||||||
|
IsWindow = ctypes.windll.user32.IsWindow
|
||||||
|
IsWindowUnicode = ctypes.windll.user32.IsWindowUnicode
|
||||||
|
IsWindowVisible = ctypes.windll.user32.IsWindowVisible
|
||||||
|
IsWindowEnabled = ctypes.windll.user32.IsWindowEnabled
|
||||||
|
MapVirtualKey = ctypes.windll.user32.MapVirtualKeyW
|
||||||
|
OpenProcess = ctypes.windll.kernel32.OpenProcess
|
||||||
|
ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory
|
||||||
|
Rectangle = ctypes.windll.gdi32.Rectangle
|
||||||
|
SelectObject = ctypes.windll.gdi32.SelectObject
|
||||||
|
SendMessage = ctypes.windll.user32.SendMessageW
|
||||||
|
SendMessageA = ctypes.windll.user32.SendMessageA
|
||||||
|
PostMessage = ctypes.windll.user32.PostMessageW
|
||||||
|
SetActiveWindow = ctypes.windll.user32.SetActiveWindow
|
||||||
|
GetFocus = ctypes.windll.user32.GetFocus
|
||||||
|
SetFocus = ctypes.windll.user32.SetFocus
|
||||||
|
SetForegroundWindow = ctypes.windll.user32.SetForegroundWindow
|
||||||
|
GetForegroundWindow = ctypes.windll.user32.GetForegroundWindow
|
||||||
|
SetWindowLong = ctypes.windll.user32.SetWindowLongW
|
||||||
|
SystemParametersInfo = ctypes.windll.user32.SystemParametersInfoW
|
||||||
|
VirtualAllocEx = ctypes.windll.kernel32.VirtualAllocEx
|
||||||
|
VirtualFreeEx = ctypes.windll.kernel32.VirtualFreeEx
|
||||||
|
VkKeyScan = ctypes.windll.user32.VkKeyScanW
|
||||||
|
WriteProcessMemory = ctypes.windll.kernel32.WriteProcessMemory
|
||||||
|
MenuItemFromPoint = ctypes.windll.user32.MenuItemFromPoint
|
||||||
|
GetActiveWindow = ctypes.windll.user32.GetActiveWindow
|
||||||
|
GetLastActivePopup = ctypes.windll.user32.GetLastActivePopup
|
||||||
|
FindWindow = ctypes.windll.user32.FindWindowW
|
||||||
|
GetTopWindow = ctypes.windll.user32.GetTopWindow
|
||||||
|
|
||||||
|
GetGUIThreadInfo = ctypes.windll.user32.GetGUIThreadInfo
|
||||||
|
ShowOwnedPopups = ctypes.windll.user32.ShowOwnedPopups
|
||||||
|
WindowFromPoint = ctypes.windll.user32.WindowFromPoint
|
||||||
|
GetMenuBarInfo = ctypes.windll.user32.GetMenuBarInfo
|
||||||
|
GetMessage = ctypes.windll.user32.GetMessageW
|
||||||
|
SendMessageTimeout = ctypes.windll.user32.SendMessageTimeoutW
|
||||||
|
|
||||||
|
WideCharToMultiByte = ctypes.windll.kernel32.WideCharToMultiByte
|
||||||
|
GetACP = ctypes.windll.kernel32.GetACP
|
||||||
|
|
||||||
|
|
||||||
|
CreateProcess = ctypes.windll.kernel32.CreateProcessW
|
||||||
|
TerminateProcess = ctypes.windll.kernel32.TerminateProcess
|
||||||
|
ExitProcess = ctypes.windll.kernel32.ExitProcess
|
||||||
|
|
||||||
|
|
||||||
|
WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject
|
||||||
|
WaitForInputIdle = ctypes.windll.user32.WaitForInputIdle
|
||||||
|
|
||||||
|
#@ stdcall(BOOL, 'kernel32', [POINTER(WCHAR), POINTER(WCHAR), c_long, c_long, c_int, c_ulong, c_long, c_long, c_long, c_long])
|
||||||
|
#def CreateProcessW(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10):
|
||||||
|
# # C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 4271
|
||||||
|
# return CreateProcessW._api_(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
|
||||||
|
GetWindowThreadProcessId = ctypes.windll.user32.GetWindowThreadProcessId
|
||||||
|
OpenProcess = ctypes.windll.kernel32.OpenProcess
|
||||||
|
GetModuleFileNameEx = ctypes.windll.psapi.GetModuleFileNameExW
|
612
pywinauto/win32structures.py
Normal file
612
pywinauto/win32structures.py
Normal file
@ -0,0 +1,612 @@
|
|||||||
|
from win32defines import LF_FACESIZE
|
||||||
|
from ctypes import *
|
||||||
|
|
||||||
|
|
||||||
|
#LPTTTOOLINFOW = POINTER(tagTOOLINFOW)
|
||||||
|
#PTOOLINFOW = POINTER(tagTOOLINFOW)
|
||||||
|
BOOL = c_int
|
||||||
|
BYTE = c_ubyte
|
||||||
|
CHAR = c_char
|
||||||
|
DWORD = c_ulong
|
||||||
|
HANDLE = c_void_p
|
||||||
|
HBITMAP = c_long
|
||||||
|
LONG = c_long
|
||||||
|
LPVOID = c_void_p
|
||||||
|
PVOID = c_void_p
|
||||||
|
UINT = c_uint
|
||||||
|
WCHAR = c_wchar
|
||||||
|
WORD = c_ushort
|
||||||
|
|
||||||
|
COLORREF = DWORD
|
||||||
|
HBITMAP = LONG
|
||||||
|
HINSTANCE = LONG
|
||||||
|
HMENU = LONG
|
||||||
|
HTREEITEM = LONG
|
||||||
|
HWND = LONG
|
||||||
|
LPARAM = LONG
|
||||||
|
LPBYTE = POINTER(BYTE)
|
||||||
|
LPWSTR = c_long# POINTER(WCHAR)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class LVCOLUMNW(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 2982
|
||||||
|
('mask', UINT),
|
||||||
|
('fmt', c_int),
|
||||||
|
('cx', c_int),
|
||||||
|
('pszText', c_long), #LPWSTR),
|
||||||
|
('cchTextMax', c_int),
|
||||||
|
('iSubItem', c_int),
|
||||||
|
('iImage', c_int),
|
||||||
|
('iOrder', c_int),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class LVITEMW(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 2679
|
||||||
|
('mask', UINT),
|
||||||
|
('iItem', c_int),
|
||||||
|
('iSubItem', c_int),
|
||||||
|
('state', UINT),
|
||||||
|
('stateMask', UINT),
|
||||||
|
('pszText', c_long), #LPWSTR),
|
||||||
|
('cchTextMax', c_int),
|
||||||
|
('iImage', c_int),
|
||||||
|
('lParam', LPARAM),
|
||||||
|
('iIndent', c_int),
|
||||||
|
]
|
||||||
|
assert sizeof(LVITEMW) == 40, sizeof(LVITEMW)
|
||||||
|
assert alignment(LVITEMW) == 1, alignment(LVITEMW)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TVITEMW(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 3755
|
||||||
|
('mask', UINT),
|
||||||
|
('hItem', HTREEITEM),
|
||||||
|
('state', UINT),
|
||||||
|
('stateMask', UINT),
|
||||||
|
('pszText', c_long), #LPWSTR),
|
||||||
|
('cchTextMax', c_int),
|
||||||
|
('iImage', c_int),
|
||||||
|
('iSelectedImage', c_int),
|
||||||
|
('cChildren', c_int),
|
||||||
|
('lParam', LPARAM),
|
||||||
|
]
|
||||||
|
assert sizeof(TVITEMW) == 40, sizeof(TVITEMW)
|
||||||
|
assert alignment(TVITEMW) == 1, alignment(TVITEMW)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class LOGFONTW(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/wingdi.h 1090
|
||||||
|
('lfHeight', LONG),
|
||||||
|
('lfWidth', LONG),
|
||||||
|
('lfEscapement', LONG),
|
||||||
|
('lfOrientation', LONG),
|
||||||
|
('lfWeight', LONG),
|
||||||
|
('lfItalic', BYTE),
|
||||||
|
('lfUnderline', BYTE),
|
||||||
|
('lfStrikeOut', BYTE),
|
||||||
|
('lfCharSet', BYTE),
|
||||||
|
('lfOutPrecision', BYTE),
|
||||||
|
('lfClipPrecision', BYTE),
|
||||||
|
('lfQuality', BYTE),
|
||||||
|
('lfPitchAndFamily', BYTE),
|
||||||
|
('lfFaceName', WCHAR * LF_FACESIZE),
|
||||||
|
]
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __str__(self):
|
||||||
|
return "('%s' %d)" %(self.lfFaceName, self.lfHeight)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __repr__(self):
|
||||||
|
return "<LOGFONTW '%s' %d>" %(self.lfFaceName, self.lfHeight)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
assert sizeof(LOGFONTW) == 92, sizeof(LOGFONTW)
|
||||||
|
assert alignment(LOGFONTW) == 4, alignment(LOGFONTW)
|
||||||
|
|
||||||
|
|
||||||
|
#====================================================================
|
||||||
|
class RECT(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/windef.h 287
|
||||||
|
('left', LONG),
|
||||||
|
('top', LONG),
|
||||||
|
('right', LONG),
|
||||||
|
('bottom', LONG),
|
||||||
|
]
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __init__(self, otherRect_or_left = None, top = None, right = None, bottom = None):
|
||||||
|
if type(otherRect_or_left) == type(self):
|
||||||
|
self.left = otherRect_or_left.left
|
||||||
|
self.right = otherRect_or_left.right
|
||||||
|
self.top = otherRect_or_left.top
|
||||||
|
self.bottom = otherRect_or_left.bottom
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.left = long(otherRect_or_left)
|
||||||
|
self.right = long(right)
|
||||||
|
self.top = long(top)
|
||||||
|
self.bottom = long(bottom)
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __eq__(self, otherRect):
|
||||||
|
"return true if the two rectangles have the same coordinates"
|
||||||
|
return \
|
||||||
|
self.left == otherRect.left and \
|
||||||
|
self.top == otherRect.top and \
|
||||||
|
self.right == otherRect.right and \
|
||||||
|
self.bottom == otherRect.bottom
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __str__(self):
|
||||||
|
return "(L%d, T%d, R%d, B%d)" %(self.left, self.top, self.right, self.bottom)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __repr__(self):
|
||||||
|
return "<RECT L%d, T%d, R%d, B%d>" %(self.left, self.top, self.right, self.bottom)
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __sub__(self, other):
|
||||||
|
"Return a new rectangle which is this rect offset from the one passed in"
|
||||||
|
newRect = RECT()
|
||||||
|
|
||||||
|
newRect.left = self.left - other.left
|
||||||
|
newRect.right = self.right - other.left
|
||||||
|
|
||||||
|
newRect.top = self.top - other.top
|
||||||
|
newRect.bottom = self.bottom - other.top
|
||||||
|
|
||||||
|
return newRect
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def __add__(self, other):
|
||||||
|
newRect = RECT()
|
||||||
|
|
||||||
|
newRect.left = self.left + other.left
|
||||||
|
newRect.right = self.right + other.left
|
||||||
|
|
||||||
|
newRect.top = self.top + other.top
|
||||||
|
newRect.bottom = self.bottom + other.top
|
||||||
|
|
||||||
|
return newRect
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def width(self):
|
||||||
|
return self.right - self.left
|
||||||
|
|
||||||
|
#----------------------------------------------------------------
|
||||||
|
def height(self):
|
||||||
|
return self.bottom - self.top
|
||||||
|
|
||||||
|
#def __hash__(self):
|
||||||
|
# return hash (self.left, self.top, self.right, self.bottom)
|
||||||
|
|
||||||
|
|
||||||
|
assert sizeof(RECT) == 16, sizeof(RECT)
|
||||||
|
assert alignment(RECT) == 4, alignment(RECT)
|
||||||
|
|
||||||
|
|
||||||
|
class TEXTMETRICW(Structure):
|
||||||
|
_pack_ = 2
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/wingdi.h 878
|
||||||
|
('tmHeight', LONG),
|
||||||
|
('tmAscent', LONG),
|
||||||
|
('tmDescent', LONG),
|
||||||
|
('tmInternalLeading', LONG),
|
||||||
|
('tmExternalLeading', LONG),
|
||||||
|
('tmAveCharWidth', LONG),
|
||||||
|
('tmMaxCharWidth', LONG),
|
||||||
|
('tmWeight', LONG),
|
||||||
|
('tmOverhang', LONG),
|
||||||
|
('tmDigitizedAspectX', LONG),
|
||||||
|
('tmDigitizedAspectY', LONG),
|
||||||
|
('tmFirstChar', WCHAR),
|
||||||
|
('tmLastChar', WCHAR),
|
||||||
|
('tmDefaultChar', WCHAR),
|
||||||
|
('tmBreakChar', WCHAR),
|
||||||
|
('tmItalic', BYTE),
|
||||||
|
('tmUnderlined', BYTE),
|
||||||
|
('tmStruckOut', BYTE),
|
||||||
|
('tmPitchAndFamily', BYTE),
|
||||||
|
('tmCharSet', BYTE),
|
||||||
|
]
|
||||||
|
assert sizeof(TEXTMETRICW) == 58, sizeof(TEXTMETRICW)
|
||||||
|
assert alignment(TEXTMETRICW) == 2, alignment(TEXTMETRICW)
|
||||||
|
|
||||||
|
|
||||||
|
class NONCLIENTMETRICSW(Structure):
|
||||||
|
_pack_ = 2
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 8767
|
||||||
|
('cbSize', UINT),
|
||||||
|
('iBorderWidth', c_int),
|
||||||
|
('iScrollWidth', c_int),
|
||||||
|
('iScrollHeight', c_int),
|
||||||
|
('iCaptionWidth', c_int),
|
||||||
|
('iCaptionHeight', c_int),
|
||||||
|
('lfCaptionFont', LOGFONTW),
|
||||||
|
('iSmCaptionWidth', c_int),
|
||||||
|
('iSmCaptionHeight', c_int),
|
||||||
|
('lfSmCaptionFont', LOGFONTW),
|
||||||
|
('iMenuWidth', c_int),
|
||||||
|
('iMenuHeight', c_int),
|
||||||
|
('lfMenuFont', LOGFONTW),
|
||||||
|
('lfStatusFont', LOGFONTW),
|
||||||
|
('lfMessageFont', LOGFONTW),
|
||||||
|
]
|
||||||
|
|
||||||
|
assert sizeof(NONCLIENTMETRICSW) == 500, sizeof(NONCLIENTMETRICSW)
|
||||||
|
assert alignment(NONCLIENTMETRICSW) == 2, alignment(NONCLIENTMETRICSW)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/wingdi.h 1025
|
||||||
|
class LOGBRUSH(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/wingdi.h 1025
|
||||||
|
('lbStyle', UINT),
|
||||||
|
('lbColor', COLORREF),
|
||||||
|
('lbHatch', LONG),
|
||||||
|
]
|
||||||
|
assert sizeof(LOGBRUSH) == 12, sizeof(LOGBRUSH)
|
||||||
|
assert alignment(LOGBRUSH) == 4, alignment(LOGBRUSH)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 5147
|
||||||
|
class MENUITEMINFOW(Structure):
|
||||||
|
_pack_ = 2
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 5147
|
||||||
|
('cbSize', UINT),
|
||||||
|
('fMask', UINT),
|
||||||
|
('fType', UINT),
|
||||||
|
('fState', UINT),
|
||||||
|
('wID', UINT),
|
||||||
|
('hSubMenu', HMENU),
|
||||||
|
('hbmpChecked', HBITMAP),
|
||||||
|
('hbmpUnchecked', HBITMAP),
|
||||||
|
('dwItemData', DWORD),
|
||||||
|
('dwTypeData', c_wchar_p), #LPWSTR),
|
||||||
|
('cch', UINT),
|
||||||
|
]
|
||||||
|
assert sizeof(MENUITEMINFOW) == 44, sizeof(MENUITEMINFOW)
|
||||||
|
assert alignment(MENUITEMINFOW) == 2, alignment(MENUITEMINFOW)
|
||||||
|
|
||||||
|
|
||||||
|
class POINT(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/windef.h 307
|
||||||
|
('x', LONG),
|
||||||
|
('y', LONG),
|
||||||
|
]
|
||||||
|
assert sizeof(POINT) == 8, sizeof(POINT)
|
||||||
|
assert alignment(POINT) == 4, alignment(POINT)
|
||||||
|
|
||||||
|
class MENUBARINFO(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
('cbSize', DWORD),
|
||||||
|
('rcBar', RECT), # rect of bar, popup, item
|
||||||
|
('hMenu', HMENU), # real menu handle of bar, popup
|
||||||
|
('hwndMenu', HWND), # hwnd of item submenu if one
|
||||||
|
('fBarFocused', BOOL, 1), # bar, popup has the focus
|
||||||
|
('fFocused', BOOL, 1), # item has the focus
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
UINT = c_uint
|
||||||
|
WPARAM = UINT
|
||||||
|
LONG = c_long
|
||||||
|
LPARAM = LONG
|
||||||
|
DWORD = c_ulong
|
||||||
|
|
||||||
|
class MSG(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 1226
|
||||||
|
('hwnd', HWND),
|
||||||
|
('message', UINT),
|
||||||
|
('wParam', WPARAM),
|
||||||
|
('lParam', LPARAM),
|
||||||
|
('time', DWORD),
|
||||||
|
('pt', POINT),
|
||||||
|
]
|
||||||
|
|
||||||
|
assert sizeof(MSG) == 28, sizeof(MSG)
|
||||||
|
assert alignment(MSG) == 4, alignment(MSG)
|
||||||
|
|
||||||
|
class POINT(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/windef.h 307
|
||||||
|
('x', LONG),
|
||||||
|
('y', LONG),
|
||||||
|
]
|
||||||
|
assert sizeof(POINT) == 8, sizeof(POINT)
|
||||||
|
assert alignment(POINT) == 4, alignment(POINT)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 1865
|
||||||
|
class TOOLINFOW(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 1865
|
||||||
|
('cbSize', UINT),
|
||||||
|
('uFlags', UINT),
|
||||||
|
('hwnd', HWND),
|
||||||
|
('uId', UINT),
|
||||||
|
('rect', RECT),
|
||||||
|
('hinst', HINSTANCE),
|
||||||
|
('lpszText', c_long),#LPWSTR),
|
||||||
|
('lParam', LPARAM),
|
||||||
|
]
|
||||||
|
assert sizeof(TOOLINFOW) == 44, sizeof(TOOLINFOW)
|
||||||
|
assert alignment(TOOLINFOW) == 1, alignment(TOOLINFOW)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 2225
|
||||||
|
class NMHDR(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 2225
|
||||||
|
('hwndFrom', HWND),
|
||||||
|
('idFrom', UINT),
|
||||||
|
('code', UINT),
|
||||||
|
]
|
||||||
|
assert sizeof(NMHDR) == 12, sizeof(NMHDR)
|
||||||
|
assert alignment(NMHDR) == 4, alignment(NMHDR)
|
||||||
|
|
||||||
|
|
||||||
|
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 2068
|
||||||
|
class NMTTDISPINFOW(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 2068
|
||||||
|
('hdr', NMHDR),
|
||||||
|
('lpszText', LPWSTR),
|
||||||
|
('szText', WCHAR * 80),
|
||||||
|
('hinst', HINSTANCE),
|
||||||
|
('uFlags', UINT),
|
||||||
|
('lParam', LPARAM),
|
||||||
|
]
|
||||||
|
|
||||||
|
assert sizeof(NMTTDISPINFOW) == 188, sizeof(NMTTDISPINFOW)
|
||||||
|
assert alignment(NMTTDISPINFOW) == 1, alignment(NMTTDISPINFOW)
|
||||||
|
|
||||||
|
|
||||||
|
NMTTDISPINFOW_V1_SIZE = 184 # Variable c_uint
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
HDITEMW_V1_SIZE = 28 # Variable c_uint
|
||||||
|
|
||||||
|
class HDITEMW(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 617
|
||||||
|
('mask', UINT),
|
||||||
|
('cxy', c_int),
|
||||||
|
('pszText', c_long),#LPWSTR),
|
||||||
|
('hbm', HBITMAP),
|
||||||
|
('cchTextMax', c_int),
|
||||||
|
('fmt', c_int),
|
||||||
|
('lParam', LPARAM),
|
||||||
|
('iImage', c_int),
|
||||||
|
('iOrder', c_int),
|
||||||
|
]
|
||||||
|
assert sizeof(HDITEMW) == 36, sizeof(HDITEMW)
|
||||||
|
assert alignment(HDITEMW) == 1, alignment(HDITEMW)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 4456
|
||||||
|
class COMBOBOXEXITEMW(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 4456
|
||||||
|
('mask', UINT),
|
||||||
|
('iItem', c_int),
|
||||||
|
('pszText', c_long),#LPWSTR),
|
||||||
|
('cchTextMax', c_int),
|
||||||
|
('iImage', c_int),
|
||||||
|
('iSelectedImage', c_int),
|
||||||
|
('iOverlay', c_int),
|
||||||
|
('iIndent', c_int),
|
||||||
|
('lParam', LPARAM),
|
||||||
|
]
|
||||||
|
assert sizeof(COMBOBOXEXITEMW) == 36, sizeof(COMBOBOXEXITEMW)
|
||||||
|
assert alignment(COMBOBOXEXITEMW) == 1, alignment(COMBOBOXEXITEMW)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 4757
|
||||||
|
class TCITEMHEADERW(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 4757
|
||||||
|
('mask', UINT),
|
||||||
|
('lpReserved1', UINT),
|
||||||
|
('lpReserved2', UINT),
|
||||||
|
('pszText', LPWSTR),
|
||||||
|
('cchTextMax', c_int),
|
||||||
|
('iImage', c_int),
|
||||||
|
]
|
||||||
|
|
||||||
|
assert sizeof(TCITEMHEADERW) == 24, sizeof(TCITEMHEADERW)
|
||||||
|
assert alignment(TCITEMHEADERW) == 1, alignment(TCITEMHEADERW)
|
||||||
|
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 4804
|
||||||
|
class TCITEMW(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 4804
|
||||||
|
('mask', UINT),
|
||||||
|
('dwState', DWORD),
|
||||||
|
('dwStateMask', DWORD),
|
||||||
|
('pszText', c_long), #LPWSTR),
|
||||||
|
('cchTextMax', c_int),
|
||||||
|
('iImage', c_int),
|
||||||
|
('lParam', LPARAM),
|
||||||
|
]
|
||||||
|
assert sizeof(TCITEMW) == 28, sizeof(TCITEMW)
|
||||||
|
assert alignment(TCITEMW) == 1, alignment(TCITEMW)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 1308
|
||||||
|
class TBBUTTONINFOW(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 1308
|
||||||
|
('cbSize', UINT),
|
||||||
|
('dwMask', DWORD),
|
||||||
|
('idCommand', c_int),
|
||||||
|
('iImage', c_int),
|
||||||
|
('fsState', BYTE),
|
||||||
|
('fsStyle', BYTE),
|
||||||
|
('cx', WORD),
|
||||||
|
('lParam', DWORD),
|
||||||
|
('pszText', LPWSTR),
|
||||||
|
('cchText', c_int),
|
||||||
|
]
|
||||||
|
assert sizeof(TBBUTTONINFOW) == 32, sizeof(TBBUTTONINFOW)
|
||||||
|
assert alignment(TBBUTTONINFOW) == 1, alignment(TBBUTTONINFOW)
|
||||||
|
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 953
|
||||||
|
class TBBUTTON(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 953
|
||||||
|
('iBitmap', c_int),
|
||||||
|
('idCommand', c_int),
|
||||||
|
('fsState', BYTE),
|
||||||
|
('fsStyle', BYTE),
|
||||||
|
('bReserved', BYTE * 2),
|
||||||
|
('dwData', DWORD),
|
||||||
|
('iString', c_int),
|
||||||
|
]
|
||||||
|
assert sizeof(TBBUTTON) == 20, sizeof(TBBUTTON)
|
||||||
|
assert alignment(TBBUTTON) == 1, alignment(TBBUTTON)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class REBARBANDINFOW(Structure):
|
||||||
|
_pack_ = 1
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 1636
|
||||||
|
('cbSize', UINT),
|
||||||
|
('fMask', UINT),
|
||||||
|
('fStyle', UINT),
|
||||||
|
('clrFore', COLORREF),
|
||||||
|
('clrBack', COLORREF),
|
||||||
|
('lpText', LPWSTR),
|
||||||
|
('cch', UINT),
|
||||||
|
('iImage', c_int),
|
||||||
|
('hwndChild', HWND),
|
||||||
|
('cxMinChild', UINT),
|
||||||
|
('cyMinChild', UINT),
|
||||||
|
('cx', UINT),
|
||||||
|
('hbmBack', HBITMAP),
|
||||||
|
('wID', UINT),
|
||||||
|
('cyChild', UINT),
|
||||||
|
('cyMaxChild', UINT),
|
||||||
|
('cyIntegral', UINT),
|
||||||
|
('cxIdeal', UINT),
|
||||||
|
('lParam', LPARAM),
|
||||||
|
('cxHeader', UINT),
|
||||||
|
]
|
||||||
|
assert sizeof(REBARBANDINFOW) == 80, sizeof(REBARBANDINFOW)
|
||||||
|
assert alignment(REBARBANDINFOW) == 1, alignment(REBARBANDINFOW)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 223
|
||||||
|
class SECURITY_ATTRIBUTES(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 223
|
||||||
|
('nLength', DWORD),
|
||||||
|
('lpSecurityDescriptor', LPVOID),
|
||||||
|
('bInheritHandle', BOOL),
|
||||||
|
]
|
||||||
|
assert sizeof(SECURITY_ATTRIBUTES) == 12, sizeof(SECURITY_ATTRIBUTES)
|
||||||
|
assert alignment(SECURITY_ATTRIBUTES) == 4, alignment(SECURITY_ATTRIBUTES)
|
||||||
|
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 3794
|
||||||
|
class STARTUPINFOW(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 3794
|
||||||
|
('cb', DWORD),
|
||||||
|
('lpReserved', LPWSTR),
|
||||||
|
('lpDesktop', LPWSTR),
|
||||||
|
('lpTitle', LPWSTR),
|
||||||
|
('dwX', DWORD),
|
||||||
|
('dwY', DWORD),
|
||||||
|
('dwXSize', DWORD),
|
||||||
|
('dwYSize', DWORD),
|
||||||
|
('dwXCountChars', DWORD),
|
||||||
|
('dwYCountChars', DWORD),
|
||||||
|
('dwFillAttribute', DWORD),
|
||||||
|
('dwFlags', DWORD),
|
||||||
|
('wShowWindow', WORD),
|
||||||
|
('cbReserved2', WORD),
|
||||||
|
('lpReserved2', LPBYTE),
|
||||||
|
('hStdInput', HANDLE),
|
||||||
|
('hStdOutput', HANDLE),
|
||||||
|
('hStdError', HANDLE),
|
||||||
|
]
|
||||||
|
assert sizeof(STARTUPINFOW) == 68, sizeof(STARTUPINFOW)
|
||||||
|
assert alignment(STARTUPINFOW) == 4, alignment(STARTUPINFOW)
|
||||||
|
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 229
|
||||||
|
class PROCESS_INFORMATION(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 229
|
||||||
|
('hProcess', HANDLE),
|
||||||
|
('hThread', HANDLE),
|
||||||
|
('dwProcessId', DWORD),
|
||||||
|
('dwThreadId', DWORD),
|
||||||
|
]
|
||||||
|
assert sizeof(PROCESS_INFORMATION) == 16, sizeof(PROCESS_INFORMATION)
|
||||||
|
assert alignment(PROCESS_INFORMATION) == 4, alignment(PROCESS_INFORMATION)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
121
sandbox/_DrawDialog_Wx.py
Normal file
121
sandbox/_DrawDialog_Wx.py
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
## import all of the wxPython GUI package
|
||||||
|
from wxPython.wx import *
|
||||||
|
|
||||||
|
import PyDlgCheckerWrapper
|
||||||
|
|
||||||
|
import sys
|
||||||
|
if len(sys.argv) < 2 :
|
||||||
|
print "Please specify the XML file to read"
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
## Create a new frame class, derived from the wxPython Frame.
|
||||||
|
class MyFrame(wxFrame):
|
||||||
|
|
||||||
|
def __init__(self, parent, id, title):
|
||||||
|
# First, call the base class' __init__ method to create the frame
|
||||||
|
|
||||||
|
|
||||||
|
PyDlgCheckerWrapper.InitDialogFromFile(sys.argv[1])
|
||||||
|
dlg = PyDlgCheckerWrapper.TestInfo['Dialog']
|
||||||
|
|
||||||
|
wxFrame.__init__(self,
|
||||||
|
parent,
|
||||||
|
id,
|
||||||
|
dlg.Title,
|
||||||
|
wxPoint(100, 100),
|
||||||
|
wxSize(dlg.Rectangle.right, dlg.Rectangle.bottom))
|
||||||
|
|
||||||
|
# Add a panel and some controls to display the size and position
|
||||||
|
panel = wxPanel(self, -1)
|
||||||
|
|
||||||
|
|
||||||
|
classes = {
|
||||||
|
"Static": wxStaticText,
|
||||||
|
"Button": wxButton,
|
||||||
|
"CheckBox": wxCheckBox,
|
||||||
|
"RadioButton": wxRadioButton,
|
||||||
|
"Dialog": None,
|
||||||
|
"#32770": None,
|
||||||
|
"SysTabControl32": None,
|
||||||
|
|
||||||
|
# "GroupBox": None,
|
||||||
|
"GroupBox": wxRadioBox,
|
||||||
|
|
||||||
|
# "Static": wxStaticText,
|
||||||
|
# "Static": wxStaticText,
|
||||||
|
# "Static": wxStaticText,
|
||||||
|
|
||||||
|
}
|
||||||
|
for ctrl in dlg.AllControls()[1:]:
|
||||||
|
wx_class_type = classes.get(ctrl.FriendlyClassName, wxStaticText)
|
||||||
|
|
||||||
|
print ctrl.FriendlyClassName, wx_class_type
|
||||||
|
|
||||||
|
if wx_class_type:
|
||||||
|
width = ctrl.Rectangle.right - ctrl.Rectangle.left
|
||||||
|
height = ctrl.Rectangle.bottom - ctrl.Rectangle.top
|
||||||
|
|
||||||
|
if wx_class_type != wxRadioBox:
|
||||||
|
wx_class_type (
|
||||||
|
panel,
|
||||||
|
-1,
|
||||||
|
ctrl.Title,
|
||||||
|
wxPoint(ctrl.Rectangle.left -3, ctrl.Rectangle.top - 23),
|
||||||
|
wxSize(width, height)).Raise()
|
||||||
|
else:
|
||||||
|
|
||||||
|
wx_class_type (
|
||||||
|
panel,
|
||||||
|
-1,
|
||||||
|
ctrl.Title,
|
||||||
|
wxPoint(ctrl.Rectangle.left -3, ctrl.Rectangle.top - 23),
|
||||||
|
wxSize(width, height),
|
||||||
|
['']).Lower()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# wxStaticText(panel, -1, "Size:",
|
||||||
|
# wxDLG_PNT(panel, wxPoint(4, 4)), wxSize(30, 20))
|
||||||
|
#
|
||||||
|
# wxStaticText(panel, -1, "Pos:",
|
||||||
|
# wxDLG_PNT(panel, wxPoint(4, 14)), wxDefaultSize)
|
||||||
|
#
|
||||||
|
# self.sizeCtrl = wxTextCtrl(panel, -1, "",
|
||||||
|
# wxDLG_PNT(panel, wxPoint(24, 4)),
|
||||||
|
# wxDLG_SZE(panel, wxSize(36, -1)),
|
||||||
|
# wxTE_READONLY)
|
||||||
|
# self.posCtrl = wxTextCtrl(panel, -1, "",
|
||||||
|
# wxDLG_PNT(panel, wxPoint(24, 14)),
|
||||||
|
# wxDLG_SZE(panel, wxSize(36, -1)),
|
||||||
|
# wxTE_READONLY)
|
||||||
|
|
||||||
|
|
||||||
|
# This method is called automatically when the CLOSE event is
|
||||||
|
# sent to this window
|
||||||
|
def OnCloseWindow(self, event):
|
||||||
|
# tell the window to kill itself
|
||||||
|
self.Destroy()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Every wxWindows application must have a class derived from wxApp
|
||||||
|
class MyApp(wxApp):
|
||||||
|
|
||||||
|
# wxWindows calls this method to initialize the application
|
||||||
|
def OnInit(self):
|
||||||
|
|
||||||
|
# Create an instance of our customized Frame class
|
||||||
|
frame = MyFrame(NULL, -1, "This is a test")
|
||||||
|
frame.Show(true)
|
||||||
|
|
||||||
|
# Tell wxWindows that this is our main window
|
||||||
|
self.SetTopWindow(frame)
|
||||||
|
|
||||||
|
# Return a success flag
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
app = MyApp(0) # Create an instance of the application class
|
||||||
|
app.MainLoop() # Tell it to start processing events
|
||||||
|
|
405
sandbox/_DrawDialog_win32_1.py
Normal file
405
sandbox/_DrawDialog_win32_1.py
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
## import all of the wxPython GUI package
|
||||||
|
from wxPython.wx import *
|
||||||
|
|
||||||
|
|
||||||
|
from ctypes import *
|
||||||
|
|
||||||
|
|
||||||
|
import PyDlgCheckerWrapper
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
if len(sys.argv) < 2 :
|
||||||
|
print "Please specify the XML file to read"
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
PyDlgCheckerWrapper.InitDialogFromFile(sys.argv[1])
|
||||||
|
dlg = PyDlgCheckerWrapper.TestInfo['Dialog']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
BOOL = c_int
|
||||||
|
BYTE = c_ubyte
|
||||||
|
CHAR = c_char
|
||||||
|
DWORD = c_ulong
|
||||||
|
LONG = c_long
|
||||||
|
LPVOID = c_void_p
|
||||||
|
PVOID = c_void_p
|
||||||
|
UINT = c_uint
|
||||||
|
WCHAR = c_wchar
|
||||||
|
WORD = c_ushort
|
||||||
|
|
||||||
|
COLORREF = DWORD
|
||||||
|
HBITMAP = LONG
|
||||||
|
HINSTANCE = LONG
|
||||||
|
HMENU = LONG
|
||||||
|
HTREEITEM = LONG
|
||||||
|
HWND = LONG
|
||||||
|
LPARAM = LONG
|
||||||
|
LPCWSTR = c_wchar_p
|
||||||
|
LPWSTR = c_wchar_p #POINTER(WCHAR)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SW_ERASE = 4 # Variable c_int
|
||||||
|
SW_FORCEMINIMIZE = 11 # Variable c_int
|
||||||
|
SW_HIDE = 0 # Variable c_int
|
||||||
|
SW_INVALIDATE = 2 # Variable c_int
|
||||||
|
SW_MAX = 11 # Variable c_int
|
||||||
|
SW_MAXIMIZE = 3 # Variable c_int
|
||||||
|
SW_MINIMIZE = 6 # Variable c_int
|
||||||
|
SW_NORMAL = 1 # Variable c_int
|
||||||
|
SW_OTHERUNZOOM = 4 # Variable c_int
|
||||||
|
SW_OTHERZOOM = 2 # Variable c_int
|
||||||
|
SW_PARENTCLOSING = 1 # Variable c_int
|
||||||
|
SW_PARENTOPENING = 3 # Variable c_int
|
||||||
|
SW_RESTORE = 9 # Variable c_int
|
||||||
|
SW_SCROLLCHILDREN = 1 # Variable c_int
|
||||||
|
SW_SHOW = 5 # Variable c_int
|
||||||
|
SW_SHOWDEFAULT = 10 # Variable c_int
|
||||||
|
SW_SHOWMAXIMIZED = 3 # Variable c_int
|
||||||
|
SW_SHOWMINIMIZED = 2 # Variable c_int
|
||||||
|
SW_SHOWMINNOACTIVE = 7 # Variable c_int
|
||||||
|
SW_SHOWNA = 8 # Variable c_int
|
||||||
|
SW_SHOWNOACTIVATE = 4 # Variable c_int
|
||||||
|
SW_SHOWNORMAL = 1 # Variable c_int
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 2186
|
||||||
|
class CREATESTRUCTW(Structure):
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 2186
|
||||||
|
('lpCreateParams', LPVOID),
|
||||||
|
('hInstance', HINSTANCE),
|
||||||
|
('hMenu', HMENU),
|
||||||
|
('hwndParent', HWND),
|
||||||
|
('cy', c_int),
|
||||||
|
('cx', c_int),
|
||||||
|
('y', c_int),
|
||||||
|
('x', c_int),
|
||||||
|
('style', LONG),
|
||||||
|
('lpszName', LPCWSTR),
|
||||||
|
('lpszClass', LPCWSTR),
|
||||||
|
('dwExStyle', DWORD),
|
||||||
|
]
|
||||||
|
assert sizeof(CREATESTRUCTW) == 48, sizeof(CREATESTRUCTW)
|
||||||
|
assert alignment(CREATESTRUCTW) == 4, alignment(CREATESTRUCTW)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 3278
|
||||||
|
class DLGTEMPLATE(Structure):
|
||||||
|
_pack_ = 2
|
||||||
|
_fields_ = [
|
||||||
|
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 3278
|
||||||
|
('style', DWORD),
|
||||||
|
('dwExtendedStyle', DWORD),
|
||||||
|
('cdit', WORD),
|
||||||
|
('x', c_short),
|
||||||
|
('y', c_short),
|
||||||
|
('cx', c_short),
|
||||||
|
('cy', c_short),
|
||||||
|
]
|
||||||
|
assert sizeof(DLGTEMPLATE) == 18, sizeof(DLGTEMPLATE)
|
||||||
|
assert alignment(DLGTEMPLATE) == 2, alignment(DLGTEMPLATE)
|
||||||
|
|
||||||
|
|
||||||
|
def Main():
|
||||||
|
import ctypes
|
||||||
|
CW = ctypes.windll.user32.CreateWindowExW
|
||||||
|
|
||||||
|
# cs = CREATESTRUCTW()
|
||||||
|
# cs.hInstance = 0 # ????
|
||||||
|
# cs.hMenu = 0
|
||||||
|
# cs.hwndParent = 0
|
||||||
|
# cs.cx = 100
|
||||||
|
# cs.cy = 200
|
||||||
|
# cs.x = 10
|
||||||
|
# cs.y = 20
|
||||||
|
# cs.style = 0 #0x80000
|
||||||
|
# cs.lpszName = u"Hi There"
|
||||||
|
# cs.lpszClass = "#32770"
|
||||||
|
# cs.dwExStyle = 0
|
||||||
|
|
||||||
|
|
||||||
|
parent = 0
|
||||||
|
for i, ctrl in enumerate(dlg.AllControls()):
|
||||||
|
|
||||||
|
if i == 0:
|
||||||
|
print "FIRST"
|
||||||
|
#klass = u"#32770"
|
||||||
|
style = ctrl.Style()
|
||||||
|
else:
|
||||||
|
klass = ctrl.Class.upper()
|
||||||
|
style = ctrl.Style()
|
||||||
|
#print klass
|
||||||
|
#style = 0
|
||||||
|
|
||||||
|
klass = ctrl.Class.upper()
|
||||||
|
|
||||||
|
if parent and ctrl.Class == "#32770":
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
handle = CW(
|
||||||
|
ctrl.ExStyle(), # dwExStyle
|
||||||
|
klass, # class
|
||||||
|
ctrl.Title,# titles
|
||||||
|
style , #ctrl.Style(), # style
|
||||||
|
ctrl.Rectangle.left, # x
|
||||||
|
ctrl.Rectangle.top, # y
|
||||||
|
ctrl.Rectangle.right - ctrl.Rectangle.left, # cx
|
||||||
|
ctrl.Rectangle.bottom - ctrl.Rectangle.top, # cy
|
||||||
|
parent, # parent
|
||||||
|
0, # menu
|
||||||
|
ctypes.windll.kernel32.GetModuleHandleW(u"user32.dll"), # hInstance ???
|
||||||
|
0, # lparam
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
import time
|
||||||
|
time.sleep(.3)
|
||||||
|
if not parent:
|
||||||
|
parent = handle
|
||||||
|
print handle,
|
||||||
|
x = (c_wchar * 200)()
|
||||||
|
ctypes.windll.user32.GetClassNameW(handle, byref(x) ,100)
|
||||||
|
print x.value
|
||||||
|
style = ctypes.windll.user32.GetWindowLongW(handle, -16)
|
||||||
|
ctypes.windll.user32.EnableWindow(handle, 1)
|
||||||
|
|
||||||
|
if style != ctrl.Style():
|
||||||
|
print "FAILED"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
ctypes.windll.user32.ShowWindow(handle, SW_SHOW)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# edit = CW(
|
||||||
|
# 0, # dwExStyle
|
||||||
|
# u"BUTTON", # class
|
||||||
|
# u"YO There",# titles
|
||||||
|
# 0, # style
|
||||||
|
# 30, # x
|
||||||
|
# 40, # y
|
||||||
|
# 40, # cx
|
||||||
|
# 20, # cy
|
||||||
|
# handle, # parent
|
||||||
|
# 0, # menu
|
||||||
|
# 0, # hInstance ???
|
||||||
|
# 0, # lparam
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# print edit
|
||||||
|
#
|
||||||
|
# ctypes.windll.user32.ShowWindow(edit, SW_SHOW)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__=='__main__':
|
||||||
|
Main()
|
||||||
|
import time
|
||||||
|
time.sleep(3)
|
||||||
|
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##class DLGTEMPLATEEX(Structure):
|
||||||
|
### _pack_ = 1
|
||||||
|
## _fields_ = [
|
||||||
|
## ('dlgVer', WORD),
|
||||||
|
## ('signature', WORD),
|
||||||
|
## ('helpID', DWORD),
|
||||||
|
## ('exStyle', DWORD),
|
||||||
|
## ('style', DWORD),
|
||||||
|
## ('cDlgItems', WORD), #LPWSTR),
|
||||||
|
## ('x', c_short),
|
||||||
|
## ('y', c_short),
|
||||||
|
## ('cx', c_short),
|
||||||
|
## ('cy', c_short),
|
||||||
|
## ('menu', c_void_p),
|
||||||
|
## ('windowClass', c_void_p), #LPWSTR),
|
||||||
|
## ('title', c_wchar_p),
|
||||||
|
## ('pointsize', WORD),
|
||||||
|
## ('weight', WORD),
|
||||||
|
## ('italic', BYTE),
|
||||||
|
## ('charset', BYTE),
|
||||||
|
## ('typeface', c_wchar_p),
|
||||||
|
## ]
|
||||||
|
##
|
||||||
|
## def __init__(self, dlg):
|
||||||
|
## self.dlgVer = 1
|
||||||
|
## self.signature = 0xFFFF
|
||||||
|
##
|
||||||
|
## #self.helpID = dlg.helpID
|
||||||
|
## #self.exStyle = dlg.helpID
|
||||||
|
## #self.style = dlg.helpID
|
||||||
|
## #self.cdlgItems = dlg.helpID
|
||||||
|
## self.x = 10
|
||||||
|
## self.y = 10
|
||||||
|
## self.cx = 50
|
||||||
|
## self.cy = 50
|
||||||
|
## #self.menu = xx
|
||||||
|
## self.windowClass = "#32770"
|
||||||
|
## #self.title = xx
|
||||||
|
## #self.pointsize = xx
|
||||||
|
## #self.weight = xx
|
||||||
|
## #self.italic = xx
|
||||||
|
## #self.charset = xx
|
||||||
|
## #self.typeface = xx
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##class DLGITEMTEMPLATEEX(Structure):
|
||||||
|
### _pack_ = 1
|
||||||
|
## _fields_ = [
|
||||||
|
## ('helpID', DWORD),
|
||||||
|
## ('exStyle', DWORD),
|
||||||
|
## ('style', DWORD),
|
||||||
|
## ('x', c_short),
|
||||||
|
## ('y', c_short),
|
||||||
|
## ('cx', c_short),
|
||||||
|
## ('cy', c_short),
|
||||||
|
## ('id', WORD),
|
||||||
|
## ('windowClass', c_void_p),
|
||||||
|
## ('title', c_void_p),
|
||||||
|
## ('extraCount', WORD),
|
||||||
|
## ]
|
||||||
|
##
|
||||||
|
## def __init__(self, ctrl):
|
||||||
|
## self.helpID = 1
|
||||||
|
## self.exStyle = 2
|
||||||
|
## self.style = 3
|
||||||
|
## self.x = 10
|
||||||
|
## self.y = 20
|
||||||
|
## self.cx = 6
|
||||||
|
## self.cy = 8
|
||||||
|
## self.id = ctrl.ControlID
|
||||||
|
## self.windowClass = 2134
|
||||||
|
## self.title = u"1234567678"
|
||||||
|
## self.extraCount = 0
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##
|
||||||
|
#### Create a new frame class, derived from the wxPython Frame.
|
||||||
|
##class MyFrame(wxFrame):
|
||||||
|
##
|
||||||
|
## def __init__(self, parent, id, title):
|
||||||
|
## # First, call the base class' __init__ method to create the frame
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## wxFrame.__init__(self,
|
||||||
|
## parent,
|
||||||
|
## id,
|
||||||
|
## dlg.Title,
|
||||||
|
## wxPoint(100, 100),
|
||||||
|
## wxSize(dlg.Rectangle.right, dlg.Rectangle.bottom))
|
||||||
|
##
|
||||||
|
## # Add a panel and some controls to display the size and position
|
||||||
|
## panel = wxPanel(self, -1)
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## classes = {
|
||||||
|
## "Static": wxStaticText,
|
||||||
|
## "Button": wxButton,
|
||||||
|
## "CheckBox": wxCheckBox,
|
||||||
|
## "RadioButton": wxRadioButton,
|
||||||
|
## "Dialog": None,
|
||||||
|
## "#32770": None,
|
||||||
|
## "SysTabControl32": None,
|
||||||
|
##
|
||||||
|
### "GroupBox": None,
|
||||||
|
## "GroupBox": wxRadioBox,
|
||||||
|
##
|
||||||
|
### "Static": wxStaticText,
|
||||||
|
### "Static": wxStaticText,
|
||||||
|
### "Static": wxStaticText,
|
||||||
|
##
|
||||||
|
## }
|
||||||
|
## for ctrl in dlg.AllControls()[1:]:
|
||||||
|
## wx_class_type = classes.get(ctrl.FriendlyClassName, wxStaticText)
|
||||||
|
##
|
||||||
|
## print ctrl.FriendlyClassName, wx_class_type
|
||||||
|
##
|
||||||
|
## if wx_class_type:
|
||||||
|
## width = ctrl.Rectangle.right - ctrl.Rectangle.left
|
||||||
|
## height = ctrl.Rectangle.bottom - ctrl.Rectangle.top
|
||||||
|
##
|
||||||
|
## if wx_class_type != wxRadioBox:
|
||||||
|
## wx_class_type (
|
||||||
|
## panel,
|
||||||
|
## -1,
|
||||||
|
## ctrl.Title,
|
||||||
|
## wxPoint(ctrl.Rectangle.left -3, ctrl.Rectangle.top - 23),
|
||||||
|
## wxSize(width, height)).Raise()
|
||||||
|
## else:
|
||||||
|
##
|
||||||
|
## wx_class_type (
|
||||||
|
## panel,
|
||||||
|
## -1,
|
||||||
|
## ctrl.Title,
|
||||||
|
## wxPoint(ctrl.Rectangle.left -3, ctrl.Rectangle.top - 23),
|
||||||
|
## wxSize(width, height),
|
||||||
|
## ['']).Lower()
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##
|
||||||
|
### wxStaticText(panel, -1, "Size:",
|
||||||
|
### wxDLG_PNT(panel, wxPoint(4, 4)), wxSize(30, 20))
|
||||||
|
###
|
||||||
|
### wxStaticText(panel, -1, "Pos:",
|
||||||
|
### wxDLG_PNT(panel, wxPoint(4, 14)), wxDefaultSize)
|
||||||
|
###
|
||||||
|
### self.sizeCtrl = wxTextCtrl(panel, -1, "",
|
||||||
|
### wxDLG_PNT(panel, wxPoint(24, 4)),
|
||||||
|
### wxDLG_SZE(panel, wxSize(36, -1)),
|
||||||
|
### wxTE_READONLY)
|
||||||
|
### self.posCtrl = wxTextCtrl(panel, -1, "",
|
||||||
|
### wxDLG_PNT(panel, wxPoint(24, 14)),
|
||||||
|
### wxDLG_SZE(panel, wxSize(36, -1)),
|
||||||
|
### wxTE_READONLY)
|
||||||
|
##
|
||||||
|
##
|
||||||
|
## # This method is called automatically when the CLOSE event is
|
||||||
|
## # sent to this window
|
||||||
|
## def OnCloseWindow(self, event):
|
||||||
|
## # tell the window to kill itself
|
||||||
|
## self.Destroy()
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##
|
||||||
|
### Every wxWindows application must have a class derived from wxApp
|
||||||
|
##class MyApp(wxApp):
|
||||||
|
##
|
||||||
|
## # wxWindows calls this method to initialize the application
|
||||||
|
## def OnInit(self):
|
||||||
|
##
|
||||||
|
## # Create an instance of our customized Frame class
|
||||||
|
## frame = MyFrame(NULL, -1, "This is a test")
|
||||||
|
## frame.Show(true)
|
||||||
|
##
|
||||||
|
## # Tell wxWindows that this is our main window
|
||||||
|
## self.SetTopWindow(frame)
|
||||||
|
##
|
||||||
|
## # Return a success flag
|
||||||
|
## return true
|
||||||
|
##
|
||||||
|
##
|
||||||
|
##app = MyApp(0) # Create an instance of the application class
|
||||||
|
##app.MainLoop() # Tell it to start processing events
|
||||||
|
|
1574
sandbox/__DeadCodeRepository__.py
Normal file
1574
sandbox/__DeadCodeRepository__.py
Normal file
File diff suppressed because it is too large
Load Diff
34
sandbox/_find_control_distance.py
Normal file
34
sandbox/_find_control_distance.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
from application import FindWindows
|
||||||
|
win = FindWindows(title = "Replace", class_name = "#32770")[0]
|
||||||
|
|
||||||
|
from findbestmatch import find_best_match
|
||||||
|
|
||||||
|
|
||||||
|
visibleTextChildren = [w for w in win.Children if w.IsVisible and w.Text]
|
||||||
|
|
||||||
|
visibleNonTextChildren = [w for w in win.Children if w.IsVisible and not w.Text]
|
||||||
|
|
||||||
|
for w2 in visibleNonTextChildren:
|
||||||
|
closest = 999
|
||||||
|
newname = ''
|
||||||
|
for text_child in visibleTextChildren:
|
||||||
|
|
||||||
|
# skip controls where w is to the right of w2
|
||||||
|
if text_child.Rectangle.left >= w2.Rectangle.right:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# skip controls where w is below w2
|
||||||
|
if text_child.Rectangle.top >= w2.Rectangle.bottom:
|
||||||
|
continue
|
||||||
|
|
||||||
|
wr = text_child.Rectangle
|
||||||
|
w2r = w2.Rectangle
|
||||||
|
distance = ((wr.left - w2r.left) ** 2.0 + (wr.top - w2r.top) ** 2.0) ** .5
|
||||||
|
|
||||||
|
|
||||||
|
if distance < closest:
|
||||||
|
closest = distance
|
||||||
|
newname = text_child.Text.replace(' ', '').replace ('&', '') + w2.FriendlyClassName
|
||||||
|
|
||||||
|
if closest != 999:
|
||||||
|
print newname
|
295
sandbox/playing_RepeatedHotkeyFixing.py
Normal file
295
sandbox/playing_RepeatedHotkeyFixing.py
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
#import set
|
||||||
|
from Test_RepeatedHotkey import GetHotkey
|
||||||
|
|
||||||
|
controls = (
|
||||||
|
"&Hello",
|
||||||
|
"&Exit",
|
||||||
|
"&OK",
|
||||||
|
"&Python",
|
||||||
|
"&Ion Trail",
|
||||||
|
"&Galaxy Quest",
|
||||||
|
"&Xenon",
|
||||||
|
"&Sierra",
|
||||||
|
"&Zope",
|
||||||
|
"&Sizzling",
|
||||||
|
"Here &and Now",
|
||||||
|
"&Later maybe",
|
||||||
|
"&Scram",
|
||||||
|
"Wo&w",
|
||||||
|
"What is T&HAT",
|
||||||
|
"&Mercury",
|
||||||
|
"&Venus",
|
||||||
|
"&Earth",
|
||||||
|
"M&ercury",
|
||||||
|
"Ven&us",
|
||||||
|
"Ea&rth",
|
||||||
|
"&Mercury",
|
||||||
|
"&OK",
|
||||||
|
"&Python",
|
||||||
|
"&Ion Trail",
|
||||||
|
"&Galaxy Quest",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
charIndex = {}
|
||||||
|
hotkeyCtrls = {}
|
||||||
|
allChars = set()
|
||||||
|
for ctrl in controls:
|
||||||
|
# try to build up information on the dialog first
|
||||||
|
# e.g. if it's possible to fix at all
|
||||||
|
# any controls that have a unique character
|
||||||
|
|
||||||
|
hotkeyCtrls.setdefault(GetHotkey(ctrl)[1].lower(), []).append(ctrl)
|
||||||
|
allChars = allChars.union(set(ctrl.lower()))
|
||||||
|
|
||||||
|
for c in ctrl:
|
||||||
|
if c in ' _&': continue
|
||||||
|
|
||||||
|
charIndex.setdefault(c.lower(), []).append(ctrl)
|
||||||
|
|
||||||
|
|
||||||
|
allChars.difference_update(" _&")
|
||||||
|
|
||||||
|
freeChars = allChars.difference(hotkeyCtrls.keys())
|
||||||
|
print freeChars
|
||||||
|
|
||||||
|
|
||||||
|
for c in hotkeyCtrls:
|
||||||
|
print c
|
||||||
|
for ctrl in hotkeyCtrls[c]:
|
||||||
|
print "\t", ctrl
|
||||||
|
|
||||||
|
if len(allChars) < len(controls):
|
||||||
|
print "impossible to fix completely because there are more hotkeys then individual characters in the dialog"
|
||||||
|
print "the following characters are free:"
|
||||||
|
|
||||||
|
#for c in freeChars:
|
||||||
|
# print "\t%s"% c
|
||||||
|
# for ctrl in charIndex[c]:
|
||||||
|
# print "\t\t%s" % ctrl
|
||||||
|
|
||||||
|
usedChars = hotkeyCtrls.keys()
|
||||||
|
|
||||||
|
changesMade = 1
|
||||||
|
while changesMade:
|
||||||
|
changesMade = 0
|
||||||
|
|
||||||
|
for char, ctrls in charIndex.items():
|
||||||
|
|
||||||
|
# if there is only one control that has this character
|
||||||
|
if len (ctrls) == 1:
|
||||||
|
# get the control
|
||||||
|
ctrl = ctrls[0]
|
||||||
|
|
||||||
|
# find the hotkey for that control
|
||||||
|
ctrlHotkey = GetHotkey(ctrl)[1].lower()
|
||||||
|
|
||||||
|
print ctrlHotkey, `ctrl`
|
||||||
|
# remove the control from the list
|
||||||
|
hotkeyCtrls[ctrlHotkey].remove(ctrl)
|
||||||
|
|
||||||
|
# if there are now now controls for that hotkey
|
||||||
|
# remove it
|
||||||
|
if len(hotkeyCtrls[ctrlHotkey]) == 0:
|
||||||
|
del(hotkeyCtrls[ctrlHotkey])
|
||||||
|
|
||||||
|
# add the new changed one to the hotkeys
|
||||||
|
hotkeyCtrls.setdefault(char, []).append(ctrl)
|
||||||
|
changesMade = 1
|
||||||
|
else:
|
||||||
|
|
||||||
|
|
||||||
|
for hotkey, ctrls in hotkeyCtrls.items():
|
||||||
|
if len(ctrls) > 1:
|
||||||
|
for ctrl in ctrls:
|
||||||
|
ctrlChars = set(ctrl.lower()).difference(" &_")
|
||||||
|
if freeChars.intersection(ctrlChars):
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
print "="*100
|
||||||
|
|
||||||
|
for c in hotkeyCtrls:
|
||||||
|
print c
|
||||||
|
for ctrl in hotkeyCtrls[c]:
|
||||||
|
print "\t", ctrl
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#for x in charIndex:
|
||||||
|
# print x, charIndex[x]
|
||||||
|
|
||||||
|
for hotkey, ctrls in hotkeyCtrls.items():
|
||||||
|
if len(ctrls) > 1:
|
||||||
|
print "***** BUG *****"
|
||||||
|
print "\t", hotkey, ctrls
|
||||||
|
|
||||||
|
# find the chars free for each control
|
||||||
|
ctrlFree = []
|
||||||
|
for ctrl in ctrls:
|
||||||
|
ctrlFree.append("".join(set(ctrl.lower()).intersection(freeChars)))
|
||||||
|
|
||||||
|
# count the controls with no hotkey free
|
||||||
|
countNotFree = len([c for c in ctrlFree if not c])
|
||||||
|
if countNotFree > 1:
|
||||||
|
print "Cannot be fixed without possibly changing other controls also"
|
||||||
|
|
||||||
|
for i, free in enumerate(ctrlFree):
|
||||||
|
if not free:
|
||||||
|
print "Must leave '%s' alone" %ctrls[i]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
sys.exit()
|
||||||
|
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
#================================================
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# return the controls that have characters in common with
|
||||||
|
# the control
|
||||||
|
def GetCtrlsWithSameChars(ctrl, controls):
|
||||||
|
ourChars = set(ctrl.lower())
|
||||||
|
ourChars.difference_update("& _")
|
||||||
|
|
||||||
|
toRet = []
|
||||||
|
for control in controls:
|
||||||
|
ctrlChars = set(control.lower())
|
||||||
|
|
||||||
|
if ourChars.intersection(ctrlChars):
|
||||||
|
toRet.append(control)
|
||||||
|
|
||||||
|
return toRet
|
||||||
|
|
||||||
|
|
||||||
|
def GetFreeCharsForControls(allFree, controls):
|
||||||
|
ctrlFree = []
|
||||||
|
allCtrlsFree = []
|
||||||
|
for ctrl in controls:
|
||||||
|
curCtrlFree = set(ctrl.lower()).intersection(allFree)
|
||||||
|
ctrlFree.append("".join(curCtrlFree))
|
||||||
|
|
||||||
|
allCtrlsFree.extend(curCtrlFree)
|
||||||
|
|
||||||
|
return ctrlFree, "".join(allCtrlsFree)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
charIndex = {}
|
||||||
|
hotkeyCtrls = {}
|
||||||
|
for c in controls:
|
||||||
|
hotkeyCtrls.setdefault(GetHotkey(c)[1].lower(), []).append(c)
|
||||||
|
|
||||||
|
for char in c.lower():
|
||||||
|
charIndex.setdefault(char, []).append(c)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
hotkeys = set(hotkeyCtrls.keys())
|
||||||
|
allKeys = set("".join(controls).lower())
|
||||||
|
allKeys.difference_update("& _")
|
||||||
|
|
||||||
|
freeKeys = allKeys.difference(hotkeys)
|
||||||
|
|
||||||
|
print len(controls)
|
||||||
|
if len(controls) > len(allKeys):
|
||||||
|
print "**** Oops - more hotkeys than available characters :-( "
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for hotkey, ctrls in hotkeyCtrls.items():
|
||||||
|
if len(ctrls) > 1:
|
||||||
|
print "**bug**"
|
||||||
|
|
||||||
|
# can it be fixed simply by changing one or more of the controls
|
||||||
|
# to another character within these controls
|
||||||
|
|
||||||
|
ctrlsFreeChars, allFree = GetFreeCharsForControls(freeKeys, ctrls)
|
||||||
|
|
||||||
|
# find out if we can use this method (0 or 1 controls with no free characters)
|
||||||
|
noFreeCount = 0
|
||||||
|
for i, ctrl in enumerate(ctrls):
|
||||||
|
if not ctrlsFreeChars[i]:
|
||||||
|
noFreeCount += 1
|
||||||
|
|
||||||
|
# ok - so more than one control has no free chars - can't use the
|
||||||
|
# simple method
|
||||||
|
if noFreeCount > 1:
|
||||||
|
print "cant use that method"
|
||||||
|
continue
|
||||||
|
|
||||||
|
if noFreeCount == 0:
|
||||||
|
extraText = ' or leave the same'
|
||||||
|
else:
|
||||||
|
extraText = ''
|
||||||
|
|
||||||
|
for i, ctrl in enumerate(ctrls):
|
||||||
|
if len(ctrlsFreeChars[i]) > 1:
|
||||||
|
print "Change '%s' to one of (%s)%s"% (ctrl, "".join(ctrlsFreeChars[i]), extraText)
|
||||||
|
elif len(ctrlsFreeChars[i]) == 1:
|
||||||
|
print "Change '%s' to %s%s"% (ctrl, "".join(ctrlsFreeChars[i]), extraText)
|
||||||
|
else:
|
||||||
|
print "do not change %s" % ctrl
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# for curCtrl in ctrls:
|
||||||
|
# # Get the letters that could be used
|
||||||
|
# ctrlAvl = set(curCtrl.lower()).intersection(freeKeys)
|
||||||
|
#
|
||||||
|
# #changesNeeded = ''
|
||||||
|
#
|
||||||
|
# # if there are no free letters in that control
|
||||||
|
# # try and find if any other control could have it's
|
||||||
|
# # hotkey changed to free up for ourself
|
||||||
|
# if len(ctrlAvl) == 0:
|
||||||
|
#
|
||||||
|
# # get the controls that share some letters
|
||||||
|
# otherCtrls = GetCtrlsWithSameChars(c, controls)
|
||||||
|
#
|
||||||
|
# suggestedChanges = []
|
||||||
|
# # check if any of the letters in those controls can be freed up
|
||||||
|
# for otherCtrl in otherCtrls:
|
||||||
|
# if GetHotkey(otherCtrl)[1].lower() == hotkey:
|
||||||
|
# freeOther = set(otherCtrl.lower()).intersection(freeKeys)
|
||||||
|
#
|
||||||
|
# if freeOther:
|
||||||
|
# print "To Fix %s Free %s in %s by changing to any of (%s)"%(curCtrl, GetHotkey(otherCtrl)[1], otherCtrl, "".join(freeOther))
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
## posChange = set(c.lower()).intersection(allKeys)
|
||||||
|
## changesNeeded = "".join(posChange)
|
||||||
|
##
|
||||||
|
## for char in posChange:
|
||||||
|
## # get the controls that have that character
|
||||||
|
## otherCtrls = charIndex[char]
|
||||||
|
##
|
||||||
|
# else:
|
||||||
|
# print "To fix %s change %s to any of (%s)"% (curCtrl, c, "".join(ctrlAvl))
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
#print "bug", ctrls, "".join(freeKeys)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user