made a copy

This commit is contained in:
markm 2006-01-04 21:21:00 +00:00
parent 0210e5352c
commit 75c90bc145
41 changed files with 22298 additions and 0 deletions

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

@ -0,0 +1,5 @@
from HwndWrapper import WrapHandle, GetDialogPropsFromHandle
import win32_controls
import common_controls

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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

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

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

File diff suppressed because it is too large Load Diff

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

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