made a copy
This commit is contained in:
parent
0210e5352c
commit
75c90bc145
36
MakeBackup.bat
Normal file
36
MakeBackup.bat
Normal file
@ -0,0 +1,36 @@
|
||||
@echo off
|
||||
if (%1)==() goto VersionNotGiven
|
||||
|
||||
md previousReleases\%1
|
||||
|
||||
if not exist previousReleases\%1 goto FolderNotCreated
|
||||
|
||||
md previousReleases\%1\DlgCheck2
|
||||
if not exist previousReleases\%1 goto FolderTwoNotCreated
|
||||
|
||||
copy *.py previousReleases\%1
|
||||
copy dlgCheck2 previousReleases\%1\DlgCheck2
|
||||
|
||||
|
||||
goto finished
|
||||
|
||||
:FolderNotCreated
|
||||
echo.
|
||||
echo Could not create the folder "previousReleases\%1"
|
||||
echo.
|
||||
goto finished
|
||||
|
||||
:FolderTwoNotCreated
|
||||
echo.
|
||||
echo Could not create the folder "previousReleases\%1\DlgCheck2"
|
||||
echo.
|
||||
goto finished
|
||||
|
||||
|
||||
VersionNotGiven
|
||||
echo.
|
||||
echo please specify the version of the backup
|
||||
echo.
|
||||
goto finished
|
||||
|
||||
:finished
|
19
Readme.txt
Normal file
19
Readme.txt
Normal file
@ -0,0 +1,19 @@
|
||||
Unzip the Zip file
|
||||
|
||||
Copy (though you could move also) Python24.dll, dlgchecks2.dll and move PythonConfig.py to the folder that contains your executable (Silktest directory).
|
||||
Modify PythonConfig.py to point to where you have unzipped the other files (this folder should contain all the *.py files and the Python24.zip file.
|
||||
|
||||
If your machine does not have the appropriate Visual Studio 7 DLLs you will also need these in the executable directory. (Included in the Zip in RequiredDLLs).
|
||||
|
||||
|
||||
|
||||
|
||||
There is also a little sample FindDialog.exe application (it does not use the DLL - but the underlying Python libraries). What it does is perform most of the tests (it doesn't do AllControls or AsianHotkeyTests) and highlights controls with errors with a red rectangle.
|
||||
|
||||
Mostly you need to run this dialog with a regular expresion as the dialog title e.g.
|
||||
FindDialog "^Explorer" (the ^ is to make sure you don't match the Command prompt window you are in!)
|
||||
|
||||
But you could use other regular expressions also e.g. - the first windows with 'Document' in the title,
|
||||
".*[D]ocument.*" (again the [ and ] are to make sure that the current command prompt window is not found!)
|
||||
|
||||
Note that this file needs Python24.dll - so if you have moved it out - this program will not work!.
|
0
basic/controls/controlactions.py
Normal file
0
basic/controls/controlactions.py
Normal file
89
doc_src/dev_notes.txt
Normal file
89
doc_src/dev_notes.txt
Normal file
@ -0,0 +1,89 @@
|
||||
====================
|
||||
ATTRIBUTE RESOLUTION
|
||||
====================
|
||||
|
||||
TWO LEVELS
|
||||
* application.member (Python resolves)
|
||||
an attribute of application object
|
||||
* application.dialog
|
||||
a dialog reference
|
||||
|
||||
THREE LEVELS
|
||||
* application.member.attr (Python resolves)
|
||||
another attribute of the previous member
|
||||
* application.dialog.member
|
||||
a member of the dialog object
|
||||
* application.dialog.control
|
||||
a control on the dialog
|
||||
|
||||
FOUR LEVELS (leaving out Python resolved)
|
||||
* application.dialog.member.member
|
||||
* application.dialog.control.member
|
||||
|
||||
|
||||
DELAYED RESOLUTION FOR SUCCESS
|
||||
Taking the example
|
||||
app.dlg.control.action()
|
||||
If we leave out syntax and programming errors there are still a number of reasons why it could fail.
|
||||
|
||||
dlg might not be found
|
||||
control might not be found
|
||||
either dlg or control may be disabled
|
||||
|
||||
dialog and control may be found but on the wrong dialog (e.g. in Notepad you can bring up 2 "Page Setup" dialogs both with an OK button)
|
||||
|
||||
One solution would just be to add a "sleep" before trying to find each new dialog (to ensure that it is there and ready) - but this will mean lots of unnecessary waiting.
|
||||
|
||||
So the solution I have tried is:
|
||||
- perform the complete attribute access resolution at the latest possible time
|
||||
- if it fails then wait and try again
|
||||
- after a specified timeout fail raising the original exception.
|
||||
|
||||
This means that in the normal case you don't have unnecessary waits - and in the failure case - you still get an exception with the error.
|
||||
|
||||
Also waiting to do resolution as late as possible stops errors where an earlier part of the path succeedes - but finds the wrong item.
|
||||
|
||||
So for example if finds the page setup dialog in Notepad
|
||||
# open the Printer setup dialog (which has "Page Setup" as title)
|
||||
app.PageSetup.Printer.Click()
|
||||
|
||||
# if this runs too quickly it actually finds the current page setup dialog
|
||||
# before the next dialog opens, but that dialog does not have a Properties
|
||||
# button - so an error is raised.
|
||||
# because we re-run the resolution from the start we find the new pagesetup dialog.
|
||||
app.PageSetup.Properties.Click()
|
||||
|
||||
|
||||
|
||||
==================
|
||||
WRITING TO DIALOGS
|
||||
==================
|
||||
We need a way of making sure that the dialog is active without having to access a control on it.
|
||||
e.g.
|
||||
app.MainWin.MenuSelect("Something That->Loads a Dialog")
|
||||
app.Dlg._write("dlg.xml")
|
||||
|
||||
or a harder problem:
|
||||
app.PageSetup.Printer.Click()
|
||||
app.PageSetup._write("pagesetup.xml")
|
||||
|
||||
In this second example it is very hard to be sure that the correct Page Setup dialog is shown.
|
||||
|
||||
The only way to be realy sure is to check for the existance of certain control(s) (ID, Class, text, whatever) - but it would be nice to not have to deal with those :-(
|
||||
|
||||
Another less declarative (more magic?) is to scan the list of available windows/controls and if they haven't changed then accept that the correct one is shown.
|
||||
|
||||
When testing and having XML files then we should use those to make sure that we have the correct dialog up (by using Class/ID)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
175
pywinauto/DemiTest.py
Normal file
175
pywinauto/DemiTest.py
Normal file
@ -0,0 +1,175 @@
|
||||
"""
|
||||
First of all, thanks Thomas for ctypes..
|
||||
|
||||
Hacked a bit into Henks Venster dialog class, using a different
|
||||
c-source as basis. see -> Microsoft Knowledge Base Article - 141201.
|
||||
|
||||
http://support.microsoft.com/default.aspx?scid=kb;en-us;141201
|
||||
|
||||
Out came a bit more bare bone dialog class. Though I don"t
|
||||
intend to start a win32 dialog discussion here I thought this source
|
||||
could come in quite handy.
|
||||
|
||||
|
||||
J\\xfcrgen
|
||||
|
||||
* had to reformat it tabs to spaces on the fly, using notepad,
|
||||
so I hope theres no error in there.
|
||||
|
||||
"""
|
||||
|
||||
from ctypes import windll, WINFUNCTYPE, byref, c_int as BOOL, \
|
||||
c_ulong as HWND, c_uint as UINT, c_long as LPARAM, \
|
||||
c_ushort as WORD
|
||||
|
||||
WPARAM = UINT
|
||||
|
||||
def LOWORD(dword): return dword & 0x0000ffff
|
||||
def HIWORD(dword): return dword >> 16
|
||||
|
||||
|
||||
DIALOGPROC = WINFUNCTYPE(BOOL, HWND, UINT, WPARAM, LPARAM)
|
||||
|
||||
|
||||
_user32 = windll.user32
|
||||
|
||||
WM_CLOSE = 16
|
||||
WS_CHILD = 1073741824
|
||||
WS_VISIBLE = 268435456
|
||||
#**************************************************
|
||||
|
||||
#**************************************************
|
||||
class dialog(object):
|
||||
|
||||
def __init__(self, title, exstyle, style, x, y, w, h):
|
||||
self._items = 0
|
||||
self._id = 0
|
||||
|
||||
p = [
|
||||
1, # dlgversion
|
||||
0xFFFF, # signature
|
||||
0, # LOWORD(helpId)
|
||||
0, # HIWORD(helpId)
|
||||
LOWORD(exstyle), # extendedstyle
|
||||
HIWORD(exstyle), # ...
|
||||
LOWORD(style), # style
|
||||
HIWORD(style), # ...
|
||||
0, # number of items
|
||||
x, # x
|
||||
y, # x
|
||||
w, # w
|
||||
h, # h
|
||||
0, # menu
|
||||
0, # class
|
||||
]
|
||||
|
||||
title = map(ord, unicode(title)) + [0]
|
||||
p += title
|
||||
if len(p) % 2:
|
||||
p.append(0)
|
||||
|
||||
#p.append(18)
|
||||
#p.append(700)
|
||||
#p.append(11)
|
||||
|
||||
|
||||
|
||||
self.p = p
|
||||
|
||||
def _dlgitem(self, classname, title, exstyle, style, x, y, w, h):
|
||||
self._items += 1
|
||||
self._id += 1
|
||||
|
||||
style |= WS_CHILD | WS_VISIBLE
|
||||
p = [
|
||||
0, # LOWORD(helpId)
|
||||
0, # HIWORD(helpId)
|
||||
LOWORD(exstyle), # extendedstyle
|
||||
HIWORD(exstyle), # ...
|
||||
LOWORD(style), # style
|
||||
HIWORD(style), # ...
|
||||
x, # x
|
||||
y, # y
|
||||
w, # w
|
||||
h, # h
|
||||
LOWORD(self._id), # controlId
|
||||
HIWORD(self._id) # ...
|
||||
]
|
||||
|
||||
classname = map(ord, unicode(classname)) + [0]
|
||||
p += classname
|
||||
title = map(ord, unicode(title)) + [0]
|
||||
p += title
|
||||
|
||||
if len(p) % 2:
|
||||
p.append(0)
|
||||
else:
|
||||
p += [0, 0]
|
||||
self.p += p
|
||||
|
||||
|
||||
def run_modal(self, hwnd=0):
|
||||
self.p[8] = self._items
|
||||
|
||||
self._p_template = (WORD * len(self.p))(*self.p)
|
||||
self._p_dialogproc = DIALOGPROC(self.dialogproc)
|
||||
|
||||
result = _user32.DialogBoxIndirectParamA(
|
||||
0, byref(self._p_template), hwnd, self._p_dialogproc, 0
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
def dialogproc(self, hwnd, message, wparam, lparam):
|
||||
if message==WM_CLOSE:
|
||||
_user32.EndDialog(hwnd, 0)
|
||||
return 0
|
||||
|
||||
|
||||
#************************************************
|
||||
#d = dialog("dialog sample", 0, 0, 30, 30, 160, 100)
|
||||
#d._dlgitem("button", "button1", 0, 0, 5, 5, 30, 20)
|
||||
#d._dlgitem("button", "button2", 0, 0, 5, 30, 30, 20)
|
||||
#d._dlgitem("edit", "edit box", 0, 0, 40, 0, 50, 60)
|
||||
#d._dlgitem("combobox", "",0, 0, 100, 0, 50, 65)
|
||||
#
|
||||
|
||||
import sys
|
||||
if len(sys.argv) < 2 :
|
||||
print "Please specify the XML file to read"
|
||||
sys.exit()
|
||||
|
||||
import PyDlgCheckerWrapper
|
||||
PyDlgCheckerWrapper.InitDialogFromFile(sys.argv[1])
|
||||
dlg = PyDlgCheckerWrapper.TestInfo['Dialog']
|
||||
|
||||
d = None
|
||||
|
||||
for i, ctrl in enumerate(dlg.AllControls()):
|
||||
print i
|
||||
if i == 0:
|
||||
d = dialog(
|
||||
ctrl.Text,
|
||||
0,#ctrl.ExStyle,
|
||||
0,#ctrl.Style,
|
||||
ctrl.Rectangle.left, # x
|
||||
ctrl.Rectangle.top, # y
|
||||
ctrl.Rectangle.right - ctrl.Rectangle.left, # cx
|
||||
ctrl.Rectangle.bottom - ctrl.Rectangle.top, # cy
|
||||
)
|
||||
|
||||
else:
|
||||
d._dlgitem(
|
||||
ctrl.Class,
|
||||
ctrl.Text,
|
||||
ctrl.ExStyle,
|
||||
ctrl.Style,
|
||||
ctrl.Rectangle.left, # x
|
||||
ctrl.Rectangle.top, # y
|
||||
ctrl.Rectangle.right - ctrl.Rectangle.left, # cx
|
||||
ctrl.Rectangle.bottom - ctrl.Rectangle.top, # cy
|
||||
)
|
||||
|
||||
print d
|
||||
d.run_modal()
|
||||
|
356
pywinauto/DrawDialogFromXML.py
Normal file
356
pywinauto/DrawDialogFromXML.py
Normal file
@ -0,0 +1,356 @@
|
||||
r"""Converts XML files captured by DLGChecks to a close aproximation
|
||||
of the original dialog
|
||||
|
||||
Usage:
|
||||
DrawDialogFromXML.py (XML filepaths or directories containing XML files)
|
||||
|
||||
e.g.
|
||||
DrawDialogFromXML.py jpn c:\temp PropertiesDialog.xml
|
||||
|
||||
|
||||
You can specify more then one file or more than one directory
|
||||
Wildcard characters are not currently handled (?*).
|
||||
|
||||
Note that there will be differences between dialogs drawn this way
|
||||
and the original dialog:
|
||||
- Anything drawn directly to the dialog will not be drawn (usually
|
||||
something done in the OnPaint handler of the dialog.
|
||||
- OwnerDrawn controls will not be handled 100% correctly,
|
||||
but as close as possible
|
||||
- Images, Bitmaps and Icons are currently not drawn
|
||||
"""
|
||||
|
||||
import pywin.mfc.dialog
|
||||
import XMLHelpers
|
||||
import win32defines
|
||||
import win32functions
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
# ignore the warning in the pywin/mfc/dialog module line number 32
|
||||
import warnings
|
||||
warnings.filterwarnings("ignore", module = ".*dialog", lineno = 32)
|
||||
|
||||
# some constants for fixing Dialog units
|
||||
rectSizerX = .675 #.675
|
||||
rectSizerY = .62 #.62
|
||||
titleBarHeight = 10
|
||||
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
def Main():
|
||||
|
||||
# check that the parameters are correct
|
||||
if len(sys.argv) < 2:
|
||||
print __doc__
|
||||
print "*** Please specify the language and the " \
|
||||
"XML files or folders with XML files to read"
|
||||
sys.exit()
|
||||
|
||||
xmlFiles = []
|
||||
|
||||
# get the full list of XML files to try
|
||||
for arg in sys.argv[1:]:
|
||||
# if the current argument is a directory
|
||||
|
||||
if os.path.isdir(arg):
|
||||
|
||||
# for all the files in this directory
|
||||
for root, dirs, files in os.walk(arg):
|
||||
for f in files:
|
||||
|
||||
# if it is an XML file
|
||||
if os.path.splitext(f)[1].lower() == ".xml":
|
||||
# add it to our list
|
||||
xmlFiles.append(os.path.join(root, f))
|
||||
|
||||
# if the argument is a file then just append it
|
||||
elif os.path.isfile(arg):
|
||||
xmlFiles.append(arg)
|
||||
|
||||
|
||||
for xmlFile in xmlFiles:
|
||||
print xmlFile
|
||||
|
||||
try:
|
||||
# try and parse the XML file
|
||||
# could easily fail if structure of XML is not exactly what
|
||||
# we are expecting - for example if it is an XML file not
|
||||
# containing Dialog Information or from a later version of
|
||||
# XML dialog
|
||||
ctrls = ParseXML(xmlFile)
|
||||
|
||||
# convert the XML information into a dialog template understood by
|
||||
# Win32ui
|
||||
template = MakeOLDDlgTemplate(ctrls)
|
||||
|
||||
#try:
|
||||
# construct the dialog
|
||||
dlg = XmlDialog(template)
|
||||
#except DeprecationWarning, e:
|
||||
# pass
|
||||
|
||||
# give the dialog the original control information
|
||||
dlg.DlgControls = ctrls
|
||||
|
||||
|
||||
# show the dialog
|
||||
ret = dlg.DoModal()
|
||||
if ret == -1:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
except (AttributeError, XMLHelpers.XMLParsingError):
|
||||
print "**Ignored** %s"% xmlFile
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
def ParseXML(filename):
|
||||
"Parse the Old style XML file to a dictionary"
|
||||
|
||||
ctrls = XMLHelpers.ReadPropertiesFromFile(filename)
|
||||
return ctrls
|
||||
|
||||
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
def ScaleToDialogUnitsAndConvert(rect, isDialogControl = False):
|
||||
bottomOffset = 0
|
||||
if isDialogControl:
|
||||
bottomOffset = titleBarHeight
|
||||
|
||||
|
||||
left = int(rect.left * rectSizerX) # x
|
||||
top = int(rect.top * rectSizerY) -titleBarHeight # y
|
||||
right =int((rect.right - rect.left)* rectSizerX) # cx
|
||||
bottom = int(((rect.bottom - rect.top) * rectSizerY) - bottomOffset) # cy
|
||||
|
||||
return (left, top, right, bottom)
|
||||
|
||||
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# For Old XML files - converts from Dictionary to template
|
||||
# ===========================================================================
|
||||
def MakeOLDDlgTemplate(controls):
|
||||
template = []
|
||||
|
||||
#pprint(controls)
|
||||
for i, ctrl in enumerate(controls):
|
||||
|
||||
title = ctrl['Texts'][0].encode('mbcs')
|
||||
|
||||
# if it is the Dialog control
|
||||
if i == 0:
|
||||
|
||||
# remove WS CHILD style if it exists
|
||||
style = ctrl["Style"]
|
||||
if style & win32defines.WS_CHILD == win32defines.WS_CHILD:
|
||||
style = style ^ win32defines.WS_CHILD
|
||||
|
||||
# Start off the template with the dilaog information
|
||||
template = [[
|
||||
str(title),
|
||||
ScaleToDialogUnitsAndConvert(ctrl["Rectangle"], True),
|
||||
style,
|
||||
int(ctrl["ExStyle"]),
|
||||
(8, ctrl["Fonts"][0].lfFaceName, )
|
||||
#(ctrl["Font").lfHeight, ctrl["Font").lfFaceName, )
|
||||
|
||||
], ]
|
||||
|
||||
#For each of the other controls
|
||||
else:
|
||||
|
||||
# do not create ComboLBoxes and Edit controls that are just part of
|
||||
# ComboBox controls - they will be created automatically by the ComboBox
|
||||
# control itself
|
||||
if ctrl["Class"] == "ComboLBox" and int(ctrl["ControlID"]) == 1000 or \
|
||||
ctrl["Class"] == "Edit" and int(ctrl["ControlID"]) == 1001:
|
||||
continue
|
||||
|
||||
|
||||
if not ctrl['IsVisible']:
|
||||
continue
|
||||
|
||||
|
||||
# remove the OwnderDraw style from ComboBoxes
|
||||
style = int(ctrl["Style"])
|
||||
if ctrl["Class"] == "ComboBox" and style & 0x10:
|
||||
style = style ^ 0x10
|
||||
|
||||
# change controls we don't know how to deal with by
|
||||
# converting them to a static and changing the title to contain
|
||||
# the old class name
|
||||
if ctrl["Class"] not in (
|
||||
"#32770",
|
||||
"Button",
|
||||
"ComboBox",
|
||||
"ComboLBox",
|
||||
"Edit",
|
||||
"ListBox",
|
||||
"ScrollBar",
|
||||
"Static",
|
||||
|
||||
"ComboBoxEx32",
|
||||
"msctls_statusbar32",
|
||||
"msctls_updown32",
|
||||
"SysHeader32",
|
||||
"SysListView32",
|
||||
"SysTabControl32",
|
||||
"SysTreeView32",
|
||||
"ToolbarWindow32",
|
||||
#"SSDemoParent",
|
||||
#"GraphCtl", # test
|
||||
#"CHECKLIST_ACLUI", # test
|
||||
#"rctrl_renwnd32",
|
||||
#"RichEdit20W",
|
||||
):
|
||||
|
||||
title = u"Was previously: " + ctrl["Class"]
|
||||
ctrl["Class"] = "Static"
|
||||
#continue
|
||||
|
||||
# don't bother doing dialogs or Tab Controls
|
||||
if ctrl["Class"] in ("#32770", "SysTabControl32"):
|
||||
continue
|
||||
|
||||
# ensure that for drop down combo boxes that the size of the drop down is enough to
|
||||
# clearly see many items
|
||||
if ctrl["Class"] in ("ComboBox",) and \
|
||||
(ctrl['Style'] & win32defines.CBS_DROPDOWNLIST == win32defines.CBS_DROPDOWNLIST or \
|
||||
ctrl['Style'] & win32defines.CBS_DROPDOWN == win32defines.CBS_DROPDOWN):
|
||||
#not ctrl['Style'] & win32defines.CBS_SIMPLE:#& win32defines.CBS_DROPDOWNLIST or ctrl['Style'] & win32defines.CBS_DROPDOWN):
|
||||
ctrl["Rectangle"].bottom += 200
|
||||
|
||||
|
||||
item = [
|
||||
str(ctrl["Class"]),
|
||||
str(title),
|
||||
int(ctrl["ControlID"]),
|
||||
ScaleToDialogUnitsAndConvert(ctrl["Rectangle"] - controls[0]['Rectangle']),
|
||||
style,
|
||||
int(ctrl["ExStyle"]),
|
||||
]
|
||||
|
||||
# append the information needed for the template
|
||||
template.append(item)
|
||||
|
||||
#pprint(template)
|
||||
|
||||
return template
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
class XmlDialog (pywin.mfc.dialog.Dialog):
|
||||
def OnInitDialog(self):
|
||||
|
||||
# loop over all the controls in the original dialog
|
||||
for x in self.DlgControls:
|
||||
|
||||
# if it is a combobox
|
||||
if x["Class"] == "ComboBox":
|
||||
|
||||
try:
|
||||
# get the control
|
||||
ctrl = self.GetDlgItem(int(x['ControlID']))
|
||||
|
||||
#ctrl.SetExtendedUI(1)
|
||||
|
||||
# add each of the text items
|
||||
for subTitle in x["Texts"][1:]:
|
||||
ctrl.AddString(subTitle.encode('mbcs'))
|
||||
|
||||
|
||||
# win32functions.SendMessage(
|
||||
# ctrl.GetSafeHwnd(),
|
||||
# win32defines.CB_ADDSTRING,
|
||||
# 0,
|
||||
# subTitle )
|
||||
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
elif x["Class"] == "ListBox":
|
||||
try:
|
||||
ctrl = self.GetDlgItem(int(x['ControlID']))
|
||||
|
||||
for subTitle in x["Texts"][1:]:
|
||||
win32functions.SendMessage(
|
||||
ctrl.GetSafeHwnd(),
|
||||
win32defines.LB_ADDSTRING,
|
||||
0,
|
||||
subTitle.encode('mbcs') )
|
||||
except:
|
||||
pass
|
||||
|
||||
# if x.has_key('Image'):
|
||||
# #print "hasImage"
|
||||
# ctrl = self.GetDlgItem(int(x['ControlID']))
|
||||
# bmp = x['Image']
|
||||
#
|
||||
# from PIL import ImageWin
|
||||
# dib = ImageWin.Dib(bmp)
|
||||
#
|
||||
# hDC = ctrl.GetDC()
|
||||
# #hDC.DrawText("Should have an image", (0, 0, 100, 199), 0)
|
||||
# #print dib
|
||||
#
|
||||
# dib.expose(hDC.GetHandleAttrib())
|
||||
#
|
||||
# #dib.draw(hDC.GetHandleOutput(), (0, 0, 990, 990))
|
||||
|
||||
def OnPaint(self):
|
||||
# loop over all the controls in the original dialog
|
||||
for x in self.DlgControls:
|
||||
|
||||
if x.has_key('Image'):
|
||||
|
||||
try:
|
||||
ctrl = self.GetDlgItem(int(x['ControlID']))
|
||||
|
||||
bmp = x['Image']
|
||||
|
||||
from PIL import ImageWin
|
||||
dib = ImageWin.Dib(bmp)
|
||||
|
||||
hdc, paint_struct = ctrl.BeginPaint()
|
||||
dib.expose(hdc.GetHandleAttrib())
|
||||
|
||||
ctrl.EndPaint(paint_struct)
|
||||
except:
|
||||
pass
|
||||
|
||||
#return True
|
||||
|
||||
|
||||
# dib = ImageWin.Dib (bmp)
|
||||
# scaled_size = tuple ([scale * i for i in bmp.size])
|
||||
# x = (printer_size[0] - scaled_size[0]) / 2
|
||||
# y = (printer_size[1] - scaled_size[1]) / 2
|
||||
# dib.draw (hDC.GetHandleOutput (), (x, y) + scaled_size)
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
if __name__ == "__main__":
|
||||
Main()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
361
pywinauto/FindDialog.py
Normal file
361
pywinauto/FindDialog.py
Normal file
@ -0,0 +1,361 @@
|
||||
# pylint: disable-msg=W0611
|
||||
"""Explanation of script"""
|
||||
from pprint import pprint
|
||||
import re
|
||||
|
||||
from ctypes import *
|
||||
from ctypes.wintypes import *
|
||||
|
||||
from win32functions import *
|
||||
from win32defines import *
|
||||
from win32structures import *
|
||||
import controls
|
||||
|
||||
|
||||
|
||||
__revision__ = "0.0.1"
|
||||
|
||||
|
||||
|
||||
# import all the necessary files that we want to end
|
||||
# up in the Py2exe zip.
|
||||
|
||||
|
||||
import PyDlgCheckerWrapper
|
||||
|
||||
#
|
||||
#def FindWindow(
|
||||
# start_window = None,
|
||||
# match_title = None,
|
||||
# match_class = None,
|
||||
# toplevel_only = False,
|
||||
# recurse_children = True,
|
||||
# this_thread_only = None,
|
||||
# )
|
||||
#
|
||||
# if start_window == None:
|
||||
# start_window = GetDesktopWindow()
|
||||
#
|
||||
# if recurse_children:
|
||||
# # get the 1st child of the start window
|
||||
# win_to_test = GetWindow (start_window, GW_CHILD)
|
||||
#
|
||||
# wrapped = WrapHandle(win_to_test)
|
||||
#
|
||||
# if match_title:
|
||||
# if re.match(match_title, wrapped.Text)
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
|
||||
|
||||
totalWindowCount = 0
|
||||
|
||||
#====================================================================
|
||||
def FindDialog(titleToFind, caseSensitive = False, testClass = None, startWin = None):
|
||||
"""Find a dialog based on the title
|
||||
|
||||
|
||||
Returns the dialog that has a title that matches the regular
|
||||
expression in titleToFind.
|
||||
If caseSensitive == True then it performs a case sensitive match
|
||||
If startWin == None then it starts searching from the desktop window
|
||||
otherwise it searches the child windows of the specified window."""
|
||||
|
||||
if caseSensitive:
|
||||
|
||||
flags = re.IGNORECASE
|
||||
else:
|
||||
flags = 0
|
||||
|
||||
titleRe = re.compile(titleToFind, flags)
|
||||
|
||||
# If the startWin is NULL then we are just starting and we
|
||||
# should start with the Desktop window and look from there
|
||||
if startWin == None:
|
||||
startWin = GetDesktopWindow()
|
||||
|
||||
# get the 1st child of the start window
|
||||
winToTest = GetWindow (startWin, GW_CHILD)
|
||||
|
||||
# Now Iterate through all the children of the startwindow
|
||||
# (ie ALL windows, dialogs, controls ) then if the HWND is a dialog
|
||||
# get the Title and compare it to what we are looking for
|
||||
# it makes a check first to make sure that the window has at
|
||||
# least 1 child window
|
||||
while winToTest:
|
||||
global totalWindowCount
|
||||
totalWindowCount += 1
|
||||
|
||||
# get the Title of the Window and if the Title the same as
|
||||
# what we want if So then return it
|
||||
title = controls.WrapHandle(winToTest).Text
|
||||
|
||||
# Check the title to see if it is the same as the title we
|
||||
# are looking for - if it is then return the handle
|
||||
found = titleRe.search(title)
|
||||
if found:
|
||||
|
||||
if testClass:
|
||||
if testClass == controls.WrapHandle(winToTest).Class:
|
||||
return winToTest
|
||||
else:
|
||||
return winToTest
|
||||
|
||||
|
||||
# Now Check through the children of the present window
|
||||
# this is recursive through all the children of the window
|
||||
# It calls FindDialog with the title and the new window
|
||||
# this will keep going recursively until the window is found
|
||||
# or we reach the end of the children
|
||||
tempWin = FindDialog (titleToFind, caseSensitive, testClass, winToTest)
|
||||
|
||||
if tempWin != None:
|
||||
return tempWin
|
||||
|
||||
# So the last one wasnt it just continue with the next
|
||||
# which will be the next window at the present level
|
||||
winToTest = GetWindow (winToTest, GW_HWNDNEXT)
|
||||
|
||||
# we have gotten to here so we didnt find the window
|
||||
# in the current depth of the tree return NULL to say
|
||||
# that we didnt get it and continue at the previous depth
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def DrawOutline(rect, colour = None, fill = BS_NULL):
|
||||
# create the brush
|
||||
|
||||
colours = {
|
||||
"green" : 0x00ff00,
|
||||
"blue" : 0xff0000,
|
||||
"red" : 0x0000ff,
|
||||
}
|
||||
|
||||
if colour not in colours:
|
||||
colour = 'green'
|
||||
|
||||
colour = colours[colour]
|
||||
|
||||
brush = LOGBRUSH()
|
||||
brush.lbStyle = fill
|
||||
brush.lbHatch = HS_DIAGCROSS
|
||||
|
||||
hBrush = CreateBrushIndirect(byref(brush))
|
||||
|
||||
hPen = CreatePen(PS_SOLID, 2, colour)
|
||||
|
||||
# get the Device Context
|
||||
dc = CreateDC(u"DISPLAY", None, None, None )
|
||||
|
||||
SelectObject(dc, hBrush)
|
||||
SelectObject(dc, hPen)
|
||||
|
||||
Rectangle(dc, rect.left, rect.top, rect.right, rect.bottom)
|
||||
|
||||
# Delete the brush and pen we created
|
||||
DeleteObject(hBrush)
|
||||
DeleteObject(hPen)
|
||||
|
||||
# delete the Display context that we created
|
||||
DeleteDC(dc)
|
||||
|
||||
|
||||
|
||||
#============================================================================
|
||||
def PopulateCommandLineParser():
|
||||
from optparse import OptionParser
|
||||
|
||||
parser = OptionParser()
|
||||
|
||||
parser.add_option("-w", "--window", dest="windowTitle", metavar="WINDOW", #action = "store",
|
||||
help="The window title to find and test")
|
||||
|
||||
parser.add_option("-f", "--xml", dest="xmlFileName",# metavar="FILE",
|
||||
help="save the window information to this XML file")
|
||||
|
||||
|
||||
|
||||
parser.add_option("-r", "--refwin", dest="refWindow", #metavar="FILE",
|
||||
action = "append",
|
||||
help="save the domino report to file")
|
||||
|
||||
|
||||
parser.add_option("-x", "--refxml", dest="refXML", #metavar="USER",
|
||||
help="username to access Domino DTS")
|
||||
|
||||
|
||||
# parser.add_option("-p", "--pwd", dest="password", metavar="PASSWORD",
|
||||
# help="password for user to access Domino DTS")
|
||||
#
|
||||
#
|
||||
# parser.add_option("--opendays", dest="open_days_report", metavar="FILE",
|
||||
# help="get number of days bug is open and"\
|
||||
# " write output to file")
|
||||
#
|
||||
# parser.add_option("--clarify", dest="clarify_report", metavar="FILE",
|
||||
# help="get number of days each bug has been" \
|
||||
# "open and write output to file")
|
||||
#
|
||||
# parser.add_option("--opengraph", dest="opengraph_report", metavar="FILE",
|
||||
# help="write a table that can be used to graph " \
|
||||
# "the historical open/closed bug graph")
|
||||
return parser
|
||||
|
||||
|
||||
#====================================================================
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
import PyDlgCheckerWrapper
|
||||
|
||||
PyDlgCheckerWrapper.InitializeModules(".")
|
||||
|
||||
|
||||
optParser = PopulateCommandLineParser()
|
||||
options, args = optParser.parse_args()
|
||||
|
||||
for arg in sys.argv:
|
||||
if arg.lower().lstrip("/-") in ("?", "h", "help"):
|
||||
optParser.print_help()
|
||||
sys.exit()
|
||||
|
||||
|
||||
|
||||
if not (options.windowTitle or options.xmlFileName):
|
||||
if args:
|
||||
arg = args[0]
|
||||
if arg.lower().endswith(".xml"):
|
||||
options.xmlFileName = arg
|
||||
else:
|
||||
options.windowTitle = arg
|
||||
else:
|
||||
|
||||
optParser.print_help()
|
||||
|
||||
print
|
||||
print "*ERROR* Please specify either Window title or XML FileName"
|
||||
sys.exit()
|
||||
|
||||
if options.windowTitle and options.xmlFileName:
|
||||
optParser.print_help()
|
||||
|
||||
print
|
||||
print "*ERROR* Please specify only one of Window title or XML FileName"
|
||||
sys.exit()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if options.windowTitle:
|
||||
# find the dialog
|
||||
handle = FindDialog ("^" + options.windowTitle.decode("mbcs"))
|
||||
|
||||
if not handle:
|
||||
print "Could not find dialog"
|
||||
sys.exit()
|
||||
|
||||
|
||||
PyDlgCheckerWrapper.InitDialogFromWindow(handle)
|
||||
|
||||
outputXML = PyDlgCheckerWrapper.TestInfo['Controls'][0].Text
|
||||
outputXML = outputXML.replace("\\", '_')
|
||||
outputXML = outputXML.replace("*", '_')
|
||||
outputXML = outputXML.replace("?", '_')
|
||||
outputXML = outputXML.replace(">", '_')
|
||||
outputXML = outputXML.replace("<", '_')
|
||||
outputXML = outputXML.replace("/", '_')
|
||||
outputXML = outputXML.replace("|", '_')
|
||||
outputXML = outputXML.replace("\"", '_')
|
||||
outputXML = outputXML.replace(":", '_')
|
||||
|
||||
|
||||
|
||||
elif options.xmlFileName:
|
||||
|
||||
|
||||
PyDlgCheckerWrapper.InitDialogFromFile(options.xmlFileName)
|
||||
|
||||
outputXML = options.xmlFileName
|
||||
|
||||
print outputXML
|
||||
|
||||
# write out the XML file
|
||||
PyDlgCheckerWrapper.WriteDialogToFile (outputXML + ".xml")
|
||||
|
||||
|
||||
if options.refWindow:
|
||||
handle = FindDialog ("^" + options.refWindow)
|
||||
PyDlgCheckerWrapper.AddReferenceFromWindow(handle)
|
||||
|
||||
elif options.refXML:
|
||||
PyDlgCheckerWrapper.AddReferenceFromFile(options.refXML)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for i in range(0, PyDlgCheckerWrapper.GetRegisteredTestCount()):
|
||||
tst = PyDlgCheckerWrapper.GetRegisteredTestName(i)
|
||||
if tst not in ("AllControls", "AsianHotkey", "Overlapping"):
|
||||
print `tst`
|
||||
PyDlgCheckerWrapper.AddTest(tst)
|
||||
#print tst
|
||||
|
||||
PyDlgCheckerWrapper.RunTests(True)
|
||||
|
||||
|
||||
|
||||
for (ctrls, info, bType, inRef) in PyDlgCheckerWrapper.TestInfo['Bugs']:
|
||||
print "BugType:", bType,
|
||||
|
||||
for i in info:
|
||||
print i, info[i],
|
||||
print
|
||||
|
||||
|
||||
for i, ctrl in enumerate(ctrls):
|
||||
print '\t"%s" "%s" (%d %d %d %d) Vis: %d'% (
|
||||
ctrl.Text,
|
||||
ctrl.FriendlyClassName,
|
||||
ctrl.Rectangle.left,
|
||||
ctrl.Rectangle.top,
|
||||
ctrl.Rectangle.right,
|
||||
ctrl.Rectangle.bottom,
|
||||
ctrl.IsVisible,)
|
||||
|
||||
try:
|
||||
|
||||
dlgRect = PyDlgCheckerWrapper.TestInfo['Controls'][0].handle.Rectangle
|
||||
|
||||
DrawOutline(ctrl.Rectangle + dlgRect, "red")
|
||||
except AttributeError, e:
|
||||
#print e
|
||||
pass
|
||||
|
||||
print
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
71
pywinauto/InfoManager.py
Normal file
71
pywinauto/InfoManager.py
Normal file
@ -0,0 +1,71 @@
|
||||
# get imported by all
|
||||
# each class and test register themselves with this
|
||||
import re
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
class ClassRegistry(object):
|
||||
#-------------------------------------------------------------------------
|
||||
def __init__(self):
|
||||
self.defaultClass = None
|
||||
self.registry = {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
def AddClass(self, className, classObj):
|
||||
self.registry[className] = classObj
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
def GetClass(self, className):
|
||||
# see if any registered class matches the class name passed in
|
||||
for cls in self.registry:
|
||||
if re.match(cls, className):
|
||||
return self.registry[cls]
|
||||
|
||||
# Ok if we got here then none matched so we need to
|
||||
# return the default class if it has been set
|
||||
if self.defaultClass:
|
||||
return self.defaultClass
|
||||
|
||||
else:
|
||||
# oops No default class - raise an exception
|
||||
raise unicode("Nothing registered with name '%s'"% className)
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
def RegisteredClasses(self):
|
||||
return self.registry.keys()
|
||||
|
||||
|
||||
|
||||
# the following functions are so that when the class is imported many times
|
||||
# the variables windowClassRegistry, testFuncRegistry are not re-set
|
||||
#-----------------------------------------------------------------------------
|
||||
def WindowClassRegistry():
|
||||
global windowClassRegistry
|
||||
|
||||
try:
|
||||
return windowClassRegistry
|
||||
except:
|
||||
windowClassRegistry = ClassRegistry()
|
||||
return windowClassRegistry
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def TestFuncRegistry():
|
||||
global testFuncRegistry
|
||||
|
||||
try:
|
||||
return testFuncRegistry
|
||||
except:
|
||||
testFuncRegistry = ClassRegistry()
|
||||
return testFuncRegistry
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def Config():
|
||||
global testConfiguration
|
||||
|
||||
try:
|
||||
return testConfiguration
|
||||
except:
|
||||
testConfiguration = {}
|
||||
return testConfiguration
|
||||
|
407
pywinauto/XMLHelpers.py
Normal file
407
pywinauto/XMLHelpers.py
Normal file
@ -0,0 +1,407 @@
|
||||
# how should we read in the XML file
|
||||
# NOT USING MS Components (requirement on machine)
|
||||
# maybe using built in XML
|
||||
# maybe using elementtree
|
||||
# others?
|
||||
|
||||
import elementtree
|
||||
from elementtree.ElementTree import Element, SubElement, ElementTree
|
||||
import xml.parsers.expat
|
||||
import ctypes
|
||||
import re
|
||||
import PIL.Image
|
||||
|
||||
|
||||
from win32structures import RECT, LOGFONTW
|
||||
|
||||
|
||||
class XMLParsingError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
charReplacements = (
|
||||
("\\\\" , "\\"),
|
||||
("\\t" , "\t"),
|
||||
("\\r" , "\r"),
|
||||
("\\n" , "\n"),
|
||||
("\\x12" , "\x12"),
|
||||
)
|
||||
|
||||
#todo - make the dialog reading function not actually know about the
|
||||
# types of each element (so that we can read the control properties
|
||||
# without having to know each and every element type)
|
||||
# probably need to store info on what type things are.
|
||||
#
|
||||
# This has mostly been done:
|
||||
# - if it is a ctypes struct then there is a __type__ field
|
||||
# which says what kind of stuct it is
|
||||
# - If it is an image then a "_IMG" is appeded to the the element tag
|
||||
# - if it is a long then _LONG is appended to attribute name
|
||||
# everything else is considered a string!
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def SetNodeProps(element, name, value):
|
||||
|
||||
|
||||
# if it is a ctypes structure
|
||||
if isinstance(value, ctypes.Structure):
|
||||
|
||||
# create an element for the structure
|
||||
structElem = SubElement(element, name)
|
||||
clsModule = value.__class__.__module__
|
||||
clsName = value.__class__.__name__
|
||||
structElem.set("__type__", "%s" % clsName)
|
||||
|
||||
# iterate over the fields in the structure
|
||||
for propName in value._fields_:
|
||||
propName = propName[0]
|
||||
itemVal = getattr(value, propName)
|
||||
|
||||
if isinstance(itemVal, (int, long)):
|
||||
propName += "_LONG"
|
||||
itemVal = unicode(itemVal)
|
||||
|
||||
structElem.set(propName, EscapeSpecials(itemVal))
|
||||
|
||||
elif isinstance(value, PIL.Image.Image):
|
||||
try:
|
||||
imageData = value.tostring().encode("bz2").encode("base64")
|
||||
SetNodeProps(
|
||||
element,
|
||||
name + "_IMG",
|
||||
{"mode": value.mode, "size":value.size, "data":imageData})
|
||||
|
||||
# a system error is raised from time to time when we try to grab
|
||||
# the image of a control that has 0 height or width
|
||||
except SystemError, e:
|
||||
pass
|
||||
#print ";;;" * 20
|
||||
#print value.mode, value.size
|
||||
#imageData = ""
|
||||
|
||||
|
||||
elif isinstance(value, (list, tuple)):
|
||||
# add the element to hold the values
|
||||
#listElem = SubElement(element, name)
|
||||
|
||||
# remove the s at the end (if there)
|
||||
#name = name.rstrip('s')
|
||||
|
||||
for i, attrVal in enumerate(value):
|
||||
SetNodeProps(element, "%s_%05d"%(name, i), attrVal)
|
||||
|
||||
elif isinstance(value, dict):
|
||||
dictElem = SubElement(element, name)
|
||||
|
||||
for n, val in value.items():
|
||||
SetNodeProps(dictElem, n, val)
|
||||
|
||||
else:
|
||||
if isinstance(value, (int, long)):
|
||||
name += "_LONG"
|
||||
|
||||
element.set(name, EscapeSpecials(value))
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def WriteDialogToFile(fileName, props):
|
||||
|
||||
# build a tree structure
|
||||
root = Element("DIALOG")
|
||||
root.set("_version_", "2.0")
|
||||
for ctrl in props:
|
||||
ctrlElem = SubElement(root, "CONTROL")
|
||||
for name, value in sorted(ctrl.items()):
|
||||
SetNodeProps(ctrlElem, name, value)
|
||||
|
||||
# wrap it in an ElementTree instance, and save as XML
|
||||
tree = ElementTree(root)
|
||||
tree.write(fileName, encoding="utf-8")
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def EscapeSpecials(inStr):
|
||||
"Ensure that some characters are escaped before writing to XML"
|
||||
|
||||
# make sure input is a unicode string or convert
|
||||
inStr = unicode(inStr)
|
||||
|
||||
# unicode_escape encoding seems to be broken
|
||||
#escaped = escaped.encode("unicode_escape")
|
||||
|
||||
for (replacement, char) in charReplacements:
|
||||
inStr = inStr.replace(char, replacement)
|
||||
|
||||
return inStr
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def UnEscapeSpecials(inStr):
|
||||
"Replace escaped characters with real character"
|
||||
|
||||
|
||||
#bits = inStr.split("\\\\")
|
||||
#for i, bit in enumerate(bits):
|
||||
# for (replacement, char) in charReplacements[1:]:
|
||||
# inStr = inStr.replace(replacement, char)
|
||||
|
||||
inStr = inStr.decode("unicode_escape")
|
||||
|
||||
return unicode(inStr)
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def XMLToStruct(element, structType = None):
|
||||
"""Convert an ElementTree to a ctypes Struct
|
||||
|
||||
If structType is not specified then element['__type__']
|
||||
will be used for the ctypes struct type"""
|
||||
|
||||
# handle if we are passed in an element or a dictionary
|
||||
if isinstance(element, elementtree.ElementTree._ElementInterface):
|
||||
attribs = element.attrib
|
||||
else:
|
||||
attribs = element
|
||||
|
||||
# if the type has not been passed in
|
||||
if not structType:
|
||||
# get the type and create an instance of the type
|
||||
struct = globals()[attribs["__type__"]]()
|
||||
else:
|
||||
# create an instance of the type
|
||||
struct = globals()[structType]()
|
||||
|
||||
# get the attribute and set them upper case
|
||||
structAttribs = dict([(at.upper(), at) for at in dir(struct)])
|
||||
|
||||
# for each of the attributes in the element
|
||||
for propName in attribs:
|
||||
|
||||
# get teh value
|
||||
val = attribs[propName]
|
||||
|
||||
# if the value ends with "_long"
|
||||
if propName.endswith("_LONG"):
|
||||
# get an long attribute out of the value
|
||||
val = long(val)
|
||||
propName = propName[:-5]
|
||||
|
||||
# if the value is a string
|
||||
elif isinstance(val, basestring):
|
||||
# make sure it if Unicode
|
||||
val = unicode(val)
|
||||
|
||||
# now we can have all upper case attribute name
|
||||
# but structure name will not be upper case
|
||||
if propName.upper() in structAttribs:
|
||||
propName = structAttribs[propName.upper()]
|
||||
|
||||
# set the appropriate attribute of the Struct
|
||||
setattr(struct, propName, val)
|
||||
|
||||
# reutrn the struct
|
||||
return struct
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def OLD_XMLToTitles(element):
|
||||
|
||||
# get all the attribute names
|
||||
titleNames = element.keys()
|
||||
|
||||
# sort them to make sure we get them in the right order
|
||||
titleNames.sort()
|
||||
|
||||
# build up the array
|
||||
titles = []
|
||||
for name in titleNames:
|
||||
val = element[name]
|
||||
val = val.replace('\\n', '\n')
|
||||
val = val.replace('\\x12', '\x12')
|
||||
val = val.replace('\\\\', '\\')
|
||||
|
||||
titles.append(unicode(val))
|
||||
|
||||
return titles
|
||||
|
||||
|
||||
#====================================================================
|
||||
def ExtractProperties(properties, propName, propValue):
|
||||
|
||||
# get the base property name and number if it in the form
|
||||
# "PROPNAME_00001" = ('PROPNAME', 1)
|
||||
propName, reqdIndex = SplitNumber(propName)
|
||||
|
||||
# if there is no required index, and the property
|
||||
# was not already set - then just set it
|
||||
|
||||
# if this is an indexed member of a list
|
||||
if reqdIndex == None:
|
||||
# Have we hit a property with this name already
|
||||
if propName in properties:
|
||||
# try to append current value to the property
|
||||
try:
|
||||
properties[propName].append(propValue)
|
||||
|
||||
# if that fails then we need to make sure that
|
||||
# the curruen property is a list and then
|
||||
# append it
|
||||
except:
|
||||
newVal = [properties[propName], propValue]
|
||||
properties[propName] = newVal
|
||||
# No index, no previous property with that name
|
||||
# - just set the property
|
||||
else:
|
||||
properties[propName] = propValue
|
||||
|
||||
# OK - so it HAS an index
|
||||
else:
|
||||
|
||||
# make sure that the property is a list
|
||||
properties.setdefault(propName, [])
|
||||
|
||||
# make sure that the list has enough elements
|
||||
while 1:
|
||||
if len(properties[propName]) <= reqdIndex:
|
||||
properties[propName].append('')
|
||||
else:
|
||||
break
|
||||
|
||||
# put our value in at the right index
|
||||
properties[propName][reqdIndex] = propValue
|
||||
|
||||
|
||||
#====================================================================
|
||||
def GetAttributes(element):
|
||||
properties = {}
|
||||
|
||||
# get all the attributes
|
||||
for attribName, val in element.attrib.items():
|
||||
|
||||
# if it is 'Long' element convert it to an long
|
||||
if attribName.endswith("_LONG"):
|
||||
val = long(val)
|
||||
attribName = attribName[:-5]
|
||||
|
||||
else:
|
||||
# otherwise it is a string - make sure we get it as a unicode string
|
||||
val = UnEscapeSpecials(val)
|
||||
|
||||
ExtractProperties(properties, attribName, val)
|
||||
|
||||
return properties
|
||||
|
||||
|
||||
#====================================================================
|
||||
number = re.compile(r"^(.*)_(\d{5})$")
|
||||
def SplitNumber(propName):
|
||||
found = number.search(propName)
|
||||
|
||||
if not found:
|
||||
return propName, None
|
||||
|
||||
return found.group(1), int(found.group(2))
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def ReadXMLStructure(controlElement):
|
||||
|
||||
# get the attributes for the current element
|
||||
properties = GetAttributes(controlElement)
|
||||
|
||||
for elem in controlElement:
|
||||
# if it is a ctypes structure
|
||||
if "__type__" in elem.attrib:
|
||||
# create a new instance of the correct type
|
||||
|
||||
# grab the data
|
||||
propVal = XMLToStruct(elem)
|
||||
|
||||
elif elem.tag.endswith("_IMG"):
|
||||
elem.tag = elem.tag[:-4]
|
||||
|
||||
# get image Attribs
|
||||
img = GetAttributes(elem)
|
||||
data = img['data'].decode('base64').decode('bz2')
|
||||
propVal = PIL.Image.fromstring(img['mode'], img['size'], data)
|
||||
|
||||
else:
|
||||
propVal = ReadXMLStructure(elem)
|
||||
|
||||
ExtractProperties(properties, elem.tag, propVal)
|
||||
|
||||
return properties
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def ReadPropertiesFromFile(filename):
|
||||
"""Return an list of controls from XML file filename"""
|
||||
# parse the file
|
||||
|
||||
parsed = ElementTree().parse(filename)
|
||||
|
||||
# Return the list that has been stored under 'CONTROL'
|
||||
props = ReadXMLStructure(parsed)['CONTROL']
|
||||
|
||||
# it is an old XML so let's fix it up a little
|
||||
if not parsed.attrib.has_key("_version_"):
|
||||
|
||||
# find each of the control elements
|
||||
for ctrlProp in props:
|
||||
|
||||
ctrlProp['Fonts'] = [XMLToStruct(ctrlProp['FONT'], "LOGFONTW"), ]
|
||||
|
||||
ctrlProp['Rectangle'] = XMLToStruct(ctrlProp["RECTANGLE"], "RECT")
|
||||
|
||||
ctrlProp['ClientRects'] = [XMLToStruct(ctrlProp["CLIENTRECT"], "RECT"),]
|
||||
|
||||
ctrlProp['Texts'] = OLD_XMLToTitles(ctrlProp["TITLES"])
|
||||
|
||||
ctrlProp['Class'] = ctrlProp['CLASS']
|
||||
ctrlProp['ContextHelpID'] = ctrlProp['HELPID']
|
||||
ctrlProp['ControlID'] = ctrlProp['CTRLID']
|
||||
ctrlProp['ExStyle'] = ctrlProp['EXSTYLE']
|
||||
ctrlProp['FriendlyClassName'] = ctrlProp['FRIENDLYCLASS']
|
||||
ctrlProp['IsUnicode'] = ctrlProp['ISUNICODE']
|
||||
ctrlProp['IsVisible'] = ctrlProp['ISVISIBLE']
|
||||
ctrlProp['Style'] = ctrlProp['STYLE']
|
||||
ctrlProp['UserData'] = ctrlProp['USERDATA']
|
||||
|
||||
for propName in [
|
||||
'CLASS',
|
||||
'CLIENTRECT',
|
||||
'CTRLID',
|
||||
'EXSTYLE',
|
||||
'FONT',
|
||||
'FRIENDLYCLASS',
|
||||
'HELPID',
|
||||
'ISUNICODE',
|
||||
'ISVISIBLE',
|
||||
'RECTANGLE',
|
||||
'STYLE',
|
||||
'TITLES',
|
||||
'USERDATA',
|
||||
]:
|
||||
del(ctrlProp[propName])
|
||||
|
||||
return props
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
if __name__ == "__main__":
|
||||
|
||||
import sys
|
||||
|
||||
props = ReadPropertiesFromFile(sys.argv[1])
|
||||
WriteDialogToFile(sys.argv[1] + "__", props)
|
||||
|
||||
import pprint
|
||||
pprint.pprint(props)
|
||||
|
||||
|
1090
pywinauto/application.py
Normal file
1090
pywinauto/application.py
Normal file
File diff suppressed because it is too large
Load Diff
635
pywinauto/controlactions.py
Normal file
635
pywinauto/controlactions.py
Normal file
@ -0,0 +1,635 @@
|
||||
#ControlActions
|
||||
import time
|
||||
|
||||
from win32defines import *
|
||||
import win32functions
|
||||
import SendKeys
|
||||
|
||||
|
||||
delay_after_click = .05
|
||||
|
||||
class ControlNotEnabled(RuntimeError):
|
||||
pass
|
||||
|
||||
class ControlNotVisible(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def verify_actionable(ctrl):
|
||||
verify_enabled(ctrl)
|
||||
verify_visible(ctrl)
|
||||
|
||||
def verify_enabled(ctrl):
|
||||
if not ctrl.FriendlyClassName == "Dialog":
|
||||
if not ctrl.Parent.IsEnabled:
|
||||
raise ControlNotEnabled()
|
||||
|
||||
if not ctrl.IsEnabled:
|
||||
raise ControlNotEnabled()
|
||||
|
||||
def verify_visible(ctrl):
|
||||
if not ctrl.IsVisible or not ctrl.Parent.IsVisible:
|
||||
raise ControlNotVisible()
|
||||
|
||||
|
||||
|
||||
mouse_flags = {
|
||||
"left": MK_LBUTTON,
|
||||
"right": MK_RBUTTON,
|
||||
"middle": MK_MBUTTON,
|
||||
"shift": MK_SHIFT,
|
||||
"control": MK_CONTROL,
|
||||
}
|
||||
|
||||
|
||||
def calc_flags_and_coords(pressed, coords):
|
||||
flags = 0
|
||||
|
||||
for key in pressed.split():
|
||||
flags |= mouse_flags[key.lower()]
|
||||
|
||||
click_point = coords[0] << 16 | coords[1]
|
||||
|
||||
return flags, click_point
|
||||
|
||||
|
||||
def perform_click(ctrl, button = "left", pressed = "", coords = (0, 0), double = False, down = True, up = True):
|
||||
verify_enabled(ctrl)
|
||||
|
||||
msgs = []
|
||||
if not double:
|
||||
if button.lower() == "left":
|
||||
if down:
|
||||
msgs.append(WM_LBUTTONDOWN)
|
||||
if up:
|
||||
msgs.append(WM_LBUTTONUP)
|
||||
|
||||
elif button.lower() == "middle":
|
||||
if down:
|
||||
msgs.append(WM_MBUTTONDOWN)
|
||||
if up:
|
||||
msgs.append(WM_MBUTTONUP)
|
||||
|
||||
elif button.lower() == "right":
|
||||
if down:
|
||||
msgs.append(WM_RBUTTONDOWN)
|
||||
if up:
|
||||
msgs.append(WM_RBUTTONUP)
|
||||
|
||||
else:
|
||||
if button.lower() == "left":
|
||||
msgs = (WM_LBUTTONDOWN, WM_LBUTTONUP, WM_LBUTTONDBLCLK, WM_LBUTTONUP)
|
||||
elif button.lower() == "middle":
|
||||
msgs = (WM_MBUTTONDOWN, WM_MBUTTONUP, WM_MBUTTONDBLCLK, WM_MBUTTONUP)
|
||||
elif button.lower() == "right":
|
||||
msgs = (WM_RBUTTONDOWN, WM_RBUTTONUP, WM_RBUTTONDBLCLK, WM_RBUTTONUP)
|
||||
|
||||
|
||||
flags, click_point = calc_flags_and_coords(pressed, coords)
|
||||
|
||||
for msg in msgs:
|
||||
ctrl.PostMessage(msg, flags, click_point)
|
||||
|
||||
#ctrl.PostMessage(msg, 1, click_point)
|
||||
time.sleep(delay_after_click)
|
||||
|
||||
|
||||
#====================================================================
|
||||
def click_action(ctrl, button = "left", pressed = "", coords = (0, 0), double = False):
|
||||
perform_click(ctrl, button, pressed, coords, double)
|
||||
|
||||
#====================================================================
|
||||
def doubleclick_action(ctrl, button = "left", pressed = "", coords = (0, 0), double = True):
|
||||
perform_click(ctrl, button, pressed, coords, double)
|
||||
|
||||
#====================================================================
|
||||
def rightclick_action(ctrl, button = "right", pressed = "", coords = (0, 0), double = True):
|
||||
perform_click(ctrl, button, pressed, coords, double)
|
||||
|
||||
|
||||
|
||||
def check_button_action(ctrl, select = True):
|
||||
ctrl.SendMessage(BM_SETCHECK, 1)
|
||||
|
||||
def uncheck_button_action(ctrl, select = True):
|
||||
ctrl.SendMessage(BM_SETCHECK, 0)
|
||||
|
||||
|
||||
#====================================================================
|
||||
def press_mouse_action(ctrl, button = "left", pressed = "", coords = (0, 0)):
|
||||
flags, click_point = calc_flags_and_coords(pressed, coords)
|
||||
|
||||
perform_click(ctrl, button, pressed, coords, up = False)
|
||||
|
||||
#====================================================================
|
||||
def release_mouse_action(ctrl, button = "left", pressed = "", coords = (0, 0)):
|
||||
flags, click_point = calc_flags_and_coords(pressed, coords)
|
||||
perform_click(ctrl, button, pressed, coords, down = False)
|
||||
|
||||
#====================================================================
|
||||
def move_mouse_action(ctrl, pressed = "left", coords = (0, 0)):
|
||||
flags, click_point = calc_flags_and_coords(pressed, coords)
|
||||
ctrl.PostMessage(WM_MOUSEMOVE, flags, click_point)
|
||||
|
||||
|
||||
|
||||
def settext_action(ctrl, text, append = False):
|
||||
if append:
|
||||
text = ctrl.Text + text
|
||||
|
||||
text = c_wchar_p(unicode(text))
|
||||
ctrl.PostMessage(WM_SETTEXT, 0, text)
|
||||
|
||||
def typekeys_action(
|
||||
ctrl,
|
||||
keys,
|
||||
pause = 0.05,
|
||||
with_spaces = False,
|
||||
with_tabs = False,
|
||||
with_newlines = False,
|
||||
turn_off_numlock = True):
|
||||
|
||||
verify_enabled(ctrl)
|
||||
|
||||
win32functions.AttachThreadInput(win32functions.GetCurrentThreadId(), ctrl.Process(), 1)
|
||||
win32functions.SetForegroundWindow(ctrl)
|
||||
SendKeys.SendKeys(keys, pause, with_spaces, with_tabs, with_newlines, turn_off_numlock)
|
||||
win32functions.AttachThreadInput(win32functions.GetCurrentThreadId(), ctrl.Process(), 0)
|
||||
|
||||
|
||||
|
||||
def combobox_select(ctrl, item):
|
||||
if isinstance(item, (int, long)):
|
||||
index = item
|
||||
else:
|
||||
index = ctrl.Texts.index(item)
|
||||
|
||||
ctrl.PostMessage(CB_SETCURSEL, index, 0)
|
||||
ctrl.PostMessage(CBN_SELCHANGE)
|
||||
|
||||
|
||||
|
||||
def listbox_select(ctrl, item):
|
||||
if isinstance(item, (int, long)):
|
||||
index = item
|
||||
else:
|
||||
index = ctrl.Texts.index(item)
|
||||
|
||||
ctrl.PostMessage(LB_SETCURSEL, index, 0)
|
||||
ctrl.PostMessage(LBN_SELCHANGE)
|
||||
|
||||
|
||||
|
||||
def set_edit_text(ctrl, text, pos_start = -1, pos_end = -1):
|
||||
set_edit_selection(ctrl, pos_start, pos_end)
|
||||
|
||||
text = c_wchar_p(unicode(text))
|
||||
ctrl.SendMessage(EM_REPLACESEL, True, text)
|
||||
|
||||
|
||||
|
||||
def set_edit_selection(ctrl, start = 0, end = -1):
|
||||
|
||||
# if we have been asked to select a string
|
||||
if isinstance(start, basestring):
|
||||
string_to_select = start
|
||||
#
|
||||
start = ctrl.texts[1].index(string_to_select)
|
||||
end = start + len(string_to_select)
|
||||
|
||||
ctrl.PostMessage(EM_SETSEL, start, end)
|
||||
|
||||
|
||||
|
||||
|
||||
def write_debug_text(ctrl, text):
|
||||
dc = win32functions.CreateDC(u"DISPLAY", None, None, None )
|
||||
|
||||
import ctypes
|
||||
|
||||
if not dc:
|
||||
raise ctypes.WinError()
|
||||
|
||||
rect = ctrl.Rectangle
|
||||
#rect.left = 0
|
||||
#rect.top = 0
|
||||
|
||||
#ret = win32functions.TextOut(dc, rect.left, rect.top, unicode(text), len(text))
|
||||
|
||||
ret = win32functions.DrawText(dc, unicode(text), len(text), ctypes.byref(rect), DT_SINGLELINE)
|
||||
|
||||
if not ret:
|
||||
raise ctypes.WinError()
|
||||
|
||||
|
||||
def menupick_action(menuitem):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def select_tab_action(ctrl, tab):
|
||||
if isinstance(tab, basestring):
|
||||
# find the string in the tab control
|
||||
import findbestmatch
|
||||
bestText = findbestmatch.find_best_match(tab, ctrl.Texts, ctrl.Texts)
|
||||
tab = ctrl.Texts.index(bestText) - 1
|
||||
|
||||
ctrl.SendMessage(TCM_SETCURFOCUS, tab)
|
||||
|
||||
|
||||
|
||||
|
||||
def draw_outline(ctrl, colour = 'green', thickness = 2, fill = BS_NULL, rect = None):
|
||||
#====================================================================
|
||||
|
||||
colours = {
|
||||
"green" : 0x00ff00,
|
||||
"blue" : 0xff0000,
|
||||
"red" : 0x0000ff,
|
||||
}
|
||||
|
||||
# if it's a known colour
|
||||
if colour in colours:
|
||||
colour = colours[colour]
|
||||
|
||||
if not rect:
|
||||
rect = ctrl.Rectangle
|
||||
|
||||
# create the pen(outline)
|
||||
hPen = CreatePen(PS_SOLID, 2, colour)
|
||||
|
||||
# create the brush (inside)
|
||||
brush = LOGBRUSH()
|
||||
brush.lbStyle = fill
|
||||
brush.lbHatch = HS_DIAGCROSS
|
||||
hBrush = CreateBrushIndirect(byref(brush))
|
||||
|
||||
# get the Device Context
|
||||
dc = CreateDC(u"DISPLAY", None, None, None )
|
||||
|
||||
# push our objects into it
|
||||
SelectObject(dc, hBrush)
|
||||
SelectObject(dc, hPen)
|
||||
|
||||
win32functions.Rectangle(dc, rect.left, rect.top, rect.right, rect.bottom)
|
||||
|
||||
# Delete the brush and pen we created
|
||||
DeleteObject(hBrush)
|
||||
DeleteObject(hPen)
|
||||
|
||||
# delete the Display context that we created
|
||||
DeleteDC(dc)
|
||||
|
||||
|
||||
|
||||
|
||||
######ANYWIN
|
||||
#CaptureBitmap
|
||||
#GetAppId
|
||||
#GetCaption
|
||||
#GetChildren
|
||||
#GetClass
|
||||
#GetHandle
|
||||
#GetNativeClass
|
||||
#GetParent
|
||||
#IsEnabled
|
||||
#IsVisible
|
||||
#TypeKeys
|
||||
#Click
|
||||
#DoubleClick
|
||||
|
||||
#GetHelpText
|
||||
|
||||
|
||||
#ClearTrap
|
||||
#Exists
|
||||
#GenerateDecl
|
||||
#GetArrayProperty
|
||||
#GetBitmapCRC
|
||||
#GetContents
|
||||
#GetEverything
|
||||
#GetIDGetIndex
|
||||
#GetInputLanguage
|
||||
#GetManyProperties
|
||||
#GetName
|
||||
#GetProperty
|
||||
#GetPropertyList
|
||||
#GetRect
|
||||
#GetTag
|
||||
#InvokeMethods
|
||||
#IsActive
|
||||
#IsArrayProperty
|
||||
#IsDefined
|
||||
#IsOfClass
|
||||
#InvokeJava
|
||||
#MenuSelect
|
||||
#MoveMouse
|
||||
#MultiClick
|
||||
#PopupSelect
|
||||
#PressKeys
|
||||
#PressMouse
|
||||
#ReleaseKeys
|
||||
#ReleaseMouse
|
||||
#ScrollIntoView
|
||||
#SetArrayProperty
|
||||
#SetInputLanguage
|
||||
#SetProperty
|
||||
#SetTrap
|
||||
#VerifyActive
|
||||
#VerifyBitmap
|
||||
#VerifyEnabled
|
||||
#VerifyEverything
|
||||
#VerifyText
|
||||
#VerifyProperties
|
||||
#WaitBitmap
|
||||
#Properties
|
||||
#
|
||||
#bActive
|
||||
#AppId
|
||||
#sCaption
|
||||
#lwChildren
|
||||
#Class
|
||||
#bEnabled
|
||||
#bExists
|
||||
#sID
|
||||
#iIndex
|
||||
#sName
|
||||
#wParent
|
||||
#Rect
|
||||
#hWnd
|
||||
#WndTag
|
||||
#
|
||||
#
|
||||
######CONTROL
|
||||
#GetPriorStatic
|
||||
#HasFocus
|
||||
#SetFocus
|
||||
#VerifyFocus
|
||||
#
|
||||
######BUTTON
|
||||
#Click
|
||||
#IsIndeterminate
|
||||
#IsPressed
|
||||
#
|
||||
#
|
||||
#####CHECKBOX
|
||||
#Check
|
||||
#GetState
|
||||
#IsChecked
|
||||
#SetState
|
||||
#Toggle
|
||||
#Uncheck
|
||||
#VerifyValue
|
||||
#
|
||||
#bChecked
|
||||
#bValue
|
||||
#
|
||||
#
|
||||
#####MENUITEM
|
||||
#Check
|
||||
#IsChecked
|
||||
#Pick
|
||||
#Uncheck
|
||||
#VerifyChecked
|
||||
#
|
||||
#bChecked
|
||||
#
|
||||
#
|
||||
#####COMBOBOX
|
||||
#ClearText
|
||||
#FindItem
|
||||
#GetContents
|
||||
#GetItemCount
|
||||
#GetItemText
|
||||
#GetSelIndex
|
||||
#GetSelText
|
||||
#GetText
|
||||
#Select
|
||||
#SetText
|
||||
#VerifyContents
|
||||
#VerifyText
|
||||
#VerifyValue
|
||||
#
|
||||
#lsContents
|
||||
#iItemCount
|
||||
#iValue
|
||||
#sValue
|
||||
#
|
||||
#####LISTBOX
|
||||
#BeginDrag
|
||||
#DoubleSelect
|
||||
#EndDrag
|
||||
#ExtendSelect
|
||||
#FindItem
|
||||
#GetContents
|
||||
#GetItemCount
|
||||
#GetItemText
|
||||
#GetMultiSelIndex
|
||||
#GetMultiSelText
|
||||
#GetSelIndex
|
||||
#GetSelText
|
||||
#IsExtendSel
|
||||
#IsMultiSel
|
||||
#MultiSelect
|
||||
#MultiUnselect
|
||||
#Select
|
||||
#SelectList
|
||||
#SelectRange
|
||||
#VerifyContents
|
||||
#VerifyValue
|
||||
#
|
||||
#lsContents
|
||||
#bIsExtend
|
||||
#bIsMulti
|
||||
#iItemCount
|
||||
#iValue
|
||||
#liValue
|
||||
#lsValue
|
||||
#sValue
|
||||
#
|
||||
#
|
||||
#####EDIT
|
||||
#ClearText
|
||||
#GetContents
|
||||
#GetFontName
|
||||
#GetFontSize
|
||||
#GetMultiSelText
|
||||
#GetMultiText
|
||||
#GetPosition
|
||||
#GetSelRange
|
||||
#GetSelText
|
||||
#GetText
|
||||
#IsBold
|
||||
#IsItalic
|
||||
#IsMultiText
|
||||
#IsRichText
|
||||
#IsUnderline
|
||||
#SetMultiText
|
||||
#SetPosition
|
||||
#SetSelRange
|
||||
#SetText
|
||||
#VerifyPosition
|
||||
#VerifySelRange
|
||||
#VerifySelText
|
||||
#VerifyValue
|
||||
#
|
||||
#bIsMulti
|
||||
#lsValue
|
||||
#sValue
|
||||
#
|
||||
#
|
||||
#####LISTVIEW
|
||||
#BeginDrag
|
||||
#DoubleSelect
|
||||
#EndDrag
|
||||
#ExposeItem
|
||||
#ExtendSelect
|
||||
#FindItem
|
||||
#GetColumnCount
|
||||
#GetColumnName
|
||||
#GetContents
|
||||
#GetItemImageState
|
||||
#GetItemImageIndex
|
||||
#GetItemRect
|
||||
#GetItemText
|
||||
#GetMultiSelIndex
|
||||
#GetMultiSelText
|
||||
#GetSelIndex
|
||||
#GetSelText
|
||||
#GetView
|
||||
#method
|
||||
#(ListView)
|
||||
#IsExtendSel
|
||||
#IsMultiSel
|
||||
#MultiSelect
|
||||
#MultiUnselect
|
||||
#PressItem
|
||||
#ReleaseItem
|
||||
#Select
|
||||
#SelectList
|
||||
#SelectRange
|
||||
#VerifyContents
|
||||
#VerifyValue
|
||||
#
|
||||
#
|
||||
#####TREEVIEW
|
||||
#BeginDrag
|
||||
#Collapse
|
||||
#DoubleSelect
|
||||
#EndDrag
|
||||
#Expand
|
||||
#ExposeItem
|
||||
#ExtendSelect
|
||||
#FindItem
|
||||
#GetContents
|
||||
#GetItemCount
|
||||
#GetItemImageIndex
|
||||
#GetItemImageState
|
||||
#GetItemLevel
|
||||
#GetItemRect
|
||||
#GetItemText
|
||||
#GetSelIndex
|
||||
#GetSelText
|
||||
#GetSubItemCount
|
||||
#GetSubItems
|
||||
#IsItemEditable
|
||||
#IsItemExpandable
|
||||
#IsItemExpanded
|
||||
#MultiSelect
|
||||
#MultiUnselect
|
||||
#PressItem
|
||||
#ReleaseItem
|
||||
#Select
|
||||
#SelectList
|
||||
#VerifyContents
|
||||
#VerifyValue
|
||||
#
|
||||
#####Static
|
||||
#GetText
|
||||
#VerifyValue
|
||||
|
||||
|
||||
standard_action_funcs = dict(
|
||||
Click = click_action,
|
||||
RightClick = rightclick_action,
|
||||
DoubleClick = doubleclick_action,
|
||||
TypeKeys = typekeys_action,
|
||||
SetText = settext_action,
|
||||
ReleaseMouse = release_mouse_action,
|
||||
MoveMouse = move_mouse_action,
|
||||
PressMouse = press_mouse_action,
|
||||
DebugMessage = write_debug_text,
|
||||
)
|
||||
|
||||
|
||||
class_specific_actions = {
|
||||
|
||||
'ComboBox' : dict(
|
||||
Select = combobox_select,
|
||||
),
|
||||
|
||||
'ListBox' : dict(
|
||||
Select = listbox_select,
|
||||
),
|
||||
|
||||
'Edit' : dict(
|
||||
Select = set_edit_selection,
|
||||
SetText = set_edit_text,
|
||||
),
|
||||
|
||||
'CheckBox' : dict(
|
||||
Check = check_button_action,
|
||||
UnCheck = uncheck_button_action,
|
||||
),
|
||||
|
||||
'Button' : dict(
|
||||
Check = check_button_action,
|
||||
UnCheck = uncheck_button_action,
|
||||
),
|
||||
"TabControl" : dict(
|
||||
Select = select_tab_action
|
||||
),
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#=========================================================================
|
||||
def deferred_func(func):
|
||||
def func_wrapper(ctrl, *args, **kwargs):
|
||||
|
||||
return func(ctrl._, *args, **kwargs)
|
||||
|
||||
return func_wrapper
|
||||
|
||||
#=========================================================================
|
||||
def identity(func):
|
||||
return func
|
||||
|
||||
|
||||
#=========================================================================
|
||||
def add_actions(to_obj, deferred = False):
|
||||
# add common actions:
|
||||
import application
|
||||
|
||||
if hasattr(to_obj, "_"):
|
||||
defer_action = deferred_func
|
||||
ctrl = to_obj._
|
||||
|
||||
else:
|
||||
defer_action = identity
|
||||
ctrl = to_obj
|
||||
|
||||
# for each of the standard actions
|
||||
for action_name in standard_action_funcs:
|
||||
# add it to the control class
|
||||
setattr (to_obj.__class__, action_name, defer_action(standard_action_funcs[action_name]))
|
||||
|
||||
if class_specific_actions.has_key(ctrl.FriendlyClassName):
|
||||
actions = class_specific_actions[ctrl.FriendlyClassName]
|
||||
|
||||
for action_name, action_func in actions.items():
|
||||
setattr (to_obj.__class__, action_name, defer_action(action_func))
|
||||
|
||||
return ctrl
|
177
pywinauto/controlproperties.py
Normal file
177
pywinauto/controlproperties.py
Normal file
@ -0,0 +1,177 @@
|
||||
# Control Properties
|
||||
from win32structures import RECT, LOGFONTW
|
||||
|
||||
from pprint import pprint
|
||||
|
||||
#====================================================================
|
||||
class ControlProps(dict):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, props = {}):
|
||||
# default to having menuItems for all things
|
||||
self.MenuItems = []
|
||||
|
||||
self.update(props)
|
||||
#for x in props:
|
||||
#self[x] = props[x]
|
||||
|
||||
if hasattr(props, "handle"):
|
||||
self.__dict__['handle'] = props.handle
|
||||
else:
|
||||
self.__dict__['handle'] = None
|
||||
|
||||
self.__dict__['ref'] = None
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# handles attribute access for dictionary items and
|
||||
# for plurals (e.g. if self.Fonts = [4, 2] then self.Font = 4)
|
||||
def __getattr__(self, key):
|
||||
|
||||
# if the key is not in the dictionary but the plural is
|
||||
if key not in self and key + "s" in self:
|
||||
|
||||
# try to get the first element of the possible list item
|
||||
try:
|
||||
return self[key + "s"][0]
|
||||
except TypeError, e:
|
||||
pass
|
||||
|
||||
if key in self:
|
||||
return self[key]
|
||||
|
||||
return self.__dict__[key]
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __setattr__(self, key, value):
|
||||
if key in self.__dict__:
|
||||
self.__dict__[key] = value
|
||||
else:
|
||||
self[key] = value
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def HasStyle(self, flag):
|
||||
return self.Style & flag == flag
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def HasExStyle(self, flag):
|
||||
return self.ExStyle & flag == flag
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def GetMenuBlocks(ctrls):
|
||||
allMenuBlocks = []
|
||||
for ctrl in ctrls:
|
||||
if ctrl.has_key('MenuItems'):
|
||||
|
||||
# we need to get all the separate menu blocks!
|
||||
menuBlocks = MenuBlockAsControls(ctrl.MenuItems)
|
||||
allMenuBlocks.extend(menuBlocks)
|
||||
|
||||
return allMenuBlocks
|
||||
|
||||
|
||||
#====================================================================
|
||||
def MenuBlockAsControls(menuItems, parentage = []):
|
||||
|
||||
blocks = []
|
||||
|
||||
curBlock = []
|
||||
for item in menuItems:
|
||||
|
||||
# do a bit of conversion first :-)
|
||||
itemAsCtrl = MenuItemAsControl(item)
|
||||
|
||||
# update the FriendlyClassName to contain the 'path' to
|
||||
# this particular item
|
||||
itemPath = "%s->%s" % ("->".join(parentage), item['Text'])
|
||||
itemAsCtrl.FriendlyClassName = "MenuItem %s" %itemPath
|
||||
|
||||
#append the item to the current menu block
|
||||
curBlock.append(itemAsCtrl)
|
||||
|
||||
# If the item has a sub menu
|
||||
if item.has_key('MenuItems'):
|
||||
|
||||
# add the current item the path
|
||||
parentage.append(item['Text'])
|
||||
|
||||
# Get the block for the SubMenu, and add it to the list of
|
||||
# blocks we have found
|
||||
blocks.extend(MenuBlockAsControls(item['MenuItems'], parentage))
|
||||
|
||||
# and seeing as we are dong with that sub menu remove the current
|
||||
# item from the path
|
||||
del(parentage[-1])
|
||||
|
||||
# add the current block to the list of blocks
|
||||
blocks.append(curBlock)
|
||||
|
||||
return blocks
|
||||
|
||||
|
||||
#====================================================================
|
||||
def MenuItemAsControl(menuItem):
|
||||
itemAsCtrl = ControlProps()
|
||||
|
||||
itemAsCtrl["Texts"] = [menuItem['Text'], ]
|
||||
itemAsCtrl["ControlID"] = menuItem['ID']
|
||||
itemAsCtrl["Type"] = menuItem['Type']
|
||||
itemAsCtrl["State"] = menuItem['State']
|
||||
|
||||
itemAsCtrl["Class"] = "MenuItem"
|
||||
itemAsCtrl["FriendlyClassName"] = "MenuItem"
|
||||
|
||||
# as most of these don't matter - just set them up with default stuff
|
||||
itemAsCtrl["Rectangle"] = RECT(0, 0, 999, 999)
|
||||
itemAsCtrl["Fonts"] = [LOGFONTW(), ]
|
||||
itemAsCtrl["ClientRects"] = [RECT(0, 0, 999, 999), ]
|
||||
itemAsCtrl["ContextHelpID"] = 0
|
||||
itemAsCtrl["UserData"] = 0
|
||||
itemAsCtrl["Style"] = 0
|
||||
itemAsCtrl["ExStyle"] = 0
|
||||
itemAsCtrl["IsVisible"] = 1
|
||||
|
||||
return itemAsCtrl
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def SetReferenceControls(controls, refControls):
|
||||
|
||||
# numbers of controls must be the same (though in future I could imagine
|
||||
# relaxing this constraint)
|
||||
|
||||
if len(controls) != len(refControls):
|
||||
#print len(controls), len(refControls)
|
||||
#pprint(controls)
|
||||
#print "==" * 20
|
||||
#pprint(refControls)
|
||||
raise "Numbers of controls on ref. dialog does not match Loc. dialog"
|
||||
|
||||
# set the controls
|
||||
for i, ctrl in enumerate(controls):
|
||||
ctrl.ref = refControls[i]
|
||||
|
||||
toRet = 1
|
||||
allIDsSameFlag = 2
|
||||
allClassesSameFlag = 4
|
||||
|
||||
# find if all the control id's match
|
||||
if [ctrl.ControlID for ctrl in controls] == \
|
||||
[ctrl.ControlID for ctrl in refControls]:
|
||||
|
||||
toRet += allIDsSameFlag
|
||||
|
||||
# check if the control classes match
|
||||
if [ctrl.Class for ctrl in controls] == \
|
||||
[ctrl.Class for ctrl in refControls]:
|
||||
|
||||
toRet += allClassesSameFlag
|
||||
|
||||
return toRet
|
||||
|
||||
|
||||
|
||||
|
517
pywinauto/controls/HwndWrapper.py
Normal file
517
pywinauto/controls/HwndWrapper.py
Normal file
@ -0,0 +1,517 @@
|
||||
# pylint: disable-msg=W0611
|
||||
import re
|
||||
from pprint import pprint
|
||||
import PIL.ImageGrab
|
||||
from ctypes import *
|
||||
|
||||
from win32defines import *
|
||||
from win32functions import *
|
||||
from win32structures import *
|
||||
|
||||
|
||||
#====================================================================
|
||||
def WrapHandle(hwnd, isDialog = False):
|
||||
|
||||
default_wrapper = HwndWrapper(hwnd)
|
||||
|
||||
for wrapper_name in HwndWrappers:
|
||||
if re.search(wrapper_name, default_wrapper.Class):
|
||||
return HwndWrappers[wrapper_name](hwnd)
|
||||
|
||||
default_wrapper._NeedsImageProp = True
|
||||
|
||||
return default_wrapper
|
||||
|
||||
|
||||
#====================================================================
|
||||
class HwndWrapper(object):
|
||||
def __init__(self, hwnd):
|
||||
|
||||
self.handle = hwnd
|
||||
|
||||
# make it so that ctypes conversion happens correctly
|
||||
self._as_parameter_ = self.handle
|
||||
|
||||
# specify whether we need to grab an image of ourselves
|
||||
# when asked for properties
|
||||
self._NeedsImageProp = False
|
||||
|
||||
# set the friendly class name to default to
|
||||
# the class name
|
||||
self.FriendlyClassName = self.Class
|
||||
self._extra_texts = []
|
||||
self._extra_clientrects = []
|
||||
self._extra_props = {}
|
||||
|
||||
self._extra_props['MenuItems'] = self.MenuItems
|
||||
|
||||
# if it is a main window
|
||||
if self.IsDialog:
|
||||
self.FriendlyClassName = "Dialog"
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_is_dialog(self):
|
||||
if (self.HasStyle(WS_OVERLAPPED) or self.HasStyle(WS_CAPTION)) and not self.HasStyle(WS_CHILD):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
IsDialog = property (get_is_dialog, doc = "Whether the window is a dialog or not")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# define the Menu Property
|
||||
def get_menuitems(self):
|
||||
if self.IsDialog:
|
||||
return GetMenuItems(GetMenu(self))
|
||||
else:
|
||||
return []
|
||||
MenuItems = property (get_menuitems, doc = "Return the menu items for the dialog")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_text(self):
|
||||
# Had to use GetWindowText and GetWindowTextLength rather than
|
||||
# using WM_GETTEXT as we want it to fail for controls that dont
|
||||
# have 'usable' first text e.g. Edit controls and Comboboxes.
|
||||
|
||||
length = GetWindowTextLength(self.handle,)
|
||||
|
||||
text = ''
|
||||
if length:
|
||||
length += 1
|
||||
|
||||
buffer = (c_wchar * length)()
|
||||
|
||||
ret = GetWindowText(self, byref(buffer), length)
|
||||
|
||||
if ret:
|
||||
text = buffer.value
|
||||
|
||||
return text
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
# # get the number of characters required
|
||||
# bufferSize = self.SendMessage (WM_GETTEXTLENGTH)
|
||||
#
|
||||
# text = ''
|
||||
# # allocate the buffer size
|
||||
# if bufferSize:
|
||||
# bufferSize += 1
|
||||
# # allocate the buffer
|
||||
# title = (c_wchar * bufferSize)()
|
||||
#
|
||||
# # retrieve the text
|
||||
# self.SendMessage (WM_GETTEXT, bufferSize, title)
|
||||
#
|
||||
# # get the unicode object from the ctypes instance
|
||||
# text = title.value
|
||||
#
|
||||
# return text
|
||||
Text = property (get_text, doc = "Main text of the control")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_texts(self):
|
||||
texts = [self.Text, ]
|
||||
texts.extend(self._extra_texts)
|
||||
return texts
|
||||
Texts = property (get_texts, doc = "All text items of the control")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_Class(self):
|
||||
# get the className
|
||||
className = (c_wchar * 257)()
|
||||
GetClassName (self, byref(className), 256)
|
||||
return className.value
|
||||
Class = property (get_Class, doc = "Class Name of the window")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_parent(self):
|
||||
return HwndWrapper(GetParent(self))
|
||||
Parent = property (get_parent, doc = "Parent window of window")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_Style(self):
|
||||
return GetWindowLong (self, GWL_STYLE)
|
||||
Style = property (get_Style, doc = "Style of window")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_ExStyle(self):
|
||||
return GetWindowLong (self.handle, GWL_EXSTYLE)
|
||||
ExStyle = property (get_ExStyle, doc = "Extended Style of window")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_ControlID(self):
|
||||
return GetWindowLong (self.handle, GWL_ID)
|
||||
ControlID = property (get_ControlID, doc = "The ID of the window")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_userdata(self):
|
||||
return GetWindowLong (self.handle, GWL_USERDATA)
|
||||
UserData = property (
|
||||
get_userdata, doc = "Extra data associted with the window")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_ContextHelpID(self):
|
||||
return GetWindowContextHelpId (self.handle)
|
||||
ContextHelpID = property (
|
||||
get_ContextHelpID, doc = "The Context Help ID of the window")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_IsVisible(self):
|
||||
return IsWindowVisible(self.handle)
|
||||
IsVisible = property (get_IsVisible, doc = "Whether the window is visible or not")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_IsUnicode(self):
|
||||
return IsWindowUnicode(self.handle)
|
||||
IsUnicode = property (get_IsUnicode, doc = "Whether the window is unicode or not")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_isenabled(self):
|
||||
return IsWindowEnabled(self.handle)
|
||||
IsEnabled = property (get_isenabled, doc = "Whether the window is enabled or not")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_clientrect(self):
|
||||
"Returns the client rectangle of the control"
|
||||
clientRect = RECT()
|
||||
GetClientRect(self.handle, byref(clientRect))
|
||||
return clientRect
|
||||
ClientRect = property (get_clientrect, doc = "ClientRect of window")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_clientrects(self):
|
||||
clientrects = [self.ClientRect, ]
|
||||
clientrects.extend(self._extra_clientrects)
|
||||
return clientrects
|
||||
ClientRects = property (
|
||||
get_clientrects, doc = "All client rectanbgles of the control")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_rectangle(self):
|
||||
# get the full rectangle
|
||||
rect = RECT()
|
||||
GetWindowRect(self.handle, byref(rect))
|
||||
return rect
|
||||
Rectangle = property (get_rectangle, doc = "Rectangle of window")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_children(self):
|
||||
|
||||
# this will be filled in the callback function
|
||||
childWindows = []
|
||||
|
||||
# callback function for EnumChildWindows
|
||||
def enumChildProc(hWnd, LPARAM):
|
||||
win = WrapHandle(hWnd)
|
||||
|
||||
# append it to our list
|
||||
childWindows.append(win)
|
||||
|
||||
# return true to keep going
|
||||
return True
|
||||
|
||||
# define the child proc type
|
||||
EnumChildProc = WINFUNCTYPE(c_int, HWND, LPARAM)
|
||||
proc = EnumChildProc(enumChildProc)
|
||||
|
||||
# loop over all the children (callback called for each)
|
||||
EnumChildWindows(self.handle, proc, 0)
|
||||
|
||||
return childWindows
|
||||
Children = property (get_children, doc = "The list of children")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_Font(self):
|
||||
# set the font
|
||||
fontHandle = self.SendMessage (WM_GETFONT)
|
||||
|
||||
# if the fondUsed is 0 then the control is using the
|
||||
# system font
|
||||
if not fontHandle:
|
||||
fontHandle = GetStockObject(SYSTEM_FONT);
|
||||
|
||||
# Get the Logfont structure of the font of the control
|
||||
font = LOGFONTW()
|
||||
ret = GetObject(fontHandle, sizeof(font), byref(font))
|
||||
|
||||
# The function could not get the font - this is probably
|
||||
# because the control does not have associated Font/Text
|
||||
# So we should make sure the elements of the font are zeroed.
|
||||
if not ret:
|
||||
font = LOGFONTW()
|
||||
|
||||
# if it is a main window
|
||||
if (self.HasStyle(WS_OVERLAPPED) or self.HasStyle(WS_CAPTION)) and not self.HasStyle(WS_CHILD):
|
||||
|
||||
if "MS Shell Dlg" in font.lfFaceName or font.lfFaceName == "System":
|
||||
# these are not usually the fonts actaully used in for
|
||||
# title bars so we need to get the default title bar font
|
||||
|
||||
# get the title font based on the system metrics rather
|
||||
# than the font of the control itself
|
||||
SPI_GETNONCLIENTMETRICS = 41 # set the message number
|
||||
ncms = NONCLIENTMETRICSW()
|
||||
ncms.cbSize = sizeof(ncms)
|
||||
SystemParametersInfo(
|
||||
SPI_GETNONCLIENTMETRICS,
|
||||
sizeof(ncms),
|
||||
byref(ncms),
|
||||
0)
|
||||
|
||||
# with either of the following 2 flags set the font of the
|
||||
# dialog isthe small one (but there is normally no difference!
|
||||
if self.HasStyle(WS_EX_TOOLWINDOW) or \
|
||||
self.HasStyle(WS_EX_PALETTEWINDOW):
|
||||
|
||||
font = ncms.lfSmCaptionFont
|
||||
else:
|
||||
font = ncms.lfCaptionFont
|
||||
|
||||
return font
|
||||
Font = property (get_Font, doc = "The font of the window")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_Fonts(self):
|
||||
return [self.Font, ]
|
||||
Fonts = property (get_Fonts, doc = "All fonts of the control")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def IsChild(self, parent):
|
||||
"Return whether the window is a child of parent."
|
||||
|
||||
# Call the IsChild API funciton and convert the result
|
||||
# to True/False
|
||||
return IsChild(self.handle, parentHwnd) != 0
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def HasStyle(self, Style):
|
||||
if self.Style & Style == Style:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def HasExStyle(self, Style):
|
||||
if self.ExStyle & Style == Style:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def SendMessage(self, message, wparam = 0 , lparam = 0):
|
||||
return SendMessage(self.handle, message, wparam, lparam)
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def PostMessage(self, message, wparam = 0 , lparam = 0):
|
||||
return PostMessage(self.handle, message, wparam, lparam)
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def GetProperties(self):
|
||||
props = self._extra_props
|
||||
|
||||
# get the className
|
||||
props['Class'] = self.Class
|
||||
|
||||
# set up the friendlyclass defaulting
|
||||
# to the class Name
|
||||
props['FriendlyClassName'] = self.FriendlyClassName
|
||||
|
||||
props['Texts'] = self.Texts
|
||||
props['Style'] = self.Style
|
||||
props['ExStyle'] = self.ExStyle
|
||||
props['ControlID'] = self.ControlID
|
||||
props['UserData'] = self.UserData
|
||||
props['ContextHelpID'] = self.ContextHelpID
|
||||
|
||||
props['Fonts'] = self.Fonts
|
||||
props['ClientRects'] = self.ClientRects
|
||||
|
||||
props['Rectangle'] = self.Rectangle
|
||||
|
||||
props['IsVisible'] = self.IsVisible
|
||||
props['IsUnicode'] = self.IsUnicode
|
||||
props['IsEnabled'] = self.IsEnabled
|
||||
|
||||
#props['MenuItems'] = []
|
||||
|
||||
if self._NeedsImageProp:
|
||||
props['Image'] = self.CaptureAsImage()
|
||||
|
||||
#return the properties
|
||||
return props
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def CaptureAsImage(self):
|
||||
|
||||
if not (self.Rectangle.width() and self.Rectangle.height()):
|
||||
return None
|
||||
|
||||
# get the control rectangle in a way that PIL likes it
|
||||
box = (
|
||||
self.Rectangle.left,
|
||||
self.Rectangle.top,
|
||||
self.Rectangle.right,
|
||||
self.Rectangle.bottom)
|
||||
|
||||
# grab the image and get raw data as a string
|
||||
image = PIL.ImageGrab.grab(box)
|
||||
|
||||
return image
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, HwndWrapper):
|
||||
return self.handle == other.handle
|
||||
else:
|
||||
return self.handle == other
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_process_id(self):
|
||||
"ID of process that controls this window"
|
||||
process_id = c_int()
|
||||
GetWindowThreadProcessId(self, byref(process_id))
|
||||
|
||||
return process_id.value
|
||||
ProcessID = property (get_process_id, doc = get_process_id.__doc__)
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def GetMenuItems(menuHandle):#, indent = ""):
|
||||
|
||||
# If it doesn't have a real menu just return
|
||||
if not IsMenu(menuHandle):
|
||||
return []
|
||||
|
||||
items = []
|
||||
|
||||
itemCount = GetMenuItemCount(menuHandle)
|
||||
|
||||
# for each menu item
|
||||
for i in range(0, itemCount):
|
||||
|
||||
itemProp = {} #Controls_Standard.ControlProps()
|
||||
|
||||
# get the information on the menu Item
|
||||
menuInfo = MENUITEMINFOW()
|
||||
menuInfo.cbSize = sizeof (menuInfo)
|
||||
menuInfo.fMask = \
|
||||
MIIM_CHECKMARKS | \
|
||||
MIIM_ID | \
|
||||
MIIM_STATE | \
|
||||
MIIM_SUBMENU | \
|
||||
MIIM_TYPE
|
||||
#MIIM_FTYPE | \
|
||||
#MIIM_STRING
|
||||
#MIIM_DATA | \
|
||||
|
||||
|
||||
ret = GetMenuItemInfo (menuHandle, i, True, byref(menuInfo))
|
||||
if not ret:
|
||||
raise WinError()
|
||||
|
||||
itemProp['State'] = menuInfo.fState
|
||||
itemProp['Type'] = menuInfo.fType
|
||||
itemProp['ID'] = menuInfo.wID
|
||||
#itemProp.handle = menuHandle
|
||||
|
||||
|
||||
|
||||
# if there is text
|
||||
if menuInfo.cch:
|
||||
# allocate a buffer
|
||||
bufferSize = menuInfo.cch+1
|
||||
text = (c_wchar * bufferSize)()
|
||||
|
||||
# update the structure and get the text info
|
||||
menuInfo.dwTypeData = addressof(text)
|
||||
menuInfo.cch = bufferSize
|
||||
GetMenuItemInfo (menuHandle, i, True, byref(menuInfo))
|
||||
itemProp['Text'] = text.value
|
||||
else:
|
||||
itemProp['Text'] = ""
|
||||
|
||||
|
||||
# if it's a sub menu then get it's items
|
||||
if menuInfo.hSubMenu:
|
||||
#indent += " "
|
||||
subMenuItems = GetMenuItems(menuInfo.hSubMenu)#, indent)
|
||||
itemProp['MenuItems'] = subMenuItems
|
||||
#indent = indent[1:-2]
|
||||
|
||||
items.append(itemProp)
|
||||
|
||||
|
||||
return items
|
||||
|
||||
|
||||
#====================================================================
|
||||
class dummy_control(dict):
|
||||
pass
|
||||
|
||||
#====================================================================
|
||||
def GetDialogPropsFromHandle(hwnd):
|
||||
# wrap the dialog handle and start a new list for the
|
||||
# controls on the dialog
|
||||
controls = [WrapHandle(hwnd, True), ]
|
||||
|
||||
# add all the children of the dialog
|
||||
controls.extend(controls[0].Children)
|
||||
|
||||
props = []
|
||||
|
||||
# Add each control to the properties for this dialog
|
||||
for ctrl in controls:
|
||||
# Get properties for each control and wrap them in
|
||||
# dummy_control so that we can assign handle
|
||||
ctrlProps = dummy_control(ctrl.GetProperties())
|
||||
|
||||
# assign the handle
|
||||
ctrlProps.handle = ctrl.handle
|
||||
|
||||
# offset the rectangle from the dialog rectangle
|
||||
ctrlProps['Rectangle'] -= controls[0].Rectangle
|
||||
|
||||
props.append(ctrlProps)
|
||||
|
||||
return props
|
||||
|
||||
|
||||
#====================================================================
|
||||
HwndWrappers = {}
|
||||
|
||||
|
||||
#====================================================================
|
||||
def Main():
|
||||
|
||||
from FindDialog import FindDialog
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
handle = GetDesktopWindow()
|
||||
else:
|
||||
try:
|
||||
handle = int(eval(sys.argv[1]))
|
||||
|
||||
except:
|
||||
|
||||
handle = FindDialog("^" + sys.argv[1], testClass = "#32770")
|
||||
|
||||
if not handle:
|
||||
print "dialog not found"
|
||||
sys.exit()
|
||||
#print handle
|
||||
#pprint (HwndWrapper(handle).GetProperties())
|
||||
|
||||
|
||||
#pprint(GetDialogPropsFromHandle(handle))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Main()
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,753 @@
|
||||
# pylint: disable-msg=W0611
|
||||
"Handles Static, Button, Edit, ListBox, ComboBox, ComboLBox"
|
||||
from ctypes import *
|
||||
from ctypes.wintypes import *
|
||||
|
||||
from APIFuncs import *
|
||||
from APIDefines import *
|
||||
from APIStructures import *
|
||||
|
||||
from XMLHelpers import *
|
||||
|
||||
__revision__ = "0.0.0.1"
|
||||
|
||||
|
||||
from InfoManager import WindowClassRegistry
|
||||
|
||||
|
||||
#====================================================================
|
||||
def GetMenuItems(menuHandle):#, indent = ""):
|
||||
items = []
|
||||
|
||||
# check that it is a menu
|
||||
if IsMenu(menuHandle):
|
||||
|
||||
itemCount = GetMenuItemCount(menuHandle)
|
||||
|
||||
# for each menu item
|
||||
for i in range(0, itemCount):
|
||||
|
||||
itemProp = {}
|
||||
|
||||
# get the information on the menu Item
|
||||
menuInfo = MENUITEMINFOW()
|
||||
menuInfo.cbSize = sizeof (menuInfo)
|
||||
menuInfo.fMask = \
|
||||
MIIM_CHECKMARKS | \
|
||||
MIIM_ID | \
|
||||
MIIM_STATE | \
|
||||
MIIM_SUBMENU | \
|
||||
MIIM_TYPE
|
||||
#MIIM_FTYPE | \
|
||||
#MIIM_STRING
|
||||
#MIIM_DATA | \
|
||||
|
||||
|
||||
ret = GetMenuItemInfo (menuHandle, i, True, byref(menuInfo))
|
||||
if not ret:
|
||||
raise WinError()
|
||||
|
||||
itemProp['State'] = menuInfo.fState
|
||||
itemProp['Type'] = menuInfo.fType
|
||||
itemProp['ID'] = menuInfo.wID
|
||||
|
||||
|
||||
# if there is text
|
||||
if menuInfo.cch:
|
||||
# allocate a buffer
|
||||
bufferSize = menuInfo.cch+1
|
||||
text = (c_wchar * bufferSize)()
|
||||
|
||||
# update the structure and get the text info
|
||||
menuInfo.dwTypeData = addressof(text)
|
||||
menuInfo.cch = bufferSize
|
||||
GetMenuItemInfo (menuHandle, i, True, byref(menuInfo))
|
||||
itemProp['Text'] = text.value
|
||||
else:
|
||||
itemProp['Text'] = ""
|
||||
|
||||
|
||||
# if it's a sub menu then get it's items
|
||||
if menuInfo.hSubMenu:
|
||||
#indent += " "
|
||||
subMenuItems = GetMenuItems(menuInfo.hSubMenu)#, indent)
|
||||
itemProp['MenuItems'] = subMenuItems
|
||||
#indent = indent[1:-2]
|
||||
|
||||
items.append(itemProp)
|
||||
|
||||
|
||||
return items
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def DefaultWindowHwndReader(hwnd, dialogRect):
|
||||
|
||||
props = {}
|
||||
|
||||
# get the className
|
||||
props['Class'] = GetClass(hwnd)
|
||||
|
||||
# set up the friendlyclass defaulting
|
||||
# to the class Name
|
||||
props['FriendlyClassName'] = props['Class']
|
||||
|
||||
# get the title
|
||||
bufferSize = SendMessage (hwnd, WM_GETTEXTLENGTH, 0, 0)
|
||||
if bufferSize:
|
||||
bufferSize += 1
|
||||
title = (c_wchar * bufferSize)()
|
||||
SendMessage (hwnd, WM_GETTEXT, bufferSize, title)
|
||||
|
||||
props['Titles'] = [title.value, ]
|
||||
else:
|
||||
props['Titles'] = ['', ]
|
||||
|
||||
|
||||
props['Style'] = GetWindowLong (hwnd, GWL_STYLE)
|
||||
props['ExStyle'] = GetWindowLong (hwnd, GWL_EXSTYLE)
|
||||
props['ControlID'] = GetWindowLong (hwnd, GWL_ID)
|
||||
props['UserData'] = GetWindowLong (hwnd, GWL_USERDATA)
|
||||
props['ContextHelpID'] = GetWindowContextHelpId (hwnd)
|
||||
|
||||
|
||||
# set the font
|
||||
fontHandle = SendMessage (hwnd, WM_GETFONT, 0, 0)
|
||||
|
||||
# if the fondUsed is 0 then the control is using the system font
|
||||
if not fontHandle:
|
||||
fontHandle = GetStockObject(SYSTEM_FONT);
|
||||
|
||||
# Get the Logfont structure of the font of the control
|
||||
font = LOGFONTW()
|
||||
ret = GetObject(fontHandle, sizeof(font), byref(font))
|
||||
|
||||
# The function could not get the font - this is probably because
|
||||
# the control does not have associated Font/Text
|
||||
# So we should make sure the elements of the font are zeroed.
|
||||
if not ret:
|
||||
font = LOGFONTW()
|
||||
|
||||
props['Fonts'] = [font,]
|
||||
|
||||
# get the client rectangle
|
||||
clientRect = RECT()
|
||||
GetClientRect(hwnd, byref(clientRect))
|
||||
props['ClientRect'] = clientRect
|
||||
|
||||
# get the full rectangle
|
||||
rect = RECT()
|
||||
GetWindowRect(hwnd, byref(rect))
|
||||
|
||||
if dialogRect:
|
||||
# offset it's rect depending on it's parents
|
||||
rect.left -= dialogRect.left
|
||||
rect.top -= dialogRect.top
|
||||
rect.right -= dialogRect.left
|
||||
rect.bottom -= dialogRect.top
|
||||
|
||||
#props['DialogRect'] = dialogRect
|
||||
|
||||
props['Rectangles'] = [rect,]
|
||||
|
||||
props['IsVisible'] = IsWindowVisible(hwnd)
|
||||
props['IsUnicode'] = IsWindowUnicode(hwnd)
|
||||
|
||||
return props
|
||||
|
||||
|
||||
#====================================================================
|
||||
def GetChildWindows(dialog, dlgRect = None):
|
||||
|
||||
# this will be filled in the callback function
|
||||
childWindows = []
|
||||
|
||||
# callback function for EnumChildWindows
|
||||
def enumChildProc(hWnd, LPARAM):
|
||||
win = Window(hWnd, dlgRect)
|
||||
|
||||
# construct an instance of the appropriate type
|
||||
win = WindowClassRegistry().GetClass(win.Class)(hWnd, dlgRect)
|
||||
|
||||
# append it to our list
|
||||
childWindows.append(win)
|
||||
|
||||
# return true to keep going
|
||||
return True
|
||||
|
||||
|
||||
# define the child proc type
|
||||
EnumChildProc = WINFUNCTYPE(c_int, HWND, LPARAM)
|
||||
proc = EnumChildProc(enumChildProc)
|
||||
|
||||
# loop over all the children (callback called for each)
|
||||
EnumChildWindows(dialog, proc, 0)
|
||||
|
||||
return childWindows
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def RemoveNonCurrentTabControls(dialog, childWindows):
|
||||
|
||||
# find out if there is a tab control and get it if there is one
|
||||
tab = None
|
||||
for child in childWindows:
|
||||
if child.Class == "SysTabControl32":
|
||||
tab = child
|
||||
break
|
||||
|
||||
|
||||
# make a copy of childWindows
|
||||
firstTabChildren = list(childWindows)
|
||||
if tab:
|
||||
|
||||
# get the parent of the tab control
|
||||
tabParent = GetParent(tab.handle)
|
||||
|
||||
# find the control with that hwnd
|
||||
tabParent = [c for c in childWindows if \
|
||||
c.handle == tabParent][0]
|
||||
|
||||
# get the index of the parent
|
||||
parentIdx = childWindows.index(tabParent) + 1
|
||||
|
||||
passedFirstTab = False
|
||||
for child in childWindows[parentIdx:]:
|
||||
|
||||
# if the current control is a dialog
|
||||
if child.Class == "#32770":
|
||||
|
||||
# if this is the first tab
|
||||
if not passedFirstTab :
|
||||
# then just skip it
|
||||
passedFirstTab = True
|
||||
else:
|
||||
# Ok so this is NOT the first tab
|
||||
# remove the dialog control itself
|
||||
firstTabChildren.remove(child)
|
||||
|
||||
# then remove all the children of that dialog
|
||||
for x in GetChildWindows(child.handle):
|
||||
firstTabChildren.remove(x)
|
||||
|
||||
return firstTabChildren
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def GetClass(hwnd):
|
||||
# get the className
|
||||
className = (c_wchar * 257)()
|
||||
GetClassName (hwnd, byref(className), 256)
|
||||
return className.value
|
||||
|
||||
|
||||
#====================================================================
|
||||
def GetTitle(hwnd):
|
||||
# get the title
|
||||
bufferSize = SendMessage (hwnd, WM_GETTEXTLENGTH, 0, 0)
|
||||
title = (c_wchar * bufferSize)()
|
||||
|
||||
if bufferSize:
|
||||
bufferSize += 1
|
||||
SendMessage (hwnd, WM_GETTEXT, bufferSize, title)
|
||||
|
||||
|
||||
return title.value
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class Window(object):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwndOrXML, dlgRect = None):
|
||||
|
||||
self.ref = None
|
||||
|
||||
if isinstance(hwndOrXML, (int, long)):
|
||||
self.handle = hwndOrXML
|
||||
self.properties = DefaultWindowHwndReader(hwndOrXML, dlgRect)
|
||||
|
||||
# this is only for use when we are running based on the dialog
|
||||
# itself - no reason to write it to the XML file so don't
|
||||
# add it to the properties
|
||||
self.actualRect = RECT()
|
||||
GetWindowRect(hwndOrXML, byref(self.actualRect))
|
||||
|
||||
self.properties["MenuItems"] = GetMenuItems(GetMenu(hwndOrXML))
|
||||
#print self.properties["MenuItems"]
|
||||
|
||||
#self.GetContextMenu()
|
||||
|
||||
else:
|
||||
self.properties = {}
|
||||
self.properties['ClientRect'] = XMLToRect(hwndOrXML.find("CLIENTRECT"))
|
||||
self.properties['Rectangles'] = []
|
||||
rectangles = hwndOrXML.find("RECTANGLES")
|
||||
for rect in rectangles:
|
||||
self.properties['Rectangles'].append(XMLToRect(rect))
|
||||
|
||||
self.properties['Fonts'] = []
|
||||
fonts = hwndOrXML.find("FONTS")
|
||||
for font in fonts:
|
||||
self.properties['Fonts'].append(XMLToFont(font))
|
||||
|
||||
self.properties['Titles'] = XMLToTitles(hwndOrXML.find("TITLES"))
|
||||
|
||||
|
||||
self.properties["MenuItems"] = XMLToMenuItems(hwndOrXML.find("MENUITEMS"))
|
||||
#print self.properties["MenuItems"]
|
||||
|
||||
|
||||
# get all the attributes
|
||||
for propName in hwndOrXML.attrib:
|
||||
|
||||
val = hwndOrXML.attrib[propName]
|
||||
|
||||
if propName.endswith("_LONG"):
|
||||
val = long(val)
|
||||
propName = propName[:-5]
|
||||
else:
|
||||
val = unicode(val)
|
||||
|
||||
self.properties[propName] = val
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __getattr__(self, name):
|
||||
if name in self.properties:
|
||||
return self.properties[name]
|
||||
else:
|
||||
raise AttributeError("'%s' has no attribute '%s'"% \
|
||||
(self.__class__.__name__, name))
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def GetTitle(self):
|
||||
return self.Titles[0]
|
||||
Title = property(GetTitle)
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def GetRectangle(self):
|
||||
return self.Rectangles[0]
|
||||
Rectangle = property(GetRectangle)
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def GetFont(self):
|
||||
return self.Fonts[0]
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def SetFont(self, font):
|
||||
self.Fonts[0] = font
|
||||
|
||||
Font = property(GetFont, SetFont)
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def Parent(self):
|
||||
# do a preliminary construction to a Window
|
||||
parent = Window(GetParent(self.handle))
|
||||
|
||||
# reconstruct it to the correct type
|
||||
return WindowClassRegistry().GetClass(parent.Class)(parent.handle)
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def Style(self, flag = None):
|
||||
style = self.properties['Style']
|
||||
if flag:
|
||||
return style & flag == flag
|
||||
else:
|
||||
return style
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def ExStyle(self, flag = None):
|
||||
exstyle = self.properties['ExStyle']
|
||||
if flag:
|
||||
return exstyle & flag == flag
|
||||
else:
|
||||
return exstyle
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def GetContextMenu(self):
|
||||
rect = self.Rectangle
|
||||
|
||||
# set the position of the context menu to be 2 pixels in from
|
||||
# the control edge
|
||||
pos = c_long ((rect.top+ 2 << 16) | (rect.left + 2))
|
||||
|
||||
# get the top window before trying to bring up a context menu
|
||||
oldTopWin = FindWindow(0, 0)
|
||||
|
||||
# send the message but time-out after 10 mili seconds
|
||||
res = DWORD()
|
||||
SendMessageTimeout (
|
||||
self.handle,
|
||||
WM_CONTEXTMENU,
|
||||
self.handle,
|
||||
pos,
|
||||
0,
|
||||
100, # time out in miliseconds
|
||||
byref(res)) # result
|
||||
|
||||
# get the top window
|
||||
popMenuWin = FindWindow(0, 0)
|
||||
|
||||
# if no context menu has opened try right clicking the control
|
||||
# if oldTopWin == popMenuWin:
|
||||
# SendMessageTimeout (
|
||||
# self.handle,
|
||||
# WM_RBUTTONDOWN,
|
||||
# 0,
|
||||
# pos,
|
||||
# 0,
|
||||
# 100, # time out in miliseconds
|
||||
# byref(res)) # result
|
||||
#
|
||||
# SendMessageTimeout (
|
||||
# self.handle,
|
||||
# WM_RBUTTONUP,
|
||||
# 2,
|
||||
# pos,
|
||||
# 0,
|
||||
# 100, # time out in miliseconds
|
||||
# byref(res)) # result
|
||||
#
|
||||
# # wait another .1 of a second to allow it to display
|
||||
# import time
|
||||
# time.sleep(.1)
|
||||
#
|
||||
# # get the top window
|
||||
# popMenuWin = FindWindow(0, 0)
|
||||
|
||||
|
||||
# if we still haven't opened a popup menu
|
||||
if oldTopWin == popMenuWin:
|
||||
return
|
||||
|
||||
|
||||
# get the MenuBar info from the PopupWindow which will
|
||||
# give you the Menu Handle for the menu itself
|
||||
mbi = MENUBARINFO()
|
||||
mbi.cbSize = sizeof(MENUBARINFO)
|
||||
ret = GetMenuBarInfo(popMenuWin, OBJID_CLIENT, 0, byref(mbi))
|
||||
|
||||
if ret:
|
||||
#print ret, mbi.rcBar.left, mbi.hMenu
|
||||
GetMenuItems(mbi.hMenu)
|
||||
self.properties["ContextMenu"] = GetMenuItems(mbi.hMenu)
|
||||
|
||||
|
||||
# make sure the popup goes away!
|
||||
SendMessage (self.handle, WM_CANCELMODE, 0, 0)
|
||||
SendMessage (popMenuWin, WM_CANCELMODE, 0, 0)
|
||||
|
||||
# if it's still open - then close it.
|
||||
if IsWindowVisible(popMenuWin):
|
||||
SendMessage (popMenuWin, WM_CLOSE, 0, 0)
|
||||
#SendMessage (popMenuWin, WM_DESTROY, 0, 0)
|
||||
#SendMessage (popMenuWin, WM_NCDESTROY , 0, 0)
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __cmp__(self, other):
|
||||
return cmp(self.handle, other.handle)
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __hash__(self):
|
||||
return hash(self.handle)
|
||||
|
||||
#----------------------------------------------------------------
|
||||
# def __str__(self):
|
||||
# return "%8d %-15s\t'%s'" % (self.handle,
|
||||
# "'%s'"% self.riendlyClassName,
|
||||
# self.Title)
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class DialogWindow(Window):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwndOrXML):
|
||||
|
||||
self.children = []
|
||||
|
||||
if isinstance(hwndOrXML, (int, long)):
|
||||
# read the properties for the dialog itself
|
||||
# Get the dialog Rectangle first - to get the control offset
|
||||
|
||||
if not IsWindow(hwndOrXML):
|
||||
raise "The window handle passed is not valid"
|
||||
|
||||
|
||||
rect = RECT()
|
||||
GetWindowRect(hwndOrXML, byref(rect))
|
||||
|
||||
Window.__init__(self, hwndOrXML, rect)
|
||||
|
||||
# Get the font of the dialog - dialog fonts are different from
|
||||
# normal fonts in that the fonts "MS Shell Dlg", "MS Shell Dlg 2"
|
||||
# and "System" actally map to the default system font - which is
|
||||
# Windows 2000/XP is usually Tahoma
|
||||
|
||||
font = self.Font
|
||||
if "MS Shell Dlg" in font.lfFaceName or font.lfFaceName == "System":
|
||||
# these are not usually the fonts actaully used in for
|
||||
# title bars so we need to get the default title bar font
|
||||
|
||||
# get the title font based on the system metrics rather
|
||||
# than the font of the control itself
|
||||
SPI_GETNONCLIENTMETRICS = 41
|
||||
ncms = NONCLIENTMETRICSW()
|
||||
ncms.cbSize = sizeof(ncms)
|
||||
SystemParametersInfo(
|
||||
SPI_GETNONCLIENTMETRICS,
|
||||
sizeof(ncms),
|
||||
byref(ncms),
|
||||
0)
|
||||
|
||||
# with either of the following 2 flags set the font of the
|
||||
# dialog isthe small one (but there is normally no difference!
|
||||
if self.Style(WS_EX_TOOLWINDOW) or self.Style(WS_EX_PALETTEWINDOW):
|
||||
self.properties['Fonts'][0] = ncms.lfSmCaptionFont
|
||||
else:
|
||||
self.properties['Fonts'][0] = ncms.lfCaptionFont
|
||||
|
||||
self.properties['FriendlyClassName'] = "Dialog"
|
||||
|
||||
self.children = GetChildWindows(self.handle, rect)
|
||||
self.children.insert(0, self)
|
||||
self.children = RemoveNonCurrentTabControls(self.handle, self.children)
|
||||
|
||||
else:
|
||||
dialogElemReached = False
|
||||
for ctrl in hwndOrXML.findall("CONTROL"):
|
||||
|
||||
# if this is the first time through the dialog
|
||||
if not dialogElemReached:
|
||||
# initialise the Dialog itself
|
||||
Window.__init__(self, ctrl)
|
||||
dialogElemReached = True
|
||||
|
||||
# otherwise contruct each control normally
|
||||
else:
|
||||
# get the class for the control with that window class
|
||||
Klass = WindowClassRegistry().GetClass(ctrl.attrib["Class"])
|
||||
|
||||
# construct the object and append it
|
||||
self.children.append(Klass(ctrl, self.Rectangle))
|
||||
self.children.insert(0, self)
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def AllControls(self):
|
||||
return self.children
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def AddReference(self, ref):
|
||||
|
||||
|
||||
#print "x"*20, ref.AllControls()
|
||||
if len(self.AllControls()) != len(ref.AllControls()):
|
||||
print len(self.AllControls()), len(ref.AllControls())
|
||||
raise "Numbers of controls on ref. dialog does not match Loc. dialog"
|
||||
|
||||
|
||||
allIDsMatched = True
|
||||
allClassesMatched = True
|
||||
for idx, ctrl in enumerate(self.AllControls()):
|
||||
refCtrl = ref.AllControls()[idx]
|
||||
ctrl.ref = refCtrl
|
||||
|
||||
if ctrl.ControlID != refCtrl.ControlID:
|
||||
allIDsMatched = False
|
||||
|
||||
if ctrl.Class != refCtrl.Class:
|
||||
allClassesMatched = False
|
||||
|
||||
toRet = 1
|
||||
|
||||
allIDsSameFlag = 2
|
||||
allClassesSameFlag = 4
|
||||
|
||||
if allIDsMatched:
|
||||
toRet += allIDsSameFlag
|
||||
|
||||
if allClassesMatched:
|
||||
toRet += allClassesSameFlag
|
||||
|
||||
return toRet
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def MenuBlocks(self):
|
||||
allMenuBlocks = []
|
||||
for win in self.children:
|
||||
if win.MenuItems:
|
||||
# we need to get all the separate menu blocks!
|
||||
menuBlocks = GetMenuItemsAsCtrlBocks(win.MenuItems)
|
||||
allMenuBlocks.extend(menuBlocks)
|
||||
|
||||
return allMenuBlocks
|
||||
|
||||
|
||||
|
||||
|
||||
class DummyCtrl(dict):
|
||||
def __getattr__(self, name):
|
||||
if name not in self:
|
||||
if name + "s" in self:
|
||||
return self[name + "s"][0]
|
||||
return self[name]
|
||||
|
||||
#====================================================================
|
||||
def GetMenuItemsAsCtrlBocks(menuItems):
|
||||
blocks = []
|
||||
|
||||
curBlock = []
|
||||
for item in menuItems:
|
||||
# do a bit of conversion first :-)
|
||||
itemAsCtrl = DummyCtrl()
|
||||
|
||||
itemAsCtrl["Titles"] = [item['Text'], ]
|
||||
itemAsCtrl["ControlID"] = item['ID']
|
||||
itemAsCtrl["Type"] = item['Type']
|
||||
itemAsCtrl["State"] = item['State']
|
||||
|
||||
itemAsCtrl["Class"] = "MenuItem"
|
||||
itemAsCtrl["FriendlyClassName"] = "MenuItem"
|
||||
|
||||
|
||||
itemAsCtrl["Rectangles"] = [RECT(), ]
|
||||
itemAsCtrl["Fonts"] = [LOGFONTW(), ]
|
||||
itemAsCtrl["ClientRect"] = RECT()
|
||||
itemAsCtrl["ContextHelpID"] = 0
|
||||
itemAsCtrl["UserData"] = 0
|
||||
itemAsCtrl["Style"] = 0
|
||||
itemAsCtrl["ExStyle"] = 0
|
||||
itemAsCtrl["IsVisible"] = 1
|
||||
itemAsCtrl.ref = 0
|
||||
|
||||
curBlock.append(itemAsCtrl)
|
||||
|
||||
if item.has_key('MenuItems'):
|
||||
blocks.extend(GetMenuItemsAsCtrlBocks(item['MenuItems']))
|
||||
|
||||
blocks.append(curBlock)
|
||||
|
||||
return blocks
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class ListBox(Window):
|
||||
GetCount = LB_GETCOUNT
|
||||
GetItemTextLen = LB_GETTEXTLEN
|
||||
GetItemText = LB_GETTEXT
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwndOrXML, dlgRect):
|
||||
Window.__init__(self, hwndOrXML, dlgRect)
|
||||
|
||||
if isinstance(hwndOrXML, (int, long)):
|
||||
self.__GetItems()
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __GetItems(self):
|
||||
# find out how many text items are in the combobox
|
||||
numItems = SendMessage(self.handle, self.GetCount, 0, 0)
|
||||
|
||||
# get the text for each item in the combobox
|
||||
for i in range(0, numItems):
|
||||
textLen = SendMessage (self.handle, self.GetItemTextLen, i, 0)
|
||||
text = create_unicode_buffer(textLen+1)
|
||||
SendMessage (self.handle, self.GetItemText, i, byref(text))
|
||||
|
||||
self.Titles.append(text.value)
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class ComboBox(ListBox):
|
||||
GetCount = CB_GETCOUNT
|
||||
GetItemTextLen = CB_GETLBTEXTLEN
|
||||
GetItemText = CB_GETLBTEXT
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwndOrXML, dlgRect):
|
||||
ListBox.__init__(self, hwndOrXML, dlgRect)
|
||||
|
||||
if isinstance(hwndOrXML, (int, long)):
|
||||
droppedRect = RECT()
|
||||
|
||||
SendMessage(
|
||||
self.handle,
|
||||
CB_GETDROPPEDCONTROLRECT,
|
||||
0,
|
||||
byref(droppedRect))
|
||||
|
||||
|
||||
self.properties['DroppedRect'] = droppedRect
|
||||
|
||||
else:
|
||||
|
||||
# get the dropped Rect form
|
||||
droppedRect = XMLToRect(hwndOrXML.find("DROPPEDRECT"))
|
||||
self.properties['DroppedRect'] = droppedRect
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class Button(Window):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwndOrXML, dlgRect):
|
||||
Window.__init__(self, hwndOrXML, dlgRect)
|
||||
|
||||
if isinstance(hwndOrXML, (int, long)):
|
||||
#state = SendMessage(self.handle, BM_GETSTATE, 0, 0)
|
||||
|
||||
style = self.Style()
|
||||
# get the least significant bit
|
||||
styleLSB = style & 0xF
|
||||
|
||||
if styleLSB == BS_3STATE or styleLSB == BS_AUTO3STATE or \
|
||||
styleLSB == BS_AUTOCHECKBOX or \
|
||||
styleLSB == BS_CHECKBOX:
|
||||
self.properties['FriendlyClassName'] = "CheckBox"
|
||||
elif styleLSB == BS_RADIOBUTTON or styleLSB == BS_AUTORADIOBUTTON:
|
||||
self.properties['FriendlyClassName'] = "RadioButton"
|
||||
elif styleLSB == BS_GROUPBOX:
|
||||
self.properties['FriendlyClassName'] = "GroupBox"
|
||||
|
||||
if style & BS_PUSHLIKE:
|
||||
self.properties['FriendlyClassName'] = "Button";
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
# todo - replace importing controlTypes with the registeredClasses functionality
|
||||
WindowClassRegistry().defaultClass = Window
|
||||
#WindowClassRegistry().AddClass("#32770", DialogWindow)
|
||||
|
||||
WindowClassRegistry().AddClass("ComboBox", ComboBox)
|
||||
WindowClassRegistry().AddClass("WindowsForms\d*\.COMBOBOX\..*", ComboBox)
|
||||
|
||||
WindowClassRegistry().AddClass("ListBox", ListBox)
|
||||
WindowClassRegistry().AddClass("WindowsForms\d*\.LISTBOX\..*", ListBox)
|
||||
|
||||
WindowClassRegistry().AddClass("Button", Button)
|
||||
WindowClassRegistry().AddClass("WindowsForms\d*\.BUTTON\..*", Button)
|
||||
|
5
pywinauto/controls/__init__.py
Normal file
5
pywinauto/controls/__init__.py
Normal file
@ -0,0 +1,5 @@
|
||||
from HwndWrapper import WrapHandle, GetDialogPropsFromHandle
|
||||
|
||||
import win32_controls
|
||||
import common_controls
|
||||
|
812
pywinauto/controls/common_controls.py
Normal file
812
pywinauto/controls/common_controls.py
Normal file
@ -0,0 +1,812 @@
|
||||
from win32functions import *
|
||||
from win32defines import *
|
||||
from win32structures import *
|
||||
|
||||
import HwndWrapper
|
||||
|
||||
class AccessDenied(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
#====================================================================
|
||||
class RemoteMemoryBlock(object):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, handle, size = 8192):
|
||||
self.memAddress = 0
|
||||
self.fileMap = 0
|
||||
|
||||
# work with either a HwndWrapper or a real hwnd
|
||||
#try:
|
||||
# handle = handle.hwnd
|
||||
#except:
|
||||
# pass
|
||||
|
||||
|
||||
processID = c_long()
|
||||
GetWindowThreadProcessId(handle, byref(processID))
|
||||
|
||||
self.process = OpenProcess(
|
||||
PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
|
||||
0,
|
||||
processID)
|
||||
|
||||
if not self.process:
|
||||
# TODO - work in a way that cannot be denied (or fail gracefully
|
||||
#raise WinError()
|
||||
#raise "could not communicate with process (%d)"% (processID.value)
|
||||
raise AccessDenied(str(WinError()) + "process: %d", processID.value)
|
||||
|
||||
if GetVersion() < 0x80000000:
|
||||
self.memAddress = VirtualAllocEx(
|
||||
self.process, # remote process
|
||||
0, # let Valloc decide where
|
||||
size, # how much to allocate
|
||||
MEM_RESERVE | MEM_COMMIT, # allocation type
|
||||
PAGE_READWRITE # protection
|
||||
)
|
||||
|
||||
if not self.memAddress:
|
||||
raise WinError()
|
||||
|
||||
|
||||
else:
|
||||
raise "Win9x allocation not supported"
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __del__(self):
|
||||
# Free the memory in the remote process's address space
|
||||
address = ctypes.c_int(self.memAddress)
|
||||
VirtualFreeEx(self.process, byref(address), 0, MEM_RELEASE)
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def Address(self):
|
||||
return self.memAddress
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def Write(self, data):
|
||||
# write the data from this process into the memory allocated
|
||||
# from the other process
|
||||
ret = WriteProcessMemory(
|
||||
self.process,
|
||||
self.memAddress,
|
||||
pointer(data),
|
||||
sizeof(data),
|
||||
0);
|
||||
|
||||
if not ret:
|
||||
raise WinError()
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def Read(self, data, address = None):
|
||||
if not address:
|
||||
address = self.memAddress
|
||||
|
||||
ret = ReadProcessMemory(self.process, address, byref(data), sizeof(data), 0)
|
||||
|
||||
# disabled as it often returns an error - but seems to work fine anyway!!
|
||||
if not ret:
|
||||
raise WinError()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class ListViewWrapper(HwndWrapper.HwndWrapper):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwnd):
|
||||
super(ListViewWrapper, self).__init__(hwnd)
|
||||
|
||||
self.FriendlyClassName = "ListView"
|
||||
|
||||
# set up a memory block in the remote application
|
||||
self.remoteMem = RemoteMemoryBlock(self)
|
||||
|
||||
self._get_column_info()
|
||||
|
||||
self._extra_texts = self.get_extra_texts()
|
||||
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_extra_texts(self):
|
||||
|
||||
colcount = len(self._get_column_info())
|
||||
|
||||
if not colcount:
|
||||
colcount = 1
|
||||
|
||||
itemCount = self.SendMessage(LVM_GETITEMCOUNT)
|
||||
|
||||
texts = []
|
||||
|
||||
# now get the item values...
|
||||
# for each of the rows
|
||||
for nRowIndex in range(0, itemCount):
|
||||
|
||||
# and each of the columns for that row
|
||||
for nColIndex in range(0, colcount):
|
||||
|
||||
# set up the item structure to get the text
|
||||
item = LVITEMW()
|
||||
item.iSubItem = nColIndex
|
||||
item.pszText = self.remoteMem.Address() + sizeof(item) + 1
|
||||
item.cchTextMax = 2000
|
||||
item.mask = LVIF_TEXT
|
||||
|
||||
# Write the local LVITEM structure to the remote memory block
|
||||
ret = self.remoteMem.Write(item)
|
||||
|
||||
# get the text for the requested item
|
||||
retval = self.SendMessage(
|
||||
LVM_GETITEMTEXTW,
|
||||
nRowIndex,
|
||||
self.remoteMem.Address())
|
||||
|
||||
# if it succeeded
|
||||
if retval:
|
||||
|
||||
# Read the remote text string
|
||||
charData = (c_wchar*2000)()
|
||||
ret = self.remoteMem.Read(charData, item.pszText)
|
||||
|
||||
# and add it to the titles
|
||||
texts.append(charData.value)
|
||||
else:
|
||||
texts.append('')
|
||||
|
||||
return texts
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def _get_column_info(self):
|
||||
cols = []
|
||||
|
||||
# Get each ListView columns text data
|
||||
nIndex = 0
|
||||
while True:
|
||||
col = LVCOLUMNW()
|
||||
col.mask = LVCF_WIDTH | LVCF_FMT
|
||||
|
||||
# put the information in the memory that the
|
||||
# other process can read/write
|
||||
self.remoteMem.Write(col)
|
||||
|
||||
# ask the other process to update the information
|
||||
retval = self.SendMessage(
|
||||
LVM_GETCOLUMNW,
|
||||
nIndex,
|
||||
self.remoteMem.Address())
|
||||
|
||||
if not retval:
|
||||
break
|
||||
else:
|
||||
col = self.remoteMem.Read(col)
|
||||
|
||||
cols.append(col)
|
||||
|
||||
nIndex += 1
|
||||
|
||||
|
||||
if cols:
|
||||
self._extra_props['ColumnWidths'] = [col.cx for col in cols]
|
||||
self._extra_props['ColumnCount'] = len(cols)
|
||||
|
||||
else:
|
||||
self._extra_props['ColumnWidths'] = [999, ]
|
||||
self._extra_props['ColumnCount'] = 1
|
||||
|
||||
return cols
|
||||
|
||||
|
||||
|
||||
# commented out as we can get these strings from the header
|
||||
# col = remoteMem.Read(col)
|
||||
#
|
||||
# charData = (c_wchar* 2000)()
|
||||
#
|
||||
# ret = remoteMem.Read(charData, col.pszText)
|
||||
#
|
||||
# self.Titles.append(charData.value)
|
||||
# else:
|
||||
# break
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def GetTreeViewElements(curElem, handle, remoteMem, items = None):
|
||||
|
||||
if items == None:
|
||||
items = []
|
||||
|
||||
item = TVITEMW()
|
||||
item.mask = TVIF_TEXT | TVIF_HANDLE | TVIF_CHILDREN #| TVIF_STATE |
|
||||
item.pszText = remoteMem.Address() + sizeof(item) + 1
|
||||
item.cchTextMax = 2000
|
||||
item.hItem = curElem
|
||||
|
||||
# Write the local LVITEM structure to the remote memory block
|
||||
ret = remoteMem.Write(item)
|
||||
|
||||
# get this entry
|
||||
retval = SendMessage(
|
||||
handle,
|
||||
TVM_GETITEMW,
|
||||
0,
|
||||
remoteMem.Address())
|
||||
|
||||
|
||||
retval = 1
|
||||
if retval:
|
||||
ret = remoteMem.Read(item)
|
||||
|
||||
# Read the remote text string
|
||||
charData = (c_wchar*2000)()
|
||||
ret = remoteMem.Read(charData, item.pszText)
|
||||
|
||||
items.append(charData.value)
|
||||
|
||||
if item.cChildren not in (0,1):
|
||||
print "##### not dealing with that TVN_GETDISPINFO stuff yet"
|
||||
pass
|
||||
|
||||
if item.cChildren == 1:
|
||||
# call this function again for each child handle
|
||||
childElem = SendMessage(
|
||||
handle,
|
||||
TVM_GETNEXTITEM,
|
||||
TVGN_CHILD,
|
||||
curElem)
|
||||
|
||||
if childElem:
|
||||
GetTreeViewElements(childElem, handle, remoteMem, items)
|
||||
|
||||
# get the next element
|
||||
nextElem = SendMessage(
|
||||
handle,
|
||||
TVM_GETNEXTITEM,
|
||||
TVGN_NEXT,
|
||||
curElem)
|
||||
if nextElem:
|
||||
GetTreeViewElements(nextElem, handle, remoteMem, items)
|
||||
|
||||
return items
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class TreeViewWrapper(HwndWrapper.HwndWrapper):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwnd):
|
||||
super(TreeViewWrapper, self).__init__(hwnd)
|
||||
|
||||
self.FriendlyClassName = "TreeView"
|
||||
|
||||
self._extra_text = []
|
||||
|
||||
remoteMem = RemoteMemoryBlock(self)
|
||||
|
||||
# get the root item:
|
||||
rootElem = self.SendMessage(
|
||||
TVM_GETNEXTITEM,
|
||||
TVGN_ROOT)
|
||||
|
||||
self._extra_texts = GetTreeViewElements(rootElem, self, remoteMem)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class HeaderWrapper(HwndWrapper.HwndWrapper):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwnd):
|
||||
super(HeaderWrapper, self).__init__(hwnd)
|
||||
|
||||
self.FriendlyClassName = "Header"
|
||||
|
||||
self._fill_header_info()
|
||||
|
||||
|
||||
#====================================================================
|
||||
def _fill_header_info(self):
|
||||
|
||||
remoteMem = RemoteMemoryBlock(self)
|
||||
|
||||
# get the number of items in the header...
|
||||
itemCount = self.SendMessage(HDM_GETITEMCOUNT)
|
||||
|
||||
for nIndex in range(0, itemCount):
|
||||
|
||||
# get the column rect
|
||||
r = RECT()
|
||||
remoteMem.Write(r)
|
||||
retval = self.SendMessage(
|
||||
HDM_GETITEMRECT,
|
||||
nIndex,
|
||||
remoteMem.Address())
|
||||
|
||||
r = remoteMem.Read(r)
|
||||
self._extra_clientrects.append(r)
|
||||
|
||||
|
||||
item = HDITEMW()
|
||||
item.mask = HDI_FORMAT | HDI_WIDTH | HDI_TEXT #| HDI_ORDER
|
||||
item.cchTextMax = 2000
|
||||
|
||||
# set up the pointer to the text
|
||||
# it should be at the
|
||||
item.pszText = remoteMem.Address() + sizeof(HDITEMW) + 1
|
||||
|
||||
# put the information in the memory that the
|
||||
# other process can read/write
|
||||
remoteMem.Write(item)
|
||||
|
||||
# ask the other process to update the information
|
||||
retval = self.SendMessage(
|
||||
HDM_GETITEMW,
|
||||
nIndex,
|
||||
remoteMem.Address())
|
||||
|
||||
if retval:
|
||||
item = remoteMem.Read(item)
|
||||
|
||||
# Read the remote text string
|
||||
charData = (c_wchar*2000)()
|
||||
remoteMem.Read(charData, item.pszText)
|
||||
self._extra_texts.append(charData.value)
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class StatusBarWrapper(HwndWrapper.HwndWrapper):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwnd):
|
||||
super(StatusBarWrapper, self).__init__(hwnd)
|
||||
|
||||
self.FriendlyClassName = "StatusBar"
|
||||
|
||||
self._fill_statusbar_info()
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def _fill_statusbar_info(self):
|
||||
|
||||
remoteMem = RemoteMemoryBlock(self)
|
||||
|
||||
|
||||
# get the borders for each of the areas there can be a border.
|
||||
borders = (c_int*3)()
|
||||
remoteMem.Write(borders)
|
||||
numParts = self.SendMessage(
|
||||
SB_GETBORDERS,
|
||||
0,
|
||||
remoteMem.Address()
|
||||
)
|
||||
borders = remoteMem.Read(borders)
|
||||
self._extra_props['HorizBorderWidth'] = borders[0]
|
||||
self._extra_props['VertBorderWidth'] = borders[1]
|
||||
self._extra_props['InterBorderWidth'] = borders[2]
|
||||
|
||||
# get the number of parts for this status bar
|
||||
parts = (c_int*99)()
|
||||
remoteMem.Write(parts)
|
||||
numParts = self.SendMessage(
|
||||
SB_GETPARTS,
|
||||
99,
|
||||
remoteMem.Address()
|
||||
)
|
||||
|
||||
for p in range(0, numParts):
|
||||
|
||||
# get the rectangle for this item
|
||||
r = RECT()
|
||||
remoteMem.Write(r)
|
||||
numParts = self.SendMessage(
|
||||
SB_GETRECT,
|
||||
p,
|
||||
remoteMem.Address()
|
||||
)
|
||||
|
||||
r = remoteMem.Read(r)
|
||||
self._extra_clientrects.append(r)
|
||||
|
||||
|
||||
# get the text for this item
|
||||
text = (c_wchar*2000)()
|
||||
remoteMem.Write(text)
|
||||
numParts = self.SendMessage(
|
||||
SB_GETTEXTW,
|
||||
p,
|
||||
remoteMem.Address()
|
||||
)
|
||||
|
||||
text = remoteMem.Read(text)
|
||||
self._extra_texts.append(text.value)
|
||||
|
||||
# remove the first title if we got other titles and the
|
||||
# 2nd is the same
|
||||
#if len (self._extra_texts) >= 1 and self.Text == self._extra_texts[0]:
|
||||
# props["Titles"][0] = ""
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class TabControlWrapper(HwndWrapper.HwndWrapper):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwnd):
|
||||
super(TabControlWrapper, self).__init__(hwnd)
|
||||
|
||||
self.FriendlyClassName = "TabControl"
|
||||
|
||||
self._fill_tabcontrol_info()
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def _fill_tabcontrol_info(self):
|
||||
tooltipHandle = self.SendMessage(TCM_GETTOOLTIPS)
|
||||
|
||||
itemCount = self.SendMessage(TCM_GETITEMCOUNT)
|
||||
self._extra_props['TabCount'] = itemCount
|
||||
|
||||
remoteMem = RemoteMemoryBlock(self)
|
||||
|
||||
for i in range(0, itemCount):
|
||||
|
||||
rect = RECT()
|
||||
remoteMem.Write(rect)
|
||||
|
||||
self.SendMessage(TCM_GETITEMRECT, i, remoteMem.Address())
|
||||
|
||||
remoteMem.Read(rect)
|
||||
|
||||
self._extra_clientrects.append(rect)
|
||||
|
||||
|
||||
item = TCITEMW()
|
||||
item.mask = TCIF_STATE | TCIF_TEXT
|
||||
item.cchTextMax = 1999
|
||||
item.pszText = remoteMem.Address() + sizeof(TCITEMW)
|
||||
remoteMem.Write(item)
|
||||
|
||||
self.SendMessage(TCM_GETITEMW, i, remoteMem.Address())
|
||||
|
||||
remoteMem.Read(item)
|
||||
|
||||
|
||||
self._extra_props.setdefault('TabState', []).append(item.dwState)
|
||||
|
||||
text = (c_wchar*2000)()
|
||||
text = remoteMem.Read(text, remoteMem.Address() + sizeof(TCITEMW))
|
||||
self._extra_texts.append(text.value)
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class ToolbarWrapper(HwndWrapper.HwndWrapper):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwnd):
|
||||
super(ToolbarWrapper, self).__init__(hwnd)
|
||||
|
||||
self.FriendlyClassName = "Toolbar"
|
||||
|
||||
self._fill_toolbar_info()
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def _fill_toolbar_info(self):
|
||||
|
||||
buttonCount = self.SendMessage(TB_BUTTONCOUNT)
|
||||
self._extra_props['ButtonCount'] = buttonCount
|
||||
|
||||
remoteMem = RemoteMemoryBlock(self)
|
||||
|
||||
for i in range(0, buttonCount):
|
||||
|
||||
button = TBBUTTON()
|
||||
|
||||
remoteMem.Write(button)
|
||||
|
||||
self.SendMessage(TB_GETBUTTON, i, remoteMem.Address())
|
||||
|
||||
remoteMem.Read(button)
|
||||
|
||||
buttonInfo = TBBUTTONINFOW()
|
||||
buttonInfo.cbSize = sizeof(TBBUTTONINFOW)
|
||||
buttonInfo.dwMask = TBIF_TEXT | TBIF_COMMAND | TBIF_SIZE | TBIF_COMMAND | TBIF_STYLE | TBIF_STATE
|
||||
buttonInfo.pszText = remoteMem.Address() + sizeof(TBBUTTONINFOW)
|
||||
buttonInfo.cchText = 2000
|
||||
|
||||
remoteMem.Write(buttonInfo)
|
||||
|
||||
self.SendMessage(TB_GETBUTTONINFOW, button.idCommand, remoteMem.Address())
|
||||
|
||||
remoteMem.Read(buttonInfo)
|
||||
|
||||
text = (c_wchar * 1999)()
|
||||
remoteMem.Read(text, remoteMem.Address() + sizeof(TBBUTTONINFOW))
|
||||
|
||||
# PrintCtypesStruct(buttonInfo, ('pszText', 'cchText', 'cbSize', 'dwMask', 'lParam', 'iImage'))
|
||||
|
||||
extendedStyle = self.SendMessage(TB_GETEXTENDEDSTYLE)
|
||||
|
||||
self._extra_props.setdefault('Buttons', []).append(
|
||||
dict(
|
||||
iBitMap = button.iBitmap,
|
||||
idCommand = button.idCommand,
|
||||
fsState = button.fsState,
|
||||
fsStyle = button.fsStyle,
|
||||
cx = buttonInfo.cx,
|
||||
ExStyle = extendedStyle
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# if button.fsStyle & TBSTYLE_DROPDOWN == TBSTYLE_DROPDOWN and \
|
||||
# extendedStyle & TBSTYLE_EX_DRAWDDARROWS != TBSTYLE_EX_DRAWDDARROWS:
|
||||
# props['Buttons'][-1]["DROPDOWNMENU"] = 1
|
||||
#
|
||||
# self.SendMessage(WM_COMMAND, button.idCommand)
|
||||
#
|
||||
# print "Pressing", text.value
|
||||
# handle.SendMessage(TB_PRESSBUTTON, button.idCommand, 1)
|
||||
# handle.SendMessage(TB_PRESSBUTTON, button.idCommand, 0)
|
||||
|
||||
|
||||
|
||||
self._extra_texts.append(text.value)
|
||||
|
||||
#====================================================================
|
||||
class RebarWrapper(HwndWrapper.HwndWrapper):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwnd):
|
||||
super(RebarWrapper, self).__init__(hwnd)
|
||||
|
||||
self.FriendlyClassName = "ReBar"
|
||||
|
||||
self._fill_rebar_info()
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def _fill_rebar_info(self):
|
||||
bandCount = self.SendMessage(RB_GETBANDCOUNT)
|
||||
|
||||
#print bandCount
|
||||
self._extra_props['BandCount'] = bandCount
|
||||
|
||||
remoteMem = RemoteMemoryBlock(self)
|
||||
|
||||
for i in range(0, bandCount):
|
||||
|
||||
bandInfo = REBARBANDINFOW()
|
||||
|
||||
bandInfo.cbSize = sizeof(bandInfo)
|
||||
bandInfo.fMask = RBBIM_ID | RBBIM_TEXT | RBBIM_SIZE | RBBIM_IDEALSIZE | RBBIM_CHILDSIZE | RBBIM_COLORS | RBBIM_STYLE | RBBIM_HEADERSIZE | RBBIM_CHILD
|
||||
bandInfo.pszText = remoteMem.Address() + sizeof(bandInfo)
|
||||
bandInfo.cchText = 2000
|
||||
|
||||
remoteMem.Write(bandInfo)
|
||||
|
||||
self.SendMessage(RB_GETBANDINFOW, i, remoteMem.Address())
|
||||
|
||||
remoteMem.Read(bandInfo)
|
||||
|
||||
text = (c_wchar * 1999)()
|
||||
remoteMem.Read(text, remoteMem.Address() + sizeof(bandInfo))
|
||||
|
||||
self._extra_texts.append(text.value)
|
||||
|
||||
#child = HwndWrapper(bandInfo.hwndChild)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class ToolTipsWrapper(HwndWrapper.HwndWrapper):
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, hwnd):
|
||||
super(ToolTipsWrapper, self).__init__(hwnd)
|
||||
|
||||
self.FriendlyClassName = "ToolTips"
|
||||
|
||||
self.PlayWithToolTipControls()
|
||||
|
||||
|
||||
def PlayWithToolTipControls(self):
|
||||
|
||||
# get the number of tooltips associated with the control
|
||||
count = self.SendMessage(TTM_GETTOOLCOUNT, 0, 0)
|
||||
|
||||
# find the owner window of the tooltip
|
||||
#parent = Window(GetWindow (winToTest, GW_OWNER))
|
||||
|
||||
|
||||
try:
|
||||
remoteMem = RemoteMemoryBlock(self)
|
||||
except AccessDenied:
|
||||
return
|
||||
|
||||
for i in range(0, count):
|
||||
|
||||
for i2 in range(0, count):
|
||||
tipInfo = TOOLINFOW()
|
||||
tipInfo.cbSize = sizeof(TOOLINFOW)
|
||||
tipInfo.lpszText = remoteMem.Address() + sizeof(tipInfo) +1
|
||||
tipInfo.uId = i2
|
||||
#tipInfo.uFlags = 0xff
|
||||
|
||||
remoteMem.Write(tipInfo)
|
||||
|
||||
ret = self.SendMessage(TTM_ENUMTOOLSW, i, remoteMem.Address())
|
||||
|
||||
if not ret:
|
||||
print WinError()
|
||||
sys.exit()
|
||||
else:
|
||||
remoteMem.Read(tipInfo)
|
||||
|
||||
|
||||
#print i, i2, tipInfo.lpszText
|
||||
#print "-" * 10
|
||||
#PrintCtypesStruct(tipInfo)
|
||||
|
||||
|
||||
if tipInfo.lpszText in (LPSTR_TEXTCALLBACKW, 0, -1):
|
||||
#print "CALLBACK CALLBACK CALLBACK"
|
||||
pass
|
||||
|
||||
else:
|
||||
try:
|
||||
text = (c_wchar*2000)()
|
||||
remoteMem.Read(text, tipInfo.lpszText)
|
||||
|
||||
#print "TTT"* 10, `text.value`, i, i2
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
|
||||
#SendMessage(y.hwnd, WM_NOTIFY, )
|
||||
|
||||
|
||||
#n = (c_wchar* 2000)()
|
||||
#ret = ReadProcessMemory(process, y.lpszText, byref(n), sizeof(n), 0)
|
||||
|
||||
#print y.uFlags, Window(y.hwnd).Class, Window(y.hwnd).Title, y.uId, y.hinst, repr(n.value)
|
||||
#curTool += 1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def PrintCtypesStruct(struct, exceptList = []):
|
||||
for f in struct._fields_:
|
||||
name = f[0]
|
||||
if name in exceptList:
|
||||
continue
|
||||
print "%20s "% name, getattr(struct, name)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
HwndWrapper.HwndWrappers["SysListView32"] = ListViewWrapper
|
||||
HwndWrapper.HwndWrappers[r"WindowsForms\d*\.SysListView32\..*"] = ListViewWrapper
|
||||
|
||||
HwndWrapper.HwndWrappers["SysTreeView32"] = TreeViewWrapper
|
||||
HwndWrapper.HwndWrappers[r"WindowsForms\d*\.SysTreeView32\..*"] = TreeViewWrapper
|
||||
|
||||
HwndWrapper.HwndWrappers["SysHeader32"] = HeaderWrapper
|
||||
|
||||
HwndWrapper.HwndWrappers["msctls_statusbar32"] = StatusBarWrapper
|
||||
HwndWrapper.HwndWrappers["HSStatusBar"] = StatusBarWrapper
|
||||
HwndWrapper.HwndWrappers[r"WindowsForms\d*\.msctls_statusbar32\..*"] = StatusBarWrapper
|
||||
|
||||
HwndWrapper.HwndWrappers["SysTabControl32"] = TabControlWrapper
|
||||
|
||||
HwndWrapper.HwndWrappers["ToolbarWindow32"] = ToolbarWrapper
|
||||
##HwndWrapper.HwndWrappers["Afx:00400000:8:00010011:00000010:00000000"] = ToolbarWrapper
|
||||
|
||||
HwndWrapper.HwndWrappers["ReBarWindow32"] = RebarWrapper
|
||||
|
||||
#HwndWrapper.HwndWrappers["tooltips_class32"] = ToolTipsWrapper
|
||||
#
|
||||
##HwndWrapper.HwndWrappers["ComboBoxEx32"] = ComboBoxEx
|
||||
#
|
||||
|
||||
|
||||
|
||||
|
||||
##
|
||||
##
|
||||
###====================================================================
|
||||
##class ComboBoxEx(Controls_Standard.ComboBox):
|
||||
## #----------------------------------------------------------------
|
||||
## def __init__(self, hwndOrXML):
|
||||
# Window.__init__(self, hwndOrXML)
|
||||
##
|
||||
# if isinstance(hwndOrXML, (int, long)):
|
||||
## comboCntrl = SendMessage(
|
||||
## hwndOrXML,
|
||||
## CBEM_GETCOMBOCONTROL,
|
||||
## 0,
|
||||
## 0)
|
||||
##
|
||||
## print "--"*20, comboCntrl
|
||||
## Controls_Standard.ComboBox.__init__(self, comboCntrl)
|
||||
## print self.DroppedRect
|
||||
##
|
||||
##
|
||||
##
|
||||
## droppedRect = RECT()
|
||||
##
|
||||
## SendMessage(
|
||||
## self,
|
||||
## CB_GETDROPPEDCONTROLRECT,
|
||||
## 0,
|
||||
## byref(droppedRect))
|
||||
##
|
||||
## props['DroppedRect'] = droppedRect
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# # find out how many text items are in the combobox
|
||||
# numItems = SendMessage(
|
||||
# self,
|
||||
# CB_GETCOUNT,
|
||||
# 0,
|
||||
# 0)
|
||||
#
|
||||
# print "*"*20, numItems
|
||||
## remoteMem = RemoteMemoryBlock(self)
|
||||
##
|
||||
##
|
||||
## # get the text for each item in the combobox
|
||||
## while True:
|
||||
## item = COMBOBOXEXITEMW()
|
||||
##
|
||||
## item.mask = CBEIF_TEXT
|
||||
## item.cchTextMax = 4000
|
||||
## item.pszText = remoteMem.Address() + sizeof(item) + 1
|
||||
##
|
||||
## remoteMem.Write(item)
|
||||
##
|
||||
## retval = SendMessage (
|
||||
## self,
|
||||
## CBEM_GETITEMW,
|
||||
## 0,
|
||||
## remoteMem.Address()
|
||||
## )
|
||||
##
|
||||
## if retval:
|
||||
## item = remoteMem.Read(item)
|
||||
##
|
||||
## # Read the remote text string
|
||||
## charData = (c_wchar*4000)()
|
||||
## remoteMem.Read(charData, item.pszText)
|
||||
## self.Titles.append(charData.value)
|
||||
## else:
|
||||
## break
|
||||
##
|
||||
#
|
||||
# else:
|
||||
#
|
||||
# # get the dropped Rect form
|
||||
# droppedRect = XMLToRect(hwndOrXML.find("DROPPEDRECT"))
|
||||
# props['DroppedRect'] = droppedRect
|
||||
|
||||
|
||||
|
228
pywinauto/controls/win32_controls.py
Normal file
228
pywinauto/controls/win32_controls.py
Normal file
@ -0,0 +1,228 @@
|
||||
from HwndWrapper import HwndWrapper, HwndWrappers
|
||||
|
||||
from ctypes import *
|
||||
|
||||
from win32defines import *
|
||||
from win32functions import *
|
||||
from win32structures import *
|
||||
|
||||
|
||||
#====================================================================
|
||||
class ButtonWrapper(HwndWrapper):
|
||||
#-----------------------------------------------------------
|
||||
def __init__(self, hwnd):
|
||||
super(ButtonWrapper, self).__init__(hwnd)
|
||||
|
||||
# default to Button for FriendlyClassName
|
||||
self.FriendlyClassName = "Button"
|
||||
self._set_FriendlyClassName()
|
||||
|
||||
self._set_if_needs_image()
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def _set_if_needs_image(self):
|
||||
|
||||
if self.IsVisible and (\
|
||||
self.HasStyle(BS_BITMAP) or \
|
||||
self.HasStyle(BS_ICON) or \
|
||||
self.HasStyle(BS_OWNERDRAW)):
|
||||
|
||||
self._NeedsImageProp = True
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def _set_FriendlyClassName(self):
|
||||
|
||||
|
||||
# get the least significant bit
|
||||
StyleLSB = self.Style & 0xF
|
||||
|
||||
if StyleLSB == BS_3STATE or StyleLSB == BS_AUTO3STATE or \
|
||||
StyleLSB == BS_AUTOCHECKBOX or \
|
||||
StyleLSB == BS_CHECKBOX:
|
||||
self.FriendlyClassName = "CheckBox"
|
||||
|
||||
elif StyleLSB == BS_RADIOBUTTON or StyleLSB == BS_AUTORADIOBUTTON:
|
||||
self.FriendlyClassName = "RadioButton"
|
||||
|
||||
elif StyleLSB == BS_GROUPBOX:
|
||||
self.FriendlyClassName = "GroupBox"
|
||||
|
||||
if self.Style & BS_PUSHLIKE:
|
||||
self.FriendlyClassName = "Button"
|
||||
|
||||
|
||||
#====================================================================
|
||||
def get_multiple_text_items(wrapper, count_msg, item_len_msg, item_get_msg):
|
||||
texts = []
|
||||
|
||||
# find out how many text items are in the combobox
|
||||
numItems = wrapper.SendMessage(count_msg)
|
||||
|
||||
# get the text for each item in the combobox
|
||||
for i in range(0, numItems):
|
||||
textLen = wrapper.SendMessage (item_len_msg, i, 0)
|
||||
|
||||
text = create_unicode_buffer(textLen+1)
|
||||
|
||||
wrapper.SendMessage (item_get_msg, i, byref(text))
|
||||
|
||||
texts.append(text.value)
|
||||
|
||||
return texts
|
||||
|
||||
|
||||
#====================================================================
|
||||
class ComboBoxWrapper(HwndWrapper):
|
||||
#-----------------------------------------------------------
|
||||
def __init__(self, hwnd):
|
||||
super(ComboBoxWrapper, self).__init__(hwnd)
|
||||
|
||||
# default to ComboBox for FriendlyClassName
|
||||
self.FriendlyClassName = "ComboBox"
|
||||
|
||||
self._extra_texts = get_multiple_text_items(self, CB_GETCOUNT, CB_GETLBTEXTLEN, CB_GETLBTEXT)
|
||||
self._extra_props['DroppedRect'] = self.get_droppedrect()
|
||||
|
||||
# get selected item
|
||||
self._extra_props['SelectedItem'] = self.get_selected_index()
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_selected_index(self):
|
||||
return self.SendMessage(CB_GETCURSEL)
|
||||
CurrentlySelected = property(get_selected_index, doc = "The currently selected index of the combobox")
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_droppedrect(self):
|
||||
|
||||
droppedRect = RECT()
|
||||
|
||||
self.SendMessage(
|
||||
CB_GETDROPPEDCONTROLRECT,
|
||||
0,
|
||||
byref(droppedRect))
|
||||
|
||||
# we need to offset the dropped rect from the control
|
||||
droppedRect -= self.Rectangle
|
||||
|
||||
return droppedRect
|
||||
DroppedRect = property(get_droppedrect, doc = "The dropped rectangle of the combobox")
|
||||
|
||||
|
||||
#====================================================================
|
||||
class ListBoxWrapper(HwndWrapper):
|
||||
#-----------------------------------------------------------
|
||||
def __init__(self, hwnd):
|
||||
super(ListBoxWrapper, self).__init__(hwnd)
|
||||
|
||||
# default to LisBox for FriendlyClassName
|
||||
self.FriendlyClassName = "ListBox"
|
||||
|
||||
self._extra_texts = get_multiple_text_items(self, LB_GETCOUNT, LB_GETTEXTLEN, LB_GETTEXT)
|
||||
|
||||
# get selected item
|
||||
self._extra_props['SelectedItem'] = self.get_selected_index()
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_selected_index(self):
|
||||
return self.SendMessage(LB_GETCURSEL)
|
||||
CurrentlySelected = property(get_selected_index, doc = "The currently selected index of the listbox")
|
||||
|
||||
|
||||
#====================================================================
|
||||
class EditWrapper(HwndWrapper):
|
||||
#-----------------------------------------------------------
|
||||
def __init__(self, hwnd):
|
||||
super(EditWrapper, self).__init__(hwnd)
|
||||
|
||||
# default to Edit for FriendlyClassName
|
||||
self.FriendlyClassName = "Edit"
|
||||
|
||||
# find out how many text items are in the combobox
|
||||
numItems = self.SendMessage(EM_GETLINECOUNT)
|
||||
|
||||
# get the text for each item in the combobox
|
||||
for i in range(0, numItems):
|
||||
textLen = self.SendMessage (EM_LINELENGTH, i, 0)
|
||||
|
||||
text = create_unicode_buffer(textLen+1)
|
||||
|
||||
# set the length - which is required
|
||||
text[0] = unichr(textLen)
|
||||
|
||||
self.SendMessage (EM_GETLINE, i, byref(text))
|
||||
|
||||
self._extra_texts.append(text.value)
|
||||
|
||||
self._extra_texts = ["\n".join(self._extra_texts), ]
|
||||
|
||||
# get selected item
|
||||
self._extra_props['SelectionIndices'] = self.get_selectionindices()
|
||||
|
||||
#-----------------------------------------------------------
|
||||
def get_selectionindices(self):
|
||||
start = c_int()
|
||||
end = c_int()
|
||||
self.SendMessage(EM_GETSEL, byref(start), byref(end))
|
||||
|
||||
return (start.value, end.value)
|
||||
SelectionIndices = property(get_selectionindices, doc = "The start and end indices of the current selection")
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
class StaticWrapper(HwndWrapper):
|
||||
def __init__(self, hwnd):
|
||||
super(StaticWrapper, self).__init__(hwnd)
|
||||
|
||||
# default to Edit for FriendlyClassName
|
||||
self.FriendlyClassName = "Static"
|
||||
|
||||
# if the control is visible - and it shows an image
|
||||
if self.IsVisible and (
|
||||
self.HasStyle(SS_ICON) or \
|
||||
self.HasStyle(SS_BITMAP) or \
|
||||
self.HasStyle(SS_CENTERIMAGE) or \
|
||||
self.HasStyle(SS_OWNERDRAW)):
|
||||
|
||||
self._NeedsImageProp = True
|
||||
|
||||
|
||||
|
||||
# the main reason for this is just to make sure that
|
||||
# a Dialog is a known class - and we don't need to take
|
||||
# an image of it (as an unknown control class)
|
||||
class DialogWrapper(HwndWrapper):
|
||||
pass
|
||||
|
||||
|
||||
#====================================================================
|
||||
HwndWrappers["ComboBox"] = ComboBoxWrapper
|
||||
HwndWrappers[r"WindowsForms\d*\.COMBOBOX\..*"] = ComboBoxWrapper
|
||||
HwndWrappers["TComboBox"] = ComboBoxWrapper
|
||||
|
||||
HwndWrappers["ListBox"] = ListBoxWrapper
|
||||
HwndWrappers[r"WindowsForms\d*\.LISTBOX\..*"] = ListBoxWrapper
|
||||
HwndWrappers["TListBox"] = ListBoxWrapper
|
||||
|
||||
HwndWrappers["Button"] = ButtonWrapper
|
||||
HwndWrappers[r"WindowsForms\d*\.BUTTON\..*"] = ButtonWrapper
|
||||
HwndWrappers["TButton"] = ButtonWrapper
|
||||
#HwndWrappers["TCheckBox"] = ButtonWrapper
|
||||
#HwndWrappers["TRadioButton"] = ButtonWrapper
|
||||
|
||||
HwndWrappers["Static"] = StaticWrapper
|
||||
HwndWrappers["TPanel"] = StaticWrapper
|
||||
|
||||
HwndWrappers["Edit"] = EditWrapper
|
||||
HwndWrappers["TEdit"] = EditWrapper
|
||||
HwndWrappers["TMemo"] = EditWrapper
|
||||
|
||||
|
||||
HwndWrappers["#32770"] = DialogWrapper
|
||||
|
||||
|
||||
|
||||
#
|
||||
# 'CheckBox': PCheckBox,
|
||||
# 'TCheckBox': PCheckBox,
|
||||
#
|
84
pywinauto/examples/watsup_ex1.py
Normal file
84
pywinauto/examples/watsup_ex1.py
Normal file
@ -0,0 +1,84 @@
|
||||
#encoding=latin1
|
||||
import sys
|
||||
from time import sleep
|
||||
import os
|
||||
import os.path
|
||||
|
||||
from application import getsinglewindow, Application, WindowNotFoundError
|
||||
|
||||
|
||||
def do_test_1():
|
||||
app = Application()._start(r"c:\windows\Notepad")
|
||||
notepadWindow = app.Notepad
|
||||
|
||||
notepadWindow.Edit1.SetText(u"Hello, ägain!", 0, -1)
|
||||
sleep(0.8)
|
||||
notepadWindow.Edit.SetText("\r\nYou still there?")
|
||||
|
||||
sleep(0.2)
|
||||
notepadWindow.Edit.SetText("\r\nGoing Bye Bye now!!")
|
||||
|
||||
sleep(1)
|
||||
notepadWindow.MenuSelect("File->Exit")
|
||||
|
||||
app.Notepad.No.Click()
|
||||
|
||||
|
||||
def do_test_2():
|
||||
|
||||
FILENAME='atestfile.txt'
|
||||
|
||||
if os.path.exists(FILENAME):
|
||||
os.remove(FILENAME)
|
||||
|
||||
app = Application()
|
||||
try:
|
||||
app._connect(title ='Simple Form')
|
||||
except WindowNotFoundError:
|
||||
app._start(r'examples\simple.exe')
|
||||
|
||||
form = app.SimpleForm
|
||||
|
||||
form.Edit.SetText(FILENAME)
|
||||
sleep(.6)
|
||||
|
||||
print 'clicking button to create file'
|
||||
form.CreateFile.Click()
|
||||
|
||||
# now check that the file is there
|
||||
if os.path.exists(FILENAME):
|
||||
print 'file %s is present' %FILENAME
|
||||
else:
|
||||
print "file %s isn't there" % FILENAME
|
||||
|
||||
form.MenuSelect("File->Exit")
|
||||
|
||||
|
||||
def do_test_3():
|
||||
app = Application()
|
||||
try:
|
||||
app._connect(title ='Performance Form 2')
|
||||
except WindowNotFoundError:
|
||||
app._start(r'examples\perform2.exe')
|
||||
|
||||
app.PerformanceForm1.Clickme.Click()
|
||||
waited = 0
|
||||
while not app.PerformacneForm1.Edit1._.Texts and not waited >= 1:
|
||||
print 'waiting'
|
||||
sleep(.1)
|
||||
waited += .1
|
||||
|
||||
print `app.PerformacneForm1.Edit1._.Texts`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def Main():
|
||||
for test_num in sys.argv[1:]:
|
||||
globals()['do_test_%s'%test_num] ()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Main()
|
184
pywinauto/findbestmatch.py
Normal file
184
pywinauto/findbestmatch.py
Normal file
@ -0,0 +1,184 @@
|
||||
import re
|
||||
import difflib
|
||||
|
||||
|
||||
#====================================================================
|
||||
class MatchError(IndexError):
|
||||
def __init__(self, msg = '', items = [], tofind = ''):
|
||||
Exception.__init__(self, msg)
|
||||
|
||||
self.items = items
|
||||
self.tofind = tofind
|
||||
|
||||
def __str__(self):
|
||||
return "Could not find '%s' in '%s'"% (self.tofind, self.items)
|
||||
|
||||
#====================================================================
|
||||
def clean_text(text):
|
||||
|
||||
# remove anything after the first tab
|
||||
text_before_tab = re.sub(r"\t.*", "", text)
|
||||
|
||||
# remove any whitespace or non alphanumeric characters
|
||||
return re.sub(r"[^\w ]|\s+", "", text_before_tab).lower()
|
||||
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def build_unique_index_map(items):
|
||||
mapped_items = {}
|
||||
|
||||
#counters = {}
|
||||
for i, text in enumerate(items):
|
||||
text = clean_text(text)
|
||||
|
||||
# no duplicates so just store it without modification
|
||||
if text not in mapped_items:
|
||||
mapped_items[text] = i
|
||||
|
||||
# else this item appears multiple times
|
||||
else:
|
||||
# find unique text
|
||||
unique_text = text
|
||||
counter = 2
|
||||
while unique_text in mapped_items:
|
||||
unique_text = text + str(counter)
|
||||
counter += 1
|
||||
|
||||
mapped_items[unique_text] = i
|
||||
|
||||
if not mapped_items.has_key(text + "0"):
|
||||
mapped_items[text + "0"] = mapped_items[text]
|
||||
mapped_items[text + "1"] = mapped_items[text]
|
||||
|
||||
return mapped_items
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def find_best_match(search_text, item_texts, items):
|
||||
search_text = clean_text(search_text)
|
||||
|
||||
# Clean each item, make it unique and map to
|
||||
# to the item index
|
||||
item_index_map = build_unique_index_map(item_texts)
|
||||
|
||||
# find the list of best matches
|
||||
matches = difflib.get_close_matches (search_text, item_index_map.keys())
|
||||
|
||||
# best match is the first one - so get the index stored
|
||||
# for that match text
|
||||
try:
|
||||
best_index = item_index_map[matches[0]]
|
||||
except IndexError:
|
||||
raise MatchError(items = item_texts, tofind = search_text)
|
||||
|
||||
|
||||
return items[best_index]
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def get_control_names(control):
|
||||
names = []
|
||||
|
||||
# if it has a reference control - then use that
|
||||
if hasattr(control, 'ref') and control.ref:
|
||||
control = control.ref
|
||||
|
||||
# Add the control based on it's friendly class name
|
||||
names.append(control.FriendlyClassName)
|
||||
|
||||
# if it has some character text then add it base on that
|
||||
# and based on that with friendly class name appended
|
||||
if clean_text(control.Text):
|
||||
names.append(control.Text)
|
||||
names.append(control.Text + control.FriendlyClassName)
|
||||
|
||||
# return the names (either 1 or 3 strings)
|
||||
return names
|
||||
|
||||
|
||||
#====================================================================
|
||||
def junk_func(char):
|
||||
if char in ':"/ \t\n\r][{}=-\\|!@#$%^&*,.<>?/()':
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def clean_text2(text): # doesn't change text to lowercase
|
||||
|
||||
# remove anything after the first tab
|
||||
text_before_tab = re.sub(r"\t.*", "", text)
|
||||
|
||||
# remove any whitespace or non alphanumeric characters
|
||||
return re.sub(r"\W", "", text_before_tab)
|
||||
|
||||
|
||||
#====================================================================
|
||||
def find_best_control_match(search_text, controls):
|
||||
|
||||
|
||||
name_control_map = {}
|
||||
|
||||
# collect all the possible names for all controls
|
||||
# and build a list of them
|
||||
for c in controls:
|
||||
ctrl_names = get_control_names(c)
|
||||
ctrl_names = [clean_text2(n) for n in ctrl_names]
|
||||
|
||||
# remove duplicates
|
||||
ctrl_names = list(set(ctrl_names))
|
||||
|
||||
# for each of the names
|
||||
for n in ctrl_names:
|
||||
|
||||
# if its not there already then just add it
|
||||
if not name_control_map.has_key(n):
|
||||
|
||||
name_control_map[n] = c
|
||||
|
||||
# else this item appears multiple times
|
||||
else:
|
||||
# find unique name
|
||||
unique_text = n
|
||||
counter = 2
|
||||
while unique_text in name_control_map:
|
||||
unique_text = n + str(counter)
|
||||
counter += 1
|
||||
|
||||
# add it with that unique text
|
||||
name_control_map[unique_text] = c
|
||||
|
||||
# and if this was the first time that we noticied that
|
||||
# it was a duplicated name then add new items based on the
|
||||
# duplicated name but add '0' and '1'
|
||||
if not name_control_map.has_key(n + "0"):
|
||||
name_control_map[n + "0"] = name_control_map[n]
|
||||
name_control_map[n + "1"] = name_control_map[n]
|
||||
|
||||
|
||||
# now time to figre out the matching
|
||||
ratio_calc = difflib.SequenceMatcher()
|
||||
ratio_calc.set_seq1(clean_text2(search_text))
|
||||
|
||||
best_ratio = 0
|
||||
best_control = None
|
||||
|
||||
for name, control in name_control_map.items():
|
||||
ratio_calc.set_seq2(name)
|
||||
|
||||
if ratio_calc.ratio() > best_ratio:
|
||||
best_ratio = ratio_calc.quick_ratio()
|
||||
best_control = control
|
||||
|
||||
if best_ratio < .5:
|
||||
raise MatchError(items = name_control_map.keys(), tofind = search_text)
|
||||
|
||||
return best_control
|
||||
|
||||
|
39
pywinauto/tests/__init__.py
Normal file
39
pywinauto/tests/__init__.py
Normal file
@ -0,0 +1,39 @@
|
||||
__all__ = ['registered_tests']
|
||||
|
||||
|
||||
test_names = (
|
||||
"AllControls",
|
||||
"AsianHotkey",
|
||||
"ComboBoxDroppedHeight",
|
||||
"CompareToRefFont",
|
||||
"LeadTrailSpaces",
|
||||
"MiscValues",
|
||||
"Missalignment",
|
||||
"MissingExtraString",
|
||||
"Overlapping",
|
||||
"RepeatedHotkey",
|
||||
"Translation",
|
||||
"Truncation",
|
||||
# "menux",
|
||||
)
|
||||
|
||||
|
||||
def __init_tests():
|
||||
"Initialize each test by loading it and then register it"
|
||||
initialized = {}
|
||||
|
||||
for test_name in test_names:
|
||||
# get a reference to the package
|
||||
package = __import__("tests."+ test_name.lower()) #
|
||||
|
||||
# get the reference to the module
|
||||
test_module = getattr(package, test_name.lower())
|
||||
|
||||
# class name is the test name + "Test"
|
||||
test_class = getattr(test_module, test_name + "Test")
|
||||
|
||||
initialized[test_name] = test_class
|
||||
|
||||
return initialized
|
||||
|
||||
registered = __init_tests()
|
79
pywinauto/tests/_menux.py
Normal file
79
pywinauto/tests/_menux.py
Normal file
@ -0,0 +1,79 @@
|
||||
"tests a set of controls for repeated hotkey errors"
|
||||
|
||||
__revision__ = "0.0.1"
|
||||
#
|
||||
#
|
||||
#from Test_AsianHotkey import AsianHotkeyFormatIncorrect
|
||||
#from win32structures import RECT, LOGFONTW
|
||||
#
|
||||
#class DummyCtrl(dict):
|
||||
# def __getattr__(self, name):
|
||||
# if name not in self:
|
||||
# if name + "s" in self:
|
||||
# return self[name + "s"][0]
|
||||
# return self[name]
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
##-----------------------------------------------------------------------------
|
||||
#def MenuRepeatedHotkeyTest(windows):
|
||||
# "Return the repeated hotkey errors"
|
||||
# bugs = []
|
||||
#
|
||||
# from InfoManager import TestFuncRegistry
|
||||
#
|
||||
# for win in windows:
|
||||
# if win.MenuItems:
|
||||
# # we need to get all the separate menu blocks!
|
||||
# menuBlocks = GetMenuItemsAsCtrlBocks(win.MenuItems)
|
||||
#
|
||||
# for menuBlock in menuBlocks:
|
||||
#
|
||||
# for test in TestFuncRegistry().RegisteredClasses():
|
||||
#
|
||||
# TestFunc = TestFuncRegistry().GetClass(test)
|
||||
#
|
||||
# if hasattr(TestFunc, "TestsMenus") and TestFunc.TestsMenus:
|
||||
#
|
||||
# testBugs = TestFunc(menuBlock)
|
||||
# bugs.extend(testBugs)
|
||||
#
|
||||
#
|
||||
##
|
||||
## if AsianHotkeyFormatIncorrect(item['Text']):
|
||||
## bugs.append(
|
||||
## (
|
||||
## [win,],
|
||||
## {
|
||||
## "MenuItem": item['Text'],
|
||||
## },
|
||||
## "MenuAsianHotkeyFormat",
|
||||
## 0)
|
||||
## )
|
||||
##
|
||||
##
|
||||
#
|
||||
#
|
||||
#
|
||||
## bugs.append((
|
||||
## controls,
|
||||
## {
|
||||
## "RepeatedHotkey" : char,
|
||||
## "CharsUsedInDialog" : SetAsString(hotkeys),
|
||||
## "AllCharsInDialog" : SetAsString(allChars),
|
||||
## "AvailableInControls" : ctrlsAvailableChars,
|
||||
## },
|
||||
## "RepeatedHotkey",
|
||||
## isInRef)
|
||||
#
|
||||
# return bugs
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
#import tests
|
||||
#tests.register("MenuRepeatedHotkey", AsiMenuRepeatedHotkeyTest)
|
18
pywinauto/tests/allcontrols.py
Normal file
18
pywinauto/tests/allcontrols.py
Normal file
@ -0,0 +1,18 @@
|
||||
import re
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def AllControlsTest(windows):
|
||||
"Returns just one bug for each control"
|
||||
|
||||
bugs = []
|
||||
for win in windows:
|
||||
bugs.append((
|
||||
[win,],
|
||||
{},
|
||||
"AllControls",
|
||||
0
|
||||
))
|
||||
|
||||
|
||||
return bugs
|
||||
|
125
pywinauto/tests/asianhotkey.py
Normal file
125
pywinauto/tests/asianhotkey.py
Normal file
@ -0,0 +1,125 @@
|
||||
import re
|
||||
|
||||
from repeatedhotkey import ImplementsHotkey, GetHotkey
|
||||
|
||||
#asianHotkeyRE = re.compile ("\(&.\)((\t.*)|(\\t.*)|(\(.*\))|:|(\.\.\.)|>|<|(\n.*)\s)*$")
|
||||
|
||||
asianHotkeyRE = re.compile (r"""
|
||||
\(&.\) # the hotkey
|
||||
(
|
||||
(\t.*)| # tab, and then anything
|
||||
#(\\t.*)| # escaped tab, and then anything
|
||||
(\(.*\) # anything in brackets
|
||||
)|
|
||||
\s*| # any whitespace
|
||||
:| # colon
|
||||
(\.\.\.)| # elipsis
|
||||
>| # greater than sign
|
||||
<| # less than sign
|
||||
(\n.*) # newline, and then anything
|
||||
\s)*$""", re.VERBOSE)
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def AsianHotkeyTest(windows):
|
||||
"Return the repeated hotkey errors"
|
||||
|
||||
bugs = []
|
||||
|
||||
for win in windows:
|
||||
# skip it if it doesn't implement hotkey functionality
|
||||
if not ImplementsHotkey(win):
|
||||
continue
|
||||
|
||||
if AsianHotkeyFormatIncorrect(win.Text):
|
||||
|
||||
bugs.append((
|
||||
[win,],
|
||||
{},
|
||||
"AsianHotkeyFormat",
|
||||
0)
|
||||
)
|
||||
|
||||
|
||||
return bugs
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def AsianHotkeyFormatIncorrect(text):
|
||||
# get the hotkey
|
||||
pos, char = GetHotkey(text)
|
||||
|
||||
# if it has a hotkey then check that it is correct Asian format
|
||||
if char:
|
||||
#print text, char
|
||||
found = asianHotkeyRE.search(text)
|
||||
if not found:
|
||||
return True
|
||||
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
|
||||
#
|
||||
#
|
||||
#void CAsianHotkeyFormatTest::operator()(ControlPair control)
|
||||
#{
|
||||
#
|
||||
# // get the 1st title
|
||||
# std::string title = control.loc->Titles()[0];
|
||||
#
|
||||
# // get the hotkey position
|
||||
# size_t hotkeyPos = GetHotkeyPos(title);
|
||||
# if (hotkeyPos == -1)
|
||||
# return;
|
||||
#
|
||||
# // if it uses this '&' as a hoktey
|
||||
# if (!GetHotkeyCtrlData(control.loc))
|
||||
# return;
|
||||
#
|
||||
# // if we have the reference check to make sure that the SAME
|
||||
# // hotkey character is used!
|
||||
# if (control.ref) {
|
||||
# char refHotkey = Hotkey(control.ref->Titles()[0]);
|
||||
# char locHotkey = Hotkey(control.loc->Titles()[0]);
|
||||
#
|
||||
# if (refHotkey != locHotkey)
|
||||
# m_Bugs.push_back(new CAsianHotkeyFormatBug(control, "AsianHotkeyDiffRef"));
|
||||
# }
|
||||
#
|
||||
#
|
||||
#
|
||||
# // ignore strings like "&X:"
|
||||
# if (title.length() <= 3)
|
||||
# return;
|
||||
#
|
||||
#
|
||||
# // so if we have reached here:
|
||||
# // the control REALLY has a hotkey
|
||||
# // and the lenght of the control is greater than 3
|
||||
#
|
||||
# if (hotkeyPos - 2 >= 0 && // at least 4th character ".(&..."
|
||||
# hotkeyPos + 1 <= title.length()-1 && // at most 2nd last character "...(&H)"
|
||||
# title[hotkeyPos-2] == '(' &&
|
||||
# title[hotkeyPos+1] == ')' &&
|
||||
# hotkeyPos +1 == title.find_last_not_of("\n\t :")
|
||||
# )
|
||||
# {
|
||||
# // OK So we know now that the format "..(&X).." is correct and that it is the
|
||||
# // last non space character in the title
|
||||
# ; // SO NO BUG!
|
||||
# }
|
||||
# else /// HEY - a bug!
|
||||
# {
|
||||
# m_Bugs.push_back(new CAsianHotkeyFormatBug(control, "AsianHotkeyFormat"));
|
||||
#
|
||||
# }
|
||||
#
|
||||
#
|
||||
#
|
||||
#}
|
||||
#
|
||||
|
||||
AsianHotkeyTest.TestsMenus = True
|
22
pywinauto/tests/comboboxdroppedheight.py
Normal file
22
pywinauto/tests/comboboxdroppedheight.py
Normal file
@ -0,0 +1,22 @@
|
||||
|
||||
def ComboBoxDroppedHeightTest(windows):
|
||||
bugs = []
|
||||
for win in windows:
|
||||
if not win.ref:
|
||||
continue
|
||||
|
||||
if win.Class != "ComboBox" or win.ref.Class != "ComboBox":
|
||||
continue
|
||||
|
||||
if win.DroppedRect.height() != win.ref.DroppedRect.height():
|
||||
#print win.DroppedRect.height(), win.DroppedRect.height()
|
||||
|
||||
bugs.append((
|
||||
[win, ],
|
||||
{},
|
||||
"ComboBoxDroppedHeight",
|
||||
0,)
|
||||
)
|
||||
|
||||
return bugs
|
||||
|
30
pywinauto/tests/comparetoreffont.py
Normal file
30
pywinauto/tests/comparetoreffont.py
Normal file
@ -0,0 +1,30 @@
|
||||
import win32structures
|
||||
|
||||
def CompareToRefFontTest(windows):
|
||||
bugs = []
|
||||
for win in windows:
|
||||
if not win.ref:
|
||||
continue
|
||||
|
||||
diffs = {}
|
||||
|
||||
for f in win32structures.LOGFONTW._fields_:
|
||||
|
||||
loc = getattr(win.Font, f[0])
|
||||
ref = getattr(win.ref.Font, f[0])
|
||||
if loc != ref:
|
||||
diffs.append(f[0], loc, ref)
|
||||
|
||||
for diff, locVal, refVal in diffs.items():
|
||||
bugs.append((
|
||||
[win, ],
|
||||
{
|
||||
"ValueType": diff,
|
||||
"Ref": unicode(refVal),
|
||||
"Loc": unicode(locVal),
|
||||
},
|
||||
"CompareToRefFont",
|
||||
0,)
|
||||
)
|
||||
return bugs
|
||||
|
54
pywinauto/tests/leadtrailspaces.py
Normal file
54
pywinauto/tests/leadtrailspaces.py
Normal file
@ -0,0 +1,54 @@
|
||||
import win32structures
|
||||
|
||||
def LeadTrailSpacesTest(windows):
|
||||
bugs = []
|
||||
for win in windows:
|
||||
if not win.ref:
|
||||
continue
|
||||
|
||||
locLeadSpaces = GetLeadSpaces(win.Text)
|
||||
locTrailSpaces = GetTrailSpaces(win.Text)
|
||||
|
||||
refLeadSpaces = GetLeadSpaces(win.ref.Text)
|
||||
refTrailSpaces = GetTrailSpaces(win.ref.Text)
|
||||
|
||||
diffs = []
|
||||
if locLeadSpaces != refLeadSpaces:
|
||||
diffs.append("Leading", locLeadSpaces, locTrailSpaces)
|
||||
|
||||
if locTrailSpaces != refTrailSpaces:
|
||||
diffs.append("Trailing", locTrailSpaces, refTrailSpaces)
|
||||
|
||||
for diff, loc, ref in diffs:
|
||||
bugs.append((
|
||||
[win, ],
|
||||
{
|
||||
"Lead-Trail": diff,
|
||||
"Ref": ref,
|
||||
"Loc": loc,
|
||||
},
|
||||
"LeadTrailSpaces",
|
||||
0,)
|
||||
)
|
||||
return bugs
|
||||
|
||||
|
||||
def GetLeadSpaces(title):
|
||||
spaces = ''
|
||||
|
||||
for i in range(0, len(title)):
|
||||
if not title[i].isspace():
|
||||
break
|
||||
|
||||
spaces += title[i]
|
||||
|
||||
return spaces
|
||||
|
||||
|
||||
def GetTrailSpaces(title):
|
||||
rev = "".join(reversed(title))
|
||||
spaces = GetLeadSpaces(rev)
|
||||
return "".join(reversed(spaces))
|
||||
|
||||
|
||||
LeadTrailSpacesTest.TestsMenus = True
|
45
pywinauto/tests/miscvalues.py
Normal file
45
pywinauto/tests/miscvalues.py
Normal file
@ -0,0 +1,45 @@
|
||||
|
||||
def MiscValuesTest(windows):
|
||||
bugs = []
|
||||
for win in windows:
|
||||
if not win.ref:
|
||||
continue
|
||||
|
||||
diffs = {}
|
||||
|
||||
if win.Class != win.ref.Class:
|
||||
diffs["Class"] = (win.Class, win.ref.Class)
|
||||
|
||||
|
||||
if win.Style != win.ref.Style:
|
||||
diffs["Style"] = (win.Style, win.ref.Style())
|
||||
|
||||
if win.ExStyle != win.ref.ExStyle:
|
||||
diffs["ExStyle"] = (win.ExStyle, win.ref.ExStyle)
|
||||
|
||||
if win.ContextHelpID != win.ref.ContextHelpID:
|
||||
diffs["HelpID"] = (win.ContextHelpID, win.ref.ContextHelpID)
|
||||
|
||||
if win.ControlID != win.ref.ControlID:
|
||||
diffs["ControlID"] = (win.ControlID, win.ref.ControlID)
|
||||
|
||||
if win.IsVisible != win.ref.IsVisible:
|
||||
diffs["Visibility"] = (win.IsVisible, win.ref.IsVisible)
|
||||
|
||||
if win.UserData != win.ref.UserData:
|
||||
diffs["UserData"] = (win.UserData, win.ref.UserData)
|
||||
|
||||
|
||||
for diff, vals in diffs.items():
|
||||
bugs.append((
|
||||
[win, ],
|
||||
{
|
||||
"ValueType": diff,
|
||||
"Ref": unicode(vals[1]),
|
||||
"Loc": unicode(vals[0]),
|
||||
},
|
||||
"MiscValues",
|
||||
0,)
|
||||
)
|
||||
return bugs
|
||||
|
51
pywinauto/tests/missalignment.py
Normal file
51
pywinauto/tests/missalignment.py
Normal file
@ -0,0 +1,51 @@
|
||||
from win32structures import RECT
|
||||
|
||||
#====================================================================
|
||||
def MissalignmentTest(windows):
|
||||
|
||||
refAlignments = {}
|
||||
|
||||
#find the controls alligned along each axis
|
||||
for win in windows:
|
||||
if not win.ref:
|
||||
continue
|
||||
|
||||
|
||||
for side in ("top", "left", "right", "bottom"):
|
||||
sideValue = getattr(win.ref.Rectangle, side)
|
||||
|
||||
# make sure that the side dictionary has been created
|
||||
sideAlignments = refAlignments.setdefault(side, {})
|
||||
|
||||
# make sure that the array of controls for this
|
||||
# alignment line has been created and add the current window
|
||||
sideAlignments.setdefault(sideValue, []).append(win)
|
||||
|
||||
bugs = []
|
||||
for side in refAlignments:
|
||||
for alignment in refAlignments[side]:
|
||||
controls = refAlignments[side][alignment]
|
||||
sides = [getattr(c.Rectangle, side) for c in controls]
|
||||
sides = set(sides)
|
||||
|
||||
if len(sides) > 1:
|
||||
|
||||
overAllRect = RECT()
|
||||
overAllRect.left = min([c.Rectangle.left for c in controls])
|
||||
overAllRect.top = min([c.Rectangle.top for c in controls])
|
||||
overAllRect.right = max([c.Rectangle.right for c in controls])
|
||||
overAllRect.bottom = max([c.Rectangle.bottom for c in controls])
|
||||
|
||||
|
||||
bugs.append((
|
||||
controls,
|
||||
{
|
||||
"AlignmentType": side.upper(),
|
||||
"AlignmentRect": overAllRect
|
||||
},
|
||||
"Missalignment",
|
||||
0)
|
||||
)
|
||||
|
||||
return bugs
|
||||
|
44
pywinauto/tests/missingextrastring.py
Normal file
44
pywinauto/tests/missingextrastring.py
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
CharsToCheck = (
|
||||
">",
|
||||
">>",
|
||||
"<",
|
||||
"<<",
|
||||
"&",
|
||||
"&&",
|
||||
"...",
|
||||
":",
|
||||
"@",
|
||||
|
||||
)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def MissingExtraStringTest(windows):
|
||||
bugs = []
|
||||
for win in windows:
|
||||
if not win.ref:
|
||||
continue
|
||||
|
||||
for char in CharsToCheck:
|
||||
missingExtra = ''
|
||||
|
||||
if win.Text.count(char) > win.ref.Text.count(char):
|
||||
missingExtra = "ExtraCharacters"
|
||||
elif win.Text.count(char) < win.ref.Text.count(char):
|
||||
missingExtra = "MissingCharacters"
|
||||
|
||||
if missingExtra:
|
||||
bugs.append((
|
||||
[win,],
|
||||
{
|
||||
"MissingOrExtra": missingExtra,
|
||||
"MissingOrExtraText": char
|
||||
},
|
||||
"MissingExtraString",
|
||||
0))
|
||||
|
||||
return bugs
|
||||
|
||||
|
||||
MissingExtraStringTest.TestsMenus = True
|
||||
|
104
pywinauto/tests/overlapping.py
Normal file
104
pywinauto/tests/overlapping.py
Normal file
@ -0,0 +1,104 @@
|
||||
"test for overlapping controls"
|
||||
|
||||
__revision__ = "0.0.1"
|
||||
|
||||
import win32structures
|
||||
|
||||
#====================================================================
|
||||
def OverlappingTest(windows):
|
||||
"Return the repeated hotkey errors"
|
||||
|
||||
bugs = []
|
||||
|
||||
for i, first in enumerate(windows[:-1]):
|
||||
for second in windows[i+1:]:
|
||||
|
||||
# if the reference controls are available
|
||||
if first.ref and second.ref:
|
||||
|
||||
if first.ref.Rectangle == second.ref.Rectangle and \
|
||||
not first.Rectangle == second.Rectangle:
|
||||
|
||||
bugs.append(([first, second], {}, "NotExactOverlap", 0))
|
||||
|
||||
elif ContainedInOther(first.ref.Rectangle, second.ref.Rectangle) and \
|
||||
not ContainedInOther(first.Rectangle, second.Rectangle):
|
||||
|
||||
bugs.append(([first, second], {}, "NotContainedOverlap", 0))
|
||||
|
||||
|
||||
if Overlapped(first.Rectangle, second.Rectangle) and \
|
||||
not ContainedInOther(first.Rectangle, second.Rectangle) and \
|
||||
not first.Rectangle == second.Rectangle:
|
||||
|
||||
ovlRect = OverlapRect(first.Rectangle, second.Rectangle)
|
||||
|
||||
isInRef = -1
|
||||
if first.ref and second.ref:
|
||||
isInRef = 0
|
||||
if Overlapped(first.ref.Rectangle, second.ref.Rectangle):
|
||||
isInRef = 1
|
||||
|
||||
bugs.append(([first, second], {"OverlappedRect":ovlRect}, "Overlapping", isInRef))
|
||||
|
||||
return bugs
|
||||
|
||||
|
||||
|
||||
#====================================================================
|
||||
def ContainedInOther(rect1, rect2):
|
||||
# check if rect2 is inside rect1
|
||||
|
||||
if rect1.left >= rect2.left and \
|
||||
rect1.top >= rect2.top and \
|
||||
rect1.right <= rect2.right and \
|
||||
rect1.bottom <= rect2.bottom:
|
||||
return True
|
||||
|
||||
# check if rect1 is inside rect2
|
||||
elif rect2.left >= rect1.left and \
|
||||
rect2.top >= rect1.top and \
|
||||
rect2.right <= rect1.right and \
|
||||
rect2.bottom <= rect1.bottom:
|
||||
return True
|
||||
|
||||
# no previous return - so must not be included
|
||||
return False
|
||||
|
||||
|
||||
def Overlapped(rect1, rect2):
|
||||
ovlRect = OverlapRect(rect1, rect2)
|
||||
|
||||
# if it is actually a bug
|
||||
if ovlRect.left < ovlRect.right and ovlRect.top < ovlRect.bottom:
|
||||
# make sure that the rectangle is the 'right way around :-)'
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# Case 1: L2 between L1 and R1 -> max(L1, L2) < min(R1, R2)
|
||||
#
|
||||
# L1 R1
|
||||
# ---------------
|
||||
# L2 R2
|
||||
# --------------
|
||||
#
|
||||
# Case 2: R2 outside L1 and R1 -> NOT max(L1, L2) < min(R1, R2)
|
||||
#
|
||||
# L1 R1
|
||||
# -------------
|
||||
# L2 R2
|
||||
# ------------
|
||||
#
|
||||
def OverlapRect (rect1, rect2):
|
||||
"check whether the 2 rectangles are actually overlapped"
|
||||
|
||||
ovlRect = win32structures.RECT()
|
||||
|
||||
ovlRect.left = max(rect1.left, rect2.left)
|
||||
ovlRect.right = min(rect1.right, rect2.right)
|
||||
ovlRect.top = max(rect1.top, rect2.top)
|
||||
ovlRect.bottom = min(rect1.bottom, rect2.bottom)
|
||||
|
||||
return ovlRect
|
||||
|
172
pywinauto/tests/repeatedhotkey.py
Normal file
172
pywinauto/tests/repeatedhotkey.py
Normal file
@ -0,0 +1,172 @@
|
||||
"tests a set of controls for repeated hotkey errors"
|
||||
|
||||
__revision__ = "0.0.1"
|
||||
|
||||
from win32defines import SS_NOPREFIX
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def RepeatedHotkeyTest(windows):
|
||||
"Return the repeated hotkey errors"
|
||||
|
||||
hotkeyControls, allChars, hotkeys = CollectDialogInfo(windows)
|
||||
|
||||
#if windows[0].Class == "MenuItem":
|
||||
# print "=-" * 20
|
||||
# import pprint
|
||||
# pprint.pprint (windows)
|
||||
|
||||
# get the available characters in the dialog
|
||||
dlgAvailable = allChars.difference(hotkeys)
|
||||
|
||||
# remove some characters that aren't good choices for hotkeys
|
||||
dlgAvailable.difference_update(set("-& _"))
|
||||
|
||||
|
||||
bugs = []
|
||||
# for each hotkey
|
||||
for char, controls in hotkeyControls.items():
|
||||
|
||||
# if there is more than one control associated then it is a bug
|
||||
if len(controls) > 1:
|
||||
|
||||
ctrlsAvailableChars = ""
|
||||
|
||||
# build up the available characters for each control
|
||||
for c in controls:
|
||||
controlChars = ""
|
||||
controlChars = set(c.Text.lower())
|
||||
|
||||
controlAvailableChars = controlChars.intersection(dlgAvailable)
|
||||
controlAvailableChars = "<%s>" % SetAsString(controlAvailableChars)
|
||||
|
||||
ctrlsAvailableChars += controlAvailableChars
|
||||
|
||||
refCtrls = [ctrl.ref for ctrl in controls if ctrl.ref]
|
||||
refHotkeyControls, refAllChars, refHotkeys = CollectDialogInfo(refCtrls)
|
||||
|
||||
isInRef = -1
|
||||
if len(refHotkeys) > 1:
|
||||
isInRef = 1
|
||||
else:
|
||||
isInRef = 0
|
||||
|
||||
bugs.append((
|
||||
controls,
|
||||
{
|
||||
"RepeatedHotkey" : char,
|
||||
"CharsUsedInDialog" : SetAsString(hotkeys),
|
||||
"AllCharsInDialog" : SetAsString(allChars),
|
||||
"AvailableInControls" : ctrlsAvailableChars,
|
||||
},
|
||||
"RepeatedHotkey",
|
||||
isInRef)
|
||||
)
|
||||
|
||||
# # What is the algorithm to try and do all that is necessary to find
|
||||
# # if it is possible to get a fix character if none of the current
|
||||
# # characters are free
|
||||
# for bug in bugs:
|
||||
# for c, chars in bug.bugData:
|
||||
# # control has no possibilities
|
||||
# if not chars:
|
||||
# # check if there are any other hotkey controls
|
||||
# # in the dialog that could be used
|
||||
# others = set(c.Title.lower()).intersection(unUsedChars)
|
||||
|
||||
|
||||
return bugs
|
||||
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def CollectDialogInfo(windows):
|
||||
hotkeyControls = {}
|
||||
allChars = ''
|
||||
|
||||
for win in windows:
|
||||
# skip it if it doesn't implement hotkey functionality
|
||||
if not ImplementsHotkey(win):
|
||||
continue
|
||||
|
||||
# get the hotkey
|
||||
pos, char = GetHotkey(win.Text)
|
||||
|
||||
# if no hotkey for this control
|
||||
# then continue
|
||||
if not char:
|
||||
continue
|
||||
|
||||
# store hotkey with list of used hotkeys
|
||||
# map this hotkey to the list of controls that have it
|
||||
hotkeyControls.setdefault(char.lower(), []).append(win)
|
||||
|
||||
|
||||
# Add the title of this control to the list of available
|
||||
# characters for the dialog
|
||||
allChars += win.Text.lower()
|
||||
|
||||
|
||||
allChars = set(allChars)
|
||||
hotkeys = set(hotkeyControls.keys())
|
||||
|
||||
return hotkeyControls, allChars, hotkeys
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# get hokey position
|
||||
def GetHotkey(text):
|
||||
"Return the position and character of the hotkey"
|
||||
|
||||
# find the last & character that is not followed
|
||||
# by & or by the end of the string
|
||||
|
||||
curEnd = len(text) + 1
|
||||
text = text.replace("&&", "__")
|
||||
while True:
|
||||
pos = text.rfind("&", 0, curEnd)
|
||||
|
||||
# One found was at the end of the text or
|
||||
# no (more) & were found
|
||||
# so return the None value
|
||||
if pos == -1 or pos == len(text):
|
||||
return (-1, '')
|
||||
|
||||
# One found but was prededed by non valid hotkey character
|
||||
# so continue, as that can't be a shortcut
|
||||
if text[pos - 1] == '&':
|
||||
curEnd = pos - 2
|
||||
continue
|
||||
|
||||
# 2 ampersands in a row - so skip
|
||||
# the 1st one and continue
|
||||
return (pos, text[pos+1])
|
||||
|
||||
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def SetAsString(s):
|
||||
return "".join(sorted(s))
|
||||
|
||||
|
||||
#=============================================================================
|
||||
def ImplementsHotkey(win):
|
||||
"checks whether a control interprets & character to be a hotkey"
|
||||
|
||||
# buttons always implement hotkey
|
||||
if win.Class == "Button":
|
||||
return True
|
||||
|
||||
# Statics do if they don't have SS_NOPREFIX style
|
||||
elif win.Class == "Static" and not win.HasStyle(SS_NOPREFIX):
|
||||
return True
|
||||
|
||||
if win.Class == "MenuItem" and win.State != "2048":
|
||||
return True
|
||||
|
||||
# Most controls don't - so just return false if
|
||||
# neither of the above condition hold
|
||||
return False
|
||||
|
||||
|
||||
RepeatedHotkeyTest.TestsMenus = True
|
||||
|
75
pywinauto/tests/translation.py
Normal file
75
pywinauto/tests/translation.py
Normal file
@ -0,0 +1,75 @@
|
||||
import re
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
def TranslationTest(windows):
|
||||
"Returns just one bug for each control"
|
||||
|
||||
bugs = []
|
||||
for win in windows:
|
||||
if not win.ref:
|
||||
continue
|
||||
|
||||
# get if any items are untranslated
|
||||
untranTitles, untranIndices = GetUntranslations(win)
|
||||
|
||||
if untranTitles:
|
||||
indicesAsString = ",".join([unicode(x) for x in untranIndices])
|
||||
|
||||
bugs.append((
|
||||
[win,],
|
||||
{
|
||||
"StringIndices": indicesAsString,
|
||||
"Strings": ('"%s"' % '","'.join(untranTitles))
|
||||
},
|
||||
"Translation",
|
||||
0)
|
||||
)
|
||||
|
||||
|
||||
return bugs
|
||||
|
||||
def GetUntranslations(win):
|
||||
# remove ampersands and other non translatable bits from the string
|
||||
|
||||
nonTransChars = re.compile(
|
||||
"""(\&(?!\&)| # ampersand not followed by an ampersand
|
||||
\.\.\.$| # elipsis ...
|
||||
^\s*| # leading whitespace
|
||||
\s*$| # trailing whitespace
|
||||
\s*:\s*$ # trailing colon (with/without whitespace)
|
||||
)* # repeated as often as necessary
|
||||
""", re.X)
|
||||
|
||||
|
||||
# clean each of the loc titles for comparison
|
||||
cleanedLocTitles = []
|
||||
for title in win.Texts:
|
||||
cleanedLocTitles.append(nonTransChars.sub("", title))
|
||||
|
||||
# clean each of the ref titles for comparison
|
||||
cleanedRefTitles = []
|
||||
for title in win.ref.Texts:
|
||||
cleanedRefTitles.append(nonTransChars.sub("", title))
|
||||
|
||||
untranslatedTitles = []
|
||||
untranslatedIndices = []
|
||||
|
||||
# loop over each of the cleaned loc title
|
||||
for index, title in enumerate(cleanedLocTitles):
|
||||
|
||||
# if the title is empty just skip it
|
||||
if not title:
|
||||
continue
|
||||
|
||||
# if that title is in the cleaned Ref Titles
|
||||
if title in cleanedRefTitles:
|
||||
# add this as one of the bugs
|
||||
untranslatedTitles.append(title)
|
||||
untranslatedIndices.append(index)
|
||||
|
||||
# return all the untranslated titles and thier indices
|
||||
return untranslatedTitles, untranslatedIndices
|
||||
|
||||
|
||||
TranslationTest.TestsMenus = True
|
||||
|
426
pywinauto/tests/truncation.py
Normal file
426
pywinauto/tests/truncation.py
Normal file
@ -0,0 +1,426 @@
|
||||
# pylint: disable-msg=W0611
|
||||
|
||||
"""info on truncation test"""
|
||||
|
||||
|
||||
__revision__ = "0.0.1"
|
||||
|
||||
|
||||
from win32defines import *
|
||||
from win32functions import *
|
||||
from ctypes import byref
|
||||
from win32structures import RECT
|
||||
|
||||
|
||||
#==============================================================================
|
||||
def TruncationTest(windows):
|
||||
"Actually do the test"
|
||||
|
||||
truncations = []
|
||||
|
||||
# for each of the windows in the dialog
|
||||
for win in windows:
|
||||
|
||||
truncIdxs, truncStrings = FindTruncations(win)
|
||||
|
||||
isInRef = -1
|
||||
|
||||
# if there were any truncations for this control
|
||||
if truncIdxs:
|
||||
|
||||
# now that we know there was at least one truncation
|
||||
# check if the reference control has truncations
|
||||
if win.ref:
|
||||
isInRef = 0
|
||||
refTruncIdxs, refTruncStrings = FindTruncations(win.ref)
|
||||
|
||||
if refTruncIdxs:
|
||||
isInRef = 1
|
||||
|
||||
|
||||
|
||||
truncIdxs = ",".join([unicode(x) for x in truncIdxs])
|
||||
truncStrings = '"%s"' % ",".join([unicode(x) for x in truncStrings])
|
||||
truncations.append((
|
||||
[win,],
|
||||
{
|
||||
"StringIndices": truncIdxs,
|
||||
"Strings": truncStrings,
|
||||
},
|
||||
u"Truncation",
|
||||
isInRef)
|
||||
)
|
||||
|
||||
# return all the truncations
|
||||
return truncations
|
||||
|
||||
#==============================================================================
|
||||
def FindTruncations(ctrl):
|
||||
|
||||
truncIdxs = []
|
||||
truncStrings = []
|
||||
|
||||
# for each of the titles this dialog
|
||||
for idx, (text, rect, font, flags) in enumerate(GetTruncationInfo(ctrl)):
|
||||
|
||||
# skip if there is no text
|
||||
if not text:
|
||||
continue
|
||||
|
||||
# get the minimum rectangle
|
||||
minRect = GetMinimumRect(text, font, rect, flags)
|
||||
|
||||
# if the min rectangle is bigger than the rectangle of the
|
||||
# object
|
||||
if minRect.right > rect.right or \
|
||||
minRect.bottom > rect.bottom:
|
||||
|
||||
# append the index and the rectangle to list of bug items
|
||||
truncIdxs.append(idx)
|
||||
truncStrings.append(text)
|
||||
|
||||
return truncIdxs, truncStrings
|
||||
|
||||
|
||||
|
||||
#==============================================================================
|
||||
def GetMinimumRect(text, font, usableRect, drawFlags):
|
||||
isTruncated = False
|
||||
|
||||
# try to create the font
|
||||
# create a Display DC (compatible to the screen)
|
||||
txtDC = CreateDC(u"DISPLAY", None, None, None )
|
||||
|
||||
hFontGUI = CreateFontIndirect(byref(font))
|
||||
|
||||
# Maybe we could not get the font or we got the system font
|
||||
if not hFontGUI:
|
||||
|
||||
# So just get the default system font
|
||||
hFontGUI = GetStockObject(DEFAULT_GUI_FONT)
|
||||
|
||||
# if we still don't have a font!
|
||||
# ----- ie, we're on an antiquated OS, like NT 3.51
|
||||
if not hFontGUI:
|
||||
|
||||
# ----- On Asian platforms, ANSI font won't show.
|
||||
if GetSystemMetrics(SM_DBCSENABLED):
|
||||
# ----- was...(SYSTEM_FONT)
|
||||
hFontGUI = GetStockObject(SYSTEM_FONT)
|
||||
else:
|
||||
# ----- was...(SYSTEM_FONT)
|
||||
hFontGUI = GetStockObject(ANSI_VAR_FONT)
|
||||
|
||||
|
||||
# put our font into the Device Context
|
||||
SelectObject (txtDC, hFontGUI)
|
||||
|
||||
|
||||
modifiedRect = RECT(usableRect)
|
||||
# Now write the text to our DC with our font to get the
|
||||
# rectangle that the text needs to fit in
|
||||
DrawText (txtDC, # The DC
|
||||
unicode(text), # The Title of the control
|
||||
-1, # -1 because sTitle is NULL terminated
|
||||
byref(modifiedRect), # The Rectangle to be calculated to
|
||||
#truncCtrlData.drawTextFormat |
|
||||
DT_CALCRECT | drawFlags)
|
||||
|
||||
#elif modifiedRect.right == usableRect.right and \
|
||||
# modifiedRect.bottom == usableRect.bottom:
|
||||
# print "Oh so you thought you were perfect!!!"
|
||||
|
||||
|
||||
# Delete the font we created
|
||||
DeleteObject(hFontGUI)
|
||||
|
||||
# delete the Display context that we created
|
||||
DeleteDC(txtDC)
|
||||
|
||||
return modifiedRect
|
||||
|
||||
|
||||
|
||||
|
||||
#==============================================================================
|
||||
def ButtonTruncInfo(win):
|
||||
"Return truncation data for buttons"
|
||||
lineFormat = DT_SINGLELINE
|
||||
|
||||
widthAdj = 0
|
||||
heightAdj = 0
|
||||
|
||||
# get the last byte of the style
|
||||
buttonStyle = win.Style & 0xF
|
||||
|
||||
if win.HasStyle(BS_MULTILINE):
|
||||
lineFormat = DT_WORDBREAK
|
||||
|
||||
if buttonStyle == BS_PUSHBUTTON:
|
||||
heightAdj = 4
|
||||
widthAdj = 5
|
||||
|
||||
elif win.HasStyle(BS_PUSHLIKE):
|
||||
widthAdj = 3
|
||||
heightAdj = 3 # 3
|
||||
if win.HasStyle(BS_MULTILINE):
|
||||
widthAdj = 9
|
||||
heightAdj = 2 # 3
|
||||
|
||||
elif buttonStyle == BS_CHECKBOX or buttonStyle == BS_AUTOCHECKBOX:
|
||||
widthAdj = 18
|
||||
|
||||
elif buttonStyle == BS_RADIOBUTTON or buttonStyle == BS_AUTORADIOBUTTON:
|
||||
widthAdj = 19
|
||||
|
||||
elif buttonStyle == BS_GROUPBOX:
|
||||
heightAdj = 4
|
||||
widthAdj = 9
|
||||
lineFormat = DT_SINGLELINE
|
||||
|
||||
if win.HasStyle(BS_BITMAP) or win.HasStyle(BS_ICON):
|
||||
heightAdj = -9000
|
||||
widthAdj = -9000
|
||||
lineFormat = DT_WORDBREAK
|
||||
|
||||
newRect = win.ClientRects[0]
|
||||
newRect.right -= widthAdj
|
||||
newRect.bottom -= heightAdj
|
||||
|
||||
return [(win.Text, newRect, win.Font, lineFormat), ]
|
||||
|
||||
#==============================================================================
|
||||
def ComboBoxTruncInfo(win):
|
||||
"Return truncation data for comboboxes"
|
||||
# canot wrap and never had a hotkey
|
||||
lineFormat = DT_SINGLELINE | DT_NOPREFIX
|
||||
|
||||
if win.HasStyle(CBS_DROPDOWN) or win.HasStyle(CBS_DROPDOWNLIST):
|
||||
widthAdj = 2#5
|
||||
else:
|
||||
widthAdj = 3
|
||||
|
||||
truncData = []
|
||||
for title in win.Texts:
|
||||
newRect = win.ClientRects[0]
|
||||
newRect.right -= widthAdj
|
||||
truncData.append((title, newRect, win.Font, lineFormat))
|
||||
#print title, newRect, win.Font, lineFormat
|
||||
|
||||
return truncData
|
||||
|
||||
#==============================================================================
|
||||
def ComboLBoxTruncInfo(win):
|
||||
"Return truncation data for comboboxes"
|
||||
# canot wrap and never had a hotkey
|
||||
lineFormat = DT_SINGLELINE | DT_NOPREFIX
|
||||
|
||||
truncData = []
|
||||
for title in win.Texts:
|
||||
newRect = win.ClientRects[0]
|
||||
newRect.right -= 5
|
||||
truncData.append((title, newRect, win.Font, lineFormat))
|
||||
|
||||
return truncData
|
||||
|
||||
|
||||
#==============================================================================
|
||||
def ListBoxTruncInfo(win):
|
||||
"Return truncation data for listboxes"
|
||||
# canot wrap and never had a hotkey
|
||||
lineFormat = DT_SINGLELINE | DT_NOPREFIX
|
||||
|
||||
truncData = []
|
||||
for title in win.Texts:
|
||||
newRect = win.ClientRects[0]
|
||||
newRect.right -= 2
|
||||
newRect.bottom -= 1
|
||||
truncData.append((title, newRect, win.Font, lineFormat))
|
||||
|
||||
return truncData
|
||||
|
||||
|
||||
#==============================================================================
|
||||
def StaticTruncInfo(win):
|
||||
"Return truncation data for static controls"
|
||||
lineFormat = DT_WORDBREAK
|
||||
|
||||
if win.HasStyle(SS_CENTERIMAGE) or \
|
||||
win.HasStyle(SS_SIMPLE) or \
|
||||
win.HasStyle(SS_LEFTNOWORDWRAP):
|
||||
lineFormat = DT_SINGLELINE
|
||||
|
||||
if win.HasStyle(SS_NOPREFIX):
|
||||
lineFormat |= DT_NOPREFIX
|
||||
|
||||
return [(win.Text, win.ClientRects[0], win.Font, lineFormat), ]
|
||||
|
||||
#==============================================================================
|
||||
def EditTruncInfo(win):
|
||||
"Return truncation data for static controls"
|
||||
lineFormat = DT_WORDBREAK | DT_NOPREFIX
|
||||
|
||||
if not win.HasStyle(ES_MULTILINE):
|
||||
lineFormat |= DT_SINGLELINE
|
||||
|
||||
return [(win.Text, win.ClientRects[0], win.Font, lineFormat), ]
|
||||
|
||||
|
||||
#==============================================================================
|
||||
def DialogTruncInfo(win):
|
||||
"Return truncation data for dialog type windows"
|
||||
# move it down more into range
|
||||
|
||||
newRect = win.ClientRects[0]
|
||||
|
||||
newRect.top += 5
|
||||
newRect.left += 5
|
||||
newRect.right -= 5
|
||||
|
||||
|
||||
if win.HasStyle(WS_THICKFRAME):
|
||||
newRect.top += 1
|
||||
newRect.left += 1
|
||||
newRect.right -= 1
|
||||
|
||||
# if it has the system menu but is a small caption
|
||||
# then the only button it can have is the close button
|
||||
if win.HasStyle(WS_SYSMENU) and (win.HasExStyle(WS_EX_PALETTEWINDOW) or
|
||||
win.HasExStyle(WS_EX_TOOLWINDOW)):
|
||||
newRect.right -= 15
|
||||
|
||||
|
||||
# all the rest only need to be considered if there is a system menu.
|
||||
elif win.HasStyle(WS_SYSMENU):
|
||||
buttons = []
|
||||
# account for the close button
|
||||
newRect.right -= 18
|
||||
buttons.append('close')
|
||||
|
||||
# account for Icon if it is not disabled
|
||||
if not win.HasExStyle(WS_EX_DLGMODALFRAME):
|
||||
newRect.left += 19 # icon
|
||||
|
||||
|
||||
# account for context sensitive help if set
|
||||
if win.HasExStyle(WS_EX_CONTEXTHELP) and not ( \
|
||||
win.HasStyle(WS_MAXIMIZEBOX) and win.HasStyle(WS_MINIMIZEBOX)):
|
||||
|
||||
newRect.right -= 17
|
||||
|
||||
# there is a bigger gap if the minimize box is there
|
||||
if win.HasStyle(WS_MINIMIZEBOX) or win.HasStyle(WS_MAXIMIZEBOX) or \
|
||||
win.HasStyle(WS_GROUP):
|
||||
newRect.right -= 3
|
||||
|
||||
buttons.append('help')
|
||||
|
||||
|
||||
# account for Maximize button (but skip if WS_GROUP is set
|
||||
if win.HasStyle(WS_MINIMIZEBOX) or win.HasStyle(WS_MAXIMIZEBOX) or \
|
||||
win.HasStyle(WS_GROUP):
|
||||
|
||||
newRect.right -= 32
|
||||
buttons.append('min')
|
||||
buttons.append('max')
|
||||
|
||||
if buttons:
|
||||
# space between first button and dialog edge
|
||||
diff = 5
|
||||
|
||||
# space for each button
|
||||
diff += len(buttons) * 16
|
||||
|
||||
# space between close and next button
|
||||
if len(buttons) > 1:
|
||||
diff += 2
|
||||
|
||||
# extra space between help and min buttons
|
||||
if 'min' in buttons and 'help' in buttons:
|
||||
diff += 4
|
||||
|
||||
return [(win.Text, newRect, win.Font, DT_SINGLELINE), ]
|
||||
|
||||
|
||||
#==============================================================================
|
||||
def StatusBarTruncInfo(win):
|
||||
truncInfo = WindowTruncInfo(win)
|
||||
for i, (title, rect, font, flag) in enumerate(truncInfo):
|
||||
|
||||
rect.bottom -= win.VertBorderWidth
|
||||
if i == 0:
|
||||
rect.right -= win.HorizBorderWidth
|
||||
else:
|
||||
rect.right -= win.InterBorderWidth
|
||||
|
||||
return truncInfo
|
||||
|
||||
#==============================================================================
|
||||
def HeaderTruncInfo(win):
|
||||
truncInfo = WindowTruncInfo(win)
|
||||
|
||||
for i, (title, rect, font, flag) in enumerate(truncInfo):
|
||||
|
||||
rect.right -= 12
|
||||
|
||||
return truncInfo
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#==============================================================================
|
||||
def WindowTruncInfo(win):
|
||||
matchedItems = []
|
||||
|
||||
for i, title in enumerate(win.Texts):
|
||||
|
||||
# Use the client rects for rectangles
|
||||
if i < len(win.ClientRects):
|
||||
rect = win.ClientRects[i]
|
||||
else:
|
||||
# until we run out then just use the first 'main' client rectangle
|
||||
rect = win.ClientRects[0]
|
||||
|
||||
# if we have fewer fonts than titles
|
||||
if len(win.Fonts)-1 < i:
|
||||
font = win.Font
|
||||
else:
|
||||
font = win.Fonts[i]
|
||||
|
||||
# add the item
|
||||
matchedItems.append((win.Texts[i], rect, font, DT_SINGLELINE))
|
||||
|
||||
return matchedItems
|
||||
|
||||
|
||||
|
||||
#==============================================================================
|
||||
TruncInfo = {
|
||||
"#32770" : DialogTruncInfo,
|
||||
"ComboBox" : ComboBoxTruncInfo,
|
||||
"ComboLBox" : ComboLBoxTruncInfo,
|
||||
"ListBox" : ListBoxTruncInfo,
|
||||
"Button" : ButtonTruncInfo,
|
||||
"Edit": EditTruncInfo,
|
||||
"Static" : StaticTruncInfo,
|
||||
|
||||
# "msctls_statusbar32" : StatusBarTruncInfo,
|
||||
# "HSStatusBar" : StatusBarTruncInfo,
|
||||
# "SysHeader32" : HeaderTruncInfo,
|
||||
|
||||
# "SysListView32" : ListViewTruncInfo,
|
||||
#"SysTreeView32" :
|
||||
}
|
||||
|
||||
#==============================================================================
|
||||
def GetTruncationInfo(win):
|
||||
"helper function to hide non special windows"
|
||||
if win.Class in TruncInfo:
|
||||
return TruncInfo[win.Class](win)
|
||||
else:
|
||||
|
||||
return WindowTruncInfo(win)
|
||||
|
||||
|
||||
|
11875
pywinauto/win32defines.py
Normal file
11875
pywinauto/win32defines.py
Normal file
File diff suppressed because it is too large
Load Diff
99
pywinauto/win32functions.py
Normal file
99
pywinauto/win32functions.py
Normal file
@ -0,0 +1,99 @@
|
||||
import ctypes
|
||||
from ctypes import *
|
||||
|
||||
import win32structures
|
||||
from win32structures import *
|
||||
|
||||
CreateBrushIndirect = ctypes.windll.gdi32.CreateBrushIndirect
|
||||
CreateDC = ctypes.windll.gdi32.CreateDCW
|
||||
CreateFontIndirect = ctypes.windll.gdi32.CreateFontIndirectW
|
||||
CreatePen = ctypes.windll.gdi32.CreatePen
|
||||
DeleteDC = ctypes.windll.gdi32.DeleteDC
|
||||
DeleteObject = ctypes.windll.gdi32.DeleteObject
|
||||
DrawText = ctypes.windll.user32.DrawTextW
|
||||
TextOut = ctypes.windll.gdi32.TextOutW
|
||||
EnableWindow = ctypes.windll.user32.EnableWindow
|
||||
EnumChildWindows = ctypes.windll.user32.EnumChildWindows
|
||||
EnumDesktopWindows = ctypes.windll.user32.EnumDesktopWindows
|
||||
EnumWindows = ctypes.windll.user32.EnumWindows
|
||||
GetClassName = ctypes.windll.user32.GetClassNameW
|
||||
GetClientRect = ctypes.windll.user32.GetClientRect
|
||||
GetDC = ctypes.windll.user32.GetDC
|
||||
GetDesktopWindow = ctypes.windll.user32.GetDesktopWindow
|
||||
GetMenu = ctypes.windll.user32.GetMenu
|
||||
GetMenuItemCount = ctypes.windll.user32.GetMenuItemCount
|
||||
GetMenuItemInfo = ctypes.windll.user32.GetMenuItemInfoW
|
||||
GetObject = ctypes.windll.gdi32.GetObjectW
|
||||
GetParent = ctypes.windll.user32.GetParent
|
||||
GetStockObject = ctypes.windll.gdi32.GetStockObject
|
||||
GetSystemMenu = ctypes.windll.user32.GetSystemMenu
|
||||
GetSystemMetrics = ctypes.windll.user32.GetSystemMetrics
|
||||
GetTextMetrics = ctypes.windll.gdi32.GetTextMetricsW
|
||||
GetVersion = ctypes.windll.kernel32.GetVersion
|
||||
GetWindow = ctypes.windll.user32.GetWindow
|
||||
ShowWindow = ctypes.windll.user32.ShowWindow
|
||||
GetWindowContextHelpId = ctypes.windll.user32.GetWindowContextHelpId
|
||||
GetWindowLong = ctypes.windll.user32.GetWindowLongW
|
||||
GetWindowRect = ctypes.windll.user32.GetWindowRect
|
||||
GetWindowText = ctypes.windll.user32.GetWindowTextW
|
||||
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
|
||||
GetCurrentThreadId = ctypes.windll.Kernel32.GetCurrentThreadId
|
||||
GetWindowThreadProcessId = ctypes.windll.user32.GetWindowThreadProcessId
|
||||
AttachThreadInput = ctypes.windll.user32.AttachThreadInput
|
||||
IsChild = ctypes.windll.user32.IsChild
|
||||
IsMenu = ctypes.windll.user32.IsMenu
|
||||
IsWindow = ctypes.windll.user32.IsWindow
|
||||
IsWindowUnicode = ctypes.windll.user32.IsWindowUnicode
|
||||
IsWindowVisible = ctypes.windll.user32.IsWindowVisible
|
||||
IsWindowEnabled = ctypes.windll.user32.IsWindowEnabled
|
||||
MapVirtualKey = ctypes.windll.user32.MapVirtualKeyW
|
||||
OpenProcess = ctypes.windll.kernel32.OpenProcess
|
||||
ReadProcessMemory = ctypes.windll.kernel32.ReadProcessMemory
|
||||
Rectangle = ctypes.windll.gdi32.Rectangle
|
||||
SelectObject = ctypes.windll.gdi32.SelectObject
|
||||
SendMessage = ctypes.windll.user32.SendMessageW
|
||||
SendMessageA = ctypes.windll.user32.SendMessageA
|
||||
PostMessage = ctypes.windll.user32.PostMessageW
|
||||
SetActiveWindow = ctypes.windll.user32.SetActiveWindow
|
||||
GetFocus = ctypes.windll.user32.GetFocus
|
||||
SetFocus = ctypes.windll.user32.SetFocus
|
||||
SetForegroundWindow = ctypes.windll.user32.SetForegroundWindow
|
||||
GetForegroundWindow = ctypes.windll.user32.GetForegroundWindow
|
||||
SetWindowLong = ctypes.windll.user32.SetWindowLongW
|
||||
SystemParametersInfo = ctypes.windll.user32.SystemParametersInfoW
|
||||
VirtualAllocEx = ctypes.windll.kernel32.VirtualAllocEx
|
||||
VirtualFreeEx = ctypes.windll.kernel32.VirtualFreeEx
|
||||
VkKeyScan = ctypes.windll.user32.VkKeyScanW
|
||||
WriteProcessMemory = ctypes.windll.kernel32.WriteProcessMemory
|
||||
MenuItemFromPoint = ctypes.windll.user32.MenuItemFromPoint
|
||||
GetActiveWindow = ctypes.windll.user32.GetActiveWindow
|
||||
GetLastActivePopup = ctypes.windll.user32.GetLastActivePopup
|
||||
FindWindow = ctypes.windll.user32.FindWindowW
|
||||
GetTopWindow = ctypes.windll.user32.GetTopWindow
|
||||
|
||||
GetGUIThreadInfo = ctypes.windll.user32.GetGUIThreadInfo
|
||||
ShowOwnedPopups = ctypes.windll.user32.ShowOwnedPopups
|
||||
WindowFromPoint = ctypes.windll.user32.WindowFromPoint
|
||||
GetMenuBarInfo = ctypes.windll.user32.GetMenuBarInfo
|
||||
GetMessage = ctypes.windll.user32.GetMessageW
|
||||
SendMessageTimeout = ctypes.windll.user32.SendMessageTimeoutW
|
||||
|
||||
WideCharToMultiByte = ctypes.windll.kernel32.WideCharToMultiByte
|
||||
GetACP = ctypes.windll.kernel32.GetACP
|
||||
|
||||
|
||||
CreateProcess = ctypes.windll.kernel32.CreateProcessW
|
||||
TerminateProcess = ctypes.windll.kernel32.TerminateProcess
|
||||
ExitProcess = ctypes.windll.kernel32.ExitProcess
|
||||
|
||||
|
||||
WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject
|
||||
WaitForInputIdle = ctypes.windll.user32.WaitForInputIdle
|
||||
|
||||
#@ stdcall(BOOL, 'kernel32', [POINTER(WCHAR), POINTER(WCHAR), c_long, c_long, c_int, c_ulong, c_long, c_long, c_long, c_long])
|
||||
#def CreateProcessW(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10):
|
||||
# # C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 4271
|
||||
# return CreateProcessW._api_(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10)
|
||||
GetWindowThreadProcessId = ctypes.windll.user32.GetWindowThreadProcessId
|
||||
OpenProcess = ctypes.windll.kernel32.OpenProcess
|
||||
GetModuleFileNameEx = ctypes.windll.psapi.GetModuleFileNameExW
|
612
pywinauto/win32structures.py
Normal file
612
pywinauto/win32structures.py
Normal file
@ -0,0 +1,612 @@
|
||||
from win32defines import LF_FACESIZE
|
||||
from ctypes import *
|
||||
|
||||
|
||||
#LPTTTOOLINFOW = POINTER(tagTOOLINFOW)
|
||||
#PTOOLINFOW = POINTER(tagTOOLINFOW)
|
||||
BOOL = c_int
|
||||
BYTE = c_ubyte
|
||||
CHAR = c_char
|
||||
DWORD = c_ulong
|
||||
HANDLE = c_void_p
|
||||
HBITMAP = c_long
|
||||
LONG = c_long
|
||||
LPVOID = c_void_p
|
||||
PVOID = c_void_p
|
||||
UINT = c_uint
|
||||
WCHAR = c_wchar
|
||||
WORD = c_ushort
|
||||
|
||||
COLORREF = DWORD
|
||||
HBITMAP = LONG
|
||||
HINSTANCE = LONG
|
||||
HMENU = LONG
|
||||
HTREEITEM = LONG
|
||||
HWND = LONG
|
||||
LPARAM = LONG
|
||||
LPBYTE = POINTER(BYTE)
|
||||
LPWSTR = c_long# POINTER(WCHAR)
|
||||
|
||||
|
||||
|
||||
|
||||
class LVCOLUMNW(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 2982
|
||||
('mask', UINT),
|
||||
('fmt', c_int),
|
||||
('cx', c_int),
|
||||
('pszText', c_long), #LPWSTR),
|
||||
('cchTextMax', c_int),
|
||||
('iSubItem', c_int),
|
||||
('iImage', c_int),
|
||||
('iOrder', c_int),
|
||||
]
|
||||
|
||||
|
||||
class LVITEMW(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 2679
|
||||
('mask', UINT),
|
||||
('iItem', c_int),
|
||||
('iSubItem', c_int),
|
||||
('state', UINT),
|
||||
('stateMask', UINT),
|
||||
('pszText', c_long), #LPWSTR),
|
||||
('cchTextMax', c_int),
|
||||
('iImage', c_int),
|
||||
('lParam', LPARAM),
|
||||
('iIndent', c_int),
|
||||
]
|
||||
assert sizeof(LVITEMW) == 40, sizeof(LVITEMW)
|
||||
assert alignment(LVITEMW) == 1, alignment(LVITEMW)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class TVITEMW(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 3755
|
||||
('mask', UINT),
|
||||
('hItem', HTREEITEM),
|
||||
('state', UINT),
|
||||
('stateMask', UINT),
|
||||
('pszText', c_long), #LPWSTR),
|
||||
('cchTextMax', c_int),
|
||||
('iImage', c_int),
|
||||
('iSelectedImage', c_int),
|
||||
('cChildren', c_int),
|
||||
('lParam', LPARAM),
|
||||
]
|
||||
assert sizeof(TVITEMW) == 40, sizeof(TVITEMW)
|
||||
assert alignment(TVITEMW) == 1, alignment(TVITEMW)
|
||||
|
||||
|
||||
|
||||
class LOGFONTW(Structure):
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/wingdi.h 1090
|
||||
('lfHeight', LONG),
|
||||
('lfWidth', LONG),
|
||||
('lfEscapement', LONG),
|
||||
('lfOrientation', LONG),
|
||||
('lfWeight', LONG),
|
||||
('lfItalic', BYTE),
|
||||
('lfUnderline', BYTE),
|
||||
('lfStrikeOut', BYTE),
|
||||
('lfCharSet', BYTE),
|
||||
('lfOutPrecision', BYTE),
|
||||
('lfClipPrecision', BYTE),
|
||||
('lfQuality', BYTE),
|
||||
('lfPitchAndFamily', BYTE),
|
||||
('lfFaceName', WCHAR * LF_FACESIZE),
|
||||
]
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __str__(self):
|
||||
return "('%s' %d)" %(self.lfFaceName, self.lfHeight)
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __repr__(self):
|
||||
return "<LOGFONTW '%s' %d>" %(self.lfFaceName, self.lfHeight)
|
||||
|
||||
|
||||
|
||||
|
||||
assert sizeof(LOGFONTW) == 92, sizeof(LOGFONTW)
|
||||
assert alignment(LOGFONTW) == 4, alignment(LOGFONTW)
|
||||
|
||||
|
||||
#====================================================================
|
||||
class RECT(Structure):
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/windef.h 287
|
||||
('left', LONG),
|
||||
('top', LONG),
|
||||
('right', LONG),
|
||||
('bottom', LONG),
|
||||
]
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __init__(self, otherRect_or_left = None, top = None, right = None, bottom = None):
|
||||
if type(otherRect_or_left) == type(self):
|
||||
self.left = otherRect_or_left.left
|
||||
self.right = otherRect_or_left.right
|
||||
self.top = otherRect_or_left.top
|
||||
self.bottom = otherRect_or_left.bottom
|
||||
else:
|
||||
try:
|
||||
self.left = long(otherRect_or_left)
|
||||
self.right = long(right)
|
||||
self.top = long(top)
|
||||
self.bottom = long(bottom)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __eq__(self, otherRect):
|
||||
"return true if the two rectangles have the same coordinates"
|
||||
return \
|
||||
self.left == otherRect.left and \
|
||||
self.top == otherRect.top and \
|
||||
self.right == otherRect.right and \
|
||||
self.bottom == otherRect.bottom
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __str__(self):
|
||||
return "(L%d, T%d, R%d, B%d)" %(self.left, self.top, self.right, self.bottom)
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __repr__(self):
|
||||
return "<RECT L%d, T%d, R%d, B%d>" %(self.left, self.top, self.right, self.bottom)
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __sub__(self, other):
|
||||
"Return a new rectangle which is this rect offset from the one passed in"
|
||||
newRect = RECT()
|
||||
|
||||
newRect.left = self.left - other.left
|
||||
newRect.right = self.right - other.left
|
||||
|
||||
newRect.top = self.top - other.top
|
||||
newRect.bottom = self.bottom - other.top
|
||||
|
||||
return newRect
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def __add__(self, other):
|
||||
newRect = RECT()
|
||||
|
||||
newRect.left = self.left + other.left
|
||||
newRect.right = self.right + other.left
|
||||
|
||||
newRect.top = self.top + other.top
|
||||
newRect.bottom = self.bottom + other.top
|
||||
|
||||
return newRect
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def width(self):
|
||||
return self.right - self.left
|
||||
|
||||
#----------------------------------------------------------------
|
||||
def height(self):
|
||||
return self.bottom - self.top
|
||||
|
||||
#def __hash__(self):
|
||||
# return hash (self.left, self.top, self.right, self.bottom)
|
||||
|
||||
|
||||
assert sizeof(RECT) == 16, sizeof(RECT)
|
||||
assert alignment(RECT) == 4, alignment(RECT)
|
||||
|
||||
|
||||
class TEXTMETRICW(Structure):
|
||||
_pack_ = 2
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/wingdi.h 878
|
||||
('tmHeight', LONG),
|
||||
('tmAscent', LONG),
|
||||
('tmDescent', LONG),
|
||||
('tmInternalLeading', LONG),
|
||||
('tmExternalLeading', LONG),
|
||||
('tmAveCharWidth', LONG),
|
||||
('tmMaxCharWidth', LONG),
|
||||
('tmWeight', LONG),
|
||||
('tmOverhang', LONG),
|
||||
('tmDigitizedAspectX', LONG),
|
||||
('tmDigitizedAspectY', LONG),
|
||||
('tmFirstChar', WCHAR),
|
||||
('tmLastChar', WCHAR),
|
||||
('tmDefaultChar', WCHAR),
|
||||
('tmBreakChar', WCHAR),
|
||||
('tmItalic', BYTE),
|
||||
('tmUnderlined', BYTE),
|
||||
('tmStruckOut', BYTE),
|
||||
('tmPitchAndFamily', BYTE),
|
||||
('tmCharSet', BYTE),
|
||||
]
|
||||
assert sizeof(TEXTMETRICW) == 58, sizeof(TEXTMETRICW)
|
||||
assert alignment(TEXTMETRICW) == 2, alignment(TEXTMETRICW)
|
||||
|
||||
|
||||
class NONCLIENTMETRICSW(Structure):
|
||||
_pack_ = 2
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 8767
|
||||
('cbSize', UINT),
|
||||
('iBorderWidth', c_int),
|
||||
('iScrollWidth', c_int),
|
||||
('iScrollHeight', c_int),
|
||||
('iCaptionWidth', c_int),
|
||||
('iCaptionHeight', c_int),
|
||||
('lfCaptionFont', LOGFONTW),
|
||||
('iSmCaptionWidth', c_int),
|
||||
('iSmCaptionHeight', c_int),
|
||||
('lfSmCaptionFont', LOGFONTW),
|
||||
('iMenuWidth', c_int),
|
||||
('iMenuHeight', c_int),
|
||||
('lfMenuFont', LOGFONTW),
|
||||
('lfStatusFont', LOGFONTW),
|
||||
('lfMessageFont', LOGFONTW),
|
||||
]
|
||||
|
||||
assert sizeof(NONCLIENTMETRICSW) == 500, sizeof(NONCLIENTMETRICSW)
|
||||
assert alignment(NONCLIENTMETRICSW) == 2, alignment(NONCLIENTMETRICSW)
|
||||
|
||||
|
||||
|
||||
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/wingdi.h 1025
|
||||
class LOGBRUSH(Structure):
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/wingdi.h 1025
|
||||
('lbStyle', UINT),
|
||||
('lbColor', COLORREF),
|
||||
('lbHatch', LONG),
|
||||
]
|
||||
assert sizeof(LOGBRUSH) == 12, sizeof(LOGBRUSH)
|
||||
assert alignment(LOGBRUSH) == 4, alignment(LOGBRUSH)
|
||||
|
||||
|
||||
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 5147
|
||||
class MENUITEMINFOW(Structure):
|
||||
_pack_ = 2
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 5147
|
||||
('cbSize', UINT),
|
||||
('fMask', UINT),
|
||||
('fType', UINT),
|
||||
('fState', UINT),
|
||||
('wID', UINT),
|
||||
('hSubMenu', HMENU),
|
||||
('hbmpChecked', HBITMAP),
|
||||
('hbmpUnchecked', HBITMAP),
|
||||
('dwItemData', DWORD),
|
||||
('dwTypeData', c_wchar_p), #LPWSTR),
|
||||
('cch', UINT),
|
||||
]
|
||||
assert sizeof(MENUITEMINFOW) == 44, sizeof(MENUITEMINFOW)
|
||||
assert alignment(MENUITEMINFOW) == 2, alignment(MENUITEMINFOW)
|
||||
|
||||
|
||||
class POINT(Structure):
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/windef.h 307
|
||||
('x', LONG),
|
||||
('y', LONG),
|
||||
]
|
||||
assert sizeof(POINT) == 8, sizeof(POINT)
|
||||
assert alignment(POINT) == 4, alignment(POINT)
|
||||
|
||||
class MENUBARINFO(Structure):
|
||||
_fields_ = [
|
||||
('cbSize', DWORD),
|
||||
('rcBar', RECT), # rect of bar, popup, item
|
||||
('hMenu', HMENU), # real menu handle of bar, popup
|
||||
('hwndMenu', HWND), # hwnd of item submenu if one
|
||||
('fBarFocused', BOOL, 1), # bar, popup has the focus
|
||||
('fFocused', BOOL, 1), # item has the focus
|
||||
]
|
||||
|
||||
|
||||
UINT = c_uint
|
||||
WPARAM = UINT
|
||||
LONG = c_long
|
||||
LPARAM = LONG
|
||||
DWORD = c_ulong
|
||||
|
||||
class MSG(Structure):
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 1226
|
||||
('hwnd', HWND),
|
||||
('message', UINT),
|
||||
('wParam', WPARAM),
|
||||
('lParam', LPARAM),
|
||||
('time', DWORD),
|
||||
('pt', POINT),
|
||||
]
|
||||
|
||||
assert sizeof(MSG) == 28, sizeof(MSG)
|
||||
assert alignment(MSG) == 4, alignment(MSG)
|
||||
|
||||
class POINT(Structure):
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/windef.h 307
|
||||
('x', LONG),
|
||||
('y', LONG),
|
||||
]
|
||||
assert sizeof(POINT) == 8, sizeof(POINT)
|
||||
assert alignment(POINT) == 4, alignment(POINT)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 1865
|
||||
class TOOLINFOW(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 1865
|
||||
('cbSize', UINT),
|
||||
('uFlags', UINT),
|
||||
('hwnd', HWND),
|
||||
('uId', UINT),
|
||||
('rect', RECT),
|
||||
('hinst', HINSTANCE),
|
||||
('lpszText', c_long),#LPWSTR),
|
||||
('lParam', LPARAM),
|
||||
]
|
||||
assert sizeof(TOOLINFOW) == 44, sizeof(TOOLINFOW)
|
||||
assert alignment(TOOLINFOW) == 1, alignment(TOOLINFOW)
|
||||
|
||||
|
||||
|
||||
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 2225
|
||||
class NMHDR(Structure):
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 2225
|
||||
('hwndFrom', HWND),
|
||||
('idFrom', UINT),
|
||||
('code', UINT),
|
||||
]
|
||||
assert sizeof(NMHDR) == 12, sizeof(NMHDR)
|
||||
assert alignment(NMHDR) == 4, alignment(NMHDR)
|
||||
|
||||
|
||||
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 2068
|
||||
class NMTTDISPINFOW(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 2068
|
||||
('hdr', NMHDR),
|
||||
('lpszText', LPWSTR),
|
||||
('szText', WCHAR * 80),
|
||||
('hinst', HINSTANCE),
|
||||
('uFlags', UINT),
|
||||
('lParam', LPARAM),
|
||||
]
|
||||
|
||||
assert sizeof(NMTTDISPINFOW) == 188, sizeof(NMTTDISPINFOW)
|
||||
assert alignment(NMTTDISPINFOW) == 1, alignment(NMTTDISPINFOW)
|
||||
|
||||
|
||||
NMTTDISPINFOW_V1_SIZE = 184 # Variable c_uint
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
HDITEMW_V1_SIZE = 28 # Variable c_uint
|
||||
|
||||
class HDITEMW(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 617
|
||||
('mask', UINT),
|
||||
('cxy', c_int),
|
||||
('pszText', c_long),#LPWSTR),
|
||||
('hbm', HBITMAP),
|
||||
('cchTextMax', c_int),
|
||||
('fmt', c_int),
|
||||
('lParam', LPARAM),
|
||||
('iImage', c_int),
|
||||
('iOrder', c_int),
|
||||
]
|
||||
assert sizeof(HDITEMW) == 36, sizeof(HDITEMW)
|
||||
assert alignment(HDITEMW) == 1, alignment(HDITEMW)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 4456
|
||||
class COMBOBOXEXITEMW(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
# C:/_tools/Python24/Lib/site-packages/ctypes/wrap/test/commctrl.h 4456
|
||||
('mask', UINT),
|
||||
('iItem', c_int),
|
||||
('pszText', c_long),#LPWSTR),
|
||||
('cchTextMax', c_int),
|
||||
('iImage', c_int),
|
||||
('iSelectedImage', c_int),
|
||||
('iOverlay', c_int),
|
||||
('iIndent', c_int),
|
||||
('lParam', LPARAM),
|
||||
]
|
||||
assert sizeof(COMBOBOXEXITEMW) == 36, sizeof(COMBOBOXEXITEMW)
|
||||
assert alignment(COMBOBOXEXITEMW) == 1, alignment(COMBOBOXEXITEMW)
|
||||
|
||||
|
||||
|
||||
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 4757
|
||||
class TCITEMHEADERW(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 4757
|
||||
('mask', UINT),
|
||||
('lpReserved1', UINT),
|
||||
('lpReserved2', UINT),
|
||||
('pszText', LPWSTR),
|
||||
('cchTextMax', c_int),
|
||||
('iImage', c_int),
|
||||
]
|
||||
|
||||
assert sizeof(TCITEMHEADERW) == 24, sizeof(TCITEMHEADERW)
|
||||
assert alignment(TCITEMHEADERW) == 1, alignment(TCITEMHEADERW)
|
||||
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 4804
|
||||
class TCITEMW(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 4804
|
||||
('mask', UINT),
|
||||
('dwState', DWORD),
|
||||
('dwStateMask', DWORD),
|
||||
('pszText', c_long), #LPWSTR),
|
||||
('cchTextMax', c_int),
|
||||
('iImage', c_int),
|
||||
('lParam', LPARAM),
|
||||
]
|
||||
assert sizeof(TCITEMW) == 28, sizeof(TCITEMW)
|
||||
assert alignment(TCITEMW) == 1, alignment(TCITEMW)
|
||||
|
||||
|
||||
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 1308
|
||||
class TBBUTTONINFOW(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 1308
|
||||
('cbSize', UINT),
|
||||
('dwMask', DWORD),
|
||||
('idCommand', c_int),
|
||||
('iImage', c_int),
|
||||
('fsState', BYTE),
|
||||
('fsStyle', BYTE),
|
||||
('cx', WORD),
|
||||
('lParam', DWORD),
|
||||
('pszText', LPWSTR),
|
||||
('cchText', c_int),
|
||||
]
|
||||
assert sizeof(TBBUTTONINFOW) == 32, sizeof(TBBUTTONINFOW)
|
||||
assert alignment(TBBUTTONINFOW) == 1, alignment(TBBUTTONINFOW)
|
||||
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 953
|
||||
class TBBUTTON(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 953
|
||||
('iBitmap', c_int),
|
||||
('idCommand', c_int),
|
||||
('fsState', BYTE),
|
||||
('fsStyle', BYTE),
|
||||
('bReserved', BYTE * 2),
|
||||
('dwData', DWORD),
|
||||
('iString', c_int),
|
||||
]
|
||||
assert sizeof(TBBUTTON) == 20, sizeof(TBBUTTON)
|
||||
assert alignment(TBBUTTON) == 1, alignment(TBBUTTON)
|
||||
|
||||
|
||||
|
||||
class REBARBANDINFOW(Structure):
|
||||
_pack_ = 1
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/commctrl.h 1636
|
||||
('cbSize', UINT),
|
||||
('fMask', UINT),
|
||||
('fStyle', UINT),
|
||||
('clrFore', COLORREF),
|
||||
('clrBack', COLORREF),
|
||||
('lpText', LPWSTR),
|
||||
('cch', UINT),
|
||||
('iImage', c_int),
|
||||
('hwndChild', HWND),
|
||||
('cxMinChild', UINT),
|
||||
('cyMinChild', UINT),
|
||||
('cx', UINT),
|
||||
('hbmBack', HBITMAP),
|
||||
('wID', UINT),
|
||||
('cyChild', UINT),
|
||||
('cyMaxChild', UINT),
|
||||
('cyIntegral', UINT),
|
||||
('cxIdeal', UINT),
|
||||
('lParam', LPARAM),
|
||||
('cxHeader', UINT),
|
||||
]
|
||||
assert sizeof(REBARBANDINFOW) == 80, sizeof(REBARBANDINFOW)
|
||||
assert alignment(REBARBANDINFOW) == 1, alignment(REBARBANDINFOW)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 223
|
||||
class SECURITY_ATTRIBUTES(Structure):
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 223
|
||||
('nLength', DWORD),
|
||||
('lpSecurityDescriptor', LPVOID),
|
||||
('bInheritHandle', BOOL),
|
||||
]
|
||||
assert sizeof(SECURITY_ATTRIBUTES) == 12, sizeof(SECURITY_ATTRIBUTES)
|
||||
assert alignment(SECURITY_ATTRIBUTES) == 4, alignment(SECURITY_ATTRIBUTES)
|
||||
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 3794
|
||||
class STARTUPINFOW(Structure):
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 3794
|
||||
('cb', DWORD),
|
||||
('lpReserved', LPWSTR),
|
||||
('lpDesktop', LPWSTR),
|
||||
('lpTitle', LPWSTR),
|
||||
('dwX', DWORD),
|
||||
('dwY', DWORD),
|
||||
('dwXSize', DWORD),
|
||||
('dwYSize', DWORD),
|
||||
('dwXCountChars', DWORD),
|
||||
('dwYCountChars', DWORD),
|
||||
('dwFillAttribute', DWORD),
|
||||
('dwFlags', DWORD),
|
||||
('wShowWindow', WORD),
|
||||
('cbReserved2', WORD),
|
||||
('lpReserved2', LPBYTE),
|
||||
('hStdInput', HANDLE),
|
||||
('hStdOutput', HANDLE),
|
||||
('hStdError', HANDLE),
|
||||
]
|
||||
assert sizeof(STARTUPINFOW) == 68, sizeof(STARTUPINFOW)
|
||||
assert alignment(STARTUPINFOW) == 4, alignment(STARTUPINFOW)
|
||||
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 229
|
||||
class PROCESS_INFORMATION(Structure):
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MICROS~4/VC98/Include/winbase.h 229
|
||||
('hProcess', HANDLE),
|
||||
('hThread', HANDLE),
|
||||
('dwProcessId', DWORD),
|
||||
('dwThreadId', DWORD),
|
||||
]
|
||||
assert sizeof(PROCESS_INFORMATION) == 16, sizeof(PROCESS_INFORMATION)
|
||||
assert alignment(PROCESS_INFORMATION) == 4, alignment(PROCESS_INFORMATION)
|
||||
|
||||
|
||||
|
||||
|
121
sandbox/_DrawDialog_Wx.py
Normal file
121
sandbox/_DrawDialog_Wx.py
Normal file
@ -0,0 +1,121 @@
|
||||
## import all of the wxPython GUI package
|
||||
from wxPython.wx import *
|
||||
|
||||
import PyDlgCheckerWrapper
|
||||
|
||||
import sys
|
||||
if len(sys.argv) < 2 :
|
||||
print "Please specify the XML file to read"
|
||||
sys.exit()
|
||||
|
||||
|
||||
## Create a new frame class, derived from the wxPython Frame.
|
||||
class MyFrame(wxFrame):
|
||||
|
||||
def __init__(self, parent, id, title):
|
||||
# First, call the base class' __init__ method to create the frame
|
||||
|
||||
|
||||
PyDlgCheckerWrapper.InitDialogFromFile(sys.argv[1])
|
||||
dlg = PyDlgCheckerWrapper.TestInfo['Dialog']
|
||||
|
||||
wxFrame.__init__(self,
|
||||
parent,
|
||||
id,
|
||||
dlg.Title,
|
||||
wxPoint(100, 100),
|
||||
wxSize(dlg.Rectangle.right, dlg.Rectangle.bottom))
|
||||
|
||||
# Add a panel and some controls to display the size and position
|
||||
panel = wxPanel(self, -1)
|
||||
|
||||
|
||||
classes = {
|
||||
"Static": wxStaticText,
|
||||
"Button": wxButton,
|
||||
"CheckBox": wxCheckBox,
|
||||
"RadioButton": wxRadioButton,
|
||||
"Dialog": None,
|
||||
"#32770": None,
|
||||
"SysTabControl32": None,
|
||||
|
||||
# "GroupBox": None,
|
||||
"GroupBox": wxRadioBox,
|
||||
|
||||
# "Static": wxStaticText,
|
||||
# "Static": wxStaticText,
|
||||
# "Static": wxStaticText,
|
||||
|
||||
}
|
||||
for ctrl in dlg.AllControls()[1:]:
|
||||
wx_class_type = classes.get(ctrl.FriendlyClassName, wxStaticText)
|
||||
|
||||
print ctrl.FriendlyClassName, wx_class_type
|
||||
|
||||
if wx_class_type:
|
||||
width = ctrl.Rectangle.right - ctrl.Rectangle.left
|
||||
height = ctrl.Rectangle.bottom - ctrl.Rectangle.top
|
||||
|
||||
if wx_class_type != wxRadioBox:
|
||||
wx_class_type (
|
||||
panel,
|
||||
-1,
|
||||
ctrl.Title,
|
||||
wxPoint(ctrl.Rectangle.left -3, ctrl.Rectangle.top - 23),
|
||||
wxSize(width, height)).Raise()
|
||||
else:
|
||||
|
||||
wx_class_type (
|
||||
panel,
|
||||
-1,
|
||||
ctrl.Title,
|
||||
wxPoint(ctrl.Rectangle.left -3, ctrl.Rectangle.top - 23),
|
||||
wxSize(width, height),
|
||||
['']).Lower()
|
||||
|
||||
|
||||
|
||||
# wxStaticText(panel, -1, "Size:",
|
||||
# wxDLG_PNT(panel, wxPoint(4, 4)), wxSize(30, 20))
|
||||
#
|
||||
# wxStaticText(panel, -1, "Pos:",
|
||||
# wxDLG_PNT(panel, wxPoint(4, 14)), wxDefaultSize)
|
||||
#
|
||||
# self.sizeCtrl = wxTextCtrl(panel, -1, "",
|
||||
# wxDLG_PNT(panel, wxPoint(24, 4)),
|
||||
# wxDLG_SZE(panel, wxSize(36, -1)),
|
||||
# wxTE_READONLY)
|
||||
# self.posCtrl = wxTextCtrl(panel, -1, "",
|
||||
# wxDLG_PNT(panel, wxPoint(24, 14)),
|
||||
# wxDLG_SZE(panel, wxSize(36, -1)),
|
||||
# wxTE_READONLY)
|
||||
|
||||
|
||||
# This method is called automatically when the CLOSE event is
|
||||
# sent to this window
|
||||
def OnCloseWindow(self, event):
|
||||
# tell the window to kill itself
|
||||
self.Destroy()
|
||||
|
||||
|
||||
|
||||
# Every wxWindows application must have a class derived from wxApp
|
||||
class MyApp(wxApp):
|
||||
|
||||
# wxWindows calls this method to initialize the application
|
||||
def OnInit(self):
|
||||
|
||||
# Create an instance of our customized Frame class
|
||||
frame = MyFrame(NULL, -1, "This is a test")
|
||||
frame.Show(true)
|
||||
|
||||
# Tell wxWindows that this is our main window
|
||||
self.SetTopWindow(frame)
|
||||
|
||||
# Return a success flag
|
||||
return true
|
||||
|
||||
|
||||
app = MyApp(0) # Create an instance of the application class
|
||||
app.MainLoop() # Tell it to start processing events
|
||||
|
405
sandbox/_DrawDialog_win32_1.py
Normal file
405
sandbox/_DrawDialog_win32_1.py
Normal file
@ -0,0 +1,405 @@
|
||||
## import all of the wxPython GUI package
|
||||
from wxPython.wx import *
|
||||
|
||||
|
||||
from ctypes import *
|
||||
|
||||
|
||||
import PyDlgCheckerWrapper
|
||||
|
||||
|
||||
import sys
|
||||
if len(sys.argv) < 2 :
|
||||
print "Please specify the XML file to read"
|
||||
sys.exit()
|
||||
|
||||
PyDlgCheckerWrapper.InitDialogFromFile(sys.argv[1])
|
||||
dlg = PyDlgCheckerWrapper.TestInfo['Dialog']
|
||||
|
||||
|
||||
|
||||
BOOL = c_int
|
||||
BYTE = c_ubyte
|
||||
CHAR = c_char
|
||||
DWORD = c_ulong
|
||||
LONG = c_long
|
||||
LPVOID = c_void_p
|
||||
PVOID = c_void_p
|
||||
UINT = c_uint
|
||||
WCHAR = c_wchar
|
||||
WORD = c_ushort
|
||||
|
||||
COLORREF = DWORD
|
||||
HBITMAP = LONG
|
||||
HINSTANCE = LONG
|
||||
HMENU = LONG
|
||||
HTREEITEM = LONG
|
||||
HWND = LONG
|
||||
LPARAM = LONG
|
||||
LPCWSTR = c_wchar_p
|
||||
LPWSTR = c_wchar_p #POINTER(WCHAR)
|
||||
|
||||
|
||||
|
||||
SW_ERASE = 4 # Variable c_int
|
||||
SW_FORCEMINIMIZE = 11 # Variable c_int
|
||||
SW_HIDE = 0 # Variable c_int
|
||||
SW_INVALIDATE = 2 # Variable c_int
|
||||
SW_MAX = 11 # Variable c_int
|
||||
SW_MAXIMIZE = 3 # Variable c_int
|
||||
SW_MINIMIZE = 6 # Variable c_int
|
||||
SW_NORMAL = 1 # Variable c_int
|
||||
SW_OTHERUNZOOM = 4 # Variable c_int
|
||||
SW_OTHERZOOM = 2 # Variable c_int
|
||||
SW_PARENTCLOSING = 1 # Variable c_int
|
||||
SW_PARENTOPENING = 3 # Variable c_int
|
||||
SW_RESTORE = 9 # Variable c_int
|
||||
SW_SCROLLCHILDREN = 1 # Variable c_int
|
||||
SW_SHOW = 5 # Variable c_int
|
||||
SW_SHOWDEFAULT = 10 # Variable c_int
|
||||
SW_SHOWMAXIMIZED = 3 # Variable c_int
|
||||
SW_SHOWMINIMIZED = 2 # Variable c_int
|
||||
SW_SHOWMINNOACTIVE = 7 # Variable c_int
|
||||
SW_SHOWNA = 8 # Variable c_int
|
||||
SW_SHOWNOACTIVATE = 4 # Variable c_int
|
||||
SW_SHOWNORMAL = 1 # Variable c_int
|
||||
|
||||
|
||||
|
||||
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 2186
|
||||
class CREATESTRUCTW(Structure):
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 2186
|
||||
('lpCreateParams', LPVOID),
|
||||
('hInstance', HINSTANCE),
|
||||
('hMenu', HMENU),
|
||||
('hwndParent', HWND),
|
||||
('cy', c_int),
|
||||
('cx', c_int),
|
||||
('y', c_int),
|
||||
('x', c_int),
|
||||
('style', LONG),
|
||||
('lpszName', LPCWSTR),
|
||||
('lpszClass', LPCWSTR),
|
||||
('dwExStyle', DWORD),
|
||||
]
|
||||
assert sizeof(CREATESTRUCTW) == 48, sizeof(CREATESTRUCTW)
|
||||
assert alignment(CREATESTRUCTW) == 4, alignment(CREATESTRUCTW)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 3278
|
||||
class DLGTEMPLATE(Structure):
|
||||
_pack_ = 2
|
||||
_fields_ = [
|
||||
# C:/PROGRA~1/MIAF9D~1/VC98/Include/winuser.h 3278
|
||||
('style', DWORD),
|
||||
('dwExtendedStyle', DWORD),
|
||||
('cdit', WORD),
|
||||
('x', c_short),
|
||||
('y', c_short),
|
||||
('cx', c_short),
|
||||
('cy', c_short),
|
||||
]
|
||||
assert sizeof(DLGTEMPLATE) == 18, sizeof(DLGTEMPLATE)
|
||||
assert alignment(DLGTEMPLATE) == 2, alignment(DLGTEMPLATE)
|
||||
|
||||
|
||||
def Main():
|
||||
import ctypes
|
||||
CW = ctypes.windll.user32.CreateWindowExW
|
||||
|
||||
# cs = CREATESTRUCTW()
|
||||
# cs.hInstance = 0 # ????
|
||||
# cs.hMenu = 0
|
||||
# cs.hwndParent = 0
|
||||
# cs.cx = 100
|
||||
# cs.cy = 200
|
||||
# cs.x = 10
|
||||
# cs.y = 20
|
||||
# cs.style = 0 #0x80000
|
||||
# cs.lpszName = u"Hi There"
|
||||
# cs.lpszClass = "#32770"
|
||||
# cs.dwExStyle = 0
|
||||
|
||||
|
||||
parent = 0
|
||||
for i, ctrl in enumerate(dlg.AllControls()):
|
||||
|
||||
if i == 0:
|
||||
print "FIRST"
|
||||
#klass = u"#32770"
|
||||
style = ctrl.Style()
|
||||
else:
|
||||
klass = ctrl.Class.upper()
|
||||
style = ctrl.Style()
|
||||
#print klass
|
||||
#style = 0
|
||||
|
||||
klass = ctrl.Class.upper()
|
||||
|
||||
if parent and ctrl.Class == "#32770":
|
||||
continue
|
||||
|
||||
|
||||
handle = CW(
|
||||
ctrl.ExStyle(), # dwExStyle
|
||||
klass, # class
|
||||
ctrl.Title,# titles
|
||||
style , #ctrl.Style(), # style
|
||||
ctrl.Rectangle.left, # x
|
||||
ctrl.Rectangle.top, # y
|
||||
ctrl.Rectangle.right - ctrl.Rectangle.left, # cx
|
||||
ctrl.Rectangle.bottom - ctrl.Rectangle.top, # cy
|
||||
parent, # parent
|
||||
0, # menu
|
||||
ctypes.windll.kernel32.GetModuleHandleW(u"user32.dll"), # hInstance ???
|
||||
0, # lparam
|
||||
)
|
||||
|
||||
|
||||
import time
|
||||
time.sleep(.3)
|
||||
if not parent:
|
||||
parent = handle
|
||||
print handle,
|
||||
x = (c_wchar * 200)()
|
||||
ctypes.windll.user32.GetClassNameW(handle, byref(x) ,100)
|
||||
print x.value
|
||||
style = ctypes.windll.user32.GetWindowLongW(handle, -16)
|
||||
ctypes.windll.user32.EnableWindow(handle, 1)
|
||||
|
||||
if style != ctrl.Style():
|
||||
print "FAILED"
|
||||
|
||||
|
||||
|
||||
ctypes.windll.user32.ShowWindow(handle, SW_SHOW)
|
||||
|
||||
|
||||
|
||||
# edit = CW(
|
||||
# 0, # dwExStyle
|
||||
# u"BUTTON", # class
|
||||
# u"YO There",# titles
|
||||
# 0, # style
|
||||
# 30, # x
|
||||
# 40, # y
|
||||
# 40, # cx
|
||||
# 20, # cy
|
||||
# handle, # parent
|
||||
# 0, # menu
|
||||
# 0, # hInstance ???
|
||||
# 0, # lparam
|
||||
# )
|
||||
#
|
||||
# print edit
|
||||
#
|
||||
# ctypes.windll.user32.ShowWindow(edit, SW_SHOW)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__=='__main__':
|
||||
Main()
|
||||
import time
|
||||
time.sleep(3)
|
||||
|
||||
sys.exit()
|
||||
|
||||
|
||||
|
||||
##class DLGTEMPLATEEX(Structure):
|
||||
### _pack_ = 1
|
||||
## _fields_ = [
|
||||
## ('dlgVer', WORD),
|
||||
## ('signature', WORD),
|
||||
## ('helpID', DWORD),
|
||||
## ('exStyle', DWORD),
|
||||
## ('style', DWORD),
|
||||
## ('cDlgItems', WORD), #LPWSTR),
|
||||
## ('x', c_short),
|
||||
## ('y', c_short),
|
||||
## ('cx', c_short),
|
||||
## ('cy', c_short),
|
||||
## ('menu', c_void_p),
|
||||
## ('windowClass', c_void_p), #LPWSTR),
|
||||
## ('title', c_wchar_p),
|
||||
## ('pointsize', WORD),
|
||||
## ('weight', WORD),
|
||||
## ('italic', BYTE),
|
||||
## ('charset', BYTE),
|
||||
## ('typeface', c_wchar_p),
|
||||
## ]
|
||||
##
|
||||
## def __init__(self, dlg):
|
||||
## self.dlgVer = 1
|
||||
## self.signature = 0xFFFF
|
||||
##
|
||||
## #self.helpID = dlg.helpID
|
||||
## #self.exStyle = dlg.helpID
|
||||
## #self.style = dlg.helpID
|
||||
## #self.cdlgItems = dlg.helpID
|
||||
## self.x = 10
|
||||
## self.y = 10
|
||||
## self.cx = 50
|
||||
## self.cy = 50
|
||||
## #self.menu = xx
|
||||
## self.windowClass = "#32770"
|
||||
## #self.title = xx
|
||||
## #self.pointsize = xx
|
||||
## #self.weight = xx
|
||||
## #self.italic = xx
|
||||
## #self.charset = xx
|
||||
## #self.typeface = xx
|
||||
##
|
||||
##
|
||||
##
|
||||
##
|
||||
##class DLGITEMTEMPLATEEX(Structure):
|
||||
### _pack_ = 1
|
||||
## _fields_ = [
|
||||
## ('helpID', DWORD),
|
||||
## ('exStyle', DWORD),
|
||||
## ('style', DWORD),
|
||||
## ('x', c_short),
|
||||
## ('y', c_short),
|
||||
## ('cx', c_short),
|
||||
## ('cy', c_short),
|
||||
## ('id', WORD),
|
||||
## ('windowClass', c_void_p),
|
||||
## ('title', c_void_p),
|
||||
## ('extraCount', WORD),
|
||||
## ]
|
||||
##
|
||||
## def __init__(self, ctrl):
|
||||
## self.helpID = 1
|
||||
## self.exStyle = 2
|
||||
## self.style = 3
|
||||
## self.x = 10
|
||||
## self.y = 20
|
||||
## self.cx = 6
|
||||
## self.cy = 8
|
||||
## self.id = ctrl.ControlID
|
||||
## self.windowClass = 2134
|
||||
## self.title = u"1234567678"
|
||||
## self.extraCount = 0
|
||||
##
|
||||
##
|
||||
##
|
||||
##
|
||||
##
|
||||
##
|
||||
##
|
||||
#### Create a new frame class, derived from the wxPython Frame.
|
||||
##class MyFrame(wxFrame):
|
||||
##
|
||||
## def __init__(self, parent, id, title):
|
||||
## # First, call the base class' __init__ method to create the frame
|
||||
##
|
||||
##
|
||||
##
|
||||
## wxFrame.__init__(self,
|
||||
## parent,
|
||||
## id,
|
||||
## dlg.Title,
|
||||
## wxPoint(100, 100),
|
||||
## wxSize(dlg.Rectangle.right, dlg.Rectangle.bottom))
|
||||
##
|
||||
## # Add a panel and some controls to display the size and position
|
||||
## panel = wxPanel(self, -1)
|
||||
##
|
||||
##
|
||||
## classes = {
|
||||
## "Static": wxStaticText,
|
||||
## "Button": wxButton,
|
||||
## "CheckBox": wxCheckBox,
|
||||
## "RadioButton": wxRadioButton,
|
||||
## "Dialog": None,
|
||||
## "#32770": None,
|
||||
## "SysTabControl32": None,
|
||||
##
|
||||
### "GroupBox": None,
|
||||
## "GroupBox": wxRadioBox,
|
||||
##
|
||||
### "Static": wxStaticText,
|
||||
### "Static": wxStaticText,
|
||||
### "Static": wxStaticText,
|
||||
##
|
||||
## }
|
||||
## for ctrl in dlg.AllControls()[1:]:
|
||||
## wx_class_type = classes.get(ctrl.FriendlyClassName, wxStaticText)
|
||||
##
|
||||
## print ctrl.FriendlyClassName, wx_class_type
|
||||
##
|
||||
## if wx_class_type:
|
||||
## width = ctrl.Rectangle.right - ctrl.Rectangle.left
|
||||
## height = ctrl.Rectangle.bottom - ctrl.Rectangle.top
|
||||
##
|
||||
## if wx_class_type != wxRadioBox:
|
||||
## wx_class_type (
|
||||
## panel,
|
||||
## -1,
|
||||
## ctrl.Title,
|
||||
## wxPoint(ctrl.Rectangle.left -3, ctrl.Rectangle.top - 23),
|
||||
## wxSize(width, height)).Raise()
|
||||
## else:
|
||||
##
|
||||
## wx_class_type (
|
||||
## panel,
|
||||
## -1,
|
||||
## ctrl.Title,
|
||||
## wxPoint(ctrl.Rectangle.left -3, ctrl.Rectangle.top - 23),
|
||||
## wxSize(width, height),
|
||||
## ['']).Lower()
|
||||
##
|
||||
##
|
||||
##
|
||||
### wxStaticText(panel, -1, "Size:",
|
||||
### wxDLG_PNT(panel, wxPoint(4, 4)), wxSize(30, 20))
|
||||
###
|
||||
### wxStaticText(panel, -1, "Pos:",
|
||||
### wxDLG_PNT(panel, wxPoint(4, 14)), wxDefaultSize)
|
||||
###
|
||||
### self.sizeCtrl = wxTextCtrl(panel, -1, "",
|
||||
### wxDLG_PNT(panel, wxPoint(24, 4)),
|
||||
### wxDLG_SZE(panel, wxSize(36, -1)),
|
||||
### wxTE_READONLY)
|
||||
### self.posCtrl = wxTextCtrl(panel, -1, "",
|
||||
### wxDLG_PNT(panel, wxPoint(24, 14)),
|
||||
### wxDLG_SZE(panel, wxSize(36, -1)),
|
||||
### wxTE_READONLY)
|
||||
##
|
||||
##
|
||||
## # This method is called automatically when the CLOSE event is
|
||||
## # sent to this window
|
||||
## def OnCloseWindow(self, event):
|
||||
## # tell the window to kill itself
|
||||
## self.Destroy()
|
||||
##
|
||||
##
|
||||
##
|
||||
### Every wxWindows application must have a class derived from wxApp
|
||||
##class MyApp(wxApp):
|
||||
##
|
||||
## # wxWindows calls this method to initialize the application
|
||||
## def OnInit(self):
|
||||
##
|
||||
## # Create an instance of our customized Frame class
|
||||
## frame = MyFrame(NULL, -1, "This is a test")
|
||||
## frame.Show(true)
|
||||
##
|
||||
## # Tell wxWindows that this is our main window
|
||||
## self.SetTopWindow(frame)
|
||||
##
|
||||
## # Return a success flag
|
||||
## return true
|
||||
##
|
||||
##
|
||||
##app = MyApp(0) # Create an instance of the application class
|
||||
##app.MainLoop() # Tell it to start processing events
|
||||
|
1574
sandbox/__DeadCodeRepository__.py
Normal file
1574
sandbox/__DeadCodeRepository__.py
Normal file
File diff suppressed because it is too large
Load Diff
34
sandbox/_find_control_distance.py
Normal file
34
sandbox/_find_control_distance.py
Normal file
@ -0,0 +1,34 @@
|
||||
from application import FindWindows
|
||||
win = FindWindows(title = "Replace", class_name = "#32770")[0]
|
||||
|
||||
from findbestmatch import find_best_match
|
||||
|
||||
|
||||
visibleTextChildren = [w for w in win.Children if w.IsVisible and w.Text]
|
||||
|
||||
visibleNonTextChildren = [w for w in win.Children if w.IsVisible and not w.Text]
|
||||
|
||||
for w2 in visibleNonTextChildren:
|
||||
closest = 999
|
||||
newname = ''
|
||||
for text_child in visibleTextChildren:
|
||||
|
||||
# skip controls where w is to the right of w2
|
||||
if text_child.Rectangle.left >= w2.Rectangle.right:
|
||||
continue
|
||||
|
||||
# skip controls where w is below w2
|
||||
if text_child.Rectangle.top >= w2.Rectangle.bottom:
|
||||
continue
|
||||
|
||||
wr = text_child.Rectangle
|
||||
w2r = w2.Rectangle
|
||||
distance = ((wr.left - w2r.left) ** 2.0 + (wr.top - w2r.top) ** 2.0) ** .5
|
||||
|
||||
|
||||
if distance < closest:
|
||||
closest = distance
|
||||
newname = text_child.Text.replace(' ', '').replace ('&', '') + w2.FriendlyClassName
|
||||
|
||||
if closest != 999:
|
||||
print newname
|
295
sandbox/playing_RepeatedHotkeyFixing.py
Normal file
295
sandbox/playing_RepeatedHotkeyFixing.py
Normal file
@ -0,0 +1,295 @@
|
||||
#import set
|
||||
from Test_RepeatedHotkey import GetHotkey
|
||||
|
||||
controls = (
|
||||
"&Hello",
|
||||
"&Exit",
|
||||
"&OK",
|
||||
"&Python",
|
||||
"&Ion Trail",
|
||||
"&Galaxy Quest",
|
||||
"&Xenon",
|
||||
"&Sierra",
|
||||
"&Zope",
|
||||
"&Sizzling",
|
||||
"Here &and Now",
|
||||
"&Later maybe",
|
||||
"&Scram",
|
||||
"Wo&w",
|
||||
"What is T&HAT",
|
||||
"&Mercury",
|
||||
"&Venus",
|
||||
"&Earth",
|
||||
"M&ercury",
|
||||
"Ven&us",
|
||||
"Ea&rth",
|
||||
"&Mercury",
|
||||
"&OK",
|
||||
"&Python",
|
||||
"&Ion Trail",
|
||||
"&Galaxy Quest",
|
||||
)
|
||||
|
||||
|
||||
charIndex = {}
|
||||
hotkeyCtrls = {}
|
||||
allChars = set()
|
||||
for ctrl in controls:
|
||||
# try to build up information on the dialog first
|
||||
# e.g. if it's possible to fix at all
|
||||
# any controls that have a unique character
|
||||
|
||||
hotkeyCtrls.setdefault(GetHotkey(ctrl)[1].lower(), []).append(ctrl)
|
||||
allChars = allChars.union(set(ctrl.lower()))
|
||||
|
||||
for c in ctrl:
|
||||
if c in ' _&': continue
|
||||
|
||||
charIndex.setdefault(c.lower(), []).append(ctrl)
|
||||
|
||||
|
||||
allChars.difference_update(" _&")
|
||||
|
||||
freeChars = allChars.difference(hotkeyCtrls.keys())
|
||||
print freeChars
|
||||
|
||||
|
||||
for c in hotkeyCtrls:
|
||||
print c
|
||||
for ctrl in hotkeyCtrls[c]:
|
||||
print "\t", ctrl
|
||||
|
||||
if len(allChars) < len(controls):
|
||||
print "impossible to fix completely because there are more hotkeys then individual characters in the dialog"
|
||||
print "the following characters are free:"
|
||||
|
||||
#for c in freeChars:
|
||||
# print "\t%s"% c
|
||||
# for ctrl in charIndex[c]:
|
||||
# print "\t\t%s" % ctrl
|
||||
|
||||
usedChars = hotkeyCtrls.keys()
|
||||
|
||||
changesMade = 1
|
||||
while changesMade:
|
||||
changesMade = 0
|
||||
|
||||
for char, ctrls in charIndex.items():
|
||||
|
||||
# if there is only one control that has this character
|
||||
if len (ctrls) == 1:
|
||||
# get the control
|
||||
ctrl = ctrls[0]
|
||||
|
||||
# find the hotkey for that control
|
||||
ctrlHotkey = GetHotkey(ctrl)[1].lower()
|
||||
|
||||
print ctrlHotkey, `ctrl`
|
||||
# remove the control from the list
|
||||
hotkeyCtrls[ctrlHotkey].remove(ctrl)
|
||||
|
||||
# if there are now now controls for that hotkey
|
||||
# remove it
|
||||
if len(hotkeyCtrls[ctrlHotkey]) == 0:
|
||||
del(hotkeyCtrls[ctrlHotkey])
|
||||
|
||||
# add the new changed one to the hotkeys
|
||||
hotkeyCtrls.setdefault(char, []).append(ctrl)
|
||||
changesMade = 1
|
||||
else:
|
||||
|
||||
|
||||
for hotkey, ctrls in hotkeyCtrls.items():
|
||||
if len(ctrls) > 1:
|
||||
for ctrl in ctrls:
|
||||
ctrlChars = set(ctrl.lower()).difference(" &_")
|
||||
if freeChars.intersection(ctrlChars):
|
||||
|
||||
|
||||
|
||||
|
||||
print "="*100
|
||||
|
||||
for c in hotkeyCtrls:
|
||||
print c
|
||||
for ctrl in hotkeyCtrls[c]:
|
||||
print "\t", ctrl
|
||||
|
||||
|
||||
|
||||
|
||||
#for x in charIndex:
|
||||
# print x, charIndex[x]
|
||||
|
||||
for hotkey, ctrls in hotkeyCtrls.items():
|
||||
if len(ctrls) > 1:
|
||||
print "***** BUG *****"
|
||||
print "\t", hotkey, ctrls
|
||||
|
||||
# find the chars free for each control
|
||||
ctrlFree = []
|
||||
for ctrl in ctrls:
|
||||
ctrlFree.append("".join(set(ctrl.lower()).intersection(freeChars)))
|
||||
|
||||
# count the controls with no hotkey free
|
||||
countNotFree = len([c for c in ctrlFree if not c])
|
||||
if countNotFree > 1:
|
||||
print "Cannot be fixed without possibly changing other controls also"
|
||||
|
||||
for i, free in enumerate(ctrlFree):
|
||||
if not free:
|
||||
print "Must leave '%s' alone" %ctrls[i]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import sys
|
||||
sys.exit()
|
||||
|
||||
#================================================
|
||||
#================================================
|
||||
#================================================
|
||||
#================================================
|
||||
#================================================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# return the controls that have characters in common with
|
||||
# the control
|
||||
def GetCtrlsWithSameChars(ctrl, controls):
|
||||
ourChars = set(ctrl.lower())
|
||||
ourChars.difference_update("& _")
|
||||
|
||||
toRet = []
|
||||
for control in controls:
|
||||
ctrlChars = set(control.lower())
|
||||
|
||||
if ourChars.intersection(ctrlChars):
|
||||
toRet.append(control)
|
||||
|
||||
return toRet
|
||||
|
||||
|
||||
def GetFreeCharsForControls(allFree, controls):
|
||||
ctrlFree = []
|
||||
allCtrlsFree = []
|
||||
for ctrl in controls:
|
||||
curCtrlFree = set(ctrl.lower()).intersection(allFree)
|
||||
ctrlFree.append("".join(curCtrlFree))
|
||||
|
||||
allCtrlsFree.extend(curCtrlFree)
|
||||
|
||||
return ctrlFree, "".join(allCtrlsFree)
|
||||
|
||||
|
||||
|
||||
charIndex = {}
|
||||
hotkeyCtrls = {}
|
||||
for c in controls:
|
||||
hotkeyCtrls.setdefault(GetHotkey(c)[1].lower(), []).append(c)
|
||||
|
||||
for char in c.lower():
|
||||
charIndex.setdefault(char, []).append(c)
|
||||
|
||||
|
||||
|
||||
hotkeys = set(hotkeyCtrls.keys())
|
||||
allKeys = set("".join(controls).lower())
|
||||
allKeys.difference_update("& _")
|
||||
|
||||
freeKeys = allKeys.difference(hotkeys)
|
||||
|
||||
print len(controls)
|
||||
if len(controls) > len(allKeys):
|
||||
print "**** Oops - more hotkeys than available characters :-( "
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for hotkey, ctrls in hotkeyCtrls.items():
|
||||
if len(ctrls) > 1:
|
||||
print "**bug**"
|
||||
|
||||
# can it be fixed simply by changing one or more of the controls
|
||||
# to another character within these controls
|
||||
|
||||
ctrlsFreeChars, allFree = GetFreeCharsForControls(freeKeys, ctrls)
|
||||
|
||||
# find out if we can use this method (0 or 1 controls with no free characters)
|
||||
noFreeCount = 0
|
||||
for i, ctrl in enumerate(ctrls):
|
||||
if not ctrlsFreeChars[i]:
|
||||
noFreeCount += 1
|
||||
|
||||
# ok - so more than one control has no free chars - can't use the
|
||||
# simple method
|
||||
if noFreeCount > 1:
|
||||
print "cant use that method"
|
||||
continue
|
||||
|
||||
if noFreeCount == 0:
|
||||
extraText = ' or leave the same'
|
||||
else:
|
||||
extraText = ''
|
||||
|
||||
for i, ctrl in enumerate(ctrls):
|
||||
if len(ctrlsFreeChars[i]) > 1:
|
||||
print "Change '%s' to one of (%s)%s"% (ctrl, "".join(ctrlsFreeChars[i]), extraText)
|
||||
elif len(ctrlsFreeChars[i]) == 1:
|
||||
print "Change '%s' to %s%s"% (ctrl, "".join(ctrlsFreeChars[i]), extraText)
|
||||
else:
|
||||
print "do not change %s" % ctrl
|
||||
|
||||
|
||||
#
|
||||
# for curCtrl in ctrls:
|
||||
# # Get the letters that could be used
|
||||
# ctrlAvl = set(curCtrl.lower()).intersection(freeKeys)
|
||||
#
|
||||
# #changesNeeded = ''
|
||||
#
|
||||
# # if there are no free letters in that control
|
||||
# # try and find if any other control could have it's
|
||||
# # hotkey changed to free up for ourself
|
||||
# if len(ctrlAvl) == 0:
|
||||
#
|
||||
# # get the controls that share some letters
|
||||
# otherCtrls = GetCtrlsWithSameChars(c, controls)
|
||||
#
|
||||
# suggestedChanges = []
|
||||
# # check if any of the letters in those controls can be freed up
|
||||
# for otherCtrl in otherCtrls:
|
||||
# if GetHotkey(otherCtrl)[1].lower() == hotkey:
|
||||
# freeOther = set(otherCtrl.lower()).intersection(freeKeys)
|
||||
#
|
||||
# if freeOther:
|
||||
# print "To Fix %s Free %s in %s by changing to any of (%s)"%(curCtrl, GetHotkey(otherCtrl)[1], otherCtrl, "".join(freeOther))
|
||||
#
|
||||
#
|
||||
#
|
||||
## posChange = set(c.lower()).intersection(allKeys)
|
||||
## changesNeeded = "".join(posChange)
|
||||
##
|
||||
## for char in posChange:
|
||||
## # get the controls that have that character
|
||||
## otherCtrls = charIndex[char]
|
||||
##
|
||||
# else:
|
||||
# print "To fix %s change %s to any of (%s)"% (curCtrl, c, "".join(ctrlAvl))
|
||||
#
|
||||
#
|
||||
|
||||
#print "bug", ctrls, "".join(freeKeys)
|
||||
|
Loading…
Reference in New Issue
Block a user