install pywin32-220

This commit is contained in:
j 2016-04-14 21:54:42 +02:00
parent 15d9012f5a
commit 93f7d415a0
581 changed files with 100203 additions and 0 deletions

View File

@ -0,0 +1,56 @@
"""adodbapi - A python DB API 2.0 (PEP 249) interface to Microsoft ADO
Copyright (C) 2002 Henrik Ekelund, version 2.1 by Vernon Cole
* http://sourceforge.net/projects/adodbapi
"""
import sys
import time
if sys.version_info < (3,0): # in Python 2, define all symbols, just like the bad old way
from apibase import *
VariantConversionMap = MultiMap # old name. Should use apibase.MultiMap
from .ado_consts import *
_makeByteBuffer = buffer
else:
# but if the user is running Python 3, then keep the dictionary clean
from .apibase import apilevel, threadsafety, paramstyle
from .apibase import Warning, Error, InterfaceError, DatabaseError, DataError, OperationalError, IntegrityError
from .apibase import InternalError, ProgrammingError, NotSupportedError, FetchFailedError
from .apibase import NUMBER, STRING, BINARY, DATETIME, ROWID
_makeByteBuffer = bytes
from .adodbapi import connect, Connection, __version__, dateconverter, Cursor
def Binary(aString):
"""This function constructs an object capable of holding a binary (long) string value. """
return _makeByteBuffer(aString)
def Date(year,month,day):
"This function constructs an object holding a date value. "
return dateconverter.Date(year,month,day)
def Time(hour,minute,second):
"This function constructs an object holding a time value. "
return dateconverter.Time(hour,minute,second)
def Timestamp(year,month,day,hour,minute,second):
"This function constructs an object holding a time stamp value. "
return dateconverter.Timestamp(year,month,day,hour,minute,second)
def DateFromTicks(ticks):
"""This function constructs an object holding a date value from the given ticks value
(number of seconds since the epoch; see the documentation of the standard Python time module for details). """
return Date(*time.gmtime(ticks)[:3])
def TimeFromTicks(ticks):
"""This function constructs an object holding a time value from the given ticks value
(number of seconds since the epoch; see the documentation of the standard Python time module for details). """
return Time(*time.gmtime(ticks)[3:6])
def TimestampFromTicks(ticks):
"""This function constructs an object holding a time stamp value from the given
ticks value (number of seconds since the epoch;
see the documentation of the standard Python time module for details). """
return Timestamp(*time.gmtime(ticks)[:6])
version = 'adodbapi v' + __version__

View File

@ -0,0 +1,276 @@
# ADO enumerated constants documented on MSDN:
# http://msdn.microsoft.com/en-us/library/ms678353(VS.85).aspx
# IsolationLevelEnum
adXactUnspecified = -1
adXactBrowse = 0x100
adXactChaos = 0x10
adXactCursorStability = 0x1000
adXactIsolated = 0x100000
adXactReadCommitted = 0x1000
adXactReadUncommitted = 0x100
adXactRepeatableRead = 0x10000
adXactSerializable = 0x100000
# CursorLocationEnum
adUseClient = 3
adUseServer = 2
# CursorTypeEnum
adOpenDynamic = 2
adOpenForwardOnly = 0
adOpenKeyset = 1
adOpenStatic = 3
adOpenUnspecified = -1
# CommandTypeEnum
adCmdText = 1
adCmdStoredProc = 4
adSchemaTables = 20
# ParameterDirectionEnum
adParamInput = 1
adParamInputOutput = 3
adParamOutput = 2
adParamReturnValue = 4
adParamUnknown = 0
directions = {
0: 'Unknown',
1: 'Input',
2: 'Output',
3: 'InputOutput',
4: 'Return',
}
def ado_direction_name(ado_dir):
try:
return 'adParam' + directions[ado_dir]
except:
return 'unknown direction ('+str(ado_dir)+')'
# ObjectStateEnum
adStateClosed = 0
adStateOpen = 1
adStateConnecting = 2
adStateExecuting = 4
adStateFetching = 8
# FieldAttributeEnum
adFldMayBeNull = 0x40
# ConnectModeEnum
adModeUnknown = 0
adModeRead = 1
adModeWrite = 2
adModeReadWrite = 3
adModeShareDenyRead = 4
adModeShareDenyWrite = 8
adModeShareExclusive = 12
adModeShareDenyNone = 16
adModeRecursive = 0x400000
# XactAttributeEnum
adXactCommitRetaining = 131072
adXactAbortRetaining = 262144
ado_error_TIMEOUT = -2147217871
# DataTypeEnum - ADO Data types documented at:
# http://msdn2.microsoft.com/en-us/library/ms675318.aspx
adArray = 0x2000
adEmpty = 0x0
adBSTR = 0x8
adBigInt = 0x14
adBinary = 0x80
adBoolean = 0xb
adChapter = 0x88
adChar = 0x81
adCurrency = 0x6
adDBDate = 0x85
adDBTime = 0x86
adDBTimeStamp = 0x87
adDate = 0x7
adDecimal = 0xe
adDouble = 0x5
adError = 0xa
adFileTime = 0x40
adGUID = 0x48
adIDispatch = 0x9
adIUnknown = 0xd
adInteger = 0x3
adLongVarBinary = 0xcd
adLongVarChar = 0xc9
adLongVarWChar = 0xcb
adNumeric = 0x83
adPropVariant = 0x8a
adSingle = 0x4
adSmallInt = 0x2
adTinyInt = 0x10
adUnsignedBigInt = 0x15
adUnsignedInt = 0x13
adUnsignedSmallInt = 0x12
adUnsignedTinyInt = 0x11
adUserDefined = 0x84
adVarBinary = 0xCC
adVarChar = 0xC8
adVarNumeric = 0x8B
adVarWChar = 0xCA
adVariant = 0xC
adWChar = 0x82
# Additional constants used by introspection but not ADO itself
AUTO_FIELD_MARKER = -1000
adTypeNames = {
adBSTR: 'adBSTR',
adBigInt: 'adBigInt',
adBinary: 'adBinary',
adBoolean: 'adBoolean',
adChapter: 'adChapter',
adChar: 'adChar',
adCurrency: 'adCurrency',
adDBDate: 'adDBDate',
adDBTime: 'adDBTime',
adDBTimeStamp: 'adDBTimeStamp',
adDate: 'adDate',
adDecimal: 'adDecimal',
adDouble: 'adDouble',
adEmpty: 'adEmpty',
adError: 'adError',
adFileTime: 'adFileTime',
adGUID: 'adGUID',
adIDispatch: 'adIDispatch',
adIUnknown: 'adIUnknown',
adInteger: 'adInteger',
adLongVarBinary: 'adLongVarBinary',
adLongVarChar: 'adLongVarChar',
adLongVarWChar: 'adLongVarWChar',
adNumeric: 'adNumeric',
adPropVariant: 'adPropVariant',
adSingle: 'adSingle',
adSmallInt: 'adSmallInt',
adTinyInt: 'adTinyInt',
adUnsignedBigInt: 'adUnsignedBigInt',
adUnsignedInt: 'adUnsignedInt',
adUnsignedSmallInt: 'adUnsignedSmallInt',
adUnsignedTinyInt: 'adUnsignedTinyInt',
adUserDefined: 'adUserDefined',
adVarBinary: 'adVarBinary',
adVarChar: 'adVarChar',
adVarNumeric: 'adVarNumeric',
adVarWChar: 'adVarWChar',
adVariant: 'adVariant',
adWChar: 'adWChar',
}
def ado_type_name(ado_type):
return adTypeNames.get(ado_type, 'unknown type ('+str(ado_type)+')')
# here in decimal, sorted by value
#adEmpty 0 Specifies no value (DBTYPE_EMPTY).
#adSmallInt 2 Indicates a two-byte signed integer (DBTYPE_I2).
#adInteger 3 Indicates a four-byte signed integer (DBTYPE_I4).
#adSingle 4 Indicates a single-precision floating-point value (DBTYPE_R4).
#adDouble 5 Indicates a double-precision floating-point value (DBTYPE_R8).
#adCurrency 6 Indicates a currency value (DBTYPE_CY). Currency is a fixed-point number
# with four digits to the right of the decimal point. It is stored in an eight-byte signed integer scaled by 10,000.
#adDate 7 Indicates a date value (DBTYPE_DATE). A date is stored as a double, the whole part of which is
# the number of days since December 30, 1899, and the fractional part of which is the fraction of a day.
#adBSTR 8 Indicates a null-terminated character string (Unicode) (DBTYPE_BSTR).
#adIDispatch 9 Indicates a pointer to an IDispatch interface on a COM object (DBTYPE_IDISPATCH).
#adError 10 Indicates a 32-bit error code (DBTYPE_ERROR).
#adBoolean 11 Indicates a boolean value (DBTYPE_BOOL).
#adVariant 12 Indicates an Automation Variant (DBTYPE_VARIANT).
#adIUnknown 13 Indicates a pointer to an IUnknown interface on a COM object (DBTYPE_IUNKNOWN).
#adDecimal 14 Indicates an exact numeric value with a fixed precision and scale (DBTYPE_DECIMAL).
#adTinyInt 16 Indicates a one-byte signed integer (DBTYPE_I1).
#adUnsignedTinyInt 17 Indicates a one-byte unsigned integer (DBTYPE_UI1).
#adUnsignedSmallInt 18 Indicates a two-byte unsigned integer (DBTYPE_UI2).
#adUnsignedInt 19 Indicates a four-byte unsigned integer (DBTYPE_UI4).
#adBigInt 20 Indicates an eight-byte signed integer (DBTYPE_I8).
#adUnsignedBigInt 21 Indicates an eight-byte unsigned integer (DBTYPE_UI8).
#adFileTime 64 Indicates a 64-bit value representing the number of 100-nanosecond intervals since
# January 1, 1601 (DBTYPE_FILETIME).
#adGUID 72 Indicates a globally unique identifier (GUID) (DBTYPE_GUID).
#adBinary 128 Indicates a binary value (DBTYPE_BYTES).
#adChar 129 Indicates a string value (DBTYPE_STR).
#adWChar 130 Indicates a null-terminated Unicode character string (DBTYPE_WSTR).
#adNumeric 131 Indicates an exact numeric value with a fixed precision and scale (DBTYPE_NUMERIC).
# adUserDefined 132 Indicates a user-defined variable (DBTYPE_UDT).
#adUserDefined 132 Indicates a user-defined variable (DBTYPE_UDT).
#adDBDate 133 Indicates a date value (yyyymmdd) (DBTYPE_DBDATE).
#adDBTime 134 Indicates a time value (hhmmss) (DBTYPE_DBTIME).
#adDBTimeStamp 135 Indicates a date/time stamp (yyyymmddhhmmss plus a fraction in billionths) (DBTYPE_DBTIMESTAMP).
#adChapter 136 Indicates a four-byte chapter value that identifies rows in a child rowset (DBTYPE_HCHAPTER).
#adPropVariant 138 Indicates an Automation PROPVARIANT (DBTYPE_PROP_VARIANT).
#adVarNumeric 139 Indicates a numeric value (Parameter object only).
#adVarChar 200 Indicates a string value (Parameter object only).
#adLongVarChar 201 Indicates a long string value (Parameter object only).
#adVarWChar 202 Indicates a null-terminated Unicode character string (Parameter object only).
#adLongVarWChar 203 Indicates a long null-terminated Unicode string value (Parameter object only).
#adVarBinary 204 Indicates a binary value (Parameter object only).
#adLongVarBinary 205 Indicates a long binary value (Parameter object only).
#adArray (Does not apply to ADOX.) 0x2000 A flag value, always combined with another data type constant,
# that indicates an array of that other data type.
# Error codes to names
adoErrors= {
0xe7b :'adErrBoundToCommand',
0xe94 :'adErrCannotComplete',
0xea4 :'adErrCantChangeConnection',
0xc94 :'adErrCantChangeProvider',
0xe8c :'adErrCantConvertvalue',
0xe8d :'adErrCantCreate',
0xea3 :'adErrCatalogNotSet',
0xe8e :'adErrColumnNotOnThisRow',
0xd5d :'adErrDataConversion',
0xe89 :'adErrDataOverflow',
0xe9a :'adErrDelResOutOfScope',
0xea6 :'adErrDenyNotSupported',
0xea7 :'adErrDenyTypeNotSupported',
0xcb3 :'adErrFeatureNotAvailable',
0xea5 :'adErrFieldsUpdateFailed',
0xc93 :'adErrIllegalOperation',
0xcae :'adErrInTransaction',
0xe87 :'adErrIntegrityViolation',
0xbb9 :'adErrInvalidArgument',
0xe7d :'adErrInvalidConnection',
0xe7c :'adErrInvalidParamInfo',
0xe82 :'adErrInvalidTransaction',
0xe91 :'adErrInvalidURL',
0xcc1 :'adErrItemNotFound',
0xbcd :'adErrNoCurrentRecord',
0xe83 :'adErrNotExecuting',
0xe7e :'adErrNotReentrant',
0xe78 :'adErrObjectClosed',
0xd27 :'adErrObjectInCollection',
0xd5c :'adErrObjectNotSet',
0xe79 :'adErrObjectOpen',
0xbba :'adErrOpeningFile',
0xe80 :'adErrOperationCancelled',
0xe96 :'adErrOutOfSpace',
0xe88 :'adErrPermissionDenied',
0xe9e :'adErrPropConflicting',
0xe9b :'adErrPropInvalidColumn',
0xe9c :'adErrPropInvalidOption',
0xe9d :'adErrPropInvalidValue',
0xe9f :'adErrPropNotAllSettable',
0xea0 :'adErrPropNotSet',
0xea1 :'adErrPropNotSettable',
0xea2 :'adErrPropNotSupported',
0xbb8 :'adErrProviderFailed',
0xe7a :'adErrProviderNotFound',
0xbbb :'adErrReadFile',
0xe93 :'adErrResourceExists',
0xe92 :'adErrResourceLocked',
0xe97 :'adErrResourceOutOfScope',
0xe8a :'adErrSchemaViolation',
0xe8b :'adErrSignMismatch',
0xe81 :'adErrStillConnecting',
0xe7f :'adErrStillExecuting',
0xe90 :'adErrTreePermissionDenied',
0xe8f :'adErrURLDoesNotExist',
0xe99 :'adErrURLNamedRowDoesNotExist',
0xe98 :'adErrUnavailable',
0xe84 :'adErrUnsafeOperation',
0xe95 :'adErrVolumeNotFound',
0xbbc :'adErrWriteFile'
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
""" db_print.py -- a simple demo for ADO database reads."""
from __future__ import with_statement #needed for Python 2.5
import sys
import adodbapi.ado_consts as adc
cmd_args = ('proxy_host', 'proxy_port', 'filename', 'table_name')
if 'help' in sys.argv:
print(('possible settings keywords are:',cmd_args))
sys.exit()
kw_args = {} # pick up filename and proxy address from command line (optionally)
for arg in sys.argv:
s = arg.split("=")
if len(s) > 1:
if s[0] in cmd_args:
kw_args[s[0]] = s[1]
kw_args.setdefault('filename', "test.mdb") # assumes server is running from examples folder
kw_args.setdefault('table_name', 'Products') # the name of the demo table
# the server needs to select the provider based on his Python installation
provider_switch = ['provider', 'Microsoft.ACE.OLEDB.12.0', "Microsoft.Jet.OLEDB.4.0"]
# ------------------------ START HERE -------------------------------------
#create the connection
constr = "Provider=%(provider)s;Data Source=%(filename)s"
if 'proxy_host' in kw_args:
import adodbapi.remote as db
else:
import adodbapi as db
con = db.connect(constr, kw_args, macro_is64bit=provider_switch)
if kw_args['table_name'] == '?':
print('The tables in your database are:')
for name in con.get_table_names():
print(name)
else:
#make a cursor on the connection
with con.cursor() as c:
#run an SQL statement on the cursor
sql = 'select * from %s' % kw_args['table_name']
print(('performing query="%s"' % sql))
c.execute(sql)
#check the results
print(('result rowcount shows as= %d. (Note: -1 means "not known")' \
% (c.rowcount,)))
print('')
print('result data description is:')
print(' NAME Type DispSize IntrnlSz Prec Scale Null?')
for d in c.description:
print((('%16s %-12s %8s %8d %4d %5d %s') % \
(d[0], adc.adTypeNames[d[1]], d[2], d[3], d[4],d[5], bool(d[6]))))
print('')
print('str() of first five records are...')
#get the results
db = c.fetchmany(5)
#print them
for rec in db:
print(rec)
print('')
print('repr() of next row is...')
print((repr(c.fetchone())))
print('')
con.close()

View File

@ -0,0 +1,19 @@
""" db_table_names.py -- a simple demo for ADO database table listing."""
import sys
import adodbapi
try:
databasename = sys.argv[1]
except IndexError:
databasename = "test.mdb"
provider = ['prv', "Microsoft.ACE.OLEDB.12.0", "Microsoft.Jet.OLEDB.4.0"]
constr = "Provider=%(prv)s;Data Source=%(db)s"
#create the connection
con = adodbapi.connect(constr, db=databasename, macro_is64bit=provider)
print(('Table names in= %s' % databasename))
for table in con.get_table_names():
print(table)

View File

@ -0,0 +1,38 @@
import sys
import adodbapi
try:
import adodbapi.is64bit as is64bit
is64 = is64bit.Python()
except ImportError:
is64 = False
if is64:
driver = "Microsoft.ACE.OLEDB.12.0"
else:
driver = "Microsoft.Jet.OLEDB.4.0"
extended = 'Extended Properties="Excel 8.0;HDR=Yes;IMEX=1;"'
try: # first command line argument will be xls file name -- default to the one written by xls_write.py
filename = sys.argv[1]
except IndexError:
filename = 'xx.xls'
constr = "Provider=%s;Data Source=%s;%s" % (driver, filename, extended)
conn = adodbapi.connect(constr)
try: # second command line argument will be worksheet name -- default to first worksheet
sheet = sys.argv[2]
except IndexError:
# use ADO feature to get the name of the first worksheet
sheet = conn.get_table_names()[0]
print(('Shreadsheet=%s Worksheet=%s' % (filename, sheet)))
print('------------------------------------------------------------')
crsr = conn.cursor()
sql = "SELECT * from [%s]" % sheet
crsr.execute(sql)
for row in crsr.fetchmany(10):
print((repr(row)))
crsr.close()
conn.close()

View File

@ -0,0 +1,32 @@
from __future__ import with_statement # needed only if running Python 2.5
import adodbapi
try:
import adodbapi.is64bit as is64bit
is64 = is64bit.Python()
except ImportError:
is64 = False # in case the user has an old version of adodbapi
if is64:
driver = "Microsoft.ACE.OLEDB.12.0"
else:
driver = "Microsoft.Jet.OLEDB.4.0"
filename = 'xx.xls' # file will be created if it does not exist
extended = 'Extended Properties="Excel 8.0;Readonly=False;"'
constr = "Provider=%s;Data Source=%s;%s" % (driver, filename, extended)
conn = adodbapi.connect(constr)
with conn: # will auto commit if no errors
with conn.cursor() as crsr:
try: crsr.execute('drop table SheetOne')
except: pass # just is case there is one already there
# create the sheet and the header row and set the types for the columns
crsr.execute('create table SheetOne (Header1 text, Header2 text, Header3 text, Header4 text, Header5 text)')
sql = "INSERT INTO SheetOne (Header1, Header2 ,Header3, Header4, Header5) values (?,?,?,?,?)"
data = (1, 2, 3, 4, 5)
crsr.execute(sql, data) # write the first row of data
crsr.execute(sql, (6, 7, 8, 9, 10)) # another row of data
conn.close()
print(('Created spreadsheet=%s worksheet=%s' % (filename, 'SheetOne')))

View File

@ -0,0 +1,33 @@
"""is64bit.Python() --> boolean value of detected Python word size. is64bit.os() --> os build version"""
import sys
def Python():
if sys.platform == 'cli': #IronPython
import System
return System.IntPtr.Size == 8
else:
try:
return sys.maxsize > 2147483647
except AttributeError:
return sys.maxint > 2147483647
def os():
import platform
pm = platform.machine()
if pm != '..' and pm.endswith('64'): # recent Python (not Iron)
return True
else:
import os
if 'PROCESSOR_ARCHITEW6432' in os.environ:
return True # 32 bit program running on 64 bit Windows
try:
return os.environ['PROCESSOR_ARCHITECTURE'].endswith('64') # 64 bit Windows 64 bit program
except IndexError:
pass # not Windows
try:
return '64' in platform.architecture()[0] # this often works in Linux
except:
return False # is an older version of Python, assume also an older os (best we can guess)
if __name__ == "__main__":
print(("is64bit.Python() =", Python(), "is64bit.os() =", os()))

View File

@ -0,0 +1,506 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -0,0 +1,92 @@
Project
-------
adodbapi
A Python DB-API 2.0 (PEP-249) module that makes it easy to use Microsoft ADO
for connecting with databases and other data sources
using either CPython or IronPython.
Home page: <http://sourceforge.net/projects/adodbapi>
Features:
* 100% DB-API 2.0 (PEP-249) compliant (including most extensions and recommendations).
* Includes pyunit testcases that describe how to use the module.
* Fully implemented in Python. -- runs in Python 2.5+ Python 3.0+ and IronPython 2.6+
* Licensed under the LGPL license, which means that it can be used freely even in commercial programs subject to certain restrictions.
* Includes SERVER and REMOTE modules so that a Windows proxy can serve ADO databases to a Linux client using PyRO.
* The user can choose between paramstyles: 'qmark' 'named' 'format' 'pyformat' 'dynamic'
* Supports data retrieval by column name e.g.:
for row in myCurser.execute("select name,age from students"):
print("Student", row.name, "is", row.age, "years old.")
* Supports user-definable system-to-Python data conversion functions (selected by ADO data type, or by column)
Prerequisites:
* C Python 2.5 or higher
and pywin32 (Mark Hammond's python for windows extensions.)
or
Iron Python 2.6 or higher. (works in IPy2.0 for all data types except BUFFER)
Installation:
* (C-Python on Windows): Download pywin32 from http://sf.net/projects/pywin32 and install from .msi (adodbapi is included)
* ((to use Windows as a server, also download and install Pyro4 (requires Python 2.6 or later))) https://pypi.python.org/pypi/Pyro4
* (IronPython on Windows): Download adodbapi from http://sf.net/projects/adodbapi. Unpack the zip.
Open a command window as an administrator. CD to the folder containing the unzipped files.
Run "setup.py install" using the IronPython of your choice.
* (Linux, as a client): download and install from PyPi: "pip install adodbapi Pyro4"
NOTE: ...........
If you do not like the new default operation of returning Numeric columns as decimal.Decimal,
you can select other options by the user defined conversion feature.
Try:
adodbapi.apibase.variantConversions[adodbapi.ado_consts.adNumeric] = adodbapi.apibase.cvtString
or:
adodbapi.apibase.variantConversions[adodbapi.ado_consts.adNumeric] = adodbapi.apibase.cvtFloat
or:
adodbapi.apibase.variantConversions[adodbapi.ado_consts.adNumeric] = write_your_own_convertion_function
............
whats new in version 2.6
A cursor.prepare() method and support for prepared SQL statements.
Lots of refactoring, especially of the Remote and Server modules (still to be treated as Beta code).
The quick start document 'quick_reference.odt' will export as a nice-looking pdf.
Added paramstyles 'pyformat' and 'dynamic'. If your 'paramstyle' is 'named' you _must_ pass a dictionary of
parameters to your .execute() method. If your 'paramstyle' is 'format' 'pyformat' or 'dynamic', you _may_
pass a dictionary of parameters -- provided your SQL operation string is formatted correctly.
whats new in version 2.5
Remote module: (works on Linux!) allows a Windows computer to serve ADO databases via PyRO
Server module: PyRO server for ADO. Run using a command like= C:>python -m adodbapi.server
(server has simple connection string macros: is64bit, getuser, sql_provider, auto_security)
Brief documentation included. See adodbapi/examples folder adodbapi.rtf
New connection method conn.get_table_names() --> list of names of tables in database
Vastly refactored. Data conversion things have been moved to the new adodbapi.apibase module.
Many former module-level attributes are now class attributes. (Should be more thread-safe)
Connection objects are now context managers for transactions and will commit or rollback.
Cursor objects are context managers and will automatically close themselves.
Autocommit can be switched on and off.
Keyword and positional arguments on the connect() method work as documented in PEP 249.
Keyword arguments from the connect call can be formatted into the connection string.
New keyword arguments defined, such as: autocommit, paramstyle, remote_proxy, remote_port.
*** Breaking change: variantConversion lookups are simplified: the following will raise KeyError:
oldconverter=adodbapi.variantConversions[adodbapi.adoStringTypes]
Refactor as: oldconverter=adodbapi.variantConversions[adodbapi.adoStringTypes[0]]
(( More information like this in older_whatsnew.txt ))
License
-------
LGPL, see http://www.opensource.org/licenses/lgpl-license.php
Documentation
-------------
Start with:
http://www.python.org/topics/database/DatabaseAPI-2.0.html
read the examples in adodbapi/examples
and look at the test cases in adodbapi/test directory.
Mailing lists
-------------
The adodbapi mailing lists have been deactivated. Submit comments to the
pywin32 or IronPython mailing lists.
-- the bug tracker on sourceforge.net/projects/adodbapi will be checked, (infrequently).

View File

@ -0,0 +1,14 @@
"""call using an open ADO connection --> list of table names"""
from . import adodbapi
def names(connection_object):
ado = connection_object.adoConn
schema = ado.OpenSchema(20) # constant = adSchemaTables
tables = []
while not schema.EOF:
name = adodbapi.getIndexedValue(schema.Fields,'TABLE_NAME').Value
tables.append(name)
schema.MoveNext()
del schema
return tables

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,161 @@
# Configure this to _YOUR_ environment in order to run the testcases.
"testADOdbapiConfig.py v 2.6.0.A00"
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# #
# # TESTERS:
# #
# # You will need to make numerous modifications to this file
# # to adapt it to your own testing environment.
# #
# # Skip down to the next "# #" line --
# # -- the things you need to change are below it.
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
import platform
import sys
import random
import is64bit
import setuptestframework
if sys.version_info >= (3,0):
import tryconnection3 as tryconnection
else:
import tryconnection2 as tryconnection
print((sys.version))
node = platform.node()
try: print(('node=%s: is64bit.os()= %s, is64bit.Python()= %s' % (node, is64bit.os(), is64bit.Python())))
except: pass
try:
onWindows = bool(sys.getwindowsversion()) # seems to work on all versions of Python
except:
onWindows = False
# create a random name for temporary table names
_alphabet = "PYFGCRLAOEUIDHTNTQJKXBMWVZ1234567890" # yes, I do use a dvorak keyboard!
tmp = ''.join([random.choice(_alphabet) for x in range(9)])
mdb_name = 'xx_' + tmp + '.mdb'
testfolder = setuptestframework.maketemp()
if '--package' in sys.argv:
pth = setuptestframework.makeadopackage(testfolder)
else:
pth = setuptestframework.find_ado_path()
if pth not in sys.path:
sys.path.insert(1,pth)
# function to clean up the temporary folder -- calling program must run this function before exit.
cleanup = setuptestframework.getcleanupfunction()
import adodbapi # will (hopefully) be imported using the "pth" discovered above
try:
print((adodbapi.version)) # show version
except:
print('"adodbapi.version" not present or not working.')
print(__doc__)
verbose = False
for a in sys.argv:
if a.startswith('--verbose'):
arg = True
try: arg = int(a.split("=")[1])
except IndexError: pass
adodbapi.adodbapi.verbose = arg
verbose = arg
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # start your environment setup here v v v
SQL_HOST_NODE = 'Vpad'
doAllTests = '--all' in sys.argv
doAccessTest = not ('--nojet' in sys.argv)
doSqlServerTest = node == SQL_HOST_NODE or '--mssql' in sys.argv or doAllTests
doMySqlTest = '--mysql' in sys.argv or doAllTests
doPostgresTest = '--pg' in sys.argv or doAllTests
iterateOverTimeTests = ('--time' in sys.argv or doAllTests) and onWindows
THE_PROXY_HOST = '25.44.77.176' if node != SQL_HOST_NODE or not onWindows else '::1' # -- change this
try: #If mx extensions are installed, use mxDateTime
import mx.DateTime
doMxDateTimeTest=True
except:
doMxDateTimeTest=False #Requires eGenixMXExtensions
doTimeTest = True # obsolete python time format
if doAccessTest:
if onWindows and (node == SQL_HOST_NODE or not is64bit.Python()):
c = {'mdb': setuptestframework.makemdb(testfolder, mdb_name)}
else:
c = {'macro_find_temp_test_path' : ['mdb', 'server_test.mdb'],
'proxy_host' : THE_PROXY_HOST}
# macro definition for keyword "driver" using macro "is64bit" -- see documentation
c['macro_is64bit'] = ['driver', "Microsoft.ACE.OLEDB.12.0", "Microsoft.Jet.OLEDB.4.0"]
connStrAccess = "Provider=%(driver)s;Data Source=%(mdb)s"
print(' ...Testing ACCESS connection...')
doAccessTest, connStrAccess, dbAccessconnect = tryconnection.try_connection(verbose, connStrAccess, 10, **c)
if doSqlServerTest:
c = {'macro_getnode' : ['host', r"%s\SQLExpress"], # name of computer with SQL Server
#'host':'25.44.77.176;' # Network Library=dbmssocn',
'database': "adotest",
'user' : 'adotestuser', # None implies Windows security
'password' : "12345678",
# macro definition for keyword "security" using macro "auto_security"
'macro_auto_security' : 'security',
'provider' : 'SQLNCLI11; MARS Connection=True'
}
connStr = "Provider=%(provider)s; Initial Catalog=%(database)s; Data Source=%(host)s; %(security)s;"
if node != SQL_HOST_NODE:
if THE_PROXY_HOST:
c["proxy_host"] = THE_PROXY_HOST # the SQL server runs a proxy for this test
else:
c["pyro_connection"] = "PYRONAME:ado.connection"
print(' ...Testing MS-SQL login...')
doSqlServerTest, connStrSQLServer, dbSqlServerconnect = tryconnection.try_connection(verbose, connStr, 30, **c)
if doMySqlTest:
c = {'host' : "25.223.161.222",
'database' : 'test',
'user' : 'adotest',
'password' : '12345678',
'driver' : "MySQL ODBC 5.3 Unicode Driver"} # or _driver="MySQL ODBC 3.51 Driver
if not onWindows:
if THE_PROXY_HOST:
c["proxy_host"] = THE_PROXY_HOST
else:
c["pyro_connection"] = "PYRONAME:ado.connection"
c['macro_is64bit'] = ['provider', 'Provider=MSDASQL;']
cs = '%(provider)sDriver={%(driver)s};Server=%(host)s;Port=3306;' + \
'Database=%(database)s;user=%(user)s;password=%(password)s;Option=3;'
print(' ...Testing MySql login...')
doMySqlTest, connStrMySql, dbMySqlconnect = tryconnection.try_connection(verbose, cs, 5, **c)
if doPostgresTest:
_computername = "25.223.161.222"
_databasename='adotest'
_username = 'adotestuser'
_password = '12345678'
kws = {'timeout' : 4}
kws['macro_is64bit'] = ['prov_drv', 'Provider=MSDASQL;Driver={PostgreSQL Unicode(x64)}',
'Driver=PostgreSQL Unicode']
if not onWindows:
if THE_PROXY_HOST:
kws['proxy_host'] = THE_PROXY_HOST
else:
kws['pyro_connection'] = 'PYRONAME:ado.connection'
# get driver from http://www.postgresql.org/ftp/odbc/versions/
# test using positional and keyword arguments (bad example for real code)
print(' ...Testing PostgreSQL login...')
doPostgresTest, connStrPostgres, dbPostgresConnect = tryconnection.try_connection(verbose,
'%(prov_drv)s;Server=%(host)s;Database=%(database)s;uid=%(user)s;pwd=%(password)s;',
_username, _password, _computername, _databasename, **kws)
assert doAccessTest or doSqlServerTest or doMySqlTest or doPostgresTest, 'No database engine found for testing'

View File

@ -0,0 +1,899 @@
#!/usr/bin/env python
''' Python DB API 2.0 driver compliance unit test suite.
This software is Public Domain and may be used without restrictions.
"Now we have booze and barflies entering the discussion, plus rumours of
DBAs on drugs... and I won't tell you what flashes through my mind each
time I read the subject line with 'Anal Compliance' in it. All around
this is turning out to be a thoroughly unwholesome unit test."
-- Ian Bicking
'''
__version__ = '$Revision: 1.14.3 $'[11:-2]
__author__ = 'Stuart Bishop <stuart@stuartbishop.net>'
import unittest
import time
import sys
if sys.version[0] >= '3': #python 3.x
_BaseException = Exception
def _failUnless(self, expr, msg=None):
self.assertTrue(expr, msg)
else: #python 2.x
from exceptions import Exception as _BaseException
def _failUnless(self, expr, msg=None):
self.failUnless(expr, msg) ## deprecated since Python 2.6
# set this to "True" to follow API 2.0 to the letter
TEST_FOR_NON_IDEMPOTENT_CLOSE = True
# Revision 1.14 2013/05/20 11:02:05 kf7xm
# Add a literal string to the format insertion test to catch trivial re-format algorithms
# Revision 1.13 2013/05/08 14:31:50 kf7xm
# Quick switch to Turn off IDEMPOTENT_CLOSE test. Also: Silence teardown failure
# Revision 1.12 2009/02/06 03:35:11 kf7xm
# Tested okay with Python 3.0, includes last minute patches from Mark H.
#
# Revision 1.1.1.1.2.1 2008/09/20 19:54:59 rupole
# Include latest changes from main branch
# Updates for py3k
#
# Revision 1.11 2005/01/02 02:41:01 zenzen
# Update author email address
#
# Revision 1.10 2003/10/09 03:14:14 zenzen
# Add test for DB API 2.0 optional extension, where database exceptions
# are exposed as attributes on the Connection object.
#
# Revision 1.9 2003/08/13 01:16:36 zenzen
# Minor tweak from Stefan Fleiter
#
# Revision 1.8 2003/04/10 00:13:25 zenzen
# Changes, as per suggestions by M.-A. Lemburg
# - Add a table prefix, to ensure namespace collisions can always be avoided
#
# Revision 1.7 2003/02/26 23:33:37 zenzen
# Break out DDL into helper functions, as per request by David Rushby
#
# Revision 1.6 2003/02/21 03:04:33 zenzen
# Stuff from Henrik Ekelund:
# added test_None
# added test_nextset & hooks
#
# Revision 1.5 2003/02/17 22:08:43 zenzen
# Implement suggestions and code from Henrik Eklund - test that cursor.arraysize
# defaults to 1 & generic cursor.callproc test added
#
# Revision 1.4 2003/02/15 00:16:33 zenzen
# Changes, as per suggestions and bug reports by M.-A. Lemburg,
# Matthew T. Kromer, Federico Di Gregorio and Daniel Dittmar
# - Class renamed
# - Now a subclass of TestCase, to avoid requiring the driver stub
# to use multiple inheritance
# - Reversed the polarity of buggy test in test_description
# - Test exception heirarchy correctly
# - self.populate is now self._populate(), so if a driver stub
# overrides self.ddl1 this change propogates
# - VARCHAR columns now have a width, which will hopefully make the
# DDL even more portible (this will be reversed if it causes more problems)
# - cursor.rowcount being checked after various execute and fetchXXX methods
# - Check for fetchall and fetchmany returning empty lists after results
# are exhausted (already checking for empty lists if select retrieved
# nothing
# - Fix bugs in test_setoutputsize_basic and test_setinputsizes
#
def str2bytes(sval):
if sys.version_info < (3,0) and isinstance(sval, str):
sval = sval.decode("latin1")
return sval.encode("latin1") #python 3 make unicode into bytes
class DatabaseAPI20Test(unittest.TestCase):
''' Test a database self.driver for DB API 2.0 compatibility.
This implementation tests Gadfly, but the TestCase
is structured so that other self.drivers can subclass this
test case to ensure compiliance with the DB-API. It is
expected that this TestCase may be expanded in the future
if ambiguities or edge conditions are discovered.
The 'Optional Extensions' are not yet being tested.
self.drivers should subclass this test, overriding setUp, tearDown,
self.driver, connect_args and connect_kw_args. Class specification
should be as follows:
import dbapi20
class mytest(dbapi20.DatabaseAPI20Test):
[...]
Don't 'import DatabaseAPI20Test from dbapi20', or you will
confuse the unit tester - just 'import dbapi20'.
'''
# The self.driver module. This should be the module where the 'connect'
# method is to be found
driver = None
connect_args = () # List of arguments to pass to connect
connect_kw_args = {} # Keyword arguments for connect
table_prefix = 'dbapi20test_' # If you need to specify a prefix for tables
ddl1 = 'create table %sbooze (name varchar(20))' % table_prefix
ddl2 = 'create table %sbarflys (name varchar(20), drink varchar(30))' % table_prefix
xddl1 = 'drop table %sbooze' % table_prefix
xddl2 = 'drop table %sbarflys' % table_prefix
lowerfunc = 'lower' # Name of stored procedure to convert string->lowercase
# Some drivers may need to override these helpers, for example adding
# a 'commit' after the execute.
def executeDDL1(self,cursor):
cursor.execute(self.ddl1)
def executeDDL2(self,cursor):
cursor.execute(self.ddl2)
def setUp(self):
''' self.drivers should override this method to perform required setup
if any is necessary, such as creating the database.
'''
pass
def tearDown(self):
''' self.drivers should override this method to perform required cleanup
if any is necessary, such as deleting the test database.
The default drops the tables that may be created.
'''
try:
con = self._connect()
try:
cur = con.cursor()
for ddl in (self.xddl1,self.xddl2):
try:
cur.execute(ddl)
con.commit()
except self.driver.Error:
# Assume table didn't exist. Other tests will check if
# execute is busted.
pass
finally:
con.close()
except _BaseException:
pass
def _connect(self):
try:
r = self.driver.connect(
*self.connect_args,**self.connect_kw_args
)
except AttributeError:
self.fail("No connect method found in self.driver module")
return r
def test_connect(self):
con = self._connect()
con.close()
def test_apilevel(self):
try:
# Must exist
apilevel = self.driver.apilevel
# Must equal 2.0
self.assertEqual(apilevel,'2.0')
except AttributeError:
self.fail("Driver doesn't define apilevel")
def test_threadsafety(self):
try:
# Must exist
threadsafety = self.driver.threadsafety
# Must be a valid value
_failUnless(self, threadsafety in (0,1,2,3))
except AttributeError:
self.fail("Driver doesn't define threadsafety")
def test_paramstyle(self):
try:
# Must exist
paramstyle = self.driver.paramstyle
# Must be a valid value
_failUnless(self, paramstyle in (
'qmark','numeric','named','format','pyformat'
))
except AttributeError:
self.fail("Driver doesn't define paramstyle")
def test_Exceptions(self):
# Make sure required exceptions exist, and are in the
# defined heirarchy.
if sys.version[0] == '3': #under Python 3 StardardError no longer exists
self.assertTrue(issubclass(self.driver.Warning,Exception))
self.assertTrue(issubclass(self.driver.Error,Exception))
else:
self.failUnless(issubclass(self.driver.Warning,Exception))
self.failUnless(issubclass(self.driver.Error,Exception))
_failUnless(self,
issubclass(self.driver.InterfaceError,self.driver.Error)
)
_failUnless(self,
issubclass(self.driver.DatabaseError,self.driver.Error)
)
_failUnless(self,
issubclass(self.driver.OperationalError,self.driver.Error)
)
_failUnless(self,
issubclass(self.driver.IntegrityError,self.driver.Error)
)
_failUnless(self,
issubclass(self.driver.InternalError,self.driver.Error)
)
_failUnless(self,
issubclass(self.driver.ProgrammingError,self.driver.Error)
)
_failUnless(self,
issubclass(self.driver.NotSupportedError,self.driver.Error)
)
def test_ExceptionsAsConnectionAttributes(self):
# OPTIONAL EXTENSION
# Test for the optional DB API 2.0 extension, where the exceptions
# are exposed as attributes on the Connection object
# I figure this optional extension will be implemented by any
# driver author who is using this test suite, so it is enabled
# by default.
con = self._connect()
drv = self.driver
_failUnless(self,con.Warning is drv.Warning)
_failUnless(self,con.Error is drv.Error)
_failUnless(self,con.InterfaceError is drv.InterfaceError)
_failUnless(self,con.DatabaseError is drv.DatabaseError)
_failUnless(self,con.OperationalError is drv.OperationalError)
_failUnless(self,con.IntegrityError is drv.IntegrityError)
_failUnless(self,con.InternalError is drv.InternalError)
_failUnless(self,con.ProgrammingError is drv.ProgrammingError)
_failUnless(self,con.NotSupportedError is drv.NotSupportedError)
def test_commit(self):
con = self._connect()
try:
# Commit must work, even if it doesn't do anything
con.commit()
finally:
con.close()
def test_rollback(self):
con = self._connect()
# If rollback is defined, it should either work or throw
# the documented exception
if hasattr(con,'rollback'):
try:
con.rollback()
except self.driver.NotSupportedError:
pass
def test_cursor(self):
con = self._connect()
try:
cur = con.cursor()
finally:
con.close()
def test_cursor_isolation(self):
con = self._connect()
try:
# Make sure cursors created from the same connection have
# the documented transaction isolation level
cur1 = con.cursor()
cur2 = con.cursor()
self.executeDDL1(cur1)
cur1.execute("insert into %sbooze values ('Victoria Bitter')" % (
self.table_prefix
))
cur2.execute("select name from %sbooze" % self.table_prefix)
booze = cur2.fetchall()
self.assertEqual(len(booze),1)
self.assertEqual(len(booze[0]),1)
self.assertEqual(booze[0][0],'Victoria Bitter')
finally:
con.close()
def test_description(self):
con = self._connect()
try:
cur = con.cursor()
self.executeDDL1(cur)
self.assertEqual(cur.description,None,
'cursor.description should be none after executing a '
'statement that can return no rows (such as DDL)'
)
cur.execute('select name from %sbooze' % self.table_prefix)
self.assertEqual(len(cur.description),1,
'cursor.description describes too many columns'
)
self.assertEqual(len(cur.description[0]),7,
'cursor.description[x] tuples must have 7 elements'
)
self.assertEqual(cur.description[0][0].lower(),'name',
'cursor.description[x][0] must return column name'
)
self.assertEqual(cur.description[0][1],self.driver.STRING,
'cursor.description[x][1] must return column type. Got %r'
% cur.description[0][1]
)
# Make sure self.description gets reset
self.executeDDL2(cur)
self.assertEqual(cur.description,None,
'cursor.description not being set to None when executing '
'no-result statements (eg. DDL)'
)
finally:
con.close()
def test_rowcount(self):
con = self._connect()
try:
cur = con.cursor()
self.executeDDL1(cur)
_failUnless(self,cur.rowcount in (-1,0), # Bug #543885
'cursor.rowcount should be -1 or 0 after executing no-result '
'statements'
)
cur.execute("insert into %sbooze values ('Victoria Bitter')" % (
self.table_prefix
))
_failUnless(self,cur.rowcount in (-1,1),
'cursor.rowcount should == number or rows inserted, or '
'set to -1 after executing an insert statement'
)
cur.execute("select name from %sbooze" % self.table_prefix)
_failUnless(self,cur.rowcount in (-1,1),
'cursor.rowcount should == number of rows returned, or '
'set to -1 after executing a select statement'
)
self.executeDDL2(cur)
self.assertEqual(cur.rowcount,-1,
'cursor.rowcount not being reset to -1 after executing '
'no-result statements'
)
finally:
con.close()
lower_func = 'lower'
def test_callproc(self):
con = self._connect()
try:
cur = con.cursor()
if self.lower_func and hasattr(cur,'callproc'):
r = cur.callproc(self.lower_func,('FOO',))
self.assertEqual(len(r),1)
self.assertEqual(r[0],'FOO')
r = cur.fetchall()
self.assertEqual(len(r),1,'callproc produced no result set')
self.assertEqual(len(r[0]),1,
'callproc produced invalid result set'
)
self.assertEqual(r[0][0],'foo',
'callproc produced invalid results'
)
finally:
con.close()
def test_close(self):
con = self._connect()
try:
cur = con.cursor()
finally:
con.close()
# cursor.execute should raise an Error if called after connection
# closed
self.assertRaises(self.driver.Error,self.executeDDL1,cur)
# connection.commit should raise an Error if called after connection'
# closed.'
self.assertRaises(self.driver.Error,con.commit)
# connection.close should raise an Error if called more than once
#!!! reasonable persons differ about the usefulness of this test and this feature !!!
if TEST_FOR_NON_IDEMPOTENT_CLOSE:
self.assertRaises(self.driver.Error,con.close)
def test_execute(self):
con = self._connect()
try:
cur = con.cursor()
self._paraminsert(cur)
finally:
con.close()
def _paraminsert(self,cur):
self.executeDDL2(cur)
cur.execute("insert into %sbarflys values ('Victoria Bitter', 'thi%%s :may ca%%(u)se? troub:1e')" % (
self.table_prefix
))
_failUnless(self,cur.rowcount in (-1,1))
if self.driver.paramstyle == 'qmark':
cur.execute(
"insert into %sbarflys values (?, 'thi%%s :may ca%%(u)se? troub:1e')" % self.table_prefix,
("Cooper's",)
)
elif self.driver.paramstyle == 'numeric':
cur.execute(
"insert into %sbarflys values (:1, 'thi%%s :may ca%%(u)se? troub:1e')" % self.table_prefix,
("Cooper's",)
)
elif self.driver.paramstyle == 'named':
cur.execute(
"insert into %sbarflys values (:beer, 'thi%%s :may ca%%(u)se? troub:1e')" % self.table_prefix,
{'beer':"Cooper's"}
)
elif self.driver.paramstyle == 'format':
cur.execute(
"insert into %sbarflys values (%%s, 'thi%%s :may ca%%(u)se? troub:1e')" % self.table_prefix,
("Cooper's",)
)
elif self.driver.paramstyle == 'pyformat':
cur.execute(
"insert into %sbarflys values (%%(beer)s, 'thi%%s :may ca%%(u)se? troub:1e')" % self.table_prefix,
{'beer':"Cooper's"}
)
else:
self.fail('Invalid paramstyle')
_failUnless(self,cur.rowcount in (-1,1))
cur.execute('select name, drink from %sbarflys' % self.table_prefix)
res = cur.fetchall()
self.assertEqual(len(res),2,'cursor.fetchall returned too few rows')
beers = [res[0][0],res[1][0]]
beers.sort()
self.assertEqual(beers[0],"Cooper's",
'cursor.fetchall retrieved incorrect data, or data inserted '
'incorrectly'
)
self.assertEqual(beers[1],"Victoria Bitter",
'cursor.fetchall retrieved incorrect data, or data inserted '
'incorrectly'
)
trouble = "thi%s :may ca%(u)se? troub:1e"
self.assertEqual(res[0][1], trouble,
'cursor.fetchall retrieved incorrect data, or data inserted '
'incorrectly. Got=%s, Expected=%s' % (repr(res[0][1]), repr(trouble)))
self.assertEqual(res[1][1], trouble,
'cursor.fetchall retrieved incorrect data, or data inserted '
'incorrectly. Got=%s, Expected=%s' % (repr(res[1][1]), repr(trouble)
))
def test_executemany(self):
con = self._connect()
try:
cur = con.cursor()
self.executeDDL1(cur)
largs = [ ("Cooper's",) , ("Boag's",) ]
margs = [ {'beer': "Cooper's"}, {'beer': "Boag's"} ]
if self.driver.paramstyle == 'qmark':
cur.executemany(
'insert into %sbooze values (?)' % self.table_prefix,
largs
)
elif self.driver.paramstyle == 'numeric':
cur.executemany(
'insert into %sbooze values (:1)' % self.table_prefix,
largs
)
elif self.driver.paramstyle == 'named':
cur.executemany(
'insert into %sbooze values (:beer)' % self.table_prefix,
margs
)
elif self.driver.paramstyle == 'format':
cur.executemany(
'insert into %sbooze values (%%s)' % self.table_prefix,
largs
)
elif self.driver.paramstyle == 'pyformat':
cur.executemany(
'insert into %sbooze values (%%(beer)s)' % (
self.table_prefix
),
margs
)
else:
self.fail('Unknown paramstyle')
_failUnless(self,cur.rowcount in (-1,2),
'insert using cursor.executemany set cursor.rowcount to '
'incorrect value %r' % cur.rowcount
)
cur.execute('select name from %sbooze' % self.table_prefix)
res = cur.fetchall()
self.assertEqual(len(res),2,
'cursor.fetchall retrieved incorrect number of rows'
)
beers = [res[0][0],res[1][0]]
beers.sort()
self.assertEqual(beers[0],"Boag's",'incorrect data "%s" retrieved' % beers[0])
self.assertEqual(beers[1],"Cooper's",'incorrect data retrieved')
finally:
con.close()
def test_fetchone(self):
con = self._connect()
try:
cur = con.cursor()
# cursor.fetchone should raise an Error if called before
# executing a select-type query
self.assertRaises(self.driver.Error,cur.fetchone)
# cursor.fetchone should raise an Error if called after
# executing a query that cannnot return rows
self.executeDDL1(cur)
self.assertRaises(self.driver.Error,cur.fetchone)
cur.execute('select name from %sbooze' % self.table_prefix)
self.assertEqual(cur.fetchone(),None,
'cursor.fetchone should return None if a query retrieves '
'no rows'
)
_failUnless(self,cur.rowcount in (-1,0))
# cursor.fetchone should raise an Error if called after
# executing a query that cannnot return rows
cur.execute("insert into %sbooze values ('Victoria Bitter')" % (
self.table_prefix
))
self.assertRaises(self.driver.Error,cur.fetchone)
cur.execute('select name from %sbooze' % self.table_prefix)
r = cur.fetchone()
self.assertEqual(len(r),1,
'cursor.fetchone should have retrieved a single row'
)
self.assertEqual(r[0],'Victoria Bitter',
'cursor.fetchone retrieved incorrect data'
)
self.assertEqual(cur.fetchone(),None,
'cursor.fetchone should return None if no more rows available'
)
_failUnless(self,cur.rowcount in (-1,1))
finally:
con.close()
samples = [
'Carlton Cold',
'Carlton Draft',
'Mountain Goat',
'Redback',
'Victoria Bitter',
'XXXX'
]
def _populate(self):
''' Return a list of sql commands to setup the DB for the fetch
tests.
'''
populate = [
"insert into %sbooze values ('%s')" % (self.table_prefix,s)
for s in self.samples
]
return populate
def test_fetchmany(self):
con = self._connect()
try:
cur = con.cursor()
# cursor.fetchmany should raise an Error if called without
#issuing a query
self.assertRaises(self.driver.Error,cur.fetchmany,4)
self.executeDDL1(cur)
for sql in self._populate():
cur.execute(sql)
cur.execute('select name from %sbooze' % self.table_prefix)
r = cur.fetchmany()
self.assertEqual(len(r),1,
'cursor.fetchmany retrieved incorrect number of rows, '
'default of arraysize is one.'
)
cur.arraysize=10
r = cur.fetchmany(3) # Should get 3 rows
self.assertEqual(len(r),3,
'cursor.fetchmany retrieved incorrect number of rows'
)
r = cur.fetchmany(4) # Should get 2 more
self.assertEqual(len(r),2,
'cursor.fetchmany retrieved incorrect number of rows'
)
r = cur.fetchmany(4) # Should be an empty sequence
self.assertEqual(len(r),0,
'cursor.fetchmany should return an empty sequence after '
'results are exhausted'
)
_failUnless(self,cur.rowcount in (-1,6))
# Same as above, using cursor.arraysize
cur.arraysize=4
cur.execute('select name from %sbooze' % self.table_prefix)
r = cur.fetchmany() # Should get 4 rows
self.assertEqual(len(r),4,
'cursor.arraysize not being honoured by fetchmany'
)
r = cur.fetchmany() # Should get 2 more
self.assertEqual(len(r),2)
r = cur.fetchmany() # Should be an empty sequence
self.assertEqual(len(r),0)
_failUnless(self,cur.rowcount in (-1,6))
cur.arraysize=6
cur.execute('select name from %sbooze' % self.table_prefix)
rows = cur.fetchmany() # Should get all rows
_failUnless(self,cur.rowcount in (-1,6))
self.assertEqual(len(rows),6)
self.assertEqual(len(rows),6)
rows = [r[0] for r in rows]
rows.sort()
# Make sure we get the right data back out
for i in range(0,6):
self.assertEqual(rows[i],self.samples[i],
'incorrect data retrieved by cursor.fetchmany'
)
rows = cur.fetchmany() # Should return an empty list
self.assertEqual(len(rows),0,
'cursor.fetchmany should return an empty sequence if '
'called after the whole result set has been fetched'
)
_failUnless(self,cur.rowcount in (-1,6))
self.executeDDL2(cur)
cur.execute('select name from %sbarflys' % self.table_prefix)
r = cur.fetchmany() # Should get empty sequence
self.assertEqual(len(r),0,
'cursor.fetchmany should return an empty sequence if '
'query retrieved no rows'
)
_failUnless(self,cur.rowcount in (-1,0))
finally:
con.close()
def test_fetchall(self):
con = self._connect()
try:
cur = con.cursor()
# cursor.fetchall should raise an Error if called
# without executing a query that may return rows (such
# as a select)
self.assertRaises(self.driver.Error, cur.fetchall)
self.executeDDL1(cur)
for sql in self._populate():
cur.execute(sql)
# cursor.fetchall should raise an Error if called
# after executing a a statement that cannot return rows
self.assertRaises(self.driver.Error,cur.fetchall)
cur.execute('select name from %sbooze' % self.table_prefix)
rows = cur.fetchall()
_failUnless(self,cur.rowcount in (-1,len(self.samples)))
self.assertEqual(len(rows),len(self.samples),
'cursor.fetchall did not retrieve all rows'
)
rows = [r[0] for r in rows]
rows.sort()
for i in range(0,len(self.samples)):
self.assertEqual(rows[i],self.samples[i],
'cursor.fetchall retrieved incorrect rows'
)
rows = cur.fetchall()
self.assertEqual(
len(rows),0,
'cursor.fetchall should return an empty list if called '
'after the whole result set has been fetched'
)
_failUnless(self,cur.rowcount in (-1,len(self.samples)))
self.executeDDL2(cur)
cur.execute('select name from %sbarflys' % self.table_prefix)
rows = cur.fetchall()
_failUnless(self,cur.rowcount in (-1,0))
self.assertEqual(len(rows),0,
'cursor.fetchall should return an empty list if '
'a select query returns no rows'
)
finally:
con.close()
def test_mixedfetch(self):
con = self._connect()
try:
cur = con.cursor()
self.executeDDL1(cur)
for sql in self._populate():
cur.execute(sql)
cur.execute('select name from %sbooze' % self.table_prefix)
rows1 = cur.fetchone()
rows23 = cur.fetchmany(2)
rows4 = cur.fetchone()
rows56 = cur.fetchall()
_failUnless(self,cur.rowcount in (-1,6))
self.assertEqual(len(rows23),2,
'fetchmany returned incorrect number of rows'
)
self.assertEqual(len(rows56),2,
'fetchall returned incorrect number of rows'
)
rows = [rows1[0]]
rows.extend([rows23[0][0],rows23[1][0]])
rows.append(rows4[0])
rows.extend([rows56[0][0],rows56[1][0]])
rows.sort()
for i in range(0,len(self.samples)):
self.assertEqual(rows[i],self.samples[i],
'incorrect data retrieved or inserted'
)
finally:
con.close()
def help_nextset_setUp(self,cur):
''' Should create a procedure called deleteme
that returns two result sets, first the
number of rows in booze then "name from booze"
'''
raise NotImplementedError('Helper not implemented')
#sql="""
# create procedure deleteme as
# begin
# select count(*) from booze
# select name from booze
# end
#"""
#cur.execute(sql)
def help_nextset_tearDown(self,cur):
'If cleaning up is needed after nextSetTest'
raise NotImplementedError('Helper not implemented')
#cur.execute("drop procedure deleteme")
def test_nextset(self):
con = self._connect()
try:
cur = con.cursor()
if not hasattr(cur,'nextset'):
return
try:
self.executeDDL1(cur)
sql=self._populate()
for sql in self._populate():
cur.execute(sql)
self.help_nextset_setUp(cur)
cur.callproc('deleteme')
numberofrows=cur.fetchone()
assert numberofrows[0]== len(self.samples)
assert cur.nextset()
names=cur.fetchall()
assert len(names) == len(self.samples)
s=cur.nextset()
assert s == None,'No more return sets, should return None'
finally:
self.help_nextset_tearDown(cur)
finally:
con.close()
def test_nextset(self):
raise NotImplementedError('Drivers need to override this test')
def test_arraysize(self):
# Not much here - rest of the tests for this are in test_fetchmany
con = self._connect()
try:
cur = con.cursor()
_failUnless(self,hasattr(cur,'arraysize'),
'cursor.arraysize must be defined'
)
finally:
con.close()
def test_setinputsizes(self):
con = self._connect()
try:
cur = con.cursor()
cur.setinputsizes( (25,) )
self._paraminsert(cur) # Make sure cursor still works
finally:
con.close()
def test_setoutputsize_basic(self):
# Basic test is to make sure setoutputsize doesn't blow up
con = self._connect()
try:
cur = con.cursor()
cur.setoutputsize(1000)
cur.setoutputsize(2000,0)
self._paraminsert(cur) # Make sure the cursor still works
finally:
con.close()
def test_setoutputsize(self):
# Real test for setoutputsize is driver dependant
raise NotImplementedError('Driver needed to override this test')
def test_None(self):
con = self._connect()
try:
cur = con.cursor()
self.executeDDL1(cur)
cur.execute('insert into %sbooze values (NULL)' % self.table_prefix)
cur.execute('select name from %sbooze' % self.table_prefix)
r = cur.fetchall()
self.assertEqual(len(r),1)
self.assertEqual(len(r[0]),1)
self.assertEqual(r[0][0],None,'NULL value not returned as None')
finally:
con.close()
def test_Date(self):
d1 = self.driver.Date(2002,12,25)
d2 = self.driver.DateFromTicks(time.mktime((2002,12,25,0,0,0,0,0,0)))
# Can we assume this? API doesn't specify, but it seems implied
# self.assertEqual(str(d1),str(d2))
def test_Time(self):
t1 = self.driver.Time(13,45,30)
t2 = self.driver.TimeFromTicks(time.mktime((2001,1,1,13,45,30,0,0,0)))
# Can we assume this? API doesn't specify, but it seems implied
# self.assertEqual(str(t1),str(t2))
def test_Timestamp(self):
t1 = self.driver.Timestamp(2002,12,25,13,45,30)
t2 = self.driver.TimestampFromTicks(
time.mktime((2002,12,25,13,45,30,0,0,0))
)
# Can we assume this? API doesn't specify, but it seems implied
# self.assertEqual(str(t1),str(t2))
def test_Binary(self):
b = self.driver.Binary(str2bytes('Something'))
b = self.driver.Binary(str2bytes(''))
def test_STRING(self):
_failUnless(self, hasattr(self.driver,'STRING'),
'module.STRING must be defined'
)
def test_BINARY(self):
_failUnless(self, hasattr(self.driver,'BINARY'),
'module.BINARY must be defined.'
)
def test_NUMBER(self):
_failUnless(self, hasattr(self.driver,'NUMBER'),
'module.NUMBER must be defined.'
)
def test_DATETIME(self):
_failUnless(self, hasattr(self.driver,'DATETIME'),
'module.DATETIME must be defined.'
)
def test_ROWID(self):
_failUnless(self, hasattr(self.driver,'ROWID'),
'module.ROWID must be defined.'
)

View File

@ -0,0 +1,33 @@
"""is64bit.Python() --> boolean value of detected Python word size. is64bit.os() --> os build version"""
import sys
def Python():
if sys.platform == 'cli': #IronPython
import System
return System.IntPtr.Size == 8
else:
try:
return sys.maxsize > 2147483647
except AttributeError:
return sys.maxint > 2147483647
def os():
import platform
pm = platform.machine()
if pm != '..' and pm.endswith('64'): # recent Python (not Iron)
return True
else:
import os
if 'PROCESSOR_ARCHITEW6432' in os.environ:
return True # 32 bit program running on 64 bit Windows
try:
return os.environ['PROCESSOR_ARCHITECTURE'].endswith('64') # 64 bit Windows 64 bit program
except IndexError:
pass # not Windows
try:
return '64' in platform.architecture()[0] # this often works in Linux
except:
return False # is an older version of Python, assume also an older os (best we can guess)
if __name__ == "__main__":
print(("is64bit.Python() =", Python(), "is64bit.os() =", os()))

View File

@ -0,0 +1,113 @@
#!/usr/bin/python2
# Configure this in order to run the testcases.
"setuptestframework.py v 2.5.0.c9"
import os
import sys
import tempfile
import shutil
try:
OSErrors = (WindowsError, OSError)
except NameError: # not running on Windows
OSErrors = OSError
def maketemp():
temphome = tempfile.gettempdir()
tempdir = os.path.join(temphome, 'adodbapi_test')
## try: ## if running simultanous test, don't erase the other thread's work
## shutil.rmtree(tempdir) # kill off an old copy
## except: pass
try: os.mkdir(tempdir)
except: pass
return tempdir
def _cleanup_function(testfolder, mdb_name):
try: os.unlink(os.path.join(testfolder, mdb_name))
except: pass # mdb database not present
try: shutil.rmtree(os.path.join(testfolder, 'adodbapi'))
except: pass # test package not present
def getcleanupfunction():
return _cleanup_function
def find_ado_path():
adoName = os.path.normpath(os.getcwd() + '/../../adodbapi.py')
adoPackage = os.path.dirname(adoName)
return adoPackage
# make a new package directory for the test copy of ado
def makeadopackage(testfolder):
adoName = os.path.normpath(os.getcwd() + '/../adodbapi.py')
adoPath = os.path.dirname(adoName)
if os.path.exists(adoName):
newpackage = os.path.join(testfolder,'adodbapi')
try:
os.mkdir(newpackage)
except OSErrors:
print('*Note: temporary adodbapi package already exists: may be two versions running?')
for f in os.listdir(adoPath):
if f.endswith('.py'):
shutil.copy(os.path.join(adoPath, f), newpackage)
if sys.version_info >= (3,0): # only when running Py3.n
save = sys.stdout
sys.stdout = None
from lib2to3.main import main # use 2to3 to make test package
main("lib2to3.fixes",args=['-n','-w', newpackage])
sys.stdout = save
return testfolder
else:
raise EnvironmentError('Connot find source of adodbapi to test.')
def makemdb(testfolder, mdb_name):
# following setup code borrowed from pywin32 odbc test suite
# kindly contributed by Frank Millman.
import os
_accessdatasource = os.path.join(testfolder, mdb_name)
if not os.path.isfile(_accessdatasource):
try:
from win32com.client.gencache import EnsureDispatch
from win32com.client import constants
win32 = True
except ImportError: #perhaps we are running IronPython
win32 = False #iron Python
try:
from System import Activator, Type
except:
pass
# Create a brand-new database - what is the story with these?
dbe = None
for suffix in (".36", ".35", ".30"):
try:
if win32:
dbe = EnsureDispatch("DAO.DBEngine" + suffix)
else:
type= Type.GetTypeFromProgID("DAO.DBEngine" + suffix)
dbe = Activator.CreateInstance(type)
break
except:
pass
if dbe:
print((' ...Creating ACCESS db at '+_accessdatasource))
if win32:
workspace = dbe.Workspaces(0)
newdb = workspace.CreateDatabase(_accessdatasource,
constants.dbLangGeneral,
constants.dbEncrypt)
else:
newdb = dbe.CreateDatabase(_accessdatasource,';LANGID=0x0409;CP=1252;COUNTRY=0')
newdb.Close()
else:
print((' ...copying test ACCESS db to '+_accessdatasource))
mdbName = os.path.normpath(os.getcwd() + '/../examples/test.mdb')
import shutil
shutil.copy(mdbName, _accessdatasource)
return _accessdatasource
if __name__ == "__main__":
print('Setting up a Jet database for server to use for remote testing...')
temp = maketemp()
makemdb(temp, 'server_test.mdb')

View File

@ -0,0 +1,181 @@
print("This module depends on the dbapi20 compliance tests created by Stuart Bishop")
print("(see db-sig mailing list history for info)")
import platform
import unittest
import sys
import dbapi20
import setuptestframework
testfolder = setuptestframework.maketemp()
if '--package' in sys.argv:
pth = setuptestframework.makeadopackage(testfolder)
sys.argv.remove('--package')
else:
pth = setuptestframework.find_ado_path()
if pth not in sys.path:
sys.path.insert(1,pth)
# function to clean up the temporary folder -- calling program must run this function before exit.
cleanup = setuptestframework.getcleanupfunction()
import adodbapi
import adodbapi.is64bit as is64bit
db = adodbapi
if '--verbose' in sys.argv:
db.adodbapi.verbose = 3
print((adodbapi.version))
print(("Tested with dbapi20 %s" % dbapi20.__version__))
try:
onWindows = bool(sys.getwindowsversion()) # seems to work on all versions of Python
except:
onWindows = False
node = platform.node()
conn_kws = {}
host = None # if None, will use macro to fill in node name
instance = r'%s\SQLExpress'
conn_kws['name'] = 'adotest'
if host is None:
conn_kws['macro_getnode'] = ['host', instance]
else:
conn_kws['host'] = host
conn_kws['provider'] = 'Provider=SQLNCLI11;DataTypeCompatibility=80;MARS Connection=True;'
connStr = "%(provider)s; Integrated Security=SSPI; Initial Catalog=%(name)s;Data Source=%(host)s"
if onWindows and node != "z-PC":
pass # default should make a local SQL Server connection
elif node == "xxx": # try Postgres database
_computername = "25.223.161.222"
_databasename='adotest'
_username = 'adotestuser'
_password = '12345678'
_driver="PostgreSQL Unicode"
_provider = ''
connStr = '%sDriver={%s};Server=%s;Database=%s;uid=%s;pwd=%s;' % \
(_provider,_driver,_computername,_databasename,_username,_password)
elif node == "yyy": # ACCESS data base is known to fail some tests.
if is64bit.Python():
driver = "Microsoft.ACE.OLEDB.12.0"
else:
driver = "Microsoft.Jet.OLEDB.4.0"
testmdb = setuptestframework.makemdb(testfolder)
connStr = r"Provider=%s;Data Source=%s" % (driver, testmdb)
else: # try a remote connection to an SQL server
conn_kws['proxy_host'] = '25.44.77.176'
import adodbapi.remote
db = adodbapi.remote
print(('Using Connection String like=%s' % connStr))
print(('Keywords=%s' % repr(conn_kws)))
class test_adodbapi(dbapi20.DatabaseAPI20Test):
driver = db
connect_args = (connStr,)
connect_kw_args = conn_kws
def __init__(self,arg):
dbapi20.DatabaseAPI20Test.__init__(self,arg)
def testMethodName(self):
return self.id().split('.')[-1]
def setUp(self):
# Call superclass setUp In case this does something in the
# future
dbapi20.DatabaseAPI20Test.setUp(self)
if self.testMethodName()=='test_callproc':
con = self._connect()
engine = con.dbms_name
## print('Using database Engine=%s' % engine) ##
if engine != 'MS Jet':
sql="""
create procedure templower
@theData varchar(50)
as
select lower(@theData)
"""
else: # Jet
sql="""
create procedure templower
(theData varchar(50))
as
select lower(theData);
"""
cur = con.cursor()
try:
cur.execute(sql)
con.commit()
except:
pass
cur.close()
con.close()
self.lower_func='templower'
def tearDown(self):
if self.testMethodName()=='test_callproc':
con = self._connect()
cur = con.cursor()
try:
cur.execute("drop procedure templower")
except:
pass
con.commit()
dbapi20.DatabaseAPI20Test.tearDown(self)
def help_nextset_setUp(self,cur):
'Should create a procedure called deleteme '
'that returns two result sets, first the number of rows in booze then "name from booze"'
sql="""
create procedure deleteme as
begin
select count(*) from %sbooze
select name from %sbooze
end
""" %(self.table_prefix,self.table_prefix)
cur.execute(sql)
def help_nextset_tearDown(self,cur):
'If cleaning up is needed after nextSetTest'
try:
cur.execute("drop procedure deleteme")
except:
pass
def test_nextset(self):
con = self._connect()
try:
cur = con.cursor()
stmts=[self.ddl1] + self._populate()
for sql in stmts:
cur.execute(sql)
self.help_nextset_setUp(cur)
cur.callproc('deleteme')
numberofrows=cur.fetchone()
assert numberofrows[0]== 6
assert cur.nextset()
names=cur.fetchall()
assert len(names) == len(self.samples)
s=cur.nextset()
assert s == None,'No more return sets, should return None'
finally:
try:
self.help_nextset_tearDown(cur)
finally:
con.close()
def test_setoutputsize(self): pass
if __name__ == '__main__':
unittest.main()
cleanup(testfolder, None)

View File

@ -0,0 +1,46 @@
# This module may be retired as soon as Python 2.5 support is dropped.
#
# It exists only to allow trapping exceptions using the "except [exception list], e" format
# which is a syntax error in Python 3
def try_connection(verbose, *args, **kwargs):
import adodbapi
if "proxy_host" in kwargs or 'pyro_connection' in kwargs or 'proxy_host' in args:
import adodbapi.remote
import Pyro4
pyroError = Pyro4.errors.PyroError
dbconnect = adodbapi.remote.connect
remote = True
else:
dbconnect = adodbapi.connect
pyroError = NotImplementedError # (will not occur)
remote = False
try:
s = dbconnect(*args, **kwargs) # connect to server
if verbose:
print('Connected to:', s.connection_string)
print('which has tables:', s.get_table_names())
s.close() # thanks, it worked, goodbye
except (adodbapi.DatabaseError, pyroError) as inst:
print(inst.args[0]) # should be the error message
print('***Failed getting connection using=', repr(args), repr(kwargs))
if remote:
print('** Is your Python2 ado.connection server running?')
print('* Have you run "setuptestframework.py" to create server_test.mdb?')
return False, (args, kwargs), None
if remote:
print(" (remote)", end=' ')
print(" (successful)")
return True, (args, kwargs, remote), dbconnect
def try_operation_with_expected_exception(expected_exceptions, some_function, args, kwargs):
try:
some_function(*args, **kwargs)
except expected_exceptions as e:
return True, e
except:
raise # an exception other than the expected occurred
return False, 'The expected exception did not occur'

View File

@ -0,0 +1,48 @@
from __future__ import print_function
# This module may be retired as soon as Python 2.5 support is dropped.
#
# It exists only to allow trapping exceptions using the "except [exception list] as e" format
# which is a syntax error in Python 2.5
def try_connection(verbose, *args, **kwargs):
import adodbapi
if "proxy_host" in kwargs or "pyro_connection" in kwargs or "proxy_host" in args:
import adodbapi.remote
import Pyro4
pyroError = Pyro4.errors.PyroError
dbconnect = adodbapi.remote.connect
remote = True
else:
dbconnect = adodbapi.connect
pyroError = NotImplementedError # (will not occur)
remote = False
try:
s = dbconnect(*args, **kwargs) # connect to server
if verbose:
print('Connected to:', s.connection_string)
print('which has tables:', s.get_table_names())
s.close() # thanks, it worked, goodbye
except adodbapi.DatabaseError as inst:
print(inst.args[0]) # should be the error message
print('***Failed getting connection using=',repr(args),repr(kwargs))
if remote:
print('** Are you running a *Python3* ado.connection server? **')
print('* Have you run "setuptestframework.py" to create server_test.mdb?')
return False, (args, kwargs), None
if remote:
print(" (remote)",end="")
print(" (successful)")
return True, (args, kwargs, remote), dbconnect
def try_operation_with_expected_exception(expected_exception_list, some_function, *args, **kwargs):
try:
some_function(*args, **kwargs)
except expected_exception_list as e:
return True, e
except:
raise # an exception other than the expected occurred
return False, 'The expected exception did not occur'

Binary file not shown.

View File

@ -0,0 +1,7 @@
A Python ISAPI extension. Contributed by Phillip Frantz, and is
Copyright 2002-2003 by Blackdog Software Pty Ltd.
See the 'samples' directory, and particularly samples\README.txt
You can find documentation in the PyWin32.chm file that comes with pywin32 -
you can open this from Pythonwin->Help, or from the start menu.

View File

@ -0,0 +1,33 @@
# The Python ISAPI package.
# Exceptions thrown by the DLL framework.
class ISAPIError(Exception):
def __init__(self, errno, strerror = None, funcname = None):
# named attributes match IOError etc.
self.errno = errno
self.strerror = strerror
self.funcname = funcname
Exception.__init__(self, errno, strerror, funcname)
def __str__(self):
if self.strerror is None:
try:
import win32api
self.strerror = win32api.FormatMessage(self.errno).strip()
except:
self.strerror = "no error message is available"
# str() looks like a win32api error.
return str( (self.errno, self.strerror, self.funcname) )
class FilterError(ISAPIError):
pass
class ExtensionError(ISAPIError):
pass
# A little development aid - a filter or extension callback function can
# raise one of these exceptions, and the handler module will be reloaded.
# This means you can change your code without restarting IIS.
# After a reload, your filter/extension will have the GetFilterVersion/
# GetExtensionVersion function called, but with None as the first arg.
class InternalReloadException(Exception):
pass

View File

@ -0,0 +1,92 @@
<!-- NOTE: This HTML is displayed inside the CHM file - hence some hrefs
will only work in that environment
-->
<HTML>
<BODY>
<TITLE>Introduction to Python ISAPI support</TITLE>
<h2>Introduction to Python ISAPI support</h2>
<h3>See also</h3>
<ul>
<li><a href="/isapi_modules.html">The isapi related modules</a>
</li>
<li><a href="/isapi_objects.html">The isapi related objects</a>
</li>
</ul>
<p><i>Note: if you are viewing this documentation directly from disk,
most links in this document will fail - you can also find this document in the
CHM file that comes with pywin32, where the links will work</i>
<h3>Introduction</h3>
This documents Python support for hosting ISAPI exensions and filters inside
Microsoft Internet Information Server (IIS). It assumes a basic understanding
of the ISAPI filter and extension mechanism.
<p>
In summary, to implement a filter or extension, you provide a Python module
which defines a Filter and/or Extension class. Once your class has been
loaded, IIS/ISAPI will, via an extension DLL, call methods on your class.
<p>
A filter and a class instance need only provide 3 methods - for filters they
are called <code>GetFilterVersion</code>, <code>HttpFilterProc</code> and
<code>TerminateFilter</code>. For extensions they
are named <code>GetExtensionVersion</code>, <code>HttpExtensionProc</code> and
<code>TerminateExtension</code>. If you are familiar with writing ISAPI
extensions in C/C++, these names and their purpose will be familiar.
<p>
Most of the work is done in the <code>HttpFilterProc</code> and
<code>HttpExtensionProc</code> methods. These both take a single
parameter - an <a href="/HTTP_FILTER_CONTEXT.html">HTTP_FILTER_CONTEXT</a> and
<a href="/EXTENSION_CONTROL_BLOCK.html">EXTENSION_CONTROL_BLOCK</a>
object respectively.
<p>
In addition to these components, there is an 'isapi' package, containing
support facilities (base-classes, exceptions, etc) which can be leveraged
by the extension.
<h4>Base classes</h4>
There are a number of base classes provided to make writing extensions a little
simpler. Of particular note is <code>isapi.threaded_extension.ThreadPoolExtension</code>.
This implements a thread-pool and informs IIS that the request is progressing
in the background. Your sub-class need only provide a <code>Dispatch</code>
method, which is called on one of the worker threads rather than the thread
that the request came in on.
<p>
There is base-class for a filter in <code>isapi.simple</code>, but there is no
equivilent threaded filter - filters work under a different model, where
background processing is not possible.
<h4>Samples</h4>
Please see the <code>isapi/samples</code> directory for some sample filters
and extensions.
<H3>Implementation</H3>
A Python ISAPI filter extension consists of 2 main components:
<UL>
<LI>A DLL used by ISAPI to interface with Python.</LI>
<LI>A Python script used by that DLL to implement the filter or extension
functionality</LI>
</UL>
<h4>Extension DLL</h4>
The DLL is usually managed automatically by the isapi.install module. As the
Python script for the extension is installed, a generic DLL provided with
the isapi package is installed next to the script, and IIS configured to
use this DLL.
<p>
The name of the DLL always has the same base name as the Python script, but
with a leading underscore (_), and an extension of .dll. For example, the
sample "redirector.py" will, when installed, have "_redirector.dll" created
in the same directory.
<p/>
The Python script may provide 2 entry points - methods named __FilterFactory__
and __ExtensionFactory__, both taking no arguments and returning a filter or
extension object.
<h3>Using py2exe and the isapi package</h3>
You can instruct py2exe to create a 'frozen' Python ISAPI filter/extension.
In this case, py2exe will create a package with everything you need in one
directory, and the Python source file embedded in the .zip file.
<p>
In general, you will want to build a seperate installation executable along
with the ISAPI extension. This executable will be built from the same script.
See the ISAPI sample in the py2exe distribution.

View File

@ -0,0 +1,730 @@
"""Installation utilities for Python ISAPI filters and extensions."""
# this code adapted from "Tomcat JK2 ISAPI redirector", part of Apache
# Created July 2004, Mark Hammond.
import sys, os, imp, shutil, stat
import operator
from win32com.client import GetObject, Dispatch
from win32com.client.gencache import EnsureModule, EnsureDispatch
import win32api
import pythoncom
import winerror
import traceback
_APP_INPROC = 0
_APP_OUTPROC = 1
_APP_POOLED = 2
_IIS_OBJECT = "IIS://LocalHost/W3SVC"
_IIS_SERVER = "IIsWebServer"
_IIS_WEBDIR = "IIsWebDirectory"
_IIS_WEBVIRTUALDIR = "IIsWebVirtualDir"
_IIS_FILTERS = "IIsFilters"
_IIS_FILTER = "IIsFilter"
_DEFAULT_SERVER_NAME = "Default Web Site"
_DEFAULT_HEADERS = "X-Powered-By: Python"
_DEFAULT_PROTECTION = _APP_POOLED
# Default is for 'execute' only access - ie, only the extension
# can be used. This can be overridden via your install script.
_DEFAULT_ACCESS_EXECUTE = True
_DEFAULT_ACCESS_READ = False
_DEFAULT_ACCESS_WRITE = False
_DEFAULT_ACCESS_SCRIPT = False
_DEFAULT_CONTENT_INDEXED = False
_DEFAULT_ENABLE_DIR_BROWSING = False
_DEFAULT_ENABLE_DEFAULT_DOC = False
_extensions = [ext for ext, _, _ in imp.get_suffixes()]
is_debug_build = '_d.pyd' in _extensions
this_dir = os.path.abspath(os.path.dirname(__file__))
class FilterParameters:
Name = None
Description = None
Path = None
Server = None
# Params that control if/how AddExtensionFile is called.
AddExtensionFile = True
AddExtensionFile_Enabled = True
AddExtensionFile_GroupID = None # defaults to Name
AddExtensionFile_CanDelete = True
AddExtensionFile_Description = None # defaults to Description.
def __init__(self, **kw):
self.__dict__.update(kw)
class VirtualDirParameters:
Name = None # Must be provided.
Description = None # defaults to Name
AppProtection = _DEFAULT_PROTECTION
Headers = _DEFAULT_HEADERS
Path = None # defaults to WWW root.
Type = _IIS_WEBVIRTUALDIR
AccessExecute = _DEFAULT_ACCESS_EXECUTE
AccessRead = _DEFAULT_ACCESS_READ
AccessWrite = _DEFAULT_ACCESS_WRITE
AccessScript = _DEFAULT_ACCESS_SCRIPT
ContentIndexed = _DEFAULT_CONTENT_INDEXED
EnableDirBrowsing = _DEFAULT_ENABLE_DIR_BROWSING
EnableDefaultDoc = _DEFAULT_ENABLE_DEFAULT_DOC
DefaultDoc = None # Only set in IIS if not None
ScriptMaps = []
ScriptMapUpdate = "end" # can be 'start', 'end', 'replace'
Server = None
def __init__(self, **kw):
self.__dict__.update(kw)
def is_root(self):
"This virtual directory is a root directory if parent and name are blank"
parent, name = self.split_path()
return not parent and not name
def split_path(self):
return split_path(self.Name)
class ScriptMapParams:
Extension = None
Module = None
Flags = 5
Verbs = ""
# Params that control if/how AddExtensionFile is called.
AddExtensionFile = True
AddExtensionFile_Enabled = True
AddExtensionFile_GroupID = None # defaults to Name
AddExtensionFile_CanDelete = True
AddExtensionFile_Description = None # defaults to Description.
def __init__(self, **kw):
self.__dict__.update(kw)
def __str__(self):
"Format this parameter suitable for IIS"
items = [self.Extension, self.Module, self.Flags]
# IIS gets upset if there is a trailing verb comma, but no verbs
if self.Verbs:
items.append(self.Verbs)
items = [str(item) for item in items]
return ','.join(items)
class ISAPIParameters:
ServerName = _DEFAULT_SERVER_NAME
# Description = None
Filters = []
VirtualDirs = []
def __init__(self, **kw):
self.__dict__.update(kw)
verbose = 1 # The level - 0 is quiet.
def log(level, what):
if verbose >= level:
print(what)
# Convert an ADSI COM exception to the Win32 error code embedded in it.
def _GetWin32ErrorCode(com_exc):
hr = com_exc.hresult
# If we have more details in the 'excepinfo' struct, use it.
if com_exc.excepinfo:
hr = com_exc.excepinfo[-1]
if winerror.HRESULT_FACILITY(hr) != winerror.FACILITY_WIN32:
raise
return winerror.SCODE_CODE(hr)
class InstallationError(Exception): pass
class ItemNotFound(InstallationError): pass
class ConfigurationError(InstallationError): pass
def FindPath(options, server, name):
if name.lower().startswith("iis://"):
return name
else:
if name and name[0] != "/":
name = "/"+name
return FindWebServer(options, server)+"/ROOT"+name
def LocateWebServerPath(description):
"""
Find an IIS web server whose name or comment matches the provided
description (case-insensitive).
>>> LocateWebServerPath('Default Web Site') # doctest: +SKIP
or
>>> LocateWebServerPath('1') #doctest: +SKIP
"""
assert len(description) >= 1, "Server name or comment is required"
iis = GetObject(_IIS_OBJECT)
description = description.lower().strip()
for site in iis:
# Name is generally a number, but no need to assume that.
site_attributes = [getattr(site, attr, "").lower().strip()
for attr in ("Name", "ServerComment")]
if description in site_attributes:
return site.AdsPath
msg = "No web sites match the description '%s'" % description
raise ItemNotFound(msg)
def GetWebServer(description = None):
"""
Load the web server instance (COM object) for a given instance
or description.
If None is specified, the default website is retrieved (indicated
by the identifier 1.
"""
description = description or "1"
path = LocateWebServerPath(description)
server = LoadWebServer(path)
return server
def LoadWebServer(path):
try:
server = GetObject(path)
except pythoncom.com_error as details:
msg = details.strerror
if exc.excepinfo and exc.excepinfo[2]:
msg = exc.excepinfo[2]
msg = "WebServer %s: %s" % (path, msg)
raise ItemNotFound(msg)
return server
def FindWebServer(options, server_desc):
"""
Legacy function to allow options to define a .server property
to override the other parameter. Use GetWebServer instead.
"""
# options takes precedence
server_desc = options.server or server_desc
# make sure server_desc is unicode (could be mbcs if passed in
# sys.argv).
if server_desc and not isinstance(server_desc, str):
server_desc = server_desc.decode('mbcs')
# get the server (if server_desc is None, the default site is acquired)
server = GetWebServer(server_desc)
return server.adsPath
def split_path(path):
"""
Get the parent path and basename.
>>> split_path('/')
['', '']
>>> split_path('')
['', '']
>>> split_path('foo')
['', 'foo']
>>> split_path('/foo')
['', 'foo']
>>> split_path('/foo/bar')
['/foo', 'bar']
>>> split_path('foo/bar')
['/foo', 'bar']
"""
if not path.startswith('/'): path = '/' + path
return path.rsplit('/', 1)
def _CreateDirectory(iis_dir, name, params):
# We used to go to lengths to keep an existing virtual directory
# in place. However, in some cases the existing directories got
# into a bad state, and an update failed to get them working.
# So we nuke it first. If this is a problem, we could consider adding
# a --keep-existing option.
try:
# Also seen the Class change to a generic IISObject - so nuke
# *any* existing object, regardless of Class
assert name.strip("/"), "mustn't delete the root!"
iis_dir.Delete('', name)
log(2, "Deleted old directory '%s'" % (name,))
except pythoncom.com_error:
pass
newDir = iis_dir.Create(params.Type, name)
log(2, "Creating new directory '%s' in %s..." % (name,iis_dir.Name))
friendly = params.Description or params.Name
newDir.AppFriendlyName = friendly
# Note that the new directory won't be visible in the IIS UI
# unless the directory exists on the filesystem.
try:
path = params.Path or iis_dir.Path
newDir.Path = path
except AttributeError:
# If params.Type is IIS_WEBDIRECTORY, an exception is thrown
pass
newDir.AppCreate2(params.AppProtection)
# XXX - note that these Headers only work in IIS6 and earlier. IIS7
# only supports them on the w3svc node - not even on individial sites,
# let alone individual extensions in the site!
if params.Headers:
newDir.HttpCustomHeaders = params.Headers
log(2, "Setting directory options...")
newDir.AccessExecute = params.AccessExecute
newDir.AccessRead = params.AccessRead
newDir.AccessWrite = params.AccessWrite
newDir.AccessScript = params.AccessScript
newDir.ContentIndexed = params.ContentIndexed
newDir.EnableDirBrowsing = params.EnableDirBrowsing
newDir.EnableDefaultDoc = params.EnableDefaultDoc
if params.DefaultDoc is not None:
newDir.DefaultDoc = params.DefaultDoc
newDir.SetInfo()
return newDir
def CreateDirectory(params, options):
_CallHook(params, "PreInstall", options)
if not params.Name:
raise ConfigurationError("No Name param")
parent, name = params.split_path()
target_dir = GetObject(FindPath(options, params.Server, parent))
if not params.is_root():
target_dir = _CreateDirectory(target_dir, name, params)
AssignScriptMaps(params.ScriptMaps, target_dir, params.ScriptMapUpdate)
_CallHook(params, "PostInstall", options, target_dir)
log(1, "Configured Virtual Directory: %s" % (params.Name,))
return target_dir
def AssignScriptMaps(script_maps, target, update='replace'):
"""Updates IIS with the supplied script map information.
script_maps is a list of ScriptMapParameter objects
target is an IIS Virtual Directory to assign the script maps to
update is a string indicating how to update the maps, one of ('start',
'end', or 'replace')
"""
# determine which function to use to assign script maps
script_map_func = '_AssignScriptMaps' + update.capitalize()
try:
script_map_func = eval(script_map_func)
except NameError:
msg = "Unknown ScriptMapUpdate option '%s'" % update
raise ConfigurationError(msg)
# use the str method to format the script maps for IIS
script_maps = [str(s) for s in script_maps]
# call the correct function
script_map_func(target, script_maps)
target.SetInfo()
def get_unique_items(sequence, reference):
"Return items in sequence that can't be found in reference."
return tuple([item for item in sequence if item not in reference])
def _AssignScriptMapsReplace(target, script_maps):
target.ScriptMaps = script_maps
def _AssignScriptMapsEnd(target, script_maps):
unique_new_maps = get_unique_items(script_maps, target.ScriptMaps)
target.ScriptMaps = target.ScriptMaps + unique_new_maps
def _AssignScriptMapsStart(target, script_maps):
unique_new_maps = get_unique_items(script_maps, target.ScriptMaps)
target.ScriptMaps = unique_new_maps + target.ScriptMaps
def CreateISAPIFilter(filterParams, options):
server = FindWebServer(options, filterParams.Server)
_CallHook(filterParams, "PreInstall", options)
try:
filters = GetObject(server+"/Filters")
except pythoncom.com_error as exc:
# Brand new sites don't have the '/Filters' collection - create it.
# Any errors other than 'not found' we shouldn't ignore.
if winerror.HRESULT_FACILITY(exc.hresult) != winerror.FACILITY_WIN32 or \
winerror.HRESULT_CODE(exc.hresult) != winerror.ERROR_PATH_NOT_FOUND:
raise
server_ob = GetObject(server)
filters = server_ob.Create(_IIS_FILTERS, "Filters")
filters.FilterLoadOrder = ""
filters.SetInfo()
# As for VirtualDir, delete an existing one.
assert filterParams.Name.strip("/"), "mustn't delete the root!"
try:
filters.Delete(_IIS_FILTER, filterParams.Name)
log(2, "Deleted old filter '%s'" % (filterParams.Name,))
except pythoncom.com_error:
pass
newFilter = filters.Create(_IIS_FILTER, filterParams.Name)
log(2, "Created new ISAPI filter...")
assert os.path.isfile(filterParams.Path)
newFilter.FilterPath = filterParams.Path
newFilter.FilterDescription = filterParams.Description
newFilter.SetInfo()
load_order = [b.strip() for b in filters.FilterLoadOrder.split(",") if b]
if filterParams.Name not in load_order:
load_order.append(filterParams.Name)
filters.FilterLoadOrder = ",".join(load_order)
filters.SetInfo()
_CallHook(filterParams, "PostInstall", options, newFilter)
log (1, "Configured Filter: %s" % (filterParams.Name,))
return newFilter
def DeleteISAPIFilter(filterParams, options):
_CallHook(filterParams, "PreRemove", options)
server = FindWebServer(options, filterParams.Server)
ob_path = server+"/Filters"
try:
filters = GetObject(ob_path)
except pythoncom.com_error as details:
# failure to open the filters just means a totally clean IIS install
# (IIS5 at least has no 'Filters' key when freshly installed).
log(2, "ISAPI filter path '%s' did not exist." % (ob_path,))
return
try:
assert filterParams.Name.strip("/"), "mustn't delete the root!"
filters.Delete(_IIS_FILTER, filterParams.Name)
log(2, "Deleted ISAPI filter '%s'" % (filterParams.Name,))
except pythoncom.com_error as details:
rc = _GetWin32ErrorCode(details)
if rc != winerror.ERROR_PATH_NOT_FOUND:
raise
log(2, "ISAPI filter '%s' did not exist." % (filterParams.Name,))
# Remove from the load order
load_order = [b.strip() for b in filters.FilterLoadOrder.split(",") if b]
if filterParams.Name in load_order:
load_order.remove(filterParams.Name)
filters.FilterLoadOrder = ",".join(load_order)
filters.SetInfo()
_CallHook(filterParams, "PostRemove", options)
log (1, "Deleted Filter: %s" % (filterParams.Name,))
def _AddExtensionFile(module, def_groupid, def_desc, params, options):
group_id = params.AddExtensionFile_GroupID or def_groupid
desc = params.AddExtensionFile_Description or def_desc
try:
ob = GetObject(_IIS_OBJECT)
ob.AddExtensionFile(module,
params.AddExtensionFile_Enabled,
group_id,
params.AddExtensionFile_CanDelete,
desc)
log(2, "Added extension file '%s' (%s)" % (module, desc))
except (pythoncom.com_error, AttributeError) as details:
# IIS5 always fails. Probably should upgrade this to
# complain more loudly if IIS6 fails.
log(2, "Failed to add extension file '%s': %s" % (module, details))
def AddExtensionFiles(params, options):
"""Register the modules used by the filters/extensions as a trusted
'extension module' - required by the default IIS6 security settings."""
# Add each module only once.
added = {}
for vd in params.VirtualDirs:
for smp in vd.ScriptMaps:
if smp.Module not in added and smp.AddExtensionFile:
_AddExtensionFile(smp.Module, vd.Name, vd.Description, smp,
options)
added[smp.Module] = True
for fd in params.Filters:
if fd.Path not in added and fd.AddExtensionFile:
_AddExtensionFile(fd.Path, fd.Name, fd.Description, fd, options)
added[fd.Path] = True
def _DeleteExtensionFileRecord(module, options):
try:
ob = GetObject(_IIS_OBJECT)
ob.DeleteExtensionFileRecord(module)
log(2, "Deleted extension file record for '%s'" % module)
except (pythoncom.com_error, AttributeError) as details:
log(2, "Failed to remove extension file '%s': %s" % (module, details))
def DeleteExtensionFileRecords(params, options):
deleted = {} # only remove each .dll once.
for vd in params.VirtualDirs:
for smp in vd.ScriptMaps:
if smp.Module not in deleted and smp.AddExtensionFile:
_DeleteExtensionFileRecord(smp.Module, options)
deleted[smp.Module] = True
for filter_def in params.Filters:
if filter_def.Path not in deleted and filter_def.AddExtensionFile:
_DeleteExtensionFileRecord(filter_def.Path, options)
deleted[filter_def.Path] = True
def CheckLoaderModule(dll_name):
suffix = ""
if is_debug_build: suffix = "_d"
template = os.path.join(this_dir,
"PyISAPI_loader" + suffix + ".dll")
if not os.path.isfile(template):
raise ConfigurationError(
"Template loader '%s' does not exist" % (template,))
# We can't do a simple "is newer" check, as the DLL is specific to the
# Python version. So we check the date-time and size are identical,
# and skip the copy in that case.
src_stat = os.stat(template)
try:
dest_stat = os.stat(dll_name)
except os.error:
same = 0
else:
same = src_stat[stat.ST_SIZE]==dest_stat[stat.ST_SIZE] and \
src_stat[stat.ST_MTIME]==dest_stat[stat.ST_MTIME]
if not same:
log(2, "Updating %s->%s" % (template, dll_name))
shutil.copyfile(template, dll_name)
shutil.copystat(template, dll_name)
else:
log(2, "%s is up to date." % (dll_name,))
def _CallHook(ob, hook_name, options, *extra_args):
func = getattr(ob, hook_name, None)
if func is not None:
args = (ob,options) + extra_args
func(*args)
def Install(params, options):
_CallHook(params, "PreInstall", options)
for vd in params.VirtualDirs:
CreateDirectory(vd, options)
for filter_def in params.Filters:
CreateISAPIFilter(filter_def, options)
AddExtensionFiles(params, options)
_CallHook(params, "PostInstall", options)
def RemoveDirectory(params, options):
if params.is_root():
return
try:
directory = GetObject(FindPath(options, params.Server, params.Name))
except pythoncom.com_error as details:
rc = _GetWin32ErrorCode(details)
if rc != winerror.ERROR_PATH_NOT_FOUND:
raise
log(2, "VirtualDirectory '%s' did not exist" % params.Name)
directory = None
if directory is not None:
# Be robust should IIS get upset about unloading.
try:
directory.AppUnLoad()
except:
exc_val = sys.exc_info()[1]
log(2, "AppUnLoad() for %s failed: %s" % (params.Name, exc_val))
# Continue trying to delete it.
try:
parent = GetObject(directory.Parent)
parent.Delete(directory.Class, directory.Name)
log (1, "Deleted Virtual Directory: %s" % (params.Name,))
except:
exc_val = sys.exc_info()[1]
log(1, "Failed to remove directory %s: %s" % (params.Name, exc_val))
def RemoveScriptMaps(vd_params, options):
"Remove script maps from the already installed virtual directory"
parent, name = vd_params.split_path()
target_dir = GetObject(FindPath(options, vd_params.Server, parent))
installed_maps = list(target_dir.ScriptMaps)
for _map in map(str, vd_params.ScriptMaps):
if _map in installed_maps:
installed_maps.remove(_map)
target_dir.ScriptMaps = installed_maps
target_dir.SetInfo()
def Uninstall(params, options):
_CallHook(params, "PreRemove", options)
DeleteExtensionFileRecords(params, options)
for vd in params.VirtualDirs:
_CallHook(vd, "PreRemove", options)
RemoveDirectory(vd, options)
if vd.is_root():
# if this is installed to the root virtual directory, we can't delete it
# so remove the script maps.
RemoveScriptMaps(vd, options)
_CallHook(vd, "PostRemove", options)
for filter_def in params.Filters:
DeleteISAPIFilter(filter_def, options)
_CallHook(params, "PostRemove", options)
# Patch up any missing module names in the params, replacing them with
# the DLL name that hosts this extension/filter.
def _PatchParamsModule(params, dll_name, file_must_exist = True):
if file_must_exist:
if not os.path.isfile(dll_name):
raise ConfigurationError("%s does not exist" % (dll_name,))
# Patch up all references to the DLL.
for f in params.Filters:
if f.Path is None: f.Path = dll_name
for d in params.VirtualDirs:
for sm in d.ScriptMaps:
if sm.Module is None: sm.Module = dll_name
def GetLoaderModuleName(mod_name, check_module = None):
# find the name of the DLL hosting us.
# By default, this is "_{module_base_name}.dll"
if hasattr(sys, "frozen"):
# What to do? The .dll knows its name, but this is likely to be
# executed via a .exe, which does not know.
base, ext = os.path.splitext(mod_name)
path, base = os.path.split(base)
# handle the common case of 'foo.exe'/'foow.exe'
if base.endswith('w'):
base = base[:-1]
# For py2exe, we have '_foo.dll' as the standard pyisapi loader - but
# 'foo.dll' is what we use (it just delegates).
# So no leading '_' on the installed name.
dll_name = os.path.abspath(os.path.join(path, base + ".dll"))
else:
base, ext = os.path.splitext(mod_name)
path, base = os.path.split(base)
dll_name = os.path.abspath(os.path.join(path, "_" + base + ".dll"))
# Check we actually have it.
if check_module is None: check_module = not hasattr(sys, "frozen")
if check_module:
CheckLoaderModule(dll_name)
return dll_name
# Note the 'log' params to these 'builtin' args - old versions of pywin32
# didn't log at all in this function (by intent; anyone calling this was
# responsible). So existing code that calls this function with the old
# signature (ie, without a 'log' param) still gets the same behaviour as
# before...
def InstallModule(conf_module_name, params, options, log=lambda *args:None):
"Install the extension"
if not hasattr(sys, "frozen"):
conf_module_name = os.path.abspath(conf_module_name)
if not os.path.isfile(conf_module_name):
raise ConfigurationError("%s does not exist" % (conf_module_name,))
loader_dll = GetLoaderModuleName(conf_module_name)
_PatchParamsModule(params, loader_dll)
Install(params, options)
log(1, "Installation complete.")
def UninstallModule(conf_module_name, params, options, log=lambda *args:None):
"Remove the extension"
loader_dll = GetLoaderModuleName(conf_module_name, False)
_PatchParamsModule(params, loader_dll, False)
Uninstall(params, options)
log(1, "Uninstallation complete.")
standard_arguments = {
"install" : InstallModule,
"remove" : UninstallModule,
}
def build_usage(handler_map):
docstrings = [handler.__doc__ for handler in handler_map.values()]
all_args = dict(zip(iter(handler_map.keys()), docstrings))
arg_names = "|".join(iter(all_args.keys()))
usage_string = "%prog [options] [" + arg_names + "]\n"
usage_string += "commands:\n"
for arg, desc in all_args.items():
usage_string += " %-10s: %s" % (arg, desc) + "\n"
return usage_string[:-1]
def MergeStandardOptions(options, params):
"""
Take an options object generated by the command line and merge
the values into the IISParameters object.
"""
pass
# We support 2 ways of extending our command-line/install support.
# * Many of the installation items allow you to specify "PreInstall",
# "PostInstall", "PreRemove" and "PostRemove" hooks
# All hooks are called with the 'params' object being operated on, and
# the 'optparser' options for this session (ie, the command-line options)
# PostInstall for VirtualDirectories and Filters both have an additional
# param - the ADSI object just created.
# * You can pass your own option parser for us to use, and/or define a map
# with your own custom arg handlers. It is a map of 'arg'->function.
# The function is called with (options, log_fn, arg). The function's
# docstring is used in the usage output.
def HandleCommandLine(params, argv=None, conf_module_name = None,
default_arg = "install",
opt_parser = None, custom_arg_handlers = {}):
"""Perform installation or removal of an ISAPI filter or extension.
This module handles standard command-line options and configuration
information, and installs, removes or updates the configuration of an
ISAPI filter or extension.
You must pass your configuration information in params - all other
arguments are optional, and allow you to configure the installation
process.
"""
global verbose
from optparse import OptionParser
argv = argv or sys.argv
if not conf_module_name:
conf_module_name = sys.argv[0]
# convert to a long name so that if we were somehow registered with
# the "short" version but unregistered with the "long" version we
# still work (that will depend on exactly how the installer was
# started)
try:
conf_module_name = win32api.GetLongPathName(conf_module_name)
except win32api.error as exc:
log(2, "Couldn't determine the long name for %r: %s" %
(conf_module_name, exc))
if opt_parser is None:
# Build our own parser.
parser = OptionParser(usage='')
else:
# The caller is providing their own filter, presumably with their
# own options all setup.
parser = opt_parser
# build a usage string if we don't have one.
if not parser.get_usage():
all_handlers = standard_arguments.copy()
all_handlers.update(custom_arg_handlers)
parser.set_usage(build_usage(all_handlers))
# allow the user to use uninstall as a synonym for remove if it wasn't
# defined by the custom arg handlers.
all_handlers.setdefault('uninstall', all_handlers['remove'])
parser.add_option("-q", "--quiet",
action="store_false", dest="verbose", default=True,
help="don't print status messages to stdout")
parser.add_option("-v", "--verbosity", action="count",
dest="verbose", default=1,
help="increase the verbosity of status messages")
parser.add_option("", "--server", action="store",
help="Specifies the IIS server to install/uninstall on." \
" Default is '%s/1'" % (_IIS_OBJECT,))
(options, args) = parser.parse_args(argv[1:])
MergeStandardOptions(options, params)
verbose = options.verbose
if not args:
args = [default_arg]
try:
for arg in args:
handler = all_handlers[arg]
handler(conf_module_name, params, options, log)
except (ItemNotFound, InstallationError) as details:
if options.verbose > 1:
traceback.print_exc()
print("%s: %s" % (details.__class__.__name__, details))
except KeyError:
parser.error("Invalid arg '%s'" % arg)

View File

@ -0,0 +1,120 @@
"""Constants needed by ISAPI filters and extensions."""
# ======================================================================
# Copyright 2002-2003 by Blackdog Software Pty Ltd.
#
# All Rights Reserved
#
# Permission to use, copy, modify, and distribute this software and
# its documentation for any purpose and without fee is hereby
# granted, provided that the above copyright notice appear in all
# copies and that both that copyright notice and this permission
# notice appear in supporting documentation, and that the name of
# Blackdog Software not be used in advertising or publicity pertaining to
# distribution of the software without specific, written prior
# permission.
#
# BLACKDOG SOFTWARE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
# INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
# NO EVENT SHALL BLACKDOG SOFTWARE BE LIABLE FOR ANY SPECIAL, INDIRECT OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
# ======================================================================
# HTTP reply codes
HTTP_CONTINUE = 100
HTTP_SWITCHING_PROTOCOLS = 101
HTTP_PROCESSING = 102
HTTP_OK = 200
HTTP_CREATED = 201
HTTP_ACCEPTED = 202
HTTP_NON_AUTHORITATIVE = 203
HTTP_NO_CONTENT = 204
HTTP_RESET_CONTENT = 205
HTTP_PARTIAL_CONTENT = 206
HTTP_MULTI_STATUS = 207
HTTP_MULTIPLE_CHOICES = 300
HTTP_MOVED_PERMANENTLY = 301
HTTP_MOVED_TEMPORARILY = 302
HTTP_SEE_OTHER = 303
HTTP_NOT_MODIFIED = 304
HTTP_USE_PROXY = 305
HTTP_TEMPORARY_REDIRECT = 307
HTTP_BAD_REQUEST = 400
HTTP_UNAUTHORIZED = 401
HTTP_PAYMENT_REQUIRED = 402
HTTP_FORBIDDEN = 403
HTTP_NOT_FOUND = 404
HTTP_METHOD_NOT_ALLOWED = 405
HTTP_NOT_ACCEPTABLE = 406
HTTP_PROXY_AUTHENTICATION_REQUIRED= 407
HTTP_REQUEST_TIME_OUT = 408
HTTP_CONFLICT = 409
HTTP_GONE = 410
HTTP_LENGTH_REQUIRED = 411
HTTP_PRECONDITION_FAILED = 412
HTTP_REQUEST_ENTITY_TOO_LARGE = 413
HTTP_REQUEST_URI_TOO_LARGE = 414
HTTP_UNSUPPORTED_MEDIA_TYPE = 415
HTTP_RANGE_NOT_SATISFIABLE = 416
HTTP_EXPECTATION_FAILED = 417
HTTP_UNPROCESSABLE_ENTITY = 422
HTTP_INTERNAL_SERVER_ERROR = 500
HTTP_NOT_IMPLEMENTED = 501
HTTP_BAD_GATEWAY = 502
HTTP_SERVICE_UNAVAILABLE = 503
HTTP_GATEWAY_TIME_OUT = 504
HTTP_VERSION_NOT_SUPPORTED = 505
HTTP_VARIANT_ALSO_VARIES = 506
HSE_STATUS_SUCCESS = 1
HSE_STATUS_SUCCESS_AND_KEEP_CONN = 2
HSE_STATUS_PENDING = 3
HSE_STATUS_ERROR = 4
SF_NOTIFY_SECURE_PORT = 0x00000001
SF_NOTIFY_NONSECURE_PORT = 0x00000002
SF_NOTIFY_READ_RAW_DATA = 0x00008000
SF_NOTIFY_PREPROC_HEADERS = 0x00004000
SF_NOTIFY_AUTHENTICATION = 0x00002000
SF_NOTIFY_URL_MAP = 0x00001000
SF_NOTIFY_ACCESS_DENIED = 0x00000800
SF_NOTIFY_SEND_RESPONSE = 0x00000040
SF_NOTIFY_SEND_RAW_DATA = 0x00000400
SF_NOTIFY_LOG = 0x00000200
SF_NOTIFY_END_OF_REQUEST = 0x00000080
SF_NOTIFY_END_OF_NET_SESSION = 0x00000100
SF_NOTIFY_ORDER_HIGH = 0x00080000
SF_NOTIFY_ORDER_MEDIUM = 0x00040000
SF_NOTIFY_ORDER_LOW = 0x00020000
SF_NOTIFY_ORDER_DEFAULT = SF_NOTIFY_ORDER_LOW
SF_NOTIFY_ORDER_MASK = (SF_NOTIFY_ORDER_HIGH | \
SF_NOTIFY_ORDER_MEDIUM | \
SF_NOTIFY_ORDER_LOW)
SF_STATUS_REQ_FINISHED = 134217728 # 0x8000000
SF_STATUS_REQ_FINISHED_KEEP_CONN = 134217728 + 1
SF_STATUS_REQ_NEXT_NOTIFICATION = 134217728 + 2
SF_STATUS_REQ_HANDLED_NOTIFICATION = 134217728 + 3
SF_STATUS_REQ_ERROR = 134217728 + 4
SF_STATUS_REQ_READ_NEXT = 134217728 + 5
HSE_IO_SYNC = 0x00000001 # for WriteClient
HSE_IO_ASYNC = 0x00000002 # for WriteClient/TF/EU
HSE_IO_DISCONNECT_AFTER_SEND = 0x00000004 # for TF
HSE_IO_SEND_HEADERS = 0x00000008 # for TF
HSE_IO_NODELAY = 0x00001000 # turn off nagling
# These two are only used by VectorSend
HSE_IO_FINAL_SEND = 0x00000010
HSE_IO_CACHE_RESPONSE = 0x00000020
HSE_EXEC_URL_NO_HEADERS = 0x02
HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR = 0x04
HSE_EXEC_URL_IGNORE_VALIDATION_AND_RANGE = 0x10
HSE_EXEC_URL_DISABLE_CUSTOM_ERROR = 0x20
HSE_EXEC_URL_SSI_CMD = 0x40
HSE_EXEC_URL_HTTP_CACHE_ELIGIBLE = 0x80

View File

@ -0,0 +1,20 @@
In this directory you will find examples of ISAPI filters and extensions.
The filter loading mechanism works like this:
* IIS loads the special Python "loader" DLL. This DLL will generally have a
leading underscore as part of its name.
* This loader DLL looks for a Python module, by removing the first letter of
the DLL base name.
This means that an ISAPI extension module consists of 2 key files - the loader
DLL (eg, "_MyIISModule.dll", and a Python module (which for this example
would be "MyIISModule.py")
When you install an ISAPI extension, the installation code checks to see if
there is a loader DLL for your implementation file - if one does not exist,
or the standard loader is different, it is copied and renamed accordingly.
We use this mechanism to provide the maximum separation between different
Python extensions installed on the same server - otherwise filter order and
other tricky IIS semantics would need to be replicated. Also, each filter
gets its own thread-pool, etc.

View File

@ -0,0 +1,196 @@
# This extension demonstrates some advanced features of the Python ISAPI
# framework.
# We demonstrate:
# * Reloading your Python module without shutting down IIS (eg, when your
# .py implementation file changes.)
# * Custom command-line handling - both additional options and commands.
# * Using a query string - any part of the URL after a '?' is assumed to
# be "variable names" separated by '&' - we will print the values of
# these server variables.
# * If the tail portion of the URL is "ReportUnhealthy", IIS will be
# notified we are unhealthy via a HSE_REQ_REPORT_UNHEALTHY request.
# Whether this is acted upon depends on if the IIS health-checking
# tools are installed, but you should always see the reason written
# to the Windows event log - see the IIS documentation for more.
from isapi import isapicon
from isapi.simple import SimpleExtension
import sys, os, stat
if hasattr(sys, "isapidllhandle"):
import win32traceutil
# Notes on reloading
# If your HttpFilterProc or HttpExtensionProc functions raises
# 'isapi.InternalReloadException', the framework will not treat it
# as an error but instead will terminate your extension, reload your
# extension module, re-initialize the instance, and re-issue the request.
# The Initialize functions are called with None as their param. The
# return code from the terminate function is ignored.
#
# This is all the framework does to help you. It is up to your code
# when you raise this exception. This sample uses a Win32 "find
# notification". Whenever windows tells us one of the files in the
# directory has changed, we check if the time of our source-file has
# changed, and set a flag. Next imcoming request, we check the flag and
# raise the special exception if set.
#
# The end result is that the module is automatically reloaded whenever
# the source-file changes - you need take no further action to see your
# changes reflected in the running server.
# The framework only reloads your module - if you have libraries you
# depend on and also want reloaded, you must arrange for this yourself.
# One way of doing this would be to special case the import of these
# modules. Eg:
# --
# try:
# my_module = reload(my_module) # module already imported - reload it
# except NameError:
# import my_module # first time around - import it.
# --
# When your module is imported for the first time, the NameError will
# be raised, and the module imported. When the ISAPI framework reloads
# your module, the existing module will avoid the NameError, and allow
# you to reload that module.
from isapi import InternalReloadException
import win32event, win32file, winerror, win32con, threading
try:
reload_counter += 1
except NameError:
reload_counter = 0
# A watcher thread that checks for __file__ changing.
# When it detects it, it simply sets "change_detected" to true.
class ReloadWatcherThread(threading.Thread):
def __init__(self):
self.change_detected = False
self.filename = __file__
if self.filename.endswith("c") or self.filename.endswith("o"):
self.filename = self.filename[:-1]
self.handle = win32file.FindFirstChangeNotification(
os.path.dirname(self.filename),
False, # watch tree?
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE)
threading.Thread.__init__(self)
def run(self):
last_time = os.stat(self.filename)[stat.ST_MTIME]
while 1:
try:
rc = win32event.WaitForSingleObject(self.handle,
win32event.INFINITE)
win32file.FindNextChangeNotification(self.handle)
except win32event.error as details:
# handle closed - thread should terminate.
if details.winerror != winerror.ERROR_INVALID_HANDLE:
raise
break
this_time = os.stat(self.filename)[stat.ST_MTIME]
if this_time != last_time:
print("Detected file change - flagging for reload.")
self.change_detected = True
last_time = this_time
def stop(self):
win32file.FindCloseChangeNotification(self.handle)
# The ISAPI extension - handles requests in our virtual dir, and sends the
# response to the client.
class Extension(SimpleExtension):
"Python advanced sample Extension"
def __init__(self):
self.reload_watcher = ReloadWatcherThread()
self.reload_watcher.start()
def HttpExtensionProc(self, ecb):
# NOTE: If you use a ThreadPoolExtension, you must still perform
# this check in HttpExtensionProc - raising the exception from
# The "Dispatch" method will just cause the exception to be
# rendered to the browser.
if self.reload_watcher.change_detected:
print("Doing reload")
raise InternalReloadException
url = ecb.GetServerVariable("UNICODE_URL")
if url.endswith("ReportUnhealthy"):
ecb.ReportUnhealthy("I'm a little sick")
ecb.SendResponseHeaders("200 OK", "Content-Type: text/html\r\n\r\n", 0)
print("<HTML><BODY>", file=ecb)
qs = ecb.GetServerVariable("QUERY_STRING")
if qs:
queries = qs.split("&")
print("<PRE>", file=ecb)
for q in queries:
val = ecb.GetServerVariable(q, '&lt;no such variable&gt;')
print("%s=%r" % (q, val), file=ecb)
print("</PRE><P/>", file=ecb)
print("This module has been imported", file=ecb)
print("%d times" % (reload_counter,), file=ecb)
print("</BODY></HTML>", file=ecb)
ecb.close()
return isapicon.HSE_STATUS_SUCCESS
def TerminateExtension(self, status):
self.reload_watcher.stop()
# The entry points for the ISAPI extension.
def __ExtensionFactory__():
return Extension()
# Our special command line customization.
# Pre-install hook for our virtual directory.
def PreInstallDirectory(params, options):
# If the user used our special '--description' option,
# then we override our default.
if options.description:
params.Description = options.description
# Post install hook for our entire script
def PostInstall(params, options):
print()
print("The sample has been installed.")
print("Point your browser to /AdvancedPythonSample")
print("If you modify the source file and reload the page,")
print("you should see the reload counter increment")
# Handler for our custom 'status' argument.
def status_handler(options, log, arg):
"Query the status of something"
print("Everything seems to be fine!")
custom_arg_handlers = {"status": status_handler}
if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters(PostInstall = PostInstall)
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = [
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name="AdvancedPythonSample",
Description = Extension.__doc__,
ScriptMaps = sm,
ScriptMapUpdate = "replace",
# specify the pre-install hook.
PreInstall = PreInstallDirectory
)
params.VirtualDirs = [vd]
# Setup our custom option parser.
from optparse import OptionParser
parser = OptionParser('') # blank usage, so isapi sets it.
parser.add_option("", "--description",
action="store",
help="custom description to use for the virtual directory")
HandleCommandLine(params, opt_parser=parser,
custom_arg_handlers = custom_arg_handlers)

View File

@ -0,0 +1,109 @@
# This is a sample ISAPI extension written in Python.
#
# Please see README.txt in this directory, and specifically the
# information about the "loader" DLL - installing this sample will create
# "_redirector.dll" in the current directory. The readme explains this.
# Executing this script (or any server config script) will install the extension
# into your web server. As the server executes, the PyISAPI framework will load
# this module and create your Extension and Filter objects.
# This is the simplest possible redirector (or proxy) we can write. The
# extension installs with a mask of '*' in the root of the site.
# As an added bonus though, we optionally show how, on IIS6 and later, we
# can use HSE_ERQ_EXEC_URL to ignore certain requests - in IIS5 and earlier
# we can only do this with an ISAPI filter - see redirector_with_filter for
# an example. If this sample is run on IIS5 or earlier it simply ignores
# any excludes.
from isapi import isapicon, threaded_extension
import sys
import traceback
try:
from urllib.request import urlopen
except ImportError:
# py3k spelling...
from urllib.request import urlopen
import win32api
# sys.isapidllhandle will exist when we are loaded by the IIS framework.
# In this case we redirect our output to the win32traceutil collector.
if hasattr(sys, "isapidllhandle"):
import win32traceutil
# The site we are proxying.
proxy = "http://www.python.org"
# Urls we exclude (ie, allow IIS to handle itself) - all are lowered,
# and these entries exist by default on Vista...
excludes = ["/iisstart.htm", "/welcome.png"]
# An "io completion" function, called when ecb.ExecURL completes...
def io_callback(ecb, url, cbIO, errcode):
# Get the status of our ExecURL
httpstatus, substatus, win32 = ecb.GetExecURLStatus()
print("ExecURL of %r finished with http status %d.%d, win32 status %d (%s)" % (
url, httpstatus, substatus, win32, win32api.FormatMessage(win32).strip()))
# nothing more to do!
ecb.DoneWithSession()
# The ISAPI extension - handles all requests in the site.
class Extension(threaded_extension.ThreadPoolExtension):
"Python sample Extension"
def Dispatch(self, ecb):
# Note that our ThreadPoolExtension base class will catch exceptions
# in our Dispatch method, and write the traceback to the client.
# That is perfect for this sample, so we don't catch our own.
#print 'IIS dispatching "%s"' % (ecb.GetServerVariable("URL"),)
url = ecb.GetServerVariable("URL").decode("ascii")
for exclude in excludes:
if url.lower().startswith(exclude):
print("excluding %s" % url)
if ecb.Version < 0x60000:
print("(but this is IIS5 or earlier - can't do 'excludes')")
else:
ecb.IOCompletion(io_callback, url)
ecb.ExecURL(None, None, None, None, None, isapicon.HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR)
return isapicon.HSE_STATUS_PENDING
new_url = proxy + url
print("Opening %s" % new_url)
fp = urlopen(new_url)
headers = fp.info()
# subtle py3k breakage: in py3k, str(headers) has normalized \r\n
# back to \n and also stuck an extra \n term. py2k leaves the
# \r\n from the server in tact and finishes with a single term.
if sys.version_info < (3,0):
header_text = str(headers) + "\r\n"
else:
# take *all* trailing \n off, replace remaining with
# \r\n, then add the 2 trailing \r\n.
header_text = str(headers).rstrip('\n').replace('\n', '\r\n') + '\r\n\r\n'
ecb.SendResponseHeaders("200 OK", header_text, False)
ecb.WriteClient(fp.read())
ecb.DoneWithSession()
print("Returned data from '%s'" % (new_url,))
return isapicon.HSE_STATUS_SUCCESS
# The entry points for the ISAPI extension.
def __ExtensionFactory__():
return Extension()
if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters()
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = [
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name="/",
Description = Extension.__doc__,
ScriptMaps = sm,
ScriptMapUpdate = "replace"
)
params.VirtualDirs = [vd]
HandleCommandLine(params)

View File

@ -0,0 +1,78 @@
# This is a sample ISAPI extension written in Python.
# This is like the other 'redirector' samples, but uses asnch IO when writing
# back to the client (it does *not* use asynch io talking to the remote
# server!)
from isapi import isapicon, threaded_extension
import sys
import traceback
import urllib.request, urllib.parse, urllib.error
# sys.isapidllhandle will exist when we are loaded by the IIS framework.
# In this case we redirect our output to the win32traceutil collector.
if hasattr(sys, "isapidllhandle"):
import win32traceutil
# The site we are proxying.
proxy = "http://www.python.org"
# We synchronously read chunks of this size then asynchronously write them.
CHUNK_SIZE=8192
# The callback made when IIS completes the asynch write.
def io_callback(ecb, fp, cbIO, errcode):
print("IO callback", ecb, fp, cbIO, errcode)
chunk = fp.read(CHUNK_SIZE)
if chunk:
ecb.WriteClient(chunk, isapicon.HSE_IO_ASYNC)
# and wait for the next callback to say this chunk is done.
else:
# eof - say we are complete.
fp.close()
ecb.DoneWithSession()
# The ISAPI extension - handles all requests in the site.
class Extension(threaded_extension.ThreadPoolExtension):
"Python sample proxy server - asynch version."
def Dispatch(self, ecb):
print('IIS dispatching "%s"' % (ecb.GetServerVariable("URL"),))
url = ecb.GetServerVariable("URL")
new_url = proxy + url
print("Opening %s" % new_url)
fp = urllib.request.urlopen(new_url)
headers = fp.info()
ecb.SendResponseHeaders("200 OK", str(headers) + "\r\n", False)
# now send the first chunk asynchronously
ecb.ReqIOCompletion(io_callback, fp)
chunk = fp.read(CHUNK_SIZE)
if chunk:
ecb.WriteClient(chunk, isapicon.HSE_IO_ASYNC)
return isapicon.HSE_STATUS_PENDING
# no data - just close things now.
ecb.DoneWithSession()
return isapicon.HSE_STATUS_SUCCESS
# The entry points for the ISAPI extension.
def __ExtensionFactory__():
return Extension()
if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters()
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = [
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name="/",
Description = Extension.__doc__,
ScriptMaps = sm,
ScriptMapUpdate = "replace"
)
params.VirtualDirs = [vd]
HandleCommandLine(params)

View File

@ -0,0 +1,155 @@
# This is a sample configuration file for an ISAPI filter and extension
# written in Python.
#
# Please see README.txt in this directory, and specifically the
# information about the "loader" DLL - installing this sample will create
# "_redirector_with_filter.dll" in the current directory. The readme explains
# this.
# Executing this script (or any server config script) will install the extension
# into your web server. As the server executes, the PyISAPI framework will load
# this module and create your Extension and Filter objects.
# This sample provides sample redirector:
# It is implemented by a filter and an extension, so that some requests can
# be ignored. Compare with 'redirector_simple' which avoids the filter, but
# is unable to selectively ignore certain requests.
# The process is sample uses is:
# * The filter is installed globally, as all filters are.
# * A Virtual Directory named "python" is setup. This dir has our ISAPI
# extension as the only application, mapped to file-extension '*'. Thus, our
# extension handles *all* requests in this directory.
# The basic process is that the filter does URL rewriting, redirecting every
# URL to our Virtual Directory. Our extension then handles this request,
# forwarding the data from the proxied site.
# For example:
# * URL of "index.html" comes in.
# * Filter rewrites this to "/python/index.html"
# * Our extension sees the full "/python/index.html", removes the leading
# portion, and opens and forwards the remote URL.
# This sample is very small - it avoid most error handling, etc. It is for
# demonstration purposes only.
from isapi import isapicon, threaded_extension
from isapi.simple import SimpleFilter
import sys
import traceback
import urllib.request, urllib.parse, urllib.error
# sys.isapidllhandle will exist when we are loaded by the IIS framework.
# In this case we redirect our output to the win32traceutil collector.
if hasattr(sys, "isapidllhandle"):
import win32traceutil
# The site we are proxying.
proxy = "http://www.python.org"
# The name of the virtual directory we install in, and redirect from.
virtualdir = "/python"
# The key feature of this redirector over the simple redirector is that it
# can choose to ignore certain responses by having the filter not rewrite them
# to our virtual dir. For this sample, we just exclude the IIS help directory.
# The ISAPI extension - handles requests in our virtual dir, and sends the
# response to the client.
class Extension(threaded_extension.ThreadPoolExtension):
"Python sample Extension"
def Dispatch(self, ecb):
# Note that our ThreadPoolExtension base class will catch exceptions
# in our Dispatch method, and write the traceback to the client.
# That is perfect for this sample, so we don't catch our own.
#print 'IIS dispatching "%s"' % (ecb.GetServerVariable("URL"),)
url = ecb.GetServerVariable("URL")
if url.startswith(virtualdir):
new_url = proxy + url[len(virtualdir):]
print("Opening", new_url)
fp = urllib.request.urlopen(new_url)
headers = fp.info()
ecb.SendResponseHeaders("200 OK", str(headers) + "\r\n", False)
ecb.WriteClient(fp.read())
ecb.DoneWithSession()
print("Returned data from '%s'!" % (new_url,))
else:
# this should never happen - we should only see requests that
# start with our virtual directory name.
print("Not proxying '%s'" % (url,))
# The ISAPI filter.
class Filter(SimpleFilter):
"Sample Python Redirector"
filter_flags = isapicon.SF_NOTIFY_PREPROC_HEADERS | \
isapicon.SF_NOTIFY_ORDER_DEFAULT
def HttpFilterProc(self, fc):
#print "Filter Dispatch"
nt = fc.NotificationType
if nt != isapicon.SF_NOTIFY_PREPROC_HEADERS:
return isapicon.SF_STATUS_REQ_NEXT_NOTIFICATION
pp = fc.GetData()
url = pp.GetHeader("url")
#print "URL is '%s'" % (url,)
prefix = virtualdir
if not url.startswith(prefix):
new_url = prefix + url
print("New proxied URL is '%s'" % (new_url,))
pp.SetHeader("url", new_url)
# For the sake of demonstration, show how the FilterContext
# attribute is used. It always starts out life as None, and
# any assignments made are automatically decref'd by the
# framework during a SF_NOTIFY_END_OF_NET_SESSION notification.
if fc.FilterContext is None:
fc.FilterContext = 0
fc.FilterContext += 1
print("This is request number %d on this connection" % fc.FilterContext)
return isapicon.SF_STATUS_REQ_HANDLED_NOTIFICATION
else:
print("Filter ignoring URL '%s'" % (url,))
# Some older code that handled SF_NOTIFY_URL_MAP.
#~ print "Have URL_MAP notify"
#~ urlmap = fc.GetData()
#~ print "URI is", urlmap.URL
#~ print "Path is", urlmap.PhysicalPath
#~ if urlmap.URL.startswith("/UC/"):
#~ # Find the /UC/ in the physical path, and nuke it (except
#~ # as the path is physical, it is \)
#~ p = urlmap.PhysicalPath
#~ pos = p.index("\\UC\\")
#~ p = p[:pos] + p[pos+3:]
#~ p = r"E:\src\pyisapi\webroot\PyTest\formTest.htm"
#~ print "New path is", p
#~ urlmap.PhysicalPath = p
# The entry points for the ISAPI extension.
def __FilterFactory__():
return Filter()
def __ExtensionFactory__():
return Extension()
if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters()
# Setup all filters - these are global to the site.
params.Filters = [
FilterParameters(Name="PythonRedirector",
Description=Filter.__doc__),
]
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = [
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name=virtualdir[1:],
Description = Extension.__doc__,
ScriptMaps = sm,
ScriptMapUpdate = "replace"
)
params.VirtualDirs = [vd]
HandleCommandLine(params)

View File

@ -0,0 +1,154 @@
# This extension is used mainly for testing purposes - it is not
# designed to be a simple sample, but instead is a hotch-potch of things
# that attempts to exercise the framework.
from isapi import isapicon
from isapi.simple import SimpleExtension
import sys, os, stat
if hasattr(sys, "isapidllhandle"):
import win32traceutil
# We use the same reload support as 'advanced.py' demonstrates.
from isapi import InternalReloadException
import win32event, win32file, winerror, win32con, threading
# A watcher thread that checks for __file__ changing.
# When it detects it, it simply sets "change_detected" to true.
class ReloadWatcherThread(threading.Thread):
def __init__(self):
self.change_detected = False
self.filename = __file__
if self.filename.endswith("c") or self.filename.endswith("o"):
self.filename = self.filename[:-1]
self.handle = win32file.FindFirstChangeNotification(
os.path.dirname(self.filename),
False, # watch tree?
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE)
threading.Thread.__init__(self)
def run(self):
last_time = os.stat(self.filename)[stat.ST_MTIME]
while 1:
try:
rc = win32event.WaitForSingleObject(self.handle,
win32event.INFINITE)
win32file.FindNextChangeNotification(self.handle)
except win32event.error as details:
# handle closed - thread should terminate.
if details.winerror != winerror.ERROR_INVALID_HANDLE:
raise
break
this_time = os.stat(self.filename)[stat.ST_MTIME]
if this_time != last_time:
print("Detected file change - flagging for reload.")
self.change_detected = True
last_time = this_time
def stop(self):
win32file.FindCloseChangeNotification(self.handle)
def TransmitFileCallback(ecb, hFile, cbIO, errCode):
print("Transmit complete!")
ecb.close()
# The ISAPI extension - handles requests in our virtual dir, and sends the
# response to the client.
class Extension(SimpleExtension):
"Python test Extension"
def __init__(self):
self.reload_watcher = ReloadWatcherThread()
self.reload_watcher.start()
def HttpExtensionProc(self, ecb):
# NOTE: If you use a ThreadPoolExtension, you must still perform
# this check in HttpExtensionProc - raising the exception from
# The "Dispatch" method will just cause the exception to be
# rendered to the browser.
if self.reload_watcher.change_detected:
print("Doing reload")
raise InternalReloadException
if ecb.GetServerVariable("UNICODE_URL").endswith("test.py"):
file_flags = win32con.FILE_FLAG_SEQUENTIAL_SCAN | win32con.FILE_FLAG_OVERLAPPED
hfile = win32file.CreateFile(__file__, win32con.GENERIC_READ,
0, None, win32con.OPEN_EXISTING,
file_flags, None)
flags = isapicon.HSE_IO_ASYNC | isapicon.HSE_IO_DISCONNECT_AFTER_SEND | \
isapicon.HSE_IO_SEND_HEADERS
# We pass hFile to the callback simply as a way of keeping it alive
# for the duration of the transmission
try:
ecb.TransmitFile(TransmitFileCallback, hfile,
int(hfile),
"200 OK",
0, 0, None, None, flags)
except:
# Errors keep this source file open!
hfile.Close()
raise
else:
# default response
ecb.SendResponseHeaders("200 OK", "Content-Type: text/html\r\n\r\n", 0)
print("<HTML><BODY>", file=ecb)
print("The root of this site is at", ecb.MapURLToPath("/"), file=ecb)
print("</BODY></HTML>", file=ecb)
ecb.close()
return isapicon.HSE_STATUS_SUCCESS
def TerminateExtension(self, status):
self.reload_watcher.stop()
# The entry points for the ISAPI extension.
def __ExtensionFactory__():
return Extension()
# Our special command line customization.
# Pre-install hook for our virtual directory.
def PreInstallDirectory(params, options):
# If the user used our special '--description' option,
# then we override our default.
if options.description:
params.Description = options.description
# Post install hook for our entire script
def PostInstall(params, options):
print()
print("The sample has been installed.")
print("Point your browser to /PyISAPITest")
# Handler for our custom 'status' argument.
def status_handler(options, log, arg):
"Query the status of something"
print("Everything seems to be fine!")
custom_arg_handlers = {"status": status_handler}
if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters(PostInstall = PostInstall)
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = [
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name="PyISAPITest",
Description = Extension.__doc__,
ScriptMaps = sm,
ScriptMapUpdate = "replace",
# specify the pre-install hook.
PreInstall = PreInstallDirectory
)
params.VirtualDirs = [vd]
# Setup our custom option parser.
from optparse import OptionParser
parser = OptionParser('') # blank usage, so isapi sets it.
parser.add_option("", "--description",
action="store",
help="custom description to use for the virtual directory")
HandleCommandLine(params, opt_parser=parser,
custom_arg_handlers = custom_arg_handlers)

View File

@ -0,0 +1,68 @@
"""Simple base-classes for extensions and filters.
None of the filter and extension functions are considered 'optional' by the
framework. These base-classes provide simple implementations for the
Initialize and Terminate functions, allowing you to omit them,
It is not necessary to use these base-classes - but if you don't, you
must ensure each of the required methods are implemented.
"""
class SimpleExtension:
"Base class for a simple ISAPI extension"
def __init__(self):
pass
def GetExtensionVersion(self, vi):
"""Called by the ISAPI framework to get the extension version
The default implementation uses the classes docstring to
set the extension description."""
# nod to our reload capability - vi is None when we are reloaded.
if vi is not None:
vi.ExtensionDesc = self.__doc__
def HttpExtensionProc(self, control_block):
"""Called by the ISAPI framework for each extension request.
sub-classes must provide an implementation for this method.
"""
raise NotImplementedError("sub-classes should override HttpExtensionProc")
def TerminateExtension(self, status):
"""Called by the ISAPI framework as the extension terminates.
"""
pass
class SimpleFilter:
"Base class for a a simple ISAPI filter"
filter_flags = None
def __init__(self):
pass
def GetFilterVersion(self, fv):
"""Called by the ISAPI framework to get the extension version
The default implementation uses the classes docstring to
set the extension description, and uses the classes
filter_flags attribute to set the ISAPI filter flags - you
must specify filter_flags in your class.
"""
if self.filter_flags is None:
raise RuntimeError("You must specify the filter flags")
# nod to our reload capability - fv is None when we are reloaded.
if fv is not None:
fv.Flags = self.filter_flags
fv.FilterDesc = self.__doc__
def HttpFilterProc(self, fc):
"""Called by the ISAPI framework for each filter request.
sub-classes must provide an implementation for this method.
"""
raise NotImplementedError("sub-classes should override HttpExtensionProc")
def TerminateFilter(self, status):
"""Called by the ISAPI framework as the filter terminates.
"""
pass

View File

@ -0,0 +1,3 @@
This is a directory for tests of the PyISAPI framework.
For demos, please see the pyisapi 'samples' directory.

View File

@ -0,0 +1,111 @@
# This is an ISAPI extension purely for testing purposes. It is NOT
# a 'demo' (even though it may be useful!)
#
# Install this extension, then point your browser to:
# "http://localhost/pyisapi_test/test1"
# This will execute the method 'test1' below. See below for the list of
# test methods that are acceptable.
from isapi import isapicon, threaded_extension, ExtensionError
from isapi.simple import SimpleFilter
import traceback
import urllib.request, urllib.parse, urllib.error
import winerror
# If we have no console (eg, am running from inside IIS), redirect output
# somewhere useful - in this case, the standard win32 trace collector.
import win32api
try:
win32api.GetConsoleTitle()
except win32api.error:
# No console - redirect
import win32traceutil
# The ISAPI extension - handles requests in our virtual dir, and sends the
# response to the client.
class Extension(threaded_extension.ThreadPoolExtension):
"Python ISAPI Tester"
def Dispatch(self, ecb):
print('Tester dispatching "%s"' % (ecb.GetServerVariable("URL"),))
url = ecb.GetServerVariable("URL")
test_name = url.split("/")[-1]
meth = getattr(self, test_name, None)
if meth is None:
raise AttributeError("No test named '%s'" % (test_name,))
result = meth(ecb)
if result is None:
# This means the test finalized everything
return
ecb.SendResponseHeaders("200 OK", "Content-type: text/html\r\n\r\n",
False)
print("<HTML><BODY>Finished running test <i>", test_name, "</i>", file=ecb)
print("<pre>", file=ecb)
print(result, file=ecb)
print("</pre>", file=ecb)
print("</BODY></HTML>", file=ecb)
ecb.DoneWithSession()
def test1(self, ecb):
try:
ecb.GetServerVariable("foo bar")
raise RuntimeError("should have failed!")
except ExtensionError as err:
assert err.errno == winerror.ERROR_INVALID_INDEX, err
return "worked!"
def test_long_vars(self, ecb):
qs = ecb.GetServerVariable("QUERY_STRING")
# Our implementation has a default buffer size of 8k - so we test
# the code that handles an overflow by ensuring there are more
# than 8k worth of chars in the URL.
expected_query = ('x' * 8500)
if len(qs)==0:
# Just the URL with no query part - redirect to myself, but with
# a huge query portion.
me = ecb.GetServerVariable("URL")
headers = "Location: " + me + "?" + expected_query + "\r\n\r\n"
ecb.SendResponseHeaders("301 Moved", headers)
ecb.DoneWithSession()
return None
if qs == expected_query:
return "Total length of variable is %d - test worked!" % (len(qs),)
else:
return "Unexpected query portion! Got %d chars, expected %d" % \
(len(qs), len(expected_query))
def test_unicode_vars(self, ecb):
# We need to check that we are running IIS6! This seems the only
# effective way from an extension.
ver = float(ecb.GetServerVariable("SERVER_SOFTWARE").split('/')[1])
if ver < 6.0:
return "This is IIS version %g - unicode only works in IIS6 and later" % ver
us = ecb.GetServerVariable("UNICODE_SERVER_NAME")
if not isinstance(us, str):
raise RuntimeError("unexpected type!")
if us != str(ecb.GetServerVariable("SERVER_NAME")):
raise RuntimeError("Unicode and non-unicode values were not the same")
return "worked!"
# The entry points for the ISAPI extension.
def __ExtensionFactory__():
return Extension()
if __name__=='__main__':
# If run from the command-line, install ourselves.
from isapi.install import *
params = ISAPIParameters()
# Setup the virtual directories - this is a list of directories our
# extension uses - in this case only 1.
# Each extension has a "script map" - this is the mapping of ISAPI
# extensions.
sm = [
ScriptMapParams(Extension="*", Flags=0)
]
vd = VirtualDirParameters(Name="pyisapi_test",
Description = Extension.__doc__,
ScriptMaps = sm,
ScriptMapUpdate = "replace"
)
params.VirtualDirs = [vd]
HandleCommandLine(params)

View File

@ -0,0 +1,171 @@
"""An ISAPI extension base class implemented using a thread-pool."""
# $Id$
import sys
import time
from isapi import isapicon, ExtensionError
import isapi.simple
from win32file import GetQueuedCompletionStatus, CreateIoCompletionPort, \
PostQueuedCompletionStatus, CloseHandle
from win32security import SetThreadToken
from win32event import INFINITE
from pywintypes import OVERLAPPED
import threading
import traceback
ISAPI_REQUEST = 1
ISAPI_SHUTDOWN = 2
class WorkerThread(threading.Thread):
def __init__(self, extension, io_req_port):
self.running = False
self.io_req_port = io_req_port
self.extension = extension
threading.Thread.__init__(self)
# We wait 15 seconds for a thread to terminate, but if it fails to,
# we don't want the process to hang at exit waiting for it...
self.setDaemon(True)
def run(self):
self.running = True
while self.running:
errCode, bytes, key, overlapped = \
GetQueuedCompletionStatus(self.io_req_port, INFINITE)
if key == ISAPI_SHUTDOWN and overlapped is None:
break
# Let the parent extension handle the command.
dispatcher = self.extension.dispatch_map.get(key)
if dispatcher is None:
raise RuntimeError("Bad request '%s'" % (key,))
dispatcher(errCode, bytes, key, overlapped)
def call_handler(self, cblock):
self.extension.Dispatch(cblock)
# A generic thread-pool based extension, using IO Completion Ports.
# Sub-classes can override one method to implement a simple extension, or
# may leverage the CompletionPort to queue their own requests, and implement a
# fully asynch extension.
class ThreadPoolExtension(isapi.simple.SimpleExtension):
"Base class for an ISAPI extension based around a thread-pool"
max_workers = 20
worker_shutdown_wait = 15000 # 15 seconds for workers to quit...
def __init__(self):
self.workers = []
# extensible dispatch map, for sub-classes that need to post their
# own requests to the completion port.
# Each of these functions is called with the result of
# GetQueuedCompletionStatus for our port.
self.dispatch_map = {
ISAPI_REQUEST: self.DispatchConnection,
}
def GetExtensionVersion(self, vi):
isapi.simple.SimpleExtension.GetExtensionVersion(self, vi)
# As per Q192800, the CompletionPort should be created with the number
# of processors, even if the number of worker threads is much larger.
# Passing 0 means the system picks the number.
self.io_req_port = CreateIoCompletionPort(-1, None, 0, 0)
# start up the workers
self.workers = []
for i in range(self.max_workers):
worker = WorkerThread(self, self.io_req_port)
worker.start()
self.workers.append(worker)
def HttpExtensionProc(self, control_block):
overlapped = OVERLAPPED()
overlapped.object = control_block
PostQueuedCompletionStatus(self.io_req_port, 0, ISAPI_REQUEST, overlapped)
return isapicon.HSE_STATUS_PENDING
def TerminateExtension(self, status):
for worker in self.workers:
worker.running = False
for worker in self.workers:
PostQueuedCompletionStatus(self.io_req_port, 0, ISAPI_SHUTDOWN, None)
# wait for them to terminate - pity we aren't using 'native' threads
# as then we could do a smart wait - but now we need to poll....
end_time = time.time() + self.worker_shutdown_wait/1000
alive = self.workers
while alive:
if time.time() > end_time:
# xxx - might be nice to log something here.
break
time.sleep(0.2)
alive = [w for w in alive if w.isAlive()]
self.dispatch_map = {} # break circles
CloseHandle(self.io_req_port)
# This is the one operation the base class supports - a simple
# Connection request. We setup the thread-token, and dispatch to the
# sub-class's 'Dispatch' method.
def DispatchConnection(self, errCode, bytes, key, overlapped):
control_block = overlapped.object
# setup the correct user for this request
hRequestToken = control_block.GetImpersonationToken()
SetThreadToken(None, hRequestToken)
try:
try:
self.Dispatch(control_block)
except:
self.HandleDispatchError(control_block)
finally:
# reset the security context
SetThreadToken(None, None)
def Dispatch(self, ecb):
"""Overridden by the sub-class to handle connection requests.
This class creates a thread-pool using a Windows completion port,
and dispatches requests via this port. Sub-classes can generally
implement each connection request using blocking reads and writes, and
the thread-pool will still provide decent response to the end user.
The sub-class can set a max_workers attribute (default is 20). Note
that this generally does *not* mean 20 threads will all be concurrently
running, via the magic of Windows completion ports.
There is no default implementation - sub-classes must implement this.
"""
raise NotImplementedError("sub-classes should override Dispatch")
def HandleDispatchError(self, ecb):
"""Handles errors in the Dispatch method.
When a Dispatch method call fails, this method is called to handle
the exception. The default implementation formats the traceback
in the browser.
"""
ecb.HttpStatusCode = isapicon.HSE_STATUS_ERROR
#control_block.LogData = "we failed!"
exc_typ, exc_val, exc_tb = sys.exc_info()
limit = None
try:
try:
import cgi
ecb.SendResponseHeaders("200 OK", "Content-type: text/html\r\n\r\n",
False)
print(file=ecb)
print("<H3>Traceback (most recent call last):</H3>", file=ecb)
list = traceback.format_tb(exc_tb, limit) + \
traceback.format_exception_only(exc_typ, exc_val)
print("<PRE>%s<B>%s</B></PRE>" % (
cgi.escape("".join(list[:-1])), cgi.escape(list[-1]),), file=ecb)
except ExtensionError:
# The client disconnected without reading the error body -
# its probably not a real browser at the other end, ignore it.
pass
except:
print("FAILED to render the error message!")
traceback.print_exc()
print("ORIGINAL extension error:")
traceback.print_exception(exc_typ, exc_val, exc_tb)
finally:
# holding tracebacks in a local of a frame that may itself be
# part of a traceback used to be evil and cause leaks!
exc_tb = None
ecb.DoneWithSession()

View File

@ -0,0 +1,3 @@
# Magic utility that "redirects" to pythoncomxx.dll
import pywintypes
pywintypes.__import_pywin32_system_module__("pythoncom", globals())

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,30 @@
Unless stated in the specfic source file, this work is
Copyright (c) 1994-2008, Mark Hammond
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
Neither name of Mark Hammond nor the name of contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,221 @@
# basictimerapp - a really simple timer application.
# This should be run using the command line:
# pythonwin /app demos\basictimerapp.py
import win32ui
import win32api
import win32con
import sys
from pywin.framework import app, cmdline, dlgappcore, cmdline
import timer
import time
import string
class TimerAppDialog(dlgappcore.AppDialog):
softspace=1
def __init__(self, appName = ""):
dlgappcore.AppDialog.__init__(self, win32ui.IDD_GENERAL_STATUS)
self.timerAppName = appName
self.argOff = 0
if len(self.timerAppName)==0:
if len(sys.argv)>1 and sys.argv[1][0]!='/':
self.timerAppName = sys.argv[1]
self.argOff = 1
def PreDoModal(self):
# sys.stderr = sys.stdout
pass
def ProcessArgs(self, args):
for arg in args:
if arg=="/now":
self.OnOK()
def OnInitDialog(self):
win32ui.SetProfileFileName('pytimer.ini')
self.title = win32ui.GetProfileVal(self.timerAppName, "Title", "Remote System Timer")
self.buildTimer = win32ui.GetProfileVal(self.timerAppName, "Timer", "EachMinuteIntervaler()")
self.doWork = win32ui.GetProfileVal(self.timerAppName, "Work", "DoDemoWork()")
# replace "\n" with real \n.
self.doWork = self.doWork.replace('\\n','\n')
dlgappcore.AppDialog.OnInitDialog(self)
self.SetWindowText(self.title)
self.prompt1 = self.GetDlgItem(win32ui.IDC_PROMPT1)
self.prompt2 = self.GetDlgItem(win32ui.IDC_PROMPT2)
self.prompt3 = self.GetDlgItem(win32ui.IDC_PROMPT3)
self.butOK = self.GetDlgItem(win32con.IDOK)
self.butCancel = self.GetDlgItem(win32con.IDCANCEL)
self.prompt1.SetWindowText("Python Timer App")
self.prompt2.SetWindowText("")
self.prompt3.SetWindowText("")
self.butOK.SetWindowText("Do it now")
self.butCancel.SetWindowText("Close")
self.timerManager = TimerManager(self)
self.ProcessArgs(sys.argv[self.argOff:])
self.timerManager.go()
return 1
def OnDestroy(self,msg):
dlgappcore.AppDialog.OnDestroy(self, msg)
self.timerManager.stop()
def OnOK(self):
# stop the timer, then restart after setting special boolean
self.timerManager.stop()
self.timerManager.bConnectNow = 1
self.timerManager.go()
return
# def OnCancel(self): default behaviour - cancel == close.
# return
class TimerManager:
def __init__(self, dlg):
self.dlg = dlg
self.timerId = None
self.intervaler = eval(self.dlg.buildTimer)
self.bConnectNow = 0
self.bHaveSetPrompt1 = 0
def CaptureOutput(self):
self.oldOut = sys.stdout
self.oldErr = sys.stderr
sys.stdout = sys.stderr = self
self.bHaveSetPrompt1 = 0
def ReleaseOutput(self):
sys.stdout = self.oldOut
sys.stderr = self.oldErr
def write(self, str):
s = str.strip()
if len(s):
if self.bHaveSetPrompt1:
dest = self.dlg.prompt3
else:
dest = self.dlg.prompt1
self.bHaveSetPrompt1 = 1
dest.SetWindowText(s)
def go(self):
self.OnTimer(None,None)
def stop(self):
if self.timerId: timer.kill_timer (self.timerId)
self.timerId = None
def OnTimer(self, id, timeVal):
if id: timer.kill_timer (id)
if self.intervaler.IsTime() or self.bConnectNow :
# do the work.
try:
self.dlg.SetWindowText(self.dlg.title + " - Working...")
self.dlg.butOK.EnableWindow(0)
self.dlg.butCancel.EnableWindow(0)
self.CaptureOutput()
try:
exec(self.dlg.doWork)
print("The last operation completed successfully.")
except:
t, v, tb = sys.exc_info()
str = "Failed: %s: %s" % (t, repr(v))
print(str)
self.oldErr.write(str)
tb = None # Prevent cycle
finally:
self.ReleaseOutput()
self.dlg.butOK.EnableWindow()
self.dlg.butCancel.EnableWindow()
self.dlg.SetWindowText(self.dlg.title)
else:
now = time.time()
nextTime = self.intervaler.GetNextTime()
if nextTime:
timeDiffSeconds = nextTime - now
timeDiffMinutes = int(timeDiffSeconds / 60)
timeDiffSeconds = timeDiffSeconds % 60
timeDiffHours = int(timeDiffMinutes / 60)
timeDiffMinutes = timeDiffMinutes % 60
self.dlg.prompt1.SetWindowText("Next connection due in %02d:%02d:%02d" % (timeDiffHours,timeDiffMinutes,timeDiffSeconds))
self.timerId = timer.set_timer (self.intervaler.GetWakeupInterval(), self.OnTimer)
self.bConnectNow = 0
class TimerIntervaler:
def __init__(self):
self.nextTime = None
self.wakeUpInterval = 2000
def GetWakeupInterval(self):
return self.wakeUpInterval
def GetNextTime(self):
return self.nextTime
def IsTime(self):
now = time.time()
if self.nextTime is None:
self.nextTime = self.SetFirstTime(now)
ret = 0
if now >= self.nextTime:
ret = 1
self.nextTime = self.SetNextTime(self.nextTime, now)
# do the work.
return ret
class EachAnyIntervaler(TimerIntervaler):
def __init__(self, timeAt, timePos, timeAdd, wakeUpInterval = None):
TimerIntervaler.__init__(self)
self.timeAt = timeAt
self.timePos = timePos
self.timeAdd = timeAdd
if wakeUpInterval:
self.wakeUpInterval = wakeUpInterval
def SetFirstTime(self, now):
timeTup = time.localtime(now)
lst = []
for item in timeTup:
lst.append(item)
bAdd = timeTup[self.timePos] > self.timeAt
lst[self.timePos] = self.timeAt
for pos in range(self.timePos+1, 6):
lst[pos]=0
ret = time.mktime(tuple(lst))
if (bAdd):
ret = ret + self.timeAdd
return ret;
def SetNextTime(self, lastTime, now):
return lastTime + self.timeAdd
class EachMinuteIntervaler(EachAnyIntervaler):
def __init__(self, at=0):
EachAnyIntervaler.__init__(self, at, 5, 60, 2000)
class EachHourIntervaler(EachAnyIntervaler):
def __init__(self, at=0):
EachAnyIntervaler.__init__(self, at, 4, 3600, 10000)
class EachDayIntervaler(EachAnyIntervaler):
def __init__(self,at=0):
EachAnyIntervaler.__init__(self, at, 3, 86400, 10000)
class TimerDialogApp(dlgappcore.DialogApp):
def CreateDialog(self):
return TimerAppDialog()
def DoDemoWork():
print("Doing the work...")
print("About to connect")
win32api.MessageBeep(win32con.MB_ICONASTERISK)
win32api.Sleep(2000)
print("Doing something else...")
win32api.MessageBeep(win32con.MB_ICONEXCLAMATION)
win32api.Sleep(2000)
print("More work.")
win32api.MessageBeep(win32con.MB_ICONHAND)
win32api.Sleep(2000)
print("The last bit.")
win32api.MessageBeep(win32con.MB_OK)
win32api.Sleep(2000)
app = TimerDialogApp()
def t():
t = TimerAppDialog("Test Dialog")
t.DoModal()
return t
if __name__=='__main__':
import demoutils
demoutils.NeedApp()

View File

@ -0,0 +1,194 @@
# A demo of an Application object that has some custom print functionality.
# If you desire, you can also run this from inside Pythonwin, in which
# case it will do the demo inside the Pythonwin environment.
# This sample was contributed by Roger Burnham.
from pywin.mfc import docview, dialog, afxres
from pywin.framework import app
import win32con
import win32ui
import win32api
PRINTDLGORD = 1538
IDC_PRINT_MAG_EDIT = 1010
class PrintDemoTemplate(docview.DocTemplate):
def _SetupSharedMenu_(self):
pass
class PrintDemoView(docview.ScrollView):
def OnInitialUpdate(self):
ret = self._obj_.OnInitialUpdate()
self.colors = {'Black' : (0x00<<0) + (0x00<<8) + (0x00<<16),
'Red' : (0xff<<0) + (0x00<<8) + (0x00<<16),
'Green' : (0x00<<0) + (0xff<<8) + (0x00<<16),
'Blue' : (0x00<<0) + (0x00<<8) + (0xff<<16),
'Cyan' : (0x00<<0) + (0xff<<8) + (0xff<<16),
'Magenta': (0xff<<0) + (0x00<<8) + (0xff<<16),
'Yellow' : (0xff<<0) + (0xff<<8) + (0x00<<16),
}
self.pens = {}
for name, color in self.colors.items():
self.pens[name] = win32ui.CreatePen(win32con.PS_SOLID,
5, color)
self.pen = None
self.size = (128,128)
self.SetScaleToFitSize(self.size)
self.HookCommand(self.OnFilePrint, afxres.ID_FILE_PRINT)
self.HookCommand(self.OnFilePrintPreview,
win32ui.ID_FILE_PRINT_PREVIEW)
return ret
def OnDraw(self, dc):
oldPen = None
x,y = self.size
delta = 2
colors = list(self.colors.keys())
colors.sort()
colors = colors*2
for color in colors:
if oldPen is None:
oldPen = dc.SelectObject(self.pens[color])
else:
dc.SelectObject(self.pens[color])
dc.MoveTo(( delta, delta))
dc.LineTo((x-delta, delta))
dc.LineTo((x-delta, y-delta))
dc.LineTo(( delta, y-delta))
dc.LineTo(( delta, delta))
delta = delta + 4
if x-delta <= 0 or y-delta <= 0:
break
dc.SelectObject(oldPen)
def OnPrepareDC (self, dc, pInfo):
if dc.IsPrinting():
mag = self.prtDlg['mag']
dc.SetMapMode(win32con.MM_ANISOTROPIC);
dc.SetWindowOrg((0, 0))
dc.SetWindowExt((1, 1))
dc.SetViewportOrg((0, 0))
dc.SetViewportExt((mag, mag))
def OnPreparePrinting(self, pInfo):
flags = (win32ui.PD_USEDEVMODECOPIES|
win32ui.PD_PAGENUMS|
win32ui.PD_NOPAGENUMS|
win32ui.PD_NOSELECTION)
self.prtDlg = ImagePrintDialog(pInfo, PRINTDLGORD, flags)
pInfo.SetPrintDialog(self.prtDlg)
pInfo.SetMinPage(1)
pInfo.SetMaxPage(1)
pInfo.SetFromPage(1)
pInfo.SetToPage(1)
ret = self.DoPreparePrinting(pInfo)
return ret
def OnBeginPrinting(self, dc, pInfo):
return self._obj_.OnBeginPrinting(dc, pInfo)
def OnEndPrinting(self, dc, pInfo):
del self.prtDlg
return self._obj_.OnEndPrinting(dc, pInfo)
def OnFilePrintPreview(self, *arg):
self._obj_.OnFilePrintPreview()
def OnFilePrint(self, *arg):
self._obj_.OnFilePrint()
def OnPrint(self, dc, pInfo):
doc = self.GetDocument()
metrics = dc.GetTextMetrics()
cxChar = metrics['tmAveCharWidth']
cyChar = metrics['tmHeight']
left, top, right, bottom = pInfo.GetDraw()
dc.TextOut(0, 2*cyChar, doc.GetTitle())
top = top + (7*cyChar)/2
dc.MoveTo(left, top)
dc.LineTo(right, top)
top = top + cyChar
# this seems to have not effect...
# get what I want with the dc.SetWindowOrg calls
pInfo.SetDraw((left, top, right, bottom))
dc.SetWindowOrg((0, -top))
self.OnDraw(dc)
dc.SetTextAlign(win32con.TA_LEFT|win32con.TA_BOTTOM)
rect = self.GetWindowRect()
rect = self.ScreenToClient(rect)
height = (rect[3]-rect[1])
dc.SetWindowOrg((0, -(top+height+cyChar)))
dc.MoveTo(left, 0)
dc.LineTo(right, 0)
x = 0
y = (3*cyChar)/2
dc.TextOut(x, y, doc.GetTitle())
y = y + cyChar
class PrintDemoApp(app.CApp):
def __init__(self):
app.CApp.__init__(self)
def InitInstance(self):
template = PrintDemoTemplate(None, None,
None, PrintDemoView)
self.AddDocTemplate(template)
self._obj_.InitMDIInstance()
self.LoadMainFrame()
doc = template.OpenDocumentFile(None)
doc.SetTitle('Custom Print Document')
class ImagePrintDialog(dialog.PrintDialog):
sectionPos = 'Image Print Demo'
def __init__(self, pInfo, dlgID, flags=win32ui.PD_USEDEVMODECOPIES):
dialog.PrintDialog.__init__(self, pInfo, dlgID, flags=flags)
mag = win32ui.GetProfileVal(self.sectionPos,
'Document Magnification',
0)
if mag <= 0:
mag = 2
win32ui.WriteProfileVal(self.sectionPos,
'Document Magnification',
mag)
self['mag'] = mag
def OnInitDialog(self):
self.magCtl = self.GetDlgItem(IDC_PRINT_MAG_EDIT)
self.magCtl.SetWindowText(repr(self['mag']))
return dialog.PrintDialog.OnInitDialog(self)
def OnOK(self):
dialog.PrintDialog.OnOK(self)
strMag = self.magCtl.GetWindowText()
try:
self['mag'] = int(strMag)
except:
pass
win32ui.WriteProfileVal(self.sectionPos,
'Document Magnification',
self['mag'])
if __name__=='__main__':
# Running under Pythonwin
def test():
template = PrintDemoTemplate(None, None,
None, PrintDemoView)
template.OpenDocumentFile(None)
test()
else:
app = PrintDemoApp()

View File

@ -0,0 +1,52 @@
# Utilities for the demos
import sys, win32api, win32con, win32ui
NotScriptMsg = """\
This demo program is not designed to be run as a Script, but is
probably used by some other test program. Please try another demo.
"""
NeedGUIMsg = """\
This demo program can only be run from inside of Pythonwin
You must start Pythonwin, and select 'Run' from the toolbar or File menu
"""
NeedAppMsg = """\
This demo program is a 'Pythonwin Application'.
It is more demo code than an example of Pythonwin's capabilities.
To run it, you must execute the command:
pythonwin.exe /app "%s"
Would you like to execute it now?
"""
def NotAScript():
import win32ui
win32ui.MessageBox(NotScriptMsg, "Demos")
def NeedGoodGUI():
from pywin.framework.app import HaveGoodGUI
rc = HaveGoodGUI()
if not rc:
win32ui.MessageBox(NeedGUIMsg, "Demos")
return rc
def NeedApp():
import win32ui
rc = win32ui.MessageBox(NeedAppMsg % sys.argv[0], "Demos", win32con.MB_YESNO)
if rc==win32con.IDYES:
try:
parent = win32ui.GetMainFrame().GetSafeHwnd()
win32api.ShellExecute(parent, None, 'pythonwin.exe', '/app "%s"' % sys.argv[0], None, 1)
except win32api.error as details:
win32ui.MessageBox("Error executing command - %s" % (details), "Demos")
if __name__=='__main__':
import demoutils
demoutils.NotAScript()

View File

@ -0,0 +1,46 @@
# dlgappdemo - a demo of a dialog application.
# This is a demonstration of both a custom "application" module,
# and a Python program in a dialog box.
#
# NOTE: You CAN NOT import this module from either PythonWin or Python.
# This module must be specified on the commandline to PythonWin only.
# eg, PythonWin /app dlgappdemo.py
from pywin.framework import dlgappcore, app
import win32ui
import sys
class TestDialogApp(dlgappcore.DialogApp):
def CreateDialog(self):
return TestAppDialog()
class TestAppDialog(dlgappcore.AppDialog):
def __init__(self):
self.edit = None
dlgappcore.AppDialog.__init__(self, win32ui.IDD_LARGE_EDIT)
def OnInitDialog(self):
self.SetWindowText('Test dialog application')
self.edit = self.GetDlgItem(win32ui.IDC_EDIT1)
print("Hello from Python")
print("args are:", end=' ')
for arg in sys.argv:
print(arg)
return 1
def PreDoModal(self):
sys.stdout = sys.stderr = self
def write(self, str):
if self.edit:
self.edit.SetSel(-2)
# translate \n to \n\r
self.edit.ReplaceSel(str.replace('\n','\r\n'))
else:
win32ui.OutputDebug("dlgapp - no edit control! >>\n%s\n<<\n" % str )
app.AppBuilder = TestDialogApp
if __name__=='__main__':
import demoutils
demoutils.NeedApp()

View File

@ -0,0 +1,62 @@
# dojobapp - do a job, show the result in a dialog, and exit.
#
# Very simple - faily minimal dialog based app.
#
# This should be run using the command line:
# pythonwin /app demos\dojobapp.py
import win32ui
import win32api
import win32con
import sys
from pywin.framework import app, dlgappcore
import string
class DoJobAppDialog(dlgappcore.AppDialog):
softspace=1
def __init__(self, appName = ""):
self.appName = appName
dlgappcore.AppDialog.__init__(self, win32ui.IDD_GENERAL_STATUS)
def PreDoModal(self):
pass
def ProcessArgs(self, args):
pass
def OnInitDialog(self):
self.SetWindowText(self.appName)
butCancel = self.GetDlgItem(win32con.IDCANCEL)
butCancel.ShowWindow(win32con.SW_HIDE)
p1 = self.GetDlgItem(win32ui.IDC_PROMPT1)
p2 = self.GetDlgItem(win32ui.IDC_PROMPT2)
# Do something here!
p1.SetWindowText("Hello there")
p2.SetWindowText("from the demo")
def OnDestroy(self,msg):
pass
# def OnOK(self):
# pass
# def OnCancel(self): default behaviour - cancel == close.
# return
class DoJobDialogApp(dlgappcore.DialogApp):
def CreateDialog(self):
return DoJobAppDialog("Do Something")
class CopyToDialogApp(DoJobDialogApp):
def __init__(self):
DoJobDialogApp.__init__(self)
app.AppBuilder = DoJobDialogApp
def t():
t = DoJobAppDialog("Copy To")
t.DoModal()
return t
if __name__=='__main__':
import demoutils
demoutils.NeedApp()

View File

@ -0,0 +1,45 @@
##
## helloapp.py
##
##
## A nice, small 'hello world' Pythonwin application.
## NOT an MDI application - just a single, normal, top-level window.
##
## MUST be run with the command line "pythonwin.exe /app helloapp.py"
## (or if you are really keen, rename "pythonwin.exe" to something else, then
## using MSVC or similar, edit the string section in the .EXE to name this file)
##
## Originally by Willy Heineman <wheineman@uconect.net>
import win32con
import win32ui
from pywin.mfc import window, dialog, afxres
from pywin.mfc.thread import WinApp
# The main frame.
# Does almost nothing at all - doesnt even create a child window!
class HelloWindow(window.Wnd):
def __init__(self):
# The window.Wnd ctor creates a Window object, and places it in
# self._obj_. Note the window object exists, but the window itself
# does not!
window.Wnd.__init__(self, win32ui.CreateWnd())
# Now we ask the window object to create the window itself.
self._obj_.CreateWindowEx(win32con.WS_EX_CLIENTEDGE, \
win32ui.RegisterWndClass(0, 0, win32con.COLOR_WINDOW + 1), \
'Hello World!', win32con.WS_OVERLAPPEDWINDOW, \
(100, 100, 400, 300), None, 0, None)
# The application object itself.
class HelloApp(WinApp):
def InitInstance(self):
self.frame = HelloWindow()
self.frame.ShowWindow(win32con.SW_SHOWNORMAL)
# We need to tell MFC what our main frame is.
self.SetMainFrame(self.frame)
# Now create the application object itself!
app = HelloApp()

View File

@ -0,0 +1,108 @@
# cmdserver.py
# Demo code that is not Pythonwin related, but too good to throw away...
import win32api
import sys
from pywin.framework import winout
import _thread, sys
import traceback
class ThreadWriter:
"Assign an instance to sys.stdout for per-thread printing objects - Courtesy Guido!"
def __init__(self):
"Constructor -- initialize the table of writers"
self.writers = {}
self.origStdOut = None
def register(self, writer):
"Register the writer for the current thread"
self.writers[_thread.get_ident()] = writer
if self.origStdOut is None:
self.origStdOut = sys.stdout
sys.stdout = self
def unregister(self):
"Remove the writer for the current thread, if any"
try:
del self.writers[_thread.get_ident()]
except KeyError:
pass
if len(self.writers)==0:
sys.stdout = self.origStdOut
self.origStdOut = None
def getwriter(self):
"Return the current thread's writer, default sys.stdout"
try:
return self.writers[_thread.get_ident()]
except KeyError:
return self.origStdOut
def write(self, str):
"Write to the current thread's writer, default sys.stdout"
self.getwriter().write(str)
def Test():
num=1
while num<1000:
print('Hello there no ' + str(num))
win32api.Sleep(50)
num = num + 1
class flags:
SERVER_BEST = 0
SERVER_IMMEDIATE = 1
SERVER_THREAD = 2
SERVER_PROCESS = 3
def StartServer( cmd, title=None, bCloseOnEnd=0, serverFlags = flags.SERVER_BEST ):
out = winout.WindowOutput( title, None, winout.flags.WQ_IDLE )
if not title:
title=cmd
out.Create(title)
# ServerThread((out, cmd, title, bCloseOnEnd))
# out = sys.stdout
_thread.start_new_thread( ServerThread, (out, cmd, title, bCloseOnEnd) )
def ServerThread(myout, cmd, title, bCloseOnEnd):
try:
writer.register(myout)
print('Executing "%s"\n' % cmd)
bOK = 1
try:
import __main__
exec (cmd+'\n', __main__.__dict__)
except:
bOK = 0
if bOK:
print("Command terminated without errors.")
else:
t, v, tb = sys.exc_info()
print(t, ': ', v)
traceback.print_tb(tb)
tb = None # prevent a cycle
print("Command terminated with an unhandled exception")
writer.unregister()
if bOK and bCloseOnEnd:
myout.frame.DestroyWindow()
# Unhandled exception of any kind in a thread kills the gui!
except:
t, v, tb = sys.exc_info()
print(t, ': ', v)
traceback.print_tb(tb)
tb = None
print("Thread failed")
# assist for reloading (when debugging) - use only 1 tracer object,
# else a large chain of tracer objects will exist.
#try:
# writer
#except NameError:
# writer=ThreadWriter()
if __name__=='__main__':
import demoutils
demoutils.NotAScript()

View File

@ -0,0 +1,98 @@
#
# Window creation example
#
# This example creates a minimal "control" that just fills in its
# window with red. To make your own control, subclass Control and
# write your own OnPaint() method. See PyCWnd.HookMessage for what
# the parameters to OnPaint are.
#
from pywin.mfc import dialog, window
import win32ui
import win32con
import win32api
class Control(window.Wnd):
"""Generic control class"""
def __init__ (self):
window.Wnd.__init__(self, win32ui.CreateWnd ())
def OnPaint (self):
dc, paintStruct = self.BeginPaint()
self.DoPaint(dc)
self.EndPaint(paintStruct)
def DoPaint (self, dc): # Override this!
pass
class RedBox (Control):
def DoPaint (self, dc):
dc.FillSolidRect (self.GetClientRect(), win32api.RGB(255,0,0))
class RedBoxWithPie (RedBox):
def DoPaint (self, dc):
RedBox.DoPaint(self, dc)
r = self.GetClientRect()
dc.Pie(r[0], r[1], r[2], r[3], 0,0,r[2], r[3]//2)
def MakeDlgTemplate():
style = (win32con.DS_MODALFRAME |
win32con.WS_POPUP |
win32con.WS_VISIBLE |
win32con.WS_CAPTION |
win32con.WS_SYSMENU |
win32con.DS_SETFONT)
cs = (win32con.WS_CHILD |
win32con.WS_VISIBLE)
w = 64
h = 64
dlg = [["Red box",
(0, 0, w, h),
style,
None,
(8, "MS Sans Serif")],
]
s = win32con.WS_TABSTOP | cs
dlg.append([128,
"Cancel",
win32con.IDCANCEL,
(7, h - 18, 50, 14), s | win32con.BS_PUSHBUTTON])
return dlg
class TestDialog(dialog.Dialog):
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
self.redbox = RedBox ()
self.redbox.CreateWindow (None, "RedBox",
win32con.WS_CHILD |
win32con.WS_VISIBLE,
(5, 5, 90, 68),
self, 1003)
return rc
class TestPieDialog(dialog.Dialog):
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
self.control = RedBoxWithPie()
self.control.CreateWindow (None, "RedBox with Pie",
win32con.WS_CHILD |
win32con.WS_VISIBLE,
(5, 5, 90, 68),
self, 1003)
def demo(modal=0):
d = TestPieDialog (MakeDlgTemplate())
if modal:
d.DoModal()
else:
d.CreateWindow()
if __name__=='__main__':
demo(1)

View File

@ -0,0 +1,54 @@
# Utilities for the demos
import sys, win32api, win32con, win32ui
NotScriptMsg = """\
This demo program is not designed to be run as a Script, but is
probably used by some other test program. Please try another demo.
"""
NeedGUIMsg = """\
This demo program can only be run from inside of Pythonwin
You must start Pythonwin, and select 'Run' from the toolbar or File menu
"""
NeedAppMsg = """\
This demo program is a 'Pythonwin Application'.
It is more demo code than an example of Pythonwin's capabilities.
To run it, you must execute the command:
pythonwin.exe /app "%s"
Would you like to execute it now?
"""
def NotAScript():
import win32ui
win32ui.MessageBox(NotScriptMsg, "Demos")
def NeedGoodGUI():
from pywin.framework.app import HaveGoodGUI
rc = HaveGoodGUI()
if not rc:
win32ui.MessageBox(NeedGUIMsg, "Demos")
return rc
def NeedApp():
import win32ui
rc = win32ui.MessageBox(NeedAppMsg % sys.argv[0], "Demos", win32con.MB_YESNO)
if rc==win32con.IDYES:
try:
parent = win32ui.GetMainFrame().GetSafeHwnd()
win32api.ShellExecute(parent, None, 'pythonwin.exe', '/app "%s"' % sys.argv[0], None, 1)
except win32api.error as details:
win32ui.MessageBox("Error executing command - %s" % (details), "Demos")
from pywin.framework.app import HaveGoodGUI
if __name__=='__main__':
import demoutils
demoutils.NotAScript()

View File

@ -0,0 +1,69 @@
# A demo which creates a view and a frame which displays a PPM format bitmap
#
# This hasnnt been run in a while, as I dont have many of that format around!
import win32ui
import win32con
import win32api
import string
class DIBView:
def __init__(self, doc, dib):
self.dib = dib
self.view = win32ui.CreateView(doc)
self.width = self.height = 0
# set up message handlers
# self.view.OnPrepareDC = self.OnPrepareDC
self.view.HookMessage (self.OnSize, win32con.WM_SIZE)
def OnSize (self, params):
lParam = params[3]
self.width = win32api.LOWORD(lParam)
self.height = win32api.HIWORD(lParam)
def OnDraw (self, ob, dc):
# set sizes used for "non strecth" mode.
self.view.SetScrollSizes(win32con.MM_TEXT, self.dib.GetSize())
dibSize = self.dib.GetSize()
dibRect = (0,0,dibSize[0], dibSize[1])
# stretch BMP.
#self.dib.Paint(dc, (0,0,self.width, self.height),dibRect)
# non stretch.
self.dib.Paint(dc)
class DIBDemo:
def __init__(self, filename, * bPBM):
# init data members
f = open(filename, 'rb')
dib=win32ui.CreateDIBitmap()
if len(bPBM)>0:
magic=f.readline()
if magic != "P6\n":
print("The file is not a PBM format file")
raise ValueError("Failed - The file is not a PBM format file")
# check magic?
rowcollist=f.readline().split()
cols=int(rowcollist[0])
rows=int(rowcollist[1])
f.readline() # whats this one?
dib.LoadPBMData(f,(cols,rows))
else:
dib.LoadWindowsFormatFile(f)
f.close()
# create doc/view
self.doc = win32ui.CreateDoc()
self.dibView = DIBView( self.doc, dib )
self.frame = win32ui.CreateMDIFrame()
self.frame.LoadFrame() # this will force OnCreateClient
self.doc.SetTitle ('DIB Demo')
self.frame.ShowWindow()
# display the sucka
self.frame.ActivateFrame()
def OnCreateClient( self, createparams, context ):
self.dibView.view.CreateWindow(self.frame)
return 1
if __name__=='__main__':
import demoutils
demoutils.NotAScript()

View File

@ -0,0 +1,137 @@
# A Demo of Pythonwin's Dialog and Property Page support.
###################
#
# First demo - use the built-in to Pythonwin "Tab Stop" dialog, but
# customise it heavily.
#
# ID's for the tabstop dialog - out test.
#
from win32ui import IDD_SET_TABSTOPS
from win32ui import IDC_EDIT_TABS
from win32ui import IDC_PROMPT_TABS
from win32con import IDOK
from win32con import IDCANCEL
import win32ui
import win32con
from pywin.mfc import dialog
class TestDialog(dialog.Dialog):
def __init__(self, modal=1):
dialog.Dialog.__init__(self, IDD_SET_TABSTOPS)
self.counter=0
if modal:
self.DoModal()
else:
self.CreateWindow()
def OnInitDialog(self):
# Set the caption of the dialog itself.
self.SetWindowText("Used to be Tab Stops!")
# Get a child control, remember it, and change its text.
self.edit=self.GetDlgItem(IDC_EDIT_TABS) # the text box.
self.edit.SetWindowText("Test")
# Hook a Windows message for the dialog.
self.edit.HookMessage(self.KillFocus, win32con.WM_KILLFOCUS)
# Get the prompt control, and change its next.
prompt=self.GetDlgItem(IDC_PROMPT_TABS) # the prompt box.
prompt.SetWindowText("Prompt")
# And the same for the button..
cancel=self.GetDlgItem(IDCANCEL) # the cancel button
cancel.SetWindowText("&Kill me")
# And just for demonstration purposes, we hook the notify message for the dialog.
# This allows us to be notified when the Edit Control text changes.
self.HookCommand(self.OnNotify, IDC_EDIT_TABS)
def OnNotify(self, controlid, code):
if code==win32con.EN_CHANGE:
print("Edit text changed!")
return 1 # I handled this, so no need to call defaults!
# kill focus for the edit box.
# Simply increment the value in the text box.
def KillFocus(self,msg):
self.counter=self.counter+1
if self.edit != None:
self.edit.SetWindowText(str(self.counter))
# Called when the dialog box is terminating...
def OnDestroy(self,msg):
del self.edit
del self.counter
# A very simply Property Sheet.
# We only make a new class for demonstration purposes.
class TestSheet(dialog.PropertySheet):
def __init__(self, title):
dialog.PropertySheet.__init__(self, title)
self.HookMessage(self.OnActivate, win32con.WM_ACTIVATE)
def OnActivate(self, msg):
pass
# A very simply Property Page, which will be "owned" by the above
# Property Sheet.
# We create a new class, just so we can hook a control notification.
class TestPage(dialog.PropertyPage):
def OnInitDialog(self):
# We use the HookNotify function to allow Python to respond to
# Windows WM_NOTIFY messages.
# In this case, we are interested in BN_CLICKED messages.
self.HookNotify(self.OnNotify, win32con.BN_CLICKED)
def OnNotify(self, std, extra):
print("OnNotify", std, extra)
# Some code that actually uses these objects.
def demo(modal = 0):
TestDialog(modal)
# property sheet/page demo
ps=win32ui.CreatePropertySheet('Property Sheet/Page Demo')
# Create a completely standard PropertyPage.
page1=win32ui.CreatePropertyPage(win32ui.IDD_PROPDEMO1)
# Create our custom property page.
page2=TestPage(win32ui.IDD_PROPDEMO2)
ps.AddPage(page1)
ps.AddPage(page2)
if modal:
ps.DoModal()
else:
style = win32con.WS_SYSMENU|win32con.WS_POPUP|win32con.WS_CAPTION|win32con.DS_MODALFRAME|win32con.WS_VISIBLE
styleex = win32con.WS_EX_DLGMODALFRAME | win32con.WS_EX_PALETTEWINDOW
ps.CreateWindow(win32ui.GetMainFrame(), style, styleex)
def test(modal=1):
# dlg=dialog.Dialog(1010)
# dlg.CreateWindow()
# dlg.EndDialog(0)
# del dlg
# return
# property sheet/page demo
ps=TestSheet('Property Sheet/Page Demo')
page1=win32ui.CreatePropertyPage(win32ui.IDD_PROPDEMO1)
page2=win32ui.CreatePropertyPage(win32ui.IDD_PROPDEMO2)
ps.AddPage(page1)
ps.AddPage(page2)
del page1
del page2
if modal:
ps.DoModal()
else:
ps.CreateWindow(win32ui.GetMainFrame())
return ps
def d():
dlg = win32ui.CreateDialog(win32ui.IDD_DEBUGGER)
dlg.datalist.append((win32ui.IDC_DBG_RADIOSTACK, "radio"))
print("data list is ", dlg.datalist)
dlg.data['radio']=1
dlg.DoModal()
print(dlg.data['radio'])
if __name__=='__main__':
demo(1)

View File

@ -0,0 +1,73 @@
# dyndlg.py
# contributed by Curt Hagenlocher <chi@earthlink.net>
# Dialog Template params:
# Parameter 0 - Window caption
# Parameter 1 - Bounds (rect tuple)
# Parameter 2 - Window style
# Parameter 3 - Extended style
# Parameter 4 - Font tuple
# Parameter 5 - Menu name
# Parameter 6 - Window class
# Dialog item params:
# Parameter 0 - Window class
# Parameter 1 - Text
# Parameter 2 - ID
# Parameter 3 - Bounds
# Parameter 4 - Style
# Parameter 5 - Extended style
# Parameter 6 - Extra data
import win32ui
import win32con
from pywin.mfc import dialog, window
def MakeDlgTemplate():
style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
dlg = [ ["Select Warehouse", (0, 0, 177, 93), style, None, (8, "MS Sans Serif")], ]
dlg.append([130, "Current Warehouse:", -1, (7, 7, 69, 9), cs | win32con.SS_LEFT])
dlg.append([130, "ASTORIA", 128, (16, 17, 99, 7), cs | win32con.SS_LEFT])
dlg.append([130, "New &Warehouse:", -1, (7, 29, 69, 9), cs | win32con.SS_LEFT])
s = win32con.WS_TABSTOP | cs
# dlg.append([131, None, 130, (5, 40, 110, 48),
# s | win32con.LBS_NOTIFY | win32con.LBS_SORT | win32con.LBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL | win32con.WS_BORDER])
dlg.append(["{8E27C92B-1264-101C-8A2F-040224009C02}", None, 131, (5, 40, 110, 48),win32con.WS_TABSTOP])
dlg.append([128, "OK", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON])
s = win32con.BS_PUSHBUTTON | s
dlg.append([128, "Cancel", win32con.IDCANCEL, (124, 22, 50, 14), s])
dlg.append([128, "&Help", 100, (124, 74, 50, 14), s])
return dlg
def test1():
win32ui.CreateDialogIndirect( MakeDlgTemplate() ).DoModal()
def test2():
dialog.Dialog( MakeDlgTemplate() ).DoModal()
def test3():
dlg = win32ui.LoadDialogResource(win32ui.IDD_SET_TABSTOPS)
dlg[0][0] = 'New Dialog Title'
dlg[0][1] = (80, 20, 161, 60)
dlg[1][1] = '&Confusion:'
cs = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON
dlg.append([128, "&Help", 100, (111, 41, 40, 14), cs])
dialog.Dialog( dlg ).DoModal()
def test4():
page1=dialog.PropertyPage(win32ui.LoadDialogResource(win32ui.IDD_PROPDEMO1))
page2=dialog.PropertyPage(win32ui.LoadDialogResource(win32ui.IDD_PROPDEMO2))
ps=dialog.PropertySheet('Property Sheet/Page Demo', None, [page1, page2])
ps.DoModal()
def testall():
test1()
test2()
test3()
test4()
if __name__=='__main__':
testall()

View File

@ -0,0 +1,79 @@
# Demo of Generic document windows, DC, and Font usage
# by Dave Brennan (brennan@hal.com)
# usage examples:
# >>> from fontdemo import *
# >>> d = FontDemo('Hello, Python')
# >>> f1 = { 'name':'Arial', 'height':36, 'weight':win32con.FW_BOLD}
# >>> d.SetFont(f1)
# >>> f2 = {'name':'Courier New', 'height':24, 'italic':1}
# >>> d.SetFont (f2)
import win32ui
import win32con
import win32api
from pywin.mfc import docview
# font is a dictionary in which the following elements matter:
# (the best matching font to supplied parameters is returned)
# name string name of the font as known by Windows
# size point size of font in logical units
# weight weight of font (win32con.FW_NORMAL, win32con.FW_BOLD)
# italic boolean; true if set to anything but None
# underline boolean; true if set to anything but None
class FontView(docview.ScrollView):
def __init__(self, doc, text = 'Python Rules!', font_spec = {'name':'Arial', 'height':42}):
docview.ScrollView.__init__(self, doc)
self.font = win32ui.CreateFont (font_spec)
self.text = text
self.width = self.height = 0
# set up message handlers
self.HookMessage (self.OnSize, win32con.WM_SIZE)
def OnAttachedObjectDeath(self):
docview.ScrollView.OnAttachedObjectDeath(self)
del self.font
def SetFont (self, new_font):
# Change font on the fly
self.font = win32ui.CreateFont (new_font)
# redraw the entire client window
selfInvalidateRect (None)
def OnSize (self, params):
lParam = params[3]
self.width = win32api.LOWORD(lParam)
self.height = win32api.HIWORD(lParam)
def OnPrepareDC (self, dc, printinfo):
# Set up the DC for forthcoming OnDraw call
self.SetScrollSizes(win32con.MM_TEXT, (100,100))
dc.SetTextColor (win32api.RGB(0,0,255))
dc.SetBkColor (win32api.GetSysColor (win32con.COLOR_WINDOW))
dc.SelectObject (self.font)
dc.SetTextAlign (win32con.TA_CENTER | win32con.TA_BASELINE)
def OnDraw (self, dc):
if (self.width == 0 and self.height == 0):
left, top, right, bottom = self.GetClientRect()
self.width = right - left
self.height = bottom - top
x, y = self.width // 2, self.height // 2
dc.TextOut (x, y, self.text)
def FontDemo():
# create doc/view
template = docview.DocTemplate(win32ui.IDR_PYTHONTYPE, None, None, FontView)
doc=template.OpenDocumentFile(None)
doc.SetTitle ('Font Demo')
# print "template is ", template, "obj is", template._obj_
template.close()
# print "closed"
# del template
if __name__=='__main__':
import demoutils
if demoutils.NeedGoodGUI():
FontDemo()

View File

@ -0,0 +1,68 @@
# GUI Demo - just a worker script to invoke all the other demo/test scripts.
import win32ui
import __main__
import sys
import regutil
import win32api
demos = [ \
# ('Font', 'import fontdemo;fontdemo.FontDemo()'),
('Open GL Demo', 'import openGLDemo;openGLDemo.test()'),
('Threaded GUI', 'import threadedgui;threadedgui.ThreadedDemo()'),
('Tree View Demo', 'import hiertest;hiertest.demoboth()'),
('3-Way Splitter Window', 'import splittst;splittst.demo()'),
('Custom Toolbars and Tooltips', 'import toolbar;toolbar.test()'),
('Progress Bar', 'import progressbar;progressbar.demo()'),
('Slider Control', 'import sliderdemo;sliderdemo.demo()'),
('Dynamic window creation', 'import createwin;createwin.demo()'),
('Various Dialog demos', 'import dlgtest;dlgtest.demo()'),
('OCX Control Demo', 'from ocx import ocxtest;ocxtest.demo()'),
('OCX Serial Port Demo', 'from ocx import ocxserialtest; ocxserialtest.test()'),
('IE4 Control Demo', 'from ocx import webbrowser; webbrowser.Demo("http://www.python.org")'),
]
def demo():
try:
# seeif I can locate the demo files.
import fontdemo
except ImportError:
# else put the demos direectory on the path (if not already)
try:
instPath = regutil.GetRegistryDefaultValue(regutil.BuildDefaultPythonKey() + "\\InstallPath")
except win32api.error:
print("The InstallPath can not be located, and the Demos directory is not on the path")
instPath="."
demosDir = win32ui.FullPath(instPath + "\\Demos")
for path in sys.path:
if win32ui.FullPath(path)==demosDir:
break
else:
sys.path.append(demosDir)
import fontdemo
import sys
if "/go" in sys.argv:
for name, cmd in demos:
try:
exec(cmd)
except:
print("Demo of %s failed - %s:%s" % (cmd,sys.exc_info()[0], sys.exc_info()[1]))
return
# Otherwise allow the user to select the demo to run
import pywin.dialogs.list
while 1:
rc = pywin.dialogs.list.SelectFromLists( "Select a Demo", demos, ['Demo Title'] )
if rc is None:
break
title, cmd = demos[rc]
try:
exec(cmd)
except:
print("Demo of %s failed - %s:%s" % (title,sys.exc_info()[0], sys.exc_info()[1]))
if __name__==__main__.__name__:
import demoutils
if demoutils.NeedGoodGUI():
demo()

View File

@ -0,0 +1,104 @@
import win32ui
import os
import commctrl
from pywin.tools import hierlist
from pywin.mfc import docview, window
# directory listbox
# This has obvious limitations - doesnt track subdirs, etc. Demonstrates
# simple use of Python code for querying the tree as needed.
# Only use strings, and lists of strings (from curdir())
class DirHierList(hierlist.HierList):
def __init__(self, root, listBoxID = win32ui.IDC_LIST1):
hierlist.HierList.__init__(self, root, win32ui.IDB_HIERFOLDERS, listBoxID)
def GetText(self, item):
return os.path.basename(item)
def GetSubList(self, item):
if os.path.isdir(item):
ret = [os.path.join(item, fname) for fname in os.listdir(item)]
else:
ret = None
return ret
# if the item is a dir, it is expandable.
def IsExpandable(self, item):
return os.path.isdir(item)
def GetSelectedBitmapColumn(self, item):
return self.GetBitmapColumn(item)+6 # Use different color for selection
class TestDocument(docview.Document):
def __init__(self, template):
docview.Document.__init__(self, template)
self.hierlist = hierlist.HierListWithItems(HLIFileDir("\\"), win32ui.IDB_HIERFOLDERS, win32ui.AFX_IDW_PANE_FIRST)
class HierListView(docview.TreeView):
def OnInitialUpdate(self):
rc = self._obj_.OnInitialUpdate()
self.hierList = self.GetDocument().hierlist
self.hierList.HierInit(self.GetParent())
self.hierList.SetStyle(commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS)
return rc
class HierListFrame(window.MDIChildWnd):
pass
def GetTestRoot():
tree1 = ('Tree 1',[('Item 1','Item 1 data'),'Item 2',3])
tree2 = ('Tree 2',[('Item 2.1','Item 2 data'),'Item 2.2',2.3])
return ('Root',[tree1,tree2,'Item 3'])
def demoboth():
template = docview.DocTemplate(win32ui.IDR_PYTHONTYPE, TestDocument, HierListFrame, HierListView)
template.OpenDocumentFile(None).SetTitle("Hierlist demo")
demomodeless()
def demomodeless():
testList2=DirHierList("\\")
dlg=hierlist.HierDialog('hier list test',testList2)
dlg.CreateWindow()
def demodlg ():
testList2=DirHierList("\\")
dlg=hierlist.HierDialog('hier list test',testList2)
dlg.DoModal()
def demo():
template = docview.DocTemplate(win32ui.IDR_PYTHONTYPE, TestDocument, HierListFrame, HierListView)
template.OpenDocumentFile(None).SetTitle("Hierlist demo")
#
# Demo/Test for HierList items.
#
# Easy to make a better directory program.
#
class HLIFileDir(hierlist.HierListItem):
def __init__( self, filename ):
self.filename = filename
hierlist.HierListItem.__init__(self)
def GetText(self):
try:
return "%-20s %d bytes" % (os.path.basename(self.filename), os.stat(self.filename)[6])
except os.error as details:
return "%-20s - %s" % (self.filename, details[1])
def IsExpandable(self):
return os.path.isdir(self.filename)
def GetSubList(self):
ret = []
for newname in os.listdir(self.filename):
if newname not in ['.', '..']:
ret.append( HLIFileDir( os.path.join(self.filename,newname ) ) )
return ret
def demohli():
template = docview.DocTemplate(win32ui.IDR_PYTHONTYPE, TestDocument, hierlist.HierListFrame, hierlist.HierListView)
template.OpenDocumentFile(None).SetTitle("Hierlist demo")
if __name__=='__main__':
import demoutils
if demoutils.HaveGoodGUI():
demoboth()
else:
demodlg()

View File

@ -0,0 +1,12 @@
# Run this as a python script, to gray "close" off the edit window system menu.
from pywin.framework import interact
import win32con
if __name__=='__main__':
import demoutils
if demoutils.NeedGoodGUI():
win=interact.edit.currentView.GetParent()
menu=win.GetSystemMenu()
id=menu.GetMenuItemID(6)
menu.EnableMenuItem(id,win32con.MF_BYCOMMAND|win32con.MF_GRAYED)
print("The interactive window's 'Close' menu item is now disabled.")

View File

@ -0,0 +1,49 @@
# This is a sample file, and shows the basic framework for using an "Object" based
# document, rather than a "filename" based document.
# This is referenced by the Pythonwin .html documentation.
# In the example below, the OpenObject() method is used instead of OpenDocumentFile,
# and all the core MFC document open functionality is retained.
import win32ui
from pywin.mfc import docview
class object_template (docview.DocTemplate):
def __init__(self):
docview.DocTemplate.__init__(self, None, None, None, object_view)
def OpenObject(self, object): # Use this instead of OpenDocumentFile.
# Look for existing open document
for doc in self.GetDocumentList():
print("document is ", doc)
if doc.object is object:
doc.GetFirstView().ActivateFrame()
return doc
# not found - new one.
doc = object_document(self, object)
frame = self.CreateNewFrame(doc)
doc.OnNewDocument()
doc.SetTitle(str(object))
self.InitialUpdateFrame(frame, doc)
return doc
class object_document (docview.Document):
def __init__(self, template, object):
docview.Document.__init__(self, template)
self.object = object
def OnOpenDocument (self, name):
raise RuntimeError("Should not be called if template strings set up correctly")
return 0
class object_view (docview.EditView):
def OnInitialUpdate (self):
self.ReplaceSel("Object is %s" % repr(self.GetDocument().object))
def demo ():
t = object_template()
d = t.OpenObject(win32ui)
return (t, d)
if __name__=='__main__':
import demoutils
if demoutils.NeedGoodGUI():
demo()

View File

@ -0,0 +1,54 @@
# Utilities for the demos
import sys, win32api, win32con, win32ui
NotScriptMsg = """\
This demo program is not designed to be run as a Script, but is
probably used by some other test program. Please try another demo.
"""
NeedGUIMsg = """\
This demo program can only be run from inside of Pythonwin
You must start Pythonwin, and select 'Run' from the toolbar or File menu
"""
NeedAppMsg = """\
This demo program is a 'Pythonwin Application'.
It is more demo code than an example of Pythonwin's capabilities.
To run it, you must execute the command:
pythonwin.exe /app "%s"
Would you like to execute it now?
"""
def NotAScript():
import win32ui
win32ui.MessageBox(NotScriptMsg, "Demos")
def NeedGoodGUI():
from pywin.framework.app import HaveGoodGUI
rc = HaveGoodGUI()
if not rc:
win32ui.MessageBox(NeedGUIMsg, "Demos")
return rc
def NeedApp():
import win32ui
rc = win32ui.MessageBox(NeedAppMsg % sys.argv[0], "Demos", win32con.MB_YESNO)
if rc==win32con.IDYES:
try:
parent = win32ui.GetMainFrame().GetSafeHwnd()
win32api.ShellExecute(parent, None, 'pythonwin.exe', '/app "%s"' % sys.argv[0], None, 1)
except win32api.error as details:
win32ui.MessageBox("Error executing command - %s" % (details), "Demos")
from pywin.framework.app import HaveGoodGUI
if __name__=='__main__':
import demoutils
demoutils.NotAScript()

View File

@ -0,0 +1,84 @@
# By Bradley Schatz
# simple flash/python application demonstrating bidirectional
# communicaion between flash and python. Click the sphere to see
# behavior. Uses Bounce.swf from FlashBounce.zip, available from
# http://pages.cpsc.ucalgary.ca/~saul/vb_examples/tutorial12/
# Update to the path of the .swf file (note it could be a true URL)
flash_url = "c:\\bounce.swf"
import win32ui, win32con, win32api, regutil
from pywin.mfc import window, activex
from win32com.client import gencache
import sys
FlashModule = gencache.EnsureModule("{D27CDB6B-AE6D-11CF-96B8-444553540000}", 0, 1, 0)
if FlashModule is None:
raise ImportError("Flash does not appear to be installed.")
class MyFlashComponent(activex.Control, FlashModule.ShockwaveFlash):
def __init__(self):
activex.Control.__init__(self)
FlashModule.ShockwaveFlash.__init__(self)
self.x = 50
self.y = 50
self.angle = 30
self.started = 0
def OnFSCommand(self, command, args):
print("FSCommend" , command, args)
self.x = self.x + 20
self.y = self.y + 20
self.angle = self.angle + 20
if self.x > 200 or self.y > 200:
self.x = 0
self.y = 0
if self.angle > 360:
self.angle = 0
self.SetVariable("xVal", self.x)
self.SetVariable("yVal", self.y)
self.SetVariable("angle", self.angle)
self.TPlay("_root.mikeBall")
def OnProgress(self, percentDone):
print("PercentDone", percentDone)
def OnReadyStateChange(self, newState):
# 0=Loading, 1=Uninitialized, 2=Loaded, 3=Interactive, 4=Complete
print("State", newState)
class BrowserFrame(window.MDIChildWnd):
def __init__(self, url = None):
if url is None:
self.url = regutil.GetRegisteredHelpFile("Main Python Documentation")
else:
self.url = url
pass # Dont call base class doc/view version...
def Create(self, title, rect = None, parent = None):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
self._obj_ = win32ui.CreateMDIChild()
self._obj_.AttachObject(self)
self._obj_.CreateWindow(None, title, style, rect, parent)
rect = self.GetClientRect()
rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
self.ocx = MyFlashComponent()
self.ocx.CreateControl("Flash Player", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 1000)
self.ocx.LoadMovie(0,flash_url)
self.ocx.Play()
self.HookMessage (self.OnSize, win32con.WM_SIZE)
def OnSize (self, params):
rect = self.GetClientRect()
rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
self.ocx.SetWindowPos(0, rect, 0)
def Demo():
url = None
if len(sys.argv)>1:
url = win32api.GetFullPathName(sys.argv[1])
f = BrowserFrame(url)
f.Create("Flash Player")
if __name__=='__main__':
Demo()

View File

@ -0,0 +1,127 @@
# This demo uses some of the Microsoft Office components.
#
# It was taken from an MSDN article showing how to embed excel.
# It is not comlpete yet, but it _does_ show an Excel spreadsheet in a frame!
#
import win32ui, win32uiole, win32con, regutil
from pywin.mfc import window, activex, object, docview
from win32com.client import gencache
#WordModule = gencache.EnsureModule('{00020905-0000-0000-C000-000000000046}', 1033, 8, 0)
#if WordModule is None:
# raise ImportError, "Microsoft Word version 8 does not appear to be installed."
class OleClientItem(object.CmdTarget):
def __init__(self, doc):
object.CmdTarget.__init__(self, win32uiole.CreateOleClientItem(doc))
def OnGetItemPosition(self):
# For now return a hard-coded rect.
return (10, 10, 210, 210)
def OnActivate(self):
# Allow only one inplace activate item per frame
view = self.GetActiveView()
item = self.GetDocument().GetInPlaceActiveItem(view)
if item is not None and item._obj_ != self._obj_:
item.Close()
self._obj_.OnActivate()
def OnChange(self, oleNotification, dwParam):
self._obj_.OnChange(oleNotification, dwParam)
self.GetDocument().UpdateAllViews(None)
def OnChangeItemPosition(self, rect):
# During in-place activation CEmbed_ExcelCntrItem::OnChangeItemPosition
# is called by the server to change the position of the in-place
# window. Usually, this is a result of the data in the server
# document changing such that the extent has changed or as a result
# of in-place resizing.
#
# The default here is to call the base class, which will call
# COleClientItem::SetItemRects to move the item
# to the new position.
if not self._obj_.OnChangeItemPosition(self, rect):
return 0
# TODO: update any cache you may have of the item's rectangle/extent
return 1
class OleDocument(object.CmdTarget):
def __init__(self, template):
object.CmdTarget.__init__(self, win32uiole.CreateOleDocument(template))
self.EnableCompoundFile()
class ExcelView(docview.ScrollView):
def OnInitialUpdate(self):
self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
self.HookMessage (self.OnSize, win32con.WM_SIZE)
self.SetScrollSizes(win32con.MM_TEXT, (100, 100))
rc = self._obj_.OnInitialUpdate()
self.EmbedExcel()
return rc
def EmbedExcel(self):
doc = self.GetDocument()
self.clientItem = OleClientItem(doc)
self.clientItem.CreateNewItem("Excel.Sheet")
self.clientItem.DoVerb(-1, self)
doc.UpdateAllViews(None)
def OnDraw(self, dc):
doc = self.GetDocument()
pos = doc.GetStartPosition()
clientItem, pos = doc.GetNextItem(pos)
clientItem.Draw(dc, (10, 10, 210, 210) )
# Special handling of OnSetFocus and OnSize are required for a container
# when an object is being edited in-place.
def OnSetFocus(self, msg):
item = self.GetDocument().GetInPlaceActiveItem(self)
if item is not None and item.GetItemState()==win32uiole.COleClientItem_activeUIState:
wnd = item.GetInPlaceWindow()
if wnd is not None:
wnd.SetFocus()
return 0 # Dont get the base version called.
return 1 # Call the base version.
def OnSize (self, params):
item = self.GetDocument().GetInPlaceActiveItem(self)
if item is not None:
item.SetItemRects()
return 1 # do call the base!
class OleTemplate(docview.DocTemplate):
def __init__(self, resourceId=None, MakeDocument=None, MakeFrame=None, MakeView=None):
if MakeDocument is None: MakeDocument = OleDocument
if MakeView is None: MakeView = ExcelView
docview.DocTemplate.__init__(self, resourceId, MakeDocument, MakeFrame, MakeView)
class WordFrame(window.MDIChildWnd):
def __init__(self, doc = None):
self._obj_ = win32ui.CreateMDIChild()
self._obj_.AttachObject(self)
# Dont call base class doc/view version...
def Create(self, title, rect = None, parent = None):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
self._obj_.CreateWindow(None, title, style, rect, parent)
rect = self.GetClientRect()
rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
self.ocx = MyWordControl()
self.ocx.CreateControl("Microsoft Word", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 20000)
def Demo():
import sys, win32api
docName = None
if len(sys.argv)>1:
docName = win32api.GetFullPathName(sys.argv[1])
OleTemplate().OpenDocumentFile(None)
# f = WordFrame(docName)
# f.Create("Microsoft Office")
if __name__=='__main__':
Demo()

View File

@ -0,0 +1,101 @@
# ocxserialtest.py
#
# Sample that uses the mscomm OCX to talk to a serial
# device.
# Very simple - queries a modem for ATI responses
import win32ui, win32uiole
import win32con
from pywin.mfc import dialog, activex
from win32com.client import gencache
import pythoncom
SERIAL_SETTINGS = '19200,n,8,1'
SERIAL_PORT = 2
win32ui.DoWaitCursor(1)
serialModule = gencache.EnsureModule("{648A5603-2C6E-101B-82B6-000000000014}", 0, 1, 1)
win32ui.DoWaitCursor(0)
if serialModule is None:
raise ImportError("MS COMM Control does not appear to be installed on the PC")
def MakeDlgTemplate():
style = win32con.DS_MODALFRAME | win32con.WS_POPUP \
| win32con.WS_VISIBLE | win32con.WS_CAPTION \
| win32con.WS_SYSMENU | win32con.DS_SETFONT
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
dlg = [ ["Very Basic Terminal",
(0, 0, 350, 180), style, None, (8, "MS Sans Serif")], ]
s = win32con.WS_TABSTOP | cs
dlg.append(["RICHEDIT", None, 132, (5, 5, 340, 170),s | win32con.ES_WANTRETURN | win32con.ES_MULTILINE | win32con.ES_AUTOVSCROLL | win32con.WS_VSCROLL])
return dlg
####################################
#
# Serial Control
#
class MySerialControl(activex.Control, serialModule.MSComm):
def __init__(self, parent):
activex.Control.__init__(self)
serialModule.MSComm.__init__(self)
self.parent = parent
def OnComm(self):
self.parent.OnComm()
class TestSerDialog(dialog.Dialog):
def __init__(self, *args):
dialog.Dialog.__init__(*(self,)+args)
self.olectl = None
def OnComm(self):
event = self.olectl.CommEvent
if event == serialModule.OnCommConstants.comEvReceive:
self.editwindow.ReplaceSel(self.olectl.Input)
def OnKey(self, key):
if self.olectl:
self.olectl.Output = chr(key)
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
self.editwindow = self.GetDlgItem(132)
self.editwindow.HookAllKeyStrokes(self.OnKey)
self.olectl = MySerialControl(self)
try:
self.olectl.CreateControl("OCX",
win32con.WS_TABSTOP | win32con.WS_VISIBLE,
(7,43,500,300), self._obj_, 131)
except win32ui.error:
self.MessageBox("The Serial Control could not be created")
self.olectl = None
self.EndDialog(win32con.IDCANCEL)
if self.olectl:
self.olectl.Settings = SERIAL_SETTINGS
self.olectl.CommPort = SERIAL_PORT
self.olectl.RThreshold = 1
try:
self.olectl.PortOpen = 1
except pythoncom.com_error as details:
print("Could not open the specified serial port - %s" % (details.excepinfo[2]))
self.EndDialog(win32con.IDCANCEL)
return rc
def OnDestroy(self, msg):
if self.olectl:
try:
self.olectl.PortOpen = 0
except pythoncom.com_error as details:
print("Error closing port - %s" % (details.excepinfo[2]))
return dialog.Dialog.OnDestroy(self, msg)
def test():
d = TestSerDialog(MakeDlgTemplate() )
d.DoModal()
if __name__ == "__main__":
import demoutils
if demoutils.NeedGoodGUI():
test()

View File

@ -0,0 +1,186 @@
# OCX Tester for Pythonwin
#
# This file _is_ ready to run. All that is required is that the OCXs being tested
# are installed on your machine.
#
# The .py files behind the OCXs will be automatically generated and imported.
from pywin.mfc import dialog, window, activex
import win32ui, win32uiole
import win32con
import os, sys, win32api, glob
from win32com.client import gencache
def MakeDlgTemplate():
style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
dlg = [ ["OCX Demos", (0, 0, 350, 350), style, None, (8, "MS Sans Serif")], ]
s = win32con.WS_TABSTOP | cs
# dlg.append([131, None, 130, (5, 40, 110, 48),
# s | win32con.LBS_NOTIFY | win32con.LBS_SORT | win32con.LBS_NOINTEGRALHEIGHT | win32con.WS_VSCROLL | win32con.WS_BORDER])
# dlg.append(["{8E27C92B-1264-101C-8A2F-040224009C02}", None, 131, (5, 40, 110, 48),win32con.WS_TABSTOP])
dlg.append([128, "About", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON])
s = win32con.BS_PUSHBUTTON | s
dlg.append([128, "Close", win32con.IDCANCEL, (124, 22, 50, 14), s])
return dlg
####################################
#
# Calendar test code
#
def GetTestCalendarClass():
global calendarParentModule
win32ui.DoWaitCursor(1)
calendarParentModule = gencache.EnsureModule("{8E27C92E-1264-101C-8A2F-040224009C02}", 0, 7, 0)
win32ui.DoWaitCursor(0)
if calendarParentModule is None:
return None
class TestCalDialog(dialog.Dialog):
def OnInitDialog(self):
class MyCal(activex.Control, calendarParentModule.Calendar):
def OnAfterUpdate(self):
print("OnAfterUpdate")
def OnClick(self):
print("OnClick")
def OnDblClick(self):
print("OnDblClick")
def OnKeyDown(self, KeyCode, Shift):
print("OnKeyDown", KeyCode, Shift)
def OnKeyPress(self, KeyAscii):
print("OnKeyPress", KeyAscii)
def OnKeyUp(self, KeyCode, Shift):
print("OnKeyUp", KeyCode, Shift)
def OnBeforeUpdate(self, Cancel):
print("OnBeforeUpdate", Cancel)
def OnNewMonth(self):
print("OnNewMonth")
def OnNewYear(self):
print("OnNewYear")
rc = dialog.Dialog.OnInitDialog(self)
self.olectl = MyCal()
try:
self.olectl.CreateControl("OCX", win32con.WS_TABSTOP | win32con.WS_VISIBLE, (7,43,500,300), self._obj_, 131)
except win32ui.error:
self.MessageBox("The Calendar Control could not be created")
self.olectl = None
self.EndDialog(win32con.IDCANCEL)
return rc
def OnOK(self):
self.olectl.AboutBox()
return TestCalDialog
####################################
#
# Video Control
#
def GetTestVideoModule():
global videoControlModule, videoControlFileName
win32ui.DoWaitCursor(1)
videoControlModule = gencache.EnsureModule("{05589FA0-C356-11CE-BF01-00AA0055595A}", 0, 2, 0)
win32ui.DoWaitCursor(0)
if videoControlModule is None:
return None
fnames = glob.glob(os.path.join(win32api.GetWindowsDirectory(), "*.avi"))
if not fnames:
print("No AVI files available in system directory")
return None
videoControlFileName = fnames[0]
return videoControlModule
def GetTestVideoDialogClass():
if GetTestVideoModule() is None:
return None
class TestVideoDialog(dialog.Dialog):
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
try:
self.olectl = activex.MakeControlInstance(videoControlModule.ActiveMovie)
self.olectl.CreateControl("", win32con.WS_TABSTOP | win32con.WS_VISIBLE, (7,43,500,300), self._obj_, 131)
except win32ui.error:
self.MessageBox("The Video Control could not be created")
self.olectl = None
self.EndDialog(win32con.IDCANCEL)
return
self.olectl.FileName = videoControlFileName
# self.olectl.Run()
return rc
def OnOK(self):
self.olectl.AboutBox()
return TestVideoDialog
###############
#
# An OCX in an MDI Frame
#
class OCXFrame(window.MDIChildWnd):
def __init__(self):
pass # Dont call base class doc/view version...
def Create(self, controlClass, title, rect = None, parent = None):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
self._obj_ = win32ui.CreateMDIChild()
self._obj_.AttachObject(self)
self._obj_.CreateWindow(None, title, style, rect, parent)
rect = self.GetClientRect()
rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
self.ocx = controlClass()
self.ocx.CreateControl("", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 1000)
def MDITest():
calendarParentModule = gencache.EnsureModule("{8E27C92E-1264-101C-8A2F-040224009C02}", 0, 7, 0)
class MyCal(activex.Control, calendarParentModule.Calendar):
def OnAfterUpdate(self):
print("OnAfterUpdate")
def OnClick(self):
print("OnClick")
f = OCXFrame()
f.Create(MyCal, "Calendar Test")
def test1():
klass = GetTestCalendarClass()
if klass is None:
print("Can not test the MSAccess Calendar control - it does not appear to be installed")
return
d = klass(MakeDlgTemplate() )
d.DoModal()
def test2():
klass = GetTestVideoDialogClass()
if klass is None:
print("Can not test the Video OCX - it does not appear to be installed,")
print("or no AVI files can be found.")
return
d = klass(MakeDlgTemplate() )
d.DoModal()
d = None
def test3():
d = TestCOMMDialog(MakeDlgTemplate() )
d.DoModal()
d = None
def testall():
test1()
test2()
def demo():
testall()
if __name__=='__main__':
import demoutils
if demoutils.NeedGoodGUI():
testall()

View File

@ -0,0 +1,55 @@
# This demo uses the IE4 Web Browser control.
# It catches an "OnNavigate" event, and updates the frame title.
# (event stuff by Neil Hodgson)
import win32ui, win32con, win32api, regutil
from pywin.mfc import window, activex
from win32com.client import gencache
import sys
WebBrowserModule = gencache.EnsureModule("{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}", 0, 1, 1)
if WebBrowserModule is None:
raise ImportError("IE4 does not appear to be installed.")
class MyWebBrowser(activex.Control, WebBrowserModule.WebBrowser):
def OnBeforeNavigate2(self, pDisp, URL, Flags, TargetFrameName, PostData, Headers, Cancel):
self.GetParent().OnNavigate(URL)
#print "BeforeNavigate2", pDisp, URL, Flags, TargetFrameName, PostData, Headers, Cancel
class BrowserFrame(window.MDIChildWnd):
def __init__(self, url = None):
if url is None:
self.url = regutil.GetRegisteredHelpFile("Main Python Documentation")
if self.url is None:
self.url = "http://www.python.org"
else:
self.url = url
pass # Dont call base class doc/view version...
def Create(self, title, rect = None, parent = None):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
self._obj_ = win32ui.CreateMDIChild()
self._obj_.AttachObject(self)
self._obj_.CreateWindow(None, title, style, rect, parent)
rect = self.GetClientRect()
rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
self.ocx = MyWebBrowser()
self.ocx.CreateControl("Web Browser", win32con.WS_VISIBLE | win32con.WS_CHILD, rect, self, 1000)
self.ocx.Navigate(self.url)
self.HookMessage (self.OnSize, win32con.WM_SIZE)
def OnSize (self, params):
rect = self.GetClientRect()
rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
self.ocx.SetWindowPos(0, rect, 0)
def OnNavigate(self, url):
title = "Web Browser - %s" % (url,)
self.SetWindowText(title)
def Demo(url=None):
if url is None and len(sys.argv)>1:
url = win32api.GetFullPathName(sys.argv[1])
f = BrowserFrame(url)
f.Create("Web Browser")
if __name__=='__main__':
Demo()

View File

@ -0,0 +1,358 @@
# Ported from the win32 and MFC OpenGL Samples.
from pywin.mfc import docview
import sys
try:
from OpenGL.GL import *
from OpenGL.GLU import *
except ImportError:
print("The OpenGL extensions do not appear to be installed.")
print("This Pythonwin demo can not run")
sys.exit(1)
import win32con
import win32ui
import win32api
import timer
PFD_TYPE_RGBA = 0
PFD_TYPE_COLORINDEX = 1
PFD_MAIN_PLANE = 0
PFD_OVERLAY_PLANE = 1
PFD_UNDERLAY_PLANE = (-1)
PFD_DOUBLEBUFFER = 0x00000001
PFD_STEREO = 0x00000002
PFD_DRAW_TO_WINDOW = 0x00000004
PFD_DRAW_TO_BITMAP = 0x00000008
PFD_SUPPORT_GDI = 0x00000010
PFD_SUPPORT_OPENGL = 0x00000020
PFD_GENERIC_FORMAT = 0x00000040
PFD_NEED_PALETTE = 0x00000080
PFD_NEED_SYSTEM_PALETTE = 0x00000100
PFD_SWAP_EXCHANGE = 0x00000200
PFD_SWAP_COPY = 0x00000400
PFD_SWAP_LAYER_BUFFERS = 0x00000800
PFD_GENERIC_ACCELERATED = 0x00001000
PFD_DEPTH_DONTCARE = 0x20000000
PFD_DOUBLEBUFFER_DONTCARE = 0x40000000
PFD_STEREO_DONTCARE = 0x80000000
#threeto8 = [0, 0o111>>1, 0o222>>1, 0o333>>1, 0o444>>1, 0o555>>1, 0o666>>1, 0o377]
threeto8 = [0, 73>>1, 146>>1, 219>>1, 292>>1, 365>>1, 438>>1, 255]
twoto8 = [0, 0x55, 0xaa, 0xff]
oneto8 = [0, 255]
def ComponentFromIndex(i, nbits, shift):
# val = (unsigned char) (i >> shift);
val = (i >> shift) & 0xF;
if nbits==1:
val = val & 0x1
return oneto8[val]
elif nbits==2:
val = val & 0x3
return twoto8[val]
elif nbits==3:
val = val & 0x7
return threeto8[val]
else:
return 0;
OpenGLViewParent=docview.ScrollView
class OpenGLView(OpenGLViewParent):
def PreCreateWindow(self, cc):
self.HookMessage (self.OnSize, win32con.WM_SIZE)
# An OpenGL window must be created with the following flags and must not
# include CS_PARENTDC for the class style. Refer to SetPixelFormat
# documentation in the "Comments" section for further information.
style = cc[5]
style = style | win32con.WS_CLIPSIBLINGS | win32con.WS_CLIPCHILDREN
cc = cc[0], cc[1], cc[2], cc[3], cc[4], style, cc[6], cc[7], cc[8]
cc = self._obj_.PreCreateWindow(cc)
return cc
def OnSize (self, params):
lParam = params[3]
cx = win32api.LOWORD(lParam)
cy = win32api.HIWORD(lParam)
glViewport(0, 0, cx, cy)
if self.oldrect[2] > cx or self.oldrect[3] > cy:
self.RedrawWindow()
self.OnSizeChange(cx, cy)
self.oldrect = self.oldrect[0], self.oldrect[1], cx, cy
def OnInitialUpdate(self):
self.SetScaleToFitSize((100,100)) # or SetScrollSizes() - A Pythonwin requirement
return self._obj_.OnInitialUpdate()
# return rc
def OnCreate(self, cs):
self.oldrect = self.GetClientRect()
self._InitContexts()
self.Init()
def OnDestroy(self, msg):
self.Term()
self._DestroyContexts()
return OpenGLViewParent.OnDestroy(self, msg)
def OnDraw(self, dc):
self.DrawScene()
def OnEraseBkgnd(self, dc):
return 1
# The OpenGL helpers
def _SetupPixelFormat(self):
dc = self.dc.GetSafeHdc()
pfd = CreatePIXELFORMATDESCRIPTOR()
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER
pfd.iPixelType = PFD_TYPE_RGBA
pfd.cColorBits = 24
pfd.cDepthBits = 32
pfd.iLayerType = PFD_MAIN_PLANE
pixelformat = ChoosePixelFormat(dc, pfd)
SetPixelFormat(dc, pixelformat, pfd)
self._CreateRGBPalette()
def _CreateRGBPalette(self):
dc = self.dc.GetSafeHdc()
n = GetPixelFormat(dc)
pfd = DescribePixelFormat(dc, n)
if pfd.dwFlags & PFD_NEED_PALETTE:
n = 1 << pfd.cColorBits
pal = []
for i in range(n):
this = ComponentFromIndex(i, pfd.cRedBits, pfd.cRedShift), \
ComponentFromIndex(i, pfd.cGreenBits, pfd.cGreenShift), \
ComponentFromIndex(i, pfd.cBlueBits, pfd.cBlueShift), \
0
pal.append(this)
hpal = win32ui.CreatePalette(pal)
self.dc.SelectPalette(hpal, 0)
self.dc.RealizePalette()
def _InitContexts(self):
self.dc = self.GetDC()
self._SetupPixelFormat()
hrc = wglCreateContext(self.dc.GetSafeHdc())
wglMakeCurrent(self.dc.GetSafeHdc(), hrc)
def _DestroyContexts(self):
hrc = wglGetCurrentContext()
wglMakeCurrent(0, 0)
if hrc: wglDeleteContext(hrc)
# The methods to support OpenGL
def DrawScene(self):
assert 0, "You must override this method"
def Init(self):
assert 0, "You must override this method"
def OnSizeChange(self, cx, cy):
pass
def Term(self):
pass
class TestView(OpenGLView):
def OnSizeChange(self, right, bottom):
glClearColor( 0.0, 0.0, 0.0, 1.0 );
glClearDepth( 1.0 );
glEnable(GL_DEPTH_TEST)
glMatrixMode( GL_PROJECTION )
if bottom:
aspect = right / bottom
else:
aspect = 0 # When window created!
glLoadIdentity()
gluPerspective( 45.0, aspect, 3.0, 7.0 )
glMatrixMode( GL_MODELVIEW )
near_plane = 3.0;
far_plane = 7.0;
maxObjectSize = 3.0;
self.radius = near_plane + maxObjectSize/2.0;
def Init(self):
pass
def DrawScene(self):
glClearColor(0.0, 0.0, 0.0, 1.0)
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT )
glPushMatrix()
glTranslatef(0.0, 0.0, -self.radius);
self._DrawCone()
self._DrawPyramid()
glPopMatrix()
glFinish()
SwapBuffers( wglGetCurrentDC() )
def _DrawCone(self):
glColor3f(0.0, 1.0, 0.0)
glPushMatrix()
glTranslatef(-1.0, 0.0, 0.0);
quadObj = gluNewQuadric();
gluQuadricDrawStyle(quadObj, GLU_FILL);
gluQuadricNormals(quadObj, GLU_SMOOTH);
gluCylinder(quadObj, 1.0, 0.0, 1.0, 20, 10);
# gluDeleteQuadric(quadObj);
glPopMatrix();
def _DrawPyramid(self):
glPushMatrix()
glTranslatef(1.0, 0.0, 0.0)
glBegin(GL_TRIANGLE_FAN)
glColor3f(1.0, 0.0, 0.0)
glVertex3f(0.0, 1.0, 0.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-1.0, 0.0, 0.0)
glColor3f(0.0, 0.0, 1.0)
glVertex3f(0.0, 0.0, 1.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(1.0, 0.0, 0.0)
glEnd()
glPopMatrix()
class CubeView(OpenGLView):
def OnSizeChange(self, right, bottom):
glClearColor( 0.0, 0.0, 0.0, 1.0 );
glClearDepth( 1.0 );
glEnable(GL_DEPTH_TEST)
glMatrixMode( GL_PROJECTION )
if bottom:
aspect = right / bottom
else:
aspect = 0 # When window created!
glLoadIdentity()
gluPerspective( 45.0, aspect, 3.0, 7.0 )
glMatrixMode( GL_MODELVIEW )
near_plane = 3.0;
far_plane = 7.0;
maxObjectSize = 3.0;
self.radius = near_plane + maxObjectSize/2.0;
def Init(self):
self.busy = 0
self.wAngleY = 10.0
self.wAngleX = 1.0
self.wAngleZ = 5.0
self.timerid = timer.set_timer (150, self.OnTimer)
def OnTimer(self, id, timeVal):
self.DrawScene()
def Term(self):
timer.kill_timer(self.timerid)
def DrawScene(self):
if self.busy: return
self.busy = 1
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
glTranslatef(0.0, 0.0, -self.radius);
glRotatef(self.wAngleX, 1.0, 0.0, 0.0);
glRotatef(self.wAngleY, 0.0, 1.0, 0.0);
glRotatef(self.wAngleZ, 0.0, 0.0, 1.0);
self.wAngleX = self.wAngleX + 1.0
self.wAngleY = self.wAngleY + 10.0
self.wAngleZ = self.wAngleZ + 5.0;
glBegin(GL_QUAD_STRIP);
glColor3f(1.0, 0.0, 1.0);
glVertex3f(-0.5, 0.5, 0.5);
glColor3f(1.0, 0.0, 0.0);
glVertex3f(-0.5, -0.5, 0.5);
glColor3f(1.0, 1.0, 1.0);
glVertex3f(0.5, 0.5, 0.5);
glColor3f(1.0, 1.0, 0.0);
glVertex3f(0.5, -0.5, 0.5);
glColor3f(0.0, 1.0, 1.0);
glVertex3f(0.5, 0.5, -0.5);
glColor3f(0.0, 1.0, 0.0);
glVertex3f(0.5, -0.5, -0.5);
glColor3f(0.0, 0.0, 1.0);
glVertex3f(-0.5, 0.5, -0.5);
glColor3f(0.0, 0.0, 0.0);
glVertex3f(-0.5, -0.5, -0.5);
glColor3f(1.0, 0.0, 1.0);
glVertex3f(-0.5, 0.5, 0.5);
glColor3f(1.0, 0.0, 0.0);
glVertex3f(-0.5, -0.5, 0.5);
glEnd();
glBegin(GL_QUADS);
glColor3f(1.0, 0.0, 1.0);
glVertex3f(-0.5, 0.5, 0.5);
glColor3f(1.0, 1.0, 1.0);
glVertex3f(0.5, 0.5, 0.5);
glColor3f(0.0, 1.0, 1.0);
glVertex3f(0.5, 0.5, -0.5);
glColor3f(0.0, 0.0, 1.0);
glVertex3f(-0.5, 0.5, -0.5);
glEnd();
glBegin(GL_QUADS);
glColor3f(1.0, 0.0, 0.0);
glVertex3f(-0.5, -0.5, 0.5);
glColor3f(1.0, 1.0, 0.0);
glVertex3f(0.5, -0.5, 0.5);
glColor3f(0.0, 1.0, 0.0);
glVertex3f(0.5, -0.5, -0.5);
glColor3f(0.0, 0.0, 0.0);
glVertex3f(-0.5, -0.5, -0.5);
glEnd();
glPopMatrix();
glFinish();
SwapBuffers(wglGetCurrentDC());
self.busy = 0
def test():
template = docview.DocTemplate(None, None, None, CubeView )
# template = docview.DocTemplate(None, None, None, TestView )
template.OpenDocumentFile(None)
if __name__=='__main__':
test()

View File

@ -0,0 +1,88 @@
#
# Progress bar control example
#
# PyCProgressCtrl encapsulates the MFC CProgressCtrl class. To use it,
# you:
#
# - Create the control with win32ui.CreateProgressCtrl()
# - Create the control window with PyCProgressCtrl.CreateWindow()
# - Initialize the range if you want it to be other than (0, 100) using
# PyCProgressCtrl.SetRange()
# - Either:
# - Set the step size with PyCProgressCtrl.SetStep(), and
# - Increment using PyCProgressCtrl.StepIt()
# or:
# - Set the amount completed using PyCProgressCtrl.SetPos()
#
# Example and progress bar code courtesy of KDL Technologies, Ltd., Hong Kong SAR, China.
#
from pywin.mfc import dialog
import win32ui
import win32con
def MakeDlgTemplate():
style = (win32con.DS_MODALFRAME |
win32con.WS_POPUP |
win32con.WS_VISIBLE |
win32con.WS_CAPTION |
win32con.WS_SYSMENU |
win32con.DS_SETFONT)
cs = (win32con.WS_CHILD |
win32con.WS_VISIBLE)
w = 215
h = 36
dlg = [["Progress bar control example",
(0, 0, w, h),
style,
None,
(8, "MS Sans Serif")],
]
s = win32con.WS_TABSTOP | cs
dlg.append([128,
"Tick",
win32con.IDOK,
(10, h - 18, 50, 14), s | win32con.BS_DEFPUSHBUTTON])
dlg.append([128,
"Cancel",
win32con.IDCANCEL,
(w - 60, h - 18, 50, 14), s | win32con.BS_PUSHBUTTON])
return dlg
class TestDialog(dialog.Dialog):
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
self.pbar = win32ui.CreateProgressCtrl()
self.pbar.CreateWindow (win32con.WS_CHILD |
win32con.WS_VISIBLE,
(10, 10, 310, 24),
self, 1001)
# self.pbar.SetStep (5)
self.progress = 0
self.pincr = 5
return rc
def OnOK(self):
# NB: StepIt wraps at the end if you increment past the upper limit!
# self.pbar.StepIt()
self.progress = self.progress + self.pincr
if self.progress > 100:
self.progress = 100
if self.progress <= 100:
self.pbar.SetPos(self.progress)
def demo(modal = 0):
d = TestDialog (MakeDlgTemplate())
if modal:
d.DoModal()
else:
d.CreateWindow ()
if __name__=='__main__':
demo(1)

View File

@ -0,0 +1,54 @@
# sliderdemo.py
# Demo of the slider control courtesy of Mike Fletcher.
import win32con, win32ui
from pywin.mfc import dialog
class MyDialog(dialog.Dialog):
'''
Example using simple controls
'''
_dialogstyle = (win32con.WS_MINIMIZEBOX | win32con.WS_DLGFRAME |
win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE |
win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT )
_buttonstyle = (win32con.BS_PUSHBUTTON | win32con.WS_TABSTOP |
win32con.WS_CHILD | win32con.WS_VISIBLE)
### The static template, contains all "normal" dialog items
DIALOGTEMPLATE = [
# the dialog itself is the first element in the template
["Example slider", (0, 0, 50, 43), _dialogstyle, None, (8, "MS SansSerif")],
# rest of elements are the controls within the dialog
# standard "Close" button
[128, "Close", win32con.IDCANCEL, (0, 30, 50, 13), _buttonstyle], ]
### ID of the control to be created during dialog initialisation
IDC_SLIDER = 9500
def __init__(self ):
dialog.Dialog.__init__(self, self.DIALOGTEMPLATE)
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
# now initialise your controls that you want to create
# programmatically, including those which are OLE controls
# those created directly by win32ui.Create*
# and your "custom controls" which are subclasses/whatever
win32ui.EnableControlContainer()
self.slider = win32ui.CreateSliderCtrl( )
self.slider.CreateWindow( win32con.WS_TABSTOP | win32con.WS_VISIBLE,
(0,0,100,30),
self._obj_,
self.IDC_SLIDER)
self.HookMessage(self.OnSliderMove, win32con.WM_HSCROLL)
return rc
def OnSliderMove(self, params):
print("Slider moved")
def OnCancel(self):
print("The slider control is at position", self.slider.GetPos())
self._obj_.OnCancel()
###
def demo():
dia = MyDialog()
dia.DoModal()
if __name__ == "__main__":
demo()

View File

@ -0,0 +1,72 @@
import win32ui
import win32con
import fontdemo
from pywin.mfc import window, docview
import commctrl
# derive from CMDIChild. This does much work for us.
class SplitterFrame(window.MDIChildWnd):
def __init__(self):
# call base CreateFrame
self.images = None
window.MDIChildWnd.__init__(self)
def OnCreateClient(self, cp, context):
splitter = win32ui.CreateSplitter()
doc = context.doc
frame_rect = self.GetWindowRect()
size = ((frame_rect[2] - frame_rect[0]),
(frame_rect[3] - frame_rect[1])//2)
sub_size = (size[0]//2, size[1])
splitter.CreateStatic (self, 2, 1)
self.v1 = win32ui.CreateEditView(doc)
self.v2 = fontdemo.FontView(doc)
# CListControl view
self.v3 = win32ui.CreateListView(doc)
sub_splitter = win32ui.CreateSplitter()
# pass "splitter" so each view knows how to get to the others
sub_splitter.CreateStatic (splitter, 1, 2)
sub_splitter.CreateView(self.v1, 0, 0, (sub_size))
sub_splitter.CreateView(self.v2, 0, 1, (0,0)) # size ignored.
splitter.SetRowInfo(0, size[1] ,0)
splitter.CreateView (self.v3, 1, 0, (0,0)) # size ignored.
# Setup items in the imagelist
self.images = win32ui.CreateImageList(32,32,1,5,5)
self.images.Add(win32ui.GetApp().LoadIcon(win32ui.IDR_MAINFRAME))
self.images.Add(win32ui.GetApp().LoadIcon(win32ui.IDR_PYTHONCONTYPE))
self.images.Add(win32ui.GetApp().LoadIcon(win32ui.IDR_TEXTTYPE))
self.v3.SetImageList(self.images, commctrl.LVSIL_NORMAL)
self.v3.InsertItem(0, "Icon 1", 0)
self.v3.InsertItem(0, "Icon 2", 1)
self.v3.InsertItem(0, "Icon 3", 2)
# self.v3.Arrange(commctrl.LVA_DEFAULT) Hmmm - win95 aligns left always???
return 1
def OnDestroy(self, msg):
window.MDIChildWnd.OnDestroy(self, msg)
if self.images:
self.images.DeleteImageList()
self.images = None
def InitialUpdateFrame(self, doc, makeVisible):
self.v1.ReplaceSel("Hello from Edit Window 1")
self.v1.SetModifiedFlag(0)
class SampleTemplate(docview.DocTemplate):
def __init__(self):
docview.DocTemplate.__init__(self, win32ui.IDR_PYTHONTYPE, None, SplitterFrame, None)
def InitialUpdateFrame(self, frame, doc, makeVisible):
# print "frame is ", frame, frame._obj_
# print "doc is ", doc, doc._obj_
self._obj_.InitialUpdateFrame(frame, doc, makeVisible) # call default handler.
frame.InitialUpdateFrame(doc, makeVisible)
def demo():
template = SampleTemplate()
doc=template.OpenDocumentFile(None)
doc.SetTitle("Splitter Demo")
if __name__=='__main__':
import demoutils
if demoutils.NeedGoodGUI():
demo()

View File

@ -0,0 +1,174 @@
# Demo of using just windows, without documents and views.
# Also demo of a GUI thread, pretty much direct from the MFC C++ sample MTMDI.
import win32ui
import win32con
import win32api
import timer
from pywin.mfc import window, docview, thread
from pywin.mfc.thread import WinThread
WM_USER_PREPARE_TO_CLOSE = win32con.WM_USER + 32
# font is a dictionary in which the following elements matter:
# (the best matching font to supplied parameters is returned)
# name string name of the font as known by Windows
# size point size of font in logical units
# weight weight of font (win32con.FW_NORMAL, win32con.FW_BOLD)
# italic boolean; true if set to anything but None
# underline boolean; true if set to anything but None
# This window is a child window of a frame. It is not the frame window itself.
class FontWindow(window.Wnd):
def __init__(self, text = 'Python Rules!'):
window.Wnd.__init__(self)
self.text = text
self.index = 0
self.incr = 1
self.width = self.height = 0
self.ChangeAttributes()
# set up message handlers
def Create(self, title, style, rect, parent):
classStyle = win32con.CS_HREDRAW | win32con.CS_VREDRAW
className = win32ui.RegisterWndClass(classStyle, 0, win32con.COLOR_WINDOW+1, 0)
self._obj_ = win32ui.CreateWnd()
self._obj_.AttachObject(self)
self._obj_.CreateWindow(className, title, style, rect, parent, win32ui.AFX_IDW_PANE_FIRST)
self.HookMessage (self.OnSize, win32con.WM_SIZE)
self.HookMessage (self.OnPrepareToClose, WM_USER_PREPARE_TO_CLOSE)
self.HookMessage (self.OnDestroy, win32con.WM_DESTROY)
self.timerid = timer.set_timer (100, self.OnTimer)
self.InvalidateRect()
def OnDestroy (self, msg):
timer.kill_timer(self.timerid)
def OnTimer(self, id, timeVal):
self.index = self.index + self.incr
if self.index > len(self.text):
self.incr = -1
self.index = len(self.text)
elif self.index < 0:
self.incr = 1
self.index = 0
self.InvalidateRect()
def OnPaint (self):
# print "Paint message from thread", win32api.GetCurrentThreadId()
dc, paintStruct = self.BeginPaint()
self.OnPrepareDC(dc, None)
if (self.width == 0 and self.height == 0):
left, top, right, bottom = self.GetClientRect()
self.width = right - left
self.height = bottom - top
x, y = self.width // 2, self.height // 2
dc.TextOut (x, y, self.text[:self.index])
self.EndPaint(paintStruct)
def ChangeAttributes(self):
font_spec = {'name':'Arial', 'height':42}
self.font = win32ui.CreateFont (font_spec)
def OnPrepareToClose(self, params):
self.DestroyWindow()
def OnSize (self, params):
lParam = params[3]
self.width = win32api.LOWORD(lParam)
self.height = win32api.HIWORD(lParam)
def OnPrepareDC (self, dc, printinfo):
# Set up the DC for forthcoming OnDraw call
dc.SetTextColor (win32api.RGB(0,0,255))
dc.SetBkColor (win32api.GetSysColor (win32con.COLOR_WINDOW))
dc.SelectObject (self.font)
dc.SetTextAlign (win32con.TA_CENTER | win32con.TA_BASELINE)
class FontFrame(window.MDIChildWnd):
def __init__(self):
pass # Dont call base class doc/view version...
def Create(self, title, rect = None, parent = None):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
self._obj_ = win32ui.CreateMDIChild()
self._obj_.AttachObject(self)
self._obj_.CreateWindow(None, title, style, rect, parent)
rect = self.GetClientRect()
rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
self.child = FontWindow("Not threaded")
self.child.Create("FontDemo", win32con.WS_CHILD | win32con.WS_VISIBLE, rect, self)
class TestThread(WinThread):
def __init__(self, parentWindow):
self.parentWindow = parentWindow
self.child = None
WinThread.__init__(self)
def InitInstance(self):
rect = self.parentWindow.GetClientRect()
rect = (0,0,rect[2]-rect[0], rect[3]-rect[1])
self.child = FontWindow()
self.child.Create("FontDemo", win32con.WS_CHILD | win32con.WS_VISIBLE, rect, self.parentWindow)
self.SetMainFrame(self.child)
return WinThread.InitInstance(self)
def ExitInstance(self):
return 0
class ThreadedFontFrame(window.MDIChildWnd):
def __init__(self):
pass # Dont call base class doc/view version...
self.thread = None
def Create(self, title, rect = None, parent = None):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_OVERLAPPEDWINDOW
self._obj_ = win32ui.CreateMDIChild()
self._obj_.CreateWindow(None, title, style, rect, parent)
self._obj_.HookMessage(self.OnDestroy, win32con.WM_DESTROY)
self._obj_.HookMessage (self.OnSize, win32con.WM_SIZE)
self.thread = TestThread(self)
self.thread.CreateThread()
def OnSize(self, msg):
pass
def OnDestroy(self, msg):
win32ui.OutputDebugString("OnDestroy\n")
if self.thread and self.thread.child:
child = self.thread.child
child.SendMessage(WM_USER_PREPARE_TO_CLOSE, 0, 0)
win32ui.OutputDebugString("Destroyed\n")
def Demo():
f = FontFrame()
f.Create("Font Demo")
def ThreadedDemo():
rect = win32ui.GetMainFrame().GetMDIClient().GetClientRect()
rect = rect[0], int(rect[3]*3/4), int(rect[2]/4), rect[3]
incr = rect[2]
for i in range(4):
if i==0:
f = FontFrame()
title = "Not threaded"
else:
f = ThreadedFontFrame()
title = "Threaded GUI Demo"
f.Create(title, rect)
rect = rect[0] + incr, rect[1], rect[2]+incr, rect[3]
# Givem a chance to start
win32api.Sleep(100)
win32ui.PumpWaitingMessages()
if __name__=='__main__':
import demoutils
if demoutils.NeedGoodGUI():
ThreadedDemo()
# Demo()

View File

@ -0,0 +1,93 @@
# Demo of ToolBars
# Shows the toolbar control.
# Demos how to make custom tooltips, etc.
import win32ui
import win32con
import win32api
from pywin.mfc import docview, window, afxres
import commctrl
class GenericFrame(window.MDIChildWnd):
def OnCreateClient(self, cp, context):
# handlers for toolbar buttons
self.HookCommand (self.OnPrevious, 401)
self.HookCommand (self.OnNext, 402)
# Its not necessary for us to hook both of these - the
# common controls should fall-back all by themselves.
# Indeed, given we hook TTN_NEEDTEXTW, commctrl.TTN_NEEDTEXTA
# will not be called.
self.HookNotify(self.GetTTText, commctrl.TTN_NEEDTEXT)
self.HookNotify(self.GetTTText, commctrl.TTN_NEEDTEXTW)
# parent = win32ui.GetMainFrame()
parent = self
style = win32con.WS_CHILD | win32con.WS_VISIBLE | \
afxres.CBRS_SIZE_DYNAMIC | afxres.CBRS_TOP | afxres.CBRS_TOOLTIPS | afxres.CBRS_FLYBY
buttons = (win32ui.ID_APP_ABOUT,win32ui.ID_VIEW_INTERACTIVE)
bitmap = win32ui.IDB_BROWSER_HIER
tbid = 0xE840
self.toolbar = tb = win32ui.CreateToolBar (parent, style, tbid)
tb.LoadBitmap(bitmap)
tb.SetButtons(buttons)
tb.EnableDocking(afxres.CBRS_ALIGN_ANY)
tb.SetWindowText("Test")
parent.EnableDocking(afxres.CBRS_ALIGN_ANY)
parent.DockControlBar(tb)
parent.LoadBarState("ToolbarTest")
window.MDIChildWnd.OnCreateClient(self, cp, context)
return 1
def OnDestroy(self, msg):
self.SaveBarState("ToolbarTest")
def GetTTText(self, std, extra):
(hwndFrom, idFrom, code) = std
text, hinst, flags = extra
if flags & commctrl.TTF_IDISHWND:
return # Not handled
if (idFrom==win32ui.ID_APP_ABOUT):
# our 'extra' return value needs to be the following
# entries from a NMTTDISPINFO[W] struct:
# (szText, hinst, uFlags). None means 'don't change
# the value'
return 0, ("It works!", None, None)
return None # not handled.
def GetMessageString(self, id):
if id==win32ui.ID_APP_ABOUT:
return "Dialog Test\nTest"
else:
return self._obj_.GetMessageString(id)
def OnSize (self, params):
print('OnSize called with ', params)
def OnNext (self, id, cmd):
print('OnNext called')
def OnPrevious (self, id, cmd):
print('OnPrevious called')
msg = """\
This toolbar was dynamically created.\r
\r
The first item's tooltips is provided by Python code.\r
\r
(Dont close the window with the toolbar in a floating state - it may not re-appear!)\r
"""
def test():
template = docview.DocTemplate( win32ui.IDR_PYTHONTYPE, None, GenericFrame, docview.EditView)
doc = template.OpenDocumentFile(None)
doc.SetTitle("Toolbar Test")
view = doc.GetFirstView()
view.SetWindowText(msg)
if __name__=='__main__':
import demoutils
if demoutils.NeedGoodGUI():
test()

View File

@ -0,0 +1,29 @@
[General]
# We base this configuration on the default config.
# You can list "Based On" as many times as you like
Based On = default
[Keys]
# Only list keys different to default.
# Note you may wish to rebind some of the default
# Pythonwin keys to "Beep" or "DoNothing"
Alt+L = LocateSelectedFile
Ctrl+Q = AppExit
# Other non-default Pythonwin keys
Alt+A = EditSelectAll
Alt+M = LocateModule
# Movement
Ctrl+D = GotoEndOfFile
# Tabs and other indent features
Alt+T = <<toggle-tabs>>
Ctrl+[ = <<indent-region>>
Ctrl+] = <<dedent-region>>
[Keys:Interactive]
Alt+P = <<history-previous>>
Alt+N = <<history-next>>

View File

@ -0,0 +1,10 @@
# is_platform_unicode is an old variable that was never correctly used and
# is no longer referenced in pywin32. It is staying for a few releases incase
# others are looking at it, but it will go away soon!
is_platform_unicode = 0
# Ditto default_platform_encoding - not referenced and will die.
default_platform_encoding = "mbcs"
# This one *is* real and used - but in practice can't be changed.
default_scintilla_encoding = "utf-8" # Scintilla _only_ supports this ATM

View File

@ -0,0 +1,113 @@
import sys
# Some cruft to deal with the Pythonwin GUI booting up from a non GUI app.
def _MakeDebuggerGUI():
app.InitInstance()
isInprocApp = -1
def _CheckNeedGUI():
global isInprocApp
if isInprocApp==-1:
import win32ui
isInprocApp = win32ui.GetApp().IsInproc()
if isInprocApp:
# MAY Need it - may already have one
need = "pywin.debugger.dbgpyapp" not in sys.modules
else:
need = 0
if need:
import pywin.framework.app
from . import dbgpyapp
pywin.framework.app.CreateDefaultGUI(dbgpyapp.DebuggerPythonApp)
else:
# Check we have the appropriate editor
# No longer necessary!
pass
return need
# Inject some methods in the top level name-space.
currentDebugger = None # Wipe out any old one on reload.
def _GetCurrentDebugger():
global currentDebugger
if currentDebugger is None:
_CheckNeedGUI()
from . import debugger
currentDebugger = debugger.Debugger()
return currentDebugger
def GetDebugger():
# An error here is not nice - as we are probably trying to
# break into the debugger on a Python error, any
# error raised by this is usually silent, and causes
# big problems later!
try:
rc = _GetCurrentDebugger()
rc.GUICheckInit()
return rc
except:
print("Could not create the debugger!")
import traceback
traceback.print_exc()
return None
def close():
if currentDebugger is not None:
currentDebugger.close()
def run(cmd,globals=None, locals=None, start_stepping = 1):
_GetCurrentDebugger().run(cmd, globals,locals, start_stepping)
def runeval(expression, globals=None, locals=None):
return _GetCurrentDebugger().runeval(expression, globals, locals)
def runcall(*args):
return _GetCurrentDebugger().runcall(*args)
def set_trace():
import sys
d = _GetCurrentDebugger()
if d.frameShutdown: return # App closing
if d.stopframe != d.botframe:
# If im not "running"
return
sys.settrace(None) # May be hooked
d.reset()
d.set_trace()
# "brk" is an alias for "set_trace" ("break" is a reserved word :-(
brk = set_trace
# Post-Mortem interface
def post_mortem(t=None):
if t is None:
t = sys.exc_info()[2] # Will be valid if we are called from an except handler.
if t is None:
try:
t = sys.last_traceback
except AttributeError:
print("No traceback can be found from which to perform post-mortem debugging!")
print("No debugging can continue")
return
p = _GetCurrentDebugger()
if p.frameShutdown: return # App closing
# No idea why I need to settrace to None - it should have been reset by now?
sys.settrace(None)
p.reset()
while t.tb_next != None: t = t.tb_next
p.bAtPostMortem = 1
p.prep_run(None)
try:
p.interaction(t.tb_frame, t)
finally:
t = None
p.bAtPostMortem = 0
p.done_run()
def pm(t=None):
post_mortem(t)

View File

@ -0,0 +1,31 @@
from . import dbgcon
from pywin.mfc import dialog
import win32ui
class DebuggerOptionsPropPage(dialog.PropertyPage):
def __init__(self):
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_DEBUGGER)
def OnInitDialog(self):
options = self.options = dbgcon.LoadDebuggerOptions()
self.AddDDX(win32ui.IDC_CHECK1, dbgcon.OPT_HIDE)
self[dbgcon.OPT_STOP_EXCEPTIONS] = options[dbgcon.OPT_STOP_EXCEPTIONS]
self.AddDDX(win32ui.IDC_CHECK2, dbgcon.OPT_STOP_EXCEPTIONS)
self[dbgcon.OPT_HIDE] = options[dbgcon.OPT_HIDE]
return dialog.PropertyPage.OnInitDialog(self)
def OnOK(self):
self.UpdateData()
dirty = 0
for key, val in list(self.items()):
if key in self.options:
if self.options[key] != val:
self.options[key] = val
dirty = 1
if dirty:
dbgcon.SaveDebuggerOptions(self.options)
# If there is a debugger open, then set its options.
import pywin.debugger
if pywin.debugger.currentDebugger is not None:
pywin.debugger.currentDebugger.options = self.options
return 1

View File

@ -0,0 +1,28 @@
# General constants for the debugger
DBGSTATE_NOT_DEBUGGING = 0
DBGSTATE_RUNNING = 1
DBGSTATE_BREAK = 2
DBGSTATE_QUITTING = 3 # Attempting to back out of the debug session.
LINESTATE_CURRENT = 0x1 # This line is where we are stopped
LINESTATE_BREAKPOINT = 0x2 # This line is a breakpoint
LINESTATE_CALLSTACK = 0x4 # This line is in the callstack.
OPT_HIDE = 'hide'
OPT_STOP_EXCEPTIONS = 'stopatexceptions'
import win32api, win32ui
def DoGetOption(optsDict, optName, default):
optsDict[optName] = win32ui.GetProfileVal("Debugger Options", optName, default)
def LoadDebuggerOptions():
opts = {}
DoGetOption(opts, OPT_HIDE, 0)
DoGetOption(opts, OPT_STOP_EXCEPTIONS, 1)
return opts
def SaveDebuggerOptions(opts):
for key, val in opts.items():
win32ui.WriteProfileVal("Debugger Options", key, val)

View File

@ -0,0 +1,47 @@
# dbgpyapp.py - Debugger Python application class
#
import win32con
import win32ui
import sys
import string
import os
from pywin.framework import intpyapp
version = '0.3.0'
class DebuggerPythonApp(intpyapp.InteractivePythonApp):
def LoadMainFrame(self):
" Create the main applications frame "
self.frame = self.CreateMainFrame()
self.SetMainFrame(self.frame)
self.frame.LoadFrame(win32ui.IDR_DEBUGGER, win32con.WS_OVERLAPPEDWINDOW)
self.frame.DragAcceptFiles() # we can accept these.
self.frame.ShowWindow(win32con.SW_HIDE);
self.frame.UpdateWindow();
# but we do rehook, hooking the new code objects.
self.HookCommands()
def InitInstance(self):
# Use a registry path of "Python\Pythonwin Debugger
win32ui.SetAppName(win32ui.LoadString(win32ui.IDR_DEBUGGER))
win32ui.SetRegistryKey("Python %s" % (sys.winver,))
# We _need_ the Scintilla color editor.
# (and we _always_ get it now :-)
numMRU = win32ui.GetProfileVal("Settings","Recent File List Size", 10)
win32ui.LoadStdProfileSettings(numMRU)
self.LoadMainFrame()
# Display the interactive window if the user wants it.
from pywin.framework import interact
interact.CreateInteractiveWindowUserPreference()
# Load the modules we use internally.
self.LoadSystemModules()
# Load additional module the user may want.
self.LoadUserModules()
# win32ui.CreateDebuggerThread()
win32ui.EnableControlContainer()

View File

@ -0,0 +1,985 @@
# debugger.py
# A debugger for Pythonwin. Built from pdb.
# Mark Hammond (MHammond@skippinet.com.au) - Dec 94.
# usage:
# >>> import pywin.debugger
# >>> pywin.debugger.GetDebugger().run("command")
import pdb
import bdb
import sys
import string
import os
import types
import win32ui
import win32api
import win32con
import pywin.docking.DockingBar
from pywin.mfc import dialog, object, afxres, window
from pywin.framework import app, interact, editor, scriptutils
from pywin.framework.editor.color.coloreditor import MARKER_CURRENT, MARKER_BREAKPOINT
from pywin.tools import browser, hierlist
import commctrl
import traceback
#import win32traceutil
if win32ui.UNICODE:
LVN_ENDLABELEDIT = commctrl.LVN_ENDLABELEDITW
else:
LVN_ENDLABELEDIT = commctrl.LVN_ENDLABELEDITA
from .dbgcon import *
error = "pywin.debugger.error"
def SetInteractiveContext(globs, locs):
if interact.edit is not None and interact.edit.currentView is not None:
interact.edit.currentView.SetContext(globs, locs)
def _LineStateToMarker(ls):
if ls==LINESTATE_CURRENT:
return MARKER_CURRENT
# elif ls == LINESTATE_CALLSTACK:
# return MARKER_CALLSTACK
return MARKER_BREAKPOINT
class HierListItem(browser.HLIPythonObject):
pass
class HierFrameItem(HierListItem):
def __init__(self, frame, debugger):
HierListItem.__init__(self, frame, repr(frame))
self.debugger = debugger
def GetText(self):
name = self.myobject.f_code.co_name
if not name or name == '?' :
# See if locals has a '__name__' (ie, a module)
if '__name__' in self.myobject.f_locals:
name = str(self.myobject.f_locals['__name__']) + " module"
else:
name = '<Debugger Context>'
return "%s (%s:%d)" % (name, os.path.split(self.myobject.f_code.co_filename)[1], self.myobject.f_lineno)
def GetBitmapColumn(self):
if self.debugger.curframe is self.myobject:
return 7
else:
return 8
def GetSubList(self):
ret = []
ret.append(HierFrameDict(self.myobject.f_locals, "Locals", 2))
ret.append(HierFrameDict(self.myobject.f_globals, "Globals", 1))
return ret
def IsExpandable(self):
return 1
def TakeDefaultAction(self):
# Set the default frame to be this frame.
self.debugger.set_cur_frame(self.myobject)
return 1
class HierFrameDict(browser.HLIDict):
def __init__(self, dict, name, bitmapColumn):
self.bitmapColumn=bitmapColumn
browser.HLIDict.__init__(self, dict, name)
def GetBitmapColumn(self):
return self.bitmapColumn
class NoStackAvailableItem(HierListItem):
def __init__(self, why):
HierListItem.__init__(self, None, why)
def IsExpandable(self):
return 0
def GetText(self):
return self.name
def GetBitmapColumn(self):
return 8
class HierStackRoot(HierListItem):
def __init__( self, debugger ):
HierListItem.__init__(self, debugger, None)
self.last_stack = []
## def __del__(self):
## print "HierStackRoot dieing"
def GetSubList(self):
debugger = self.myobject
# print self.debugger.stack, self.debugger.curframe
ret = []
if debugger.debuggerState==DBGSTATE_BREAK:
stackUse=debugger.stack[:]
stackUse.reverse()
self.last_stack = []
for frame, lineno in stackUse:
self.last_stack.append( (frame, lineno) )
if frame is debugger.userbotframe: # Dont bother showing frames below our bottom frame.
break
for frame, lineno in self.last_stack:
ret.append( HierFrameItem( frame, debugger ) )
## elif debugger.debuggerState==DBGSTATE_NOT_DEBUGGING:
## ret.append(NoStackAvailableItem('<nothing is being debugged>'))
## else:
## ret.append(NoStackAvailableItem('<stack not available while running>'))
return ret
def GetText(self):
return 'root item'
def IsExpandable(self):
return 1
class HierListDebugger(hierlist.HierListWithItems):
""" Hier List of stack frames, breakpoints, whatever """
def __init__(self):
hierlist.HierListWithItems.__init__(self, None, win32ui.IDB_DEBUGGER_HIER, None, win32api.RGB(255,0,0))
def Setup(self, debugger):
root = HierStackRoot(debugger)
self.AcceptRoot(root)
# def Refresh(self):
# self.Setup()
class DebuggerWindow(window.Wnd):
def __init__(self, ob):
window.Wnd.__init__(self, ob)
self.debugger = None
def Init(self, debugger):
self.debugger = debugger
def GetDefRect(self):
defRect = app.LoadWindowSize("Debugger Windows\\" + self.title)
if defRect[2]-defRect[0]==0:
defRect = 0, 0, 150, 150
return defRect
def OnDestroy(self, msg):
newSize = self.GetWindowPlacement()[4]
pywin.framework.app.SaveWindowSize("Debugger Windows\\" + self.title, newSize)
return window.Wnd.OnDestroy(self, msg)
def OnKeyDown(self, msg):
key = msg[2]
if key in [13, 27, 32]: return 1
if key in [46,8]: # delete/BS key
self.DeleteSelected()
return 0
view = scriptutils.GetActiveView()
try:
firer = view.bindings.fire_key_event
except AttributeError:
firer = None
if firer is not None:
return firer(msg)
else:
return 1
def DeleteSelected(self):
win32api.MessageBeep()
def EditSelected(self):
win32api.MessageBeep()
class DebuggerStackWindow(DebuggerWindow):
title = "Stack"
def __init__(self):
DebuggerWindow.__init__(self, win32ui.CreateTreeCtrl())
self.list = HierListDebugger()
self.listOK = 0
def SaveState(self):
self.list.DeleteAllItems()
self.listOK = 0
win32ui.WriteProfileVal("Debugger Windows\\" + self.title, "Visible", self.IsWindowVisible())
def CreateWindow(self, parent):
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS
self._obj_.CreateWindow(style, self.GetDefRect(), parent, win32ui.IDC_LIST1)
self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN)
self.HookMessage(self.OnKeyDown, win32con.WM_SYSKEYDOWN)
self.list.HierInit (parent, self)
self.listOK = 0 # delayed setup
#self.list.Setup()
def RespondDebuggerState(self, state):
assert self.debugger is not None, "Init not called"
if not self.listOK:
self.listOK = 1
self.list.Setup(self.debugger)
else:
self.list.Refresh()
def RespondDebuggerData(self):
try:
handle = self.GetChildItem(0)
except win32ui.error:
return # No items
while 1:
item = self.list.ItemFromHandle(handle)
col = self.list.GetBitmapColumn(item)
selCol = self.list.GetSelectedBitmapColumn(item)
if selCol is None: selCol = col
if self.list.GetItemImage(handle)!= (col, selCol):
self.list.SetItemImage(handle, col, selCol)
try:
handle = self.GetNextSiblingItem(handle)
except win32ui.error:
break
class DebuggerListViewWindow(DebuggerWindow):
def __init__(self):
DebuggerWindow.__init__(self, win32ui.CreateListCtrl())
def CreateWindow(self, parent):
list = self
style = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | commctrl.LVS_EDITLABELS | commctrl.LVS_REPORT
self._obj_.CreateWindow(style, self.GetDefRect(), parent, win32ui.IDC_LIST1)
self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN)
self.HookMessage(self.OnKeyDown, win32con.WM_SYSKEYDOWN)
list = self
title, width = self.columns[0]
itemDetails = (commctrl.LVCFMT_LEFT, width, title, 0)
list.InsertColumn(0, itemDetails)
col = 1
for title, width in self.columns[1:]:
col = col + 1
itemDetails = (commctrl.LVCFMT_LEFT, width, title, 0)
list.InsertColumn(col, itemDetails)
parent.HookNotify(self.OnListEndLabelEdit, LVN_ENDLABELEDIT)
parent.HookNotify(self.OnItemRightClick, commctrl.NM_RCLICK)
parent.HookNotify(self.OnItemDoubleClick, commctrl.NM_DBLCLK)
def RespondDebuggerData(self):
pass
def RespondDebuggerState(self, state):
pass
def EditSelected(self):
try:
sel = self.GetNextItem(-1, commctrl.LVNI_SELECTED)
except win32ui.error:
return
self.EditLabel(sel)
def OnKeyDown(self, msg):
key = msg[2]
# If someone starts typing, they probably are trying to edit the text!
if chr(key) in string.ascii_uppercase:
self.EditSelected()
return 0
return DebuggerWindow.OnKeyDown(self, msg)
def OnItemDoubleClick(self, notify_data, extra):
self.EditSelected()
def OnItemRightClick(self, notify_data, extra):
# First select the item we right-clicked on.
pt = self.ScreenToClient(win32api.GetCursorPos())
flags, hItem, subitem = self.HitTest(pt)
if hItem==-1 or commctrl.TVHT_ONITEM & flags==0:
return None
self.SetItemState(hItem, commctrl.LVIS_SELECTED, commctrl.LVIS_SELECTED)
menu = win32ui.CreatePopupMenu()
menu.AppendMenu(win32con.MF_STRING|win32con.MF_ENABLED,1000, "Edit item")
menu.AppendMenu(win32con.MF_STRING|win32con.MF_ENABLED,1001, "Delete item")
dockbar = self.GetParent()
if dockbar.IsFloating():
hook_parent = win32ui.GetMainFrame()
else:
hook_parent = self.GetParentFrame()
hook_parent.HookCommand(self.OnEditItem, 1000)
hook_parent.HookCommand(self.OnDeleteItem, 1001)
menu.TrackPopupMenu(win32api.GetCursorPos()) # track at mouse position.
return None
def OnDeleteItem(self,command, code):
self.DeleteSelected()
def OnEditItem(self, command, code):
self.EditSelected()
class DebuggerBreakpointsWindow(DebuggerListViewWindow):
title = "Breakpoints"
columns = [ ("Condition", 70), ("Location", 1024)]
def SaveState(self):
items = []
for i in range(self.GetItemCount()):
items.append(self.GetItemText(i,0))
items.append(self.GetItemText(i,1))
win32ui.WriteProfileVal("Debugger Windows\\" + self.title, "BreakpointList", "\t".join(items))
win32ui.WriteProfileVal("Debugger Windows\\" + self.title, "Visible", self.IsWindowVisible())
return 1
def OnListEndLabelEdit(self, std, extra):
item = extra[0]
text = item[4]
if text is None: return
item_id = self.GetItem(item[0])[6]
from bdb import Breakpoint
for bplist in Breakpoint.bplist.values():
for bp in bplist:
if id(bp)==item_id:
if text.strip().lower()=="none":
text = None
bp.cond = text
break
self.RespondDebuggerData()
def DeleteSelected(self):
try:
num = self.GetNextItem(-1, commctrl.LVNI_SELECTED)
item_id = self.GetItem(num)[6]
from bdb import Breakpoint
for bplist in list(Breakpoint.bplist.values()):
for bp in bplist:
if id(bp)==item_id:
self.debugger.clear_break(bp.file, bp.line)
break
except win32ui.error:
win32api.MessageBeep()
self.RespondDebuggerData()
def RespondDebuggerData(self):
l = self
l.DeleteAllItems()
index = -1
from bdb import Breakpoint
for bplist in Breakpoint.bplist.values():
for bp in bplist:
baseName = os.path.split(bp.file)[1]
cond = bp.cond
item = index+1, 0, 0, 0, str(cond), 0, id(bp)
index = l.InsertItem(item)
l.SetItemText(index, 1, "%s: %s" % (baseName, bp.line))
class DebuggerWatchWindow(DebuggerListViewWindow):
title = "Watch"
columns = [ ("Expression", 70), ("Value", 1024)]
def CreateWindow(self, parent):
DebuggerListViewWindow.CreateWindow(self, parent)
items = win32ui.GetProfileVal("Debugger Windows\\" + self.title, "Items", "").split("\t")
index = -1
for item in items:
if item:
index = self.InsertItem(index+1, item)
self.InsertItem(index+1, "<New Item>")
def SaveState(self):
items = []
for i in range(self.GetItemCount()-1):
items.append(self.GetItemText(i,0))
win32ui.WriteProfileVal("Debugger Windows\\" + self.title, "Items", "\t".join(items))
win32ui.WriteProfileVal("Debugger Windows\\" + self.title, "Visible", self.IsWindowVisible())
return 1
def OnListEndLabelEdit(self, std, extra):
item = extra[0]
itemno = item[0]
text = item[4]
if text is None: return
self.SetItemText(itemno, 0, text)
if itemno == self.GetItemCount()-1:
self.InsertItem(itemno+1, "<New Item>")
self.RespondDebuggerState(self.debugger.debuggerState)
def DeleteSelected(self):
try:
num = self.GetNextItem(-1, commctrl.LVNI_SELECTED)
if num < self.GetItemCount()-1: # We cant delete the last
self.DeleteItem(num)
except win32ui.error:
win32api.MessageBeep()
def RespondDebuggerState(self, state):
globs = locs = None
if state==DBGSTATE_BREAK:
if self.debugger.curframe:
globs = self.debugger.curframe.f_globals
locs = self.debugger.curframe.f_locals
elif state==DBGSTATE_NOT_DEBUGGING:
import __main__
globs = locs = __main__.__dict__
for i in range(self.GetItemCount()-1):
text = self.GetItemText(i, 0)
if globs is None:
val = ""
else:
try:
val = repr( eval( text, globs, locs) )
except SyntaxError:
val = "Syntax Error"
except:
t, v, tb = sys.exc_info()
val = traceback.format_exception_only(t, v)[0].strip()
tb = None # prevent a cycle.
self.SetItemText(i, 1, val)
def CreateDebuggerDialog(parent, klass):
control = klass()
control.CreateWindow(parent)
return control
DebuggerDialogInfos = (
(0xe810, DebuggerStackWindow, None),
(0xe811, DebuggerBreakpointsWindow, (10, 10)),
(0xe812, DebuggerWatchWindow, None),
)
# Prepare all the "control bars" for this package.
# If control bars are not all loaded when the toolbar-state functions are
# called, things go horribly wrong.
def PrepareControlBars(frame):
style = win32con.WS_CHILD | afxres.CBRS_SIZE_DYNAMIC | afxres.CBRS_TOP | afxres.CBRS_TOOLTIPS | afxres.CBRS_FLYBY
tbd = win32ui.CreateToolBar (frame, style, win32ui.ID_VIEW_TOOLBAR_DBG)
tbd.ModifyStyle(0, commctrl.TBSTYLE_FLAT)
tbd.LoadToolBar(win32ui.IDR_DEBUGGER)
tbd.EnableDocking(afxres.CBRS_ALIGN_ANY)
tbd.SetWindowText("Debugger")
frame.DockControlBar(tbd)
# and the other windows.
for id, klass, float in DebuggerDialogInfos:
try:
frame.GetControlBar(id)
exists=1
except win32ui.error:
exists=0
if exists: continue
bar = pywin.docking.DockingBar.DockingBar()
style=win32con.WS_CHILD | afxres.CBRS_LEFT # don't create visible.
bar.CreateWindow(frame, CreateDebuggerDialog, klass.title, id, style, childCreatorArgs=(klass,))
bar.SetBarStyle( bar.GetBarStyle()|afxres.CBRS_TOOLTIPS|afxres.CBRS_FLYBY|afxres.CBRS_SIZE_DYNAMIC)
bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
if float is None:
frame.DockControlBar(bar)
else:
frame.FloatControlBar(bar, float, afxres.CBRS_ALIGN_ANY)
## frame.ShowControlBar(bar, 0, 1)
SKIP_NONE=0
SKIP_STEP=1
SKIP_RUN=2
debugger_parent=pdb.Pdb
class Debugger(debugger_parent):
def __init__(self):
self.inited = 0
self.skipBotFrame = SKIP_NONE
self.userbotframe = None
self.frameShutdown = 0
self.pumping = 0
self.debuggerState = DBGSTATE_NOT_DEBUGGING # Assume so, anyway.
self.shownLineCurrent = None # The last filename I highlighted.
self.shownLineCallstack = None # The last filename I highlighted.
self.last_cmd_debugged = ""
self.abortClosed = 0
self.isInitialBreakpoint = 0
debugger_parent.__init__(self)
# See if any break-points have been set in the editor
for doc in editor.editorTemplate.GetDocumentList():
lineNo = -1
while 1:
lineNo = doc.MarkerGetNext(lineNo+1, MARKER_BREAKPOINT)
if lineNo <= 0: break
self.set_break(doc.GetPathName(), lineNo)
self.reset()
self.inForcedGUI = win32ui.GetApp().IsInproc()
self.options = LoadDebuggerOptions()
self.bAtException = self.bAtPostMortem = 0
def __del__(self):
self.close()
def close(self, frameShutdown = 0):
# abortClose indicates if we have total shutdown
# (ie, main window is dieing)
if self.pumping:
# Can stop pump here, as it only posts a message, and
# returns immediately.
if not self.StopDebuggerPump(): # User cancelled close.
return 0
# NOTE - from this point on the close can not be
# stopped - the WM_QUIT message is already in the queue.
self.frameShutdown = frameShutdown
if not self.inited: return 1
self.inited = 0
SetInteractiveContext(None, None)
frame = win32ui.GetMainFrame()
# Hide the debuger toolbars (as they wont normally form part of the main toolbar state.
for id, klass, float in DebuggerDialogInfos:
try:
tb = frame.GetControlBar(id)
if tb.dialog is not None: # We may never have actually been shown.
tb.dialog.SaveState()
frame.ShowControlBar(tb, 0, 1)
except win32ui.error:
pass
self._UnshowCurrentLine()
self.set_quit()
return 1
def StopDebuggerPump(self):
assert self.pumping, "Can't stop the debugger pump if Im not pumping!"
# After stopping a pump, I may never return.
if self.GUIAboutToFinishInteract():
self.pumping = 0
win32ui.StopDebuggerPump() # Posts a message, so we do return.
return 1
return 0
def get_option(self, option):
"""Public interface into debugger options
"""
try:
return self.options[option]
except KeyError:
raise error("Option %s is not a valid option" % option)
def prep_run(self, cmd):
pass
def done_run(self, cmd=None):
self.RespondDebuggerState(DBGSTATE_NOT_DEBUGGING)
self.close()
def canonic(self, fname):
return os.path.abspath(fname).lower()
def reset(self):
debugger_parent.reset(self)
self.userbotframe = None
self.UpdateAllLineStates()
self._UnshowCurrentLine()
def setup(self, f, t):
debugger_parent.setup(self, f, t)
self.bAtException = t is not None
def set_break(self, filename, lineno, temporary=0, cond = None):
filename = self.canonic(filename)
self.SetLineState(filename, lineno, LINESTATE_BREAKPOINT)
return debugger_parent.set_break(self, filename, lineno, temporary, cond)
def clear_break(self, filename, lineno):
filename = self.canonic(filename)
self.ResetLineState(filename, lineno, LINESTATE_BREAKPOINT)
return debugger_parent.clear_break(self, filename, lineno)
def cmdloop(self):
if self.frameShutdown: return # App in the process of closing - never break in!
self.GUIAboutToBreak()
def print_stack_entry(self, frame):
# We dont want a stack printed - our GUI is better :-)
pass
def user_return(self, frame, return_value):
# Same as parent, just no "print"
# This function is called when a return trap is set here
frame.f_locals['__return__'] = return_value
self.interaction(frame, None)
def user_call(self, frame, args):
# base class has an annoying 'print' that adds no value to us...
if self.stop_here(frame):
self.interaction(frame, None)
def user_exception(self, frame, exc_info):
# This function is called if an exception occurs,
# but only if we are to stop at or just below this level
(exc_type, exc_value, exc_traceback) = exc_info
if self.get_option(OPT_STOP_EXCEPTIONS):
frame.f_locals['__exception__'] = exc_type, exc_value
print("Unhandled exception while debugging...")
# on both py2k and py3k, we may be called with exc_value
# being the args to the exception, or it may already be
# instantiated (IOW, PyErr_Normalize() hasn't been
# called on the args). In py2k this is fine, but in
# py3k, traceback.print_exception fails. So on py3k
# we instantiate an exception instance to print.
if sys.version_info > (3,) and not isinstance(exc_value, BaseException):
# they are args - may be a single item or already a tuple
if not isinstance(exc_value, tuple):
exc_value = (exc_value,)
exc_value = exc_type(*exc_value)
traceback.print_exception(exc_type, exc_value, exc_traceback)
self.interaction(frame, exc_traceback)
def user_line(self, frame):
if frame.f_lineno==0: return
debugger_parent.user_line(self, frame)
def stop_here(self, frame):
if self.isInitialBreakpoint:
self.isInitialBreakpoint = 0
self.set_continue()
return 0
if frame is self.botframe and self.skipBotFrame == SKIP_RUN:
self.set_continue()
return 0
if frame is self.botframe and self.skipBotFrame == SKIP_STEP:
self.set_step()
return 0
return debugger_parent.stop_here(self, frame)
def run(self, cmd,globals=None, locals=None, start_stepping = 1):
if not isinstance(cmd, (str, types.CodeType)):
raise TypeError("Only strings can be run")
self.last_cmd_debugged = cmd
if start_stepping:
self.isInitialBreakpoint = 0
else:
self.isInitialBreakpoint = 1
try:
if globals is None:
import __main__
globals = __main__.__dict__
if locals is None:
locals = globals
self.reset()
self.prep_run(cmd)
sys.settrace(self.trace_dispatch)
if type(cmd) != types.CodeType:
cmd = cmd+'\n'
try:
try:
if start_stepping: self.skipBotFrame = SKIP_STEP
else: self.skipBotFrame = SKIP_RUN
exec(cmd, globals, locals)
except bdb.BdbQuit:
pass
finally:
self.skipBotFrame = SKIP_NONE
self.quitting = 1
sys.settrace(None)
finally:
self.done_run(cmd)
def runeval(self, expr, globals=None, locals=None):
self.prep_run(expr)
try:
debugger_parent.runeval(self, expr, globals, locals)
finally:
self.done_run(expr)
def runexec(self, what, globs=None, locs=None):
self.reset()
sys.settrace(self.trace_dispatch)
try:
try:
exec(what, globs, locs)
except bdb.BdbQuit:
pass
finally:
self.quitting = 1
sys.settrace(None)
def do_set_step(self):
if self.GUIAboutToRun():
self.set_step()
def do_set_next(self):
if self.GUIAboutToRun():
self.set_next(self.curframe)
def do_set_return(self):
if self.GUIAboutToRun():
self.set_return(self.curframe)
def do_set_continue(self):
if self.GUIAboutToRun():
self.set_continue()
def set_quit(self):
ok = 1
if self.pumping:
ok = self.StopDebuggerPump()
if ok:
debugger_parent.set_quit(self)
def _dump_frame_(self, frame,name=None):
if name is None: name = ""
if frame:
if frame.f_code and frame.f_code.co_filename:
fname = os.path.split(frame.f_code.co_filename)[1]
else:
fname = "??"
print(repr(name), fname, frame.f_lineno, frame)
else:
print(repr(name), "None")
def set_trace(self):
# Start debugging from _2_ levels up!
try:
1 + ''
except:
frame = sys.exc_info()[2].tb_frame.f_back.f_back
self.reset()
self.userbotframe = None
while frame:
# scriptutils.py creates a local variable with name
# '_debugger_stop_frame_', and we dont go past it
# (everything above this is Pythonwin framework code)
if "_debugger_stop_frame_" in frame.f_locals:
self.userbotframe = frame
break
frame.f_trace = self.trace_dispatch
self.botframe = frame
frame = frame.f_back
self.set_step()
sys.settrace(self.trace_dispatch)
def set_cur_frame(self, frame):
# Sets the "current" frame - ie, the frame with focus. This is the
# frame on which "step out" etc actions are taken.
# This may or may not be the top of the stack.
assert frame is not None, "You must pass a valid frame"
self.curframe = frame
for f, index in self.stack:
if f is frame:
self.curindex = index
break
else:
assert 0, "Can't find the frame in the stack."
SetInteractiveContext(frame.f_globals, frame.f_locals)
self.GUIRespondDebuggerData()
self.ShowCurrentLine()
def IsBreak(self):
return self.debuggerState == DBGSTATE_BREAK
def IsDebugging(self):
return self.debuggerState != DBGSTATE_NOT_DEBUGGING
def RespondDebuggerState(self, state):
if state == self.debuggerState: return
if state==DBGSTATE_NOT_DEBUGGING: # Debugger exists, but not doing anything
title = ""
elif state==DBGSTATE_RUNNING: # Code is running under the debugger.
title = " - running"
elif state==DBGSTATE_BREAK: # We are at a breakpoint or stepping or whatever.
if self.bAtException:
if self.bAtPostMortem:
title = " - post mortem exception"
else:
title = " - exception"
else:
title = " - break"
else:
raise error("Invalid debugger state passed!")
win32ui.GetMainFrame().SetWindowText(win32ui.LoadString(win32ui.IDR_MAINFRAME) + title)
if self.debuggerState == DBGSTATE_QUITTING and state != DBGSTATE_NOT_DEBUGGING:
print("Ignoring state change cos Im trying to stop!", state)
return
self.debuggerState = state
try:
frame = win32ui.GetMainFrame()
except win32ui.error:
frame = None
if frame is not None:
for id, klass, float in DebuggerDialogInfos:
cb = win32ui.GetMainFrame().GetControlBar(id).dialog
cb.RespondDebuggerState(state)
# Tell each open editor window about the state transition
for doc in editor.editorTemplate.GetDocumentList():
doc.OnDebuggerStateChange(state)
self.ShowCurrentLine()
#
# GUI debugger interface.
#
def GUICheckInit(self):
if self.inited: return
self.inited = 1
frame = win32ui.GetMainFrame()
# Ensure the debugger windows are attached to the debugger.
for id, klass, float in DebuggerDialogInfos:
w = frame.GetControlBar(id)
w.dialog.Init(self)
# Show toolbar if it was visible during last debug session
# This would be better done using a CDockState, but that class is not wrapped yet
if win32ui.GetProfileVal("Debugger Windows\\" + w.dialog.title, "Visible", 0):
frame.ShowControlBar(w, 1, 1)
# ALWAYS show debugging toolbar, regardless of saved state
tb = frame.GetControlBar(win32ui.ID_VIEW_TOOLBAR_DBG)
frame.ShowControlBar(tb, 1, 1)
self.GUIRespondDebuggerData()
# frame.RecalcLayout()
def GetDebuggerBar(self, barName):
frame = win32ui.GetMainFrame()
for id, klass, float in DebuggerDialogInfos:
if klass.title == barName:
return frame.GetControlBar(id)
assert 0, "Can't find a bar of that name!"
def GUIRespondDebuggerData(self):
if not self.inited: # GUI not inited - no toolbars etc.
return
for id, klass, float in DebuggerDialogInfos:
cb = win32ui.GetMainFrame().GetControlBar(id).dialog
cb.RespondDebuggerData()
def GUIAboutToRun(self):
if not self.StopDebuggerPump():
return 0
self._UnshowCurrentLine()
self.RespondDebuggerState(DBGSTATE_RUNNING)
SetInteractiveContext(None, None)
return 1
def GUIAboutToBreak(self):
"Called as the GUI debugger is about to get context, and take control of the running program."
self.GUICheckInit()
self.RespondDebuggerState(DBGSTATE_BREAK)
self.GUIAboutToInteract()
if self.pumping:
print("!!! Already pumping - outa here")
return
self.pumping = 1
win32ui.StartDebuggerPump() # NOTE - This will NOT return until the user is finished interacting
assert not self.pumping, "Should not be pumping once the pump has finished"
if self.frameShutdown: # User shut down app while debugging
win32ui.GetMainFrame().PostMessage(win32con.WM_CLOSE)
def GUIAboutToInteract(self):
"Called as the GUI is about to perform any interaction with the user"
frame = win32ui.GetMainFrame()
# Remember the enabled state of our main frame
# may be disabled primarily if a modal dialog is displayed.
# Only get at enabled via GetWindowLong.
self.bFrameEnabled = frame.IsWindowEnabled()
self.oldForeground = None
fw = win32ui.GetForegroundWindow()
if fw is not frame:
self.oldForeground = fw
# fw.EnableWindow(0) Leave enabled for now?
self.oldFrameEnableState = frame.IsWindowEnabled()
frame.EnableWindow(1)
if self.inForcedGUI and not frame.IsWindowVisible():
frame.ShowWindow(win32con.SW_SHOW)
frame.UpdateWindow()
if self.curframe:
SetInteractiveContext(self.curframe.f_globals, self.curframe.f_locals)
else:
SetInteractiveContext(None, None)
self.GUIRespondDebuggerData()
def GUIAboutToFinishInteract(self):
"""Called as the GUI is about to finish any interaction with the user
Returns non zero if we are allowed to stop interacting"""
if self.oldForeground is not None:
try:
win32ui.GetMainFrame().EnableWindow(self.oldFrameEnableState)
self.oldForeground.EnableWindow(1)
except win32ui.error:
# old window may be dead.
pass
# self.oldForeground.SetForegroundWindow() - fails??
if not self.inForcedGUI:
return 1 # Never a problem, and nothing else to do.
# If we are running a forced GUI, we may never get an opportunity
# to interact again. Therefore we perform a "SaveAll", to makesure that
# any documents are saved before leaving.
for template in win32ui.GetApp().GetDocTemplateList():
for doc in template.GetDocumentList():
if not doc.SaveModified():
return 0
# All documents saved - now hide the app and debugger.
if self.get_option(OPT_HIDE):
frame = win32ui.GetMainFrame()
frame.ShowWindow(win32con.SW_HIDE)
return 1
#
# Pythonwin interface - all stuff to do with showing source files,
# changing line states etc.
#
def ShowLineState(self, fileName, lineNo, lineState):
# Set the state of a line, open if not already
self.ShowLineNo(fileName, lineNo)
self.SetLineState(fileName, lineNo, lineState)
def SetLineState(self, fileName, lineNo, lineState):
# Set the state of a line if the document is open.
doc = editor.editorTemplate.FindOpenDocument(fileName)
if doc is not None:
marker = _LineStateToMarker(lineState)
if not doc.MarkerCheck(lineNo, marker):
doc.MarkerAdd(lineNo, marker)
def ResetLineState(self, fileName, lineNo, lineState):
# Set the state of a line if the document is open.
doc = editor.editorTemplate.FindOpenDocument(fileName)
if doc is not None:
marker = _LineStateToMarker(lineState)
doc.MarkerDelete(lineNo, marker)
def UpdateDocumentLineStates(self, doc):
# Show all lines in their special status color. If the doc is open
# all line states are reset.
doc.MarkerDeleteAll( MARKER_BREAKPOINT )
doc.MarkerDeleteAll( MARKER_CURRENT )
fname = self.canonic(doc.GetPathName())
# Now loop over all break-points
for line in self.breaks.get(fname, []):
doc.MarkerAdd(line, MARKER_BREAKPOINT)
# And the current line if in this document.
if self.shownLineCurrent and fname == self.shownLineCurrent[0]:
lineNo = self.shownLineCurrent[1]
if not doc.MarkerCheck(lineNo, MARKER_CURRENT):
doc.MarkerAdd(lineNo, MARKER_CURRENT)
# if self.shownLineCallstack and fname == self.shownLineCallstack[0]:
# doc.MarkerAdd(self.shownLineCallstack[1], MARKER_CURRENT)
def UpdateAllLineStates(self):
for doc in editor.editorTemplate.GetDocumentList():
self.UpdateDocumentLineStates(doc)
def ShowCurrentLine(self):
# Show the current line. Only ever 1 current line - undoes last current
# The "Current Line" is self.curframe.
# The "Callstack Line" is the top of the stack.
# If current == callstack, only show as current.
self._UnshowCurrentLine() # un-highlight the old one.
if self.curframe:
fileName = self.canonic(self.curframe.f_code.co_filename)
lineNo = self.curframe.f_lineno
self.shownLineCurrent = fileName, lineNo
self.ShowLineState(fileName, lineNo, LINESTATE_CURRENT)
def _UnshowCurrentLine(self):
"Unshow the current line, and forget it"
if self.shownLineCurrent is not None:
fname, lineno = self.shownLineCurrent
self.ResetLineState(fname, lineno, LINESTATE_CURRENT)
self.shownLineCurrent = None
def ShowLineNo( self, filename, lineno ):
wasOpen = editor.editorTemplate.FindOpenDocument(filename) is not None
if os.path.isfile(filename) and scriptutils.JumpToDocument(filename, lineno):
if not wasOpen:
doc = editor.editorTemplate.FindOpenDocument(filename)
if doc is not None:
self.UpdateDocumentLineStates(doc)
return 1
return 0
return 1
else:
# Can't find the source file - linecache may have it?
import linecache
line = linecache.getline(filename, lineno)
print("%s(%d): %s" % (os.path.basename(filename), lineno, line[:-1].expandtabs(4)))
return 0

View File

@ -0,0 +1,48 @@
# NOTE NOTE - This module is designed to fail!
#
# The ONLY purpose for this script is testing/demoing the
# Pythonwin debugger package.
# It does nothing useful, and it even doesnt do that!
import pywin.debugger, sys, time
import traceback
def a():
a=1
try:
b()
except:
# Break into the debugger with the exception information.
pywin.debugger.post_mortem(sys.exc_info()[2])
a=1
a=2
a=3
a=4
pass
def b():
b=1
pywin.debugger.set_trace()
# After importing or running this module, you are likely to be
# sitting at the next line. This is because we explicitely
# broke into the debugger using the "set_trace() function
# "pywin.debugger.brk()" is a shorter alias for this.
c()
pass
def c():
c=1
d()
def d():
d=1
e(d)
raise ValueError("Hi")
def e(arg):
e=1
time.sleep(1)
return e
a()

View File

@ -0,0 +1,215 @@
# The default keyboard etc configuration file for Pythonwin.
#
# The format of this file is very similar to a Windows INI file.
# Sections are identified with [Section] lines, but comments
# use the standatd Python # character. Depending on the section,
# lines may not be in the standard "key=value" format.
# NOTE: You should not need to modify this file.
# Simply create a new .CFG file, and add an entry:
# [General]
# BasedOn = Default
#
# and add your customisations. Then select your new configuration
# from the Pythonwin View/Options/Editor dialog.
# This way you get to add your own customisations,
# but still take advantage of changes to the default
# configuration in new releases.
# See IDLE.cfg for an example extension configuration.
#
##########################################################################
[IDLE Extensions]
# The list of IDLE extensions to load. The extensions
# AutoIndent, AutoFormat and possibly others are
# "built-in", so do not need specifying.
FormatParagraph
CallTips
[Keys]
# The list of _default_ key definitions.
# See [Keys:Interactive] and [Keys:Editor] below for further defs.
#Events of the format <<event-name>>
# are events defined in IDLE extensions.
Alt+Q = <<format-paragraph>>
Ctrl+W = ViewWhitespace
Ctrl+Shift+8 = ViewWhitespace # The MSVC default key def.
Ctrl+Shift+F = ViewFixedFont
# Auto-complete, call-tips, etc.
Alt+/ = <<expand-word>>
Ctrl+Space = <<expand-word>>
( = <<paren-open>>
) = <<paren-close>>
Up = <<check-calltip-cancel>>
Down = <<check-calltip-cancel>>
Left = <<check-calltip-cancel>>
Right = <<check-calltip-cancel>>
. = KeyDot
# Debugger - These are the MSVC default keys, for want of a better choice.
F9 = DbgBreakpointToggle
F5 = DbgGo
Shift+F5 = DbgClose
F11 = DbgStep
F10 = DbgStepOver
Shift+F11 = DbgStepOut
Ctrl+F3 = AutoFindNext
[Keys:Editor]
# Key bindings specific to the editor
F2 = GotoNextBookmark
Ctrl+F2 = ToggleBookmark
Ctrl+G = GotoLine
Alt+I = ShowInteractiveWindow
Alt-B = AddBanner # A sample Event defined in this file.
# Block operations
Alt+3 = <<comment-region>>
Shift+Alt+3 = <<uncomment-region>>
Alt+4 = <<uncomment-region>> # IDLE default.
Alt+5 = <<tabify-region>>
Alt+6 = <<untabify-region>>
# Tabs and other indent features
Back = <<smart-backspace>>
Ctrl+T = <<toggle-tabs>>
Alt+U = <<change-indentwidth>>
Enter = EnterKey
Tab = TabKey
Shift-Tab = <<dedent-region>>
# Folding
Add = FoldExpand
Alt+Add = FoldExpandAll
Shift+Add = FoldExpandSecondLevel
Subtract = FoldCollapse
Alt+Subtract = FoldCollapseAll
Shift+Subtract = FoldCollapseSecondLevel
Multiply = FoldTopLevel
[Keys:Interactive]
# Key bindings specific to the interactive window.
# History for the interactive window
Ctrl+Up = <<history-previous>>
Ctrl+Down = <<history-next>>
Enter = ProcessEnter
Ctrl+Enter = ProcessEnter
Shift+Enter = ProcessEnter
Esc = ProcessEsc
Alt+I = WindowBack # Toggle back to previous window.
Home = InteractiveHome # A sample Event defined in this file.
Shift+Home = InteractiveHomeExtend # A sample Event defined in this file.
# When docked, the Ctrl+Tab and Shift+Ctrl+Tab keys dont work as expected.
Ctrl+Tab = MDINext
Ctrl+Shift+Tab = MDIPrev
[Extensions]
# Python event handlers specific to this config file.
# All functions not starting with an "_" are assumed
# to be events, and take 2 params:
# * editor_window is the same object passed to IDLE
# extensions. editor_window.text is a text widget
# that conforms to the Tk text widget interface.
# * event is the event being fired. Will always be None
# in the current implementation.
# Simply by defining these functions, they are available as
# events.
# Note that we bind keystrokes to these events in the various
# [Keys] sections.
# Add a simple file/class/function simple banner
def AddBanner(editor_window, event):
text = editor_window.text
big_line = "#" * 70
banner = "%s\n## \n## \n## \n%s\n" % (big_line, big_line)
# Insert at the start of the current line.
pos = text.index("insert linestart")
text.undo_block_start() # Allow action to be undone as a single unit.
text.insert(pos, banner)
text.undo_block_stop()
# Now set the insert point to the middle of the banner.
line, col = [int(s) for s in pos.split(".")]
text.mark_set("insert", "%d.1 lineend" % (line+2, ) )
# Here is a sample event bound to the "Home" key in the
# interactive window
def InteractiveHome(editor_window, event):
return _DoInteractiveHome(editor_window.text, 0)
def InteractiveHomeExtend(editor_window, event):
return _DoInteractiveHome(editor_window.text, 1)
def _DoInteractiveHome(text, extend):
import sys
# If Scintilla has an autocomplete window open, then let Scintilla handle it.
if text.edit.SCIAutoCActive():
return 1
of_interest = "insert linestart + %d c" % len(sys.ps1)
if not text.compare("insert", "==", of_interest) and \
text.get("insert linestart", of_interest) in [sys.ps1, sys.ps2]: # Not sys.ps? line
end = of_interest
else:
end = "insert linestart"
if extend: start = "insert"
else: start = end
text.tag_add("sel", start, end)
# From Niki Spahie
def AutoFindNext(editor_window, event):
"find selected text or word under cursor"
from pywin.scintilla import find
from pywin.scintilla import scintillacon
try:
sci = editor_window.edit
word = sci.GetSelText()
if word:
find.lastSearch.findText = word
find.lastSearch.sel = sci.GetSel()
else:
pos = sci.SendScintilla( scintillacon.SCI_GETCURRENTPOS )
start = sci.SendScintilla( scintillacon.SCI_WORDSTARTPOSITION, pos, 1 )
end = sci.SendScintilla( scintillacon.SCI_WORDENDPOSITION, pos, 1 )
word = sci.GetTextRange( start, end )
if word:
find.lastSearch.findText = word
find.lastSearch.sel = (start,end)
except Exception:
import traceback
traceback.print_exc()
find.FindNext()
# A couple of generic events.
def Beep(editor_window, event):
editor_window.text.beep()
def DoNothing(editor_window, event):
pass
def ContinueEvent(editor_window, event):
# Almost an "unbind" - allows Pythonwin/MFC to handle the keystroke
return 1

View File

@ -0,0 +1,116 @@
# The property page to define generic IDE options for Pythonwin
from pywin.mfc import dialog
from pywin.framework import interact
import win32ui
import win32con
buttonControlMap = {
win32ui.IDC_BUTTON1: win32ui.IDC_EDIT1,
win32ui.IDC_BUTTON2: win32ui.IDC_EDIT2,
win32ui.IDC_BUTTON3: win32ui.IDC_EDIT3,
}
class OptionsPropPage(dialog.PropertyPage):
def __init__(self):
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_IDE)
self.AddDDX(win32ui.IDC_CHECK1, "bShowAtStartup")
self.AddDDX(win32ui.IDC_CHECK2, "bDocking")
self.AddDDX(win32ui.IDC_EDIT4, 'MRUSize', "i")
def OnInitDialog(self):
edit = self.GetDlgItem(win32ui.IDC_EDIT1)
format = eval(win32ui.GetProfileVal(interact.sectionProfile, interact.STYLE_INTERACTIVE_PROMPT, str(interact.formatInput)))
edit.SetDefaultCharFormat(format)
edit.SetWindowText("Input Text")
edit = self.GetDlgItem(win32ui.IDC_EDIT2)
format = eval(win32ui.GetProfileVal(interact.sectionProfile, interact.STYLE_INTERACTIVE_OUTPUT, str(interact.formatOutput)))
edit.SetDefaultCharFormat(format)
edit.SetWindowText("Output Text")
edit = self.GetDlgItem(win32ui.IDC_EDIT3)
format = eval(win32ui.GetProfileVal(interact.sectionProfile, interact.STYLE_INTERACTIVE_ERROR, str(interact.formatOutputError)))
edit.SetDefaultCharFormat(format)
edit.SetWindowText("Error Text")
self['bShowAtStartup'] = interact.LoadPreference("Show at startup", 1)
self['bDocking'] = interact.LoadPreference("Docking", 0)
self['MRUSize'] = win32ui.GetProfileVal("Settings","Recent File List Size", 10)
# Hook the button clicks.
self.HookCommand(self.HandleCharFormatChange, win32ui.IDC_BUTTON1)
self.HookCommand(self.HandleCharFormatChange, win32ui.IDC_BUTTON2)
self.HookCommand(self.HandleCharFormatChange, win32ui.IDC_BUTTON3)
# Ensure the spin control remains in range.
spinner = self.GetDlgItem(win32ui.IDC_SPIN1)
spinner.SetRange(1, 16)
return dialog.PropertyPage.OnInitDialog(self)
# Called to save away the new format tuple for the specified item.
def HandleCharFormatChange(self, id, code):
if code == win32con.BN_CLICKED:
editId = buttonControlMap.get(id)
assert editId is not None, "Format button has no associated edit control"
editControl = self.GetDlgItem(editId)
existingFormat = editControl.GetDefaultCharFormat()
flags = win32con.CF_SCREENFONTS
d=win32ui.CreateFontDialog(existingFormat, flags, None, self)
if d.DoModal()==win32con.IDOK:
cf = d.GetCharFormat()
editControl.SetDefaultCharFormat(cf)
self.SetModified(1)
return 0 # We handled this fully!
def OnOK(self):
# Handle the edit controls - get all the fonts, put them back into interact, then
# get interact to save its stuff!
controlAttrs = [
(win32ui.IDC_EDIT1, interact.STYLE_INTERACTIVE_PROMPT),
(win32ui.IDC_EDIT2, interact.STYLE_INTERACTIVE_OUTPUT),
(win32ui.IDC_EDIT3, interact.STYLE_INTERACTIVE_ERROR)]
for id, key in controlAttrs:
control = self.GetDlgItem(id)
fmt = control.GetDefaultCharFormat()
win32ui.WriteProfileVal(interact.sectionProfile, key, str(fmt))
# Save the other interactive window options.
interact.SavePreference("Show at startup", self['bShowAtStartup'])
interact.SavePreference("Docking", self['bDocking'])
# And the other options.
win32ui.WriteProfileVal("Settings","Recent File List Size", self['MRUSize'])
return 1
def ChangeFormat(self, fmtAttribute, fmt):
dlg = win32ui.CreateFontDialog(fmt)
if dlg.DoModal() != win32con.IDOK: return None
return dlg.GetCharFormat()
def OnFormatTitle(self, command, code):
fmt = self.GetFormat(interact.formatTitle)
if fmt:
formatTitle = fmt
SaveFontPreferences()
def OnFormatInput(self, command, code):
global formatInput
fmt = self.GetFormat(formatInput)
if fmt:
formatInput = fmt
SaveFontPreferences()
def OnFormatOutput(self, command, code):
global formatOutput
fmt = self.GetFormat(formatOutput)
if fmt:
formatOutput = fmt
SaveFontPreferences()
def OnFormatError(self, command, code):
global formatOutputError
fmt = self.GetFormat(formatOutputError)
if fmt:
formatOutputError = fmt
SaveFontPreferences()

View File

@ -0,0 +1,122 @@
from pywin.mfc import dialog
import win32ui, win32con, commctrl, win32api
class ListDialog (dialog.Dialog):
def __init__ (self, title, list):
dialog.Dialog.__init__ (self, self._maketemplate(title))
self.HookMessage (self.on_size, win32con.WM_SIZE)
self.HookNotify(self.OnListItemChange, commctrl.LVN_ITEMCHANGED)
self.HookCommand(self.OnListClick, win32ui.IDC_LIST1)
self.items = list
def _maketemplate(self, title):
style = win32con.WS_DLGFRAME | win32con.WS_SYSMENU | win32con.WS_VISIBLE
ls = (
win32con.WS_CHILD |
win32con.WS_VISIBLE |
commctrl.LVS_ALIGNLEFT |
commctrl.LVS_REPORT
)
bs = (
win32con.WS_CHILD |
win32con.WS_VISIBLE
)
return [ [title, (0, 0, 200, 200), style, None, (8, "MS Sans Serif")],
["SysListView32", None, win32ui.IDC_LIST1, (0, 0, 200, 200), ls],
[128, "OK", win32con.IDOK, (10, 0, 50, 14), bs | win32con.BS_DEFPUSHBUTTON],
[128, "Cancel",win32con.IDCANCEL,(0, 0, 50, 14), bs],
]
def FillList(self):
size = self.GetWindowRect()
width = size[2] - size[0] - (10)
itemDetails = (commctrl.LVCFMT_LEFT, width, "Item", 0)
self.itemsControl.InsertColumn(0, itemDetails)
index = 0
for item in self.items:
index = self.itemsControl.InsertItem(index+1, str(item), 0)
def OnListClick(self, id, code):
if code==commctrl.NM_DBLCLK:
self.EndDialog(win32con.IDOK)
return 1
def OnListItemChange(self,std, extra):
(hwndFrom, idFrom, code), (itemNotify, sub, newState, oldState, change, point, lparam) = std, extra
oldSel = (oldState & commctrl.LVIS_SELECTED)!=0
newSel = (newState & commctrl.LVIS_SELECTED)!=0
if oldSel != newSel:
try:
self.selecteditem = itemNotify
self.butOK.EnableWindow(1)
except win32ui.error:
self.selecteditem = None
def OnInitDialog (self):
rc = dialog.Dialog.OnInitDialog (self)
self.itemsControl = self.GetDlgItem(win32ui.IDC_LIST1)
self.butOK = self.GetDlgItem(win32con.IDOK)
self.butCancel = self.GetDlgItem(win32con.IDCANCEL)
self.FillList()
size = self.GetWindowRect()
self.LayoutControls(size[2]-size[0], size[3]-size[1])
self.butOK.EnableWindow(0) # wait for first selection
return rc
def LayoutControls(self, w, h):
self.itemsControl.MoveWindow((0,0,w,h-30))
self.butCancel.MoveWindow((10, h-24, 60, h-4))
self.butOK.MoveWindow((w-60, h-24, w-10, h-4))
def on_size (self, params):
lparam = params[3]
w = win32api.LOWORD(lparam)
h = win32api.HIWORD(lparam)
self.LayoutControls(w, h)
class ListsDialog(ListDialog):
def __init__(self, title, list, colHeadings = ['Item']):
ListDialog.__init__(self, title, list)
self.colHeadings = colHeadings
def FillList(self):
index = 0
size = self.GetWindowRect()
width = size[2] - size[0] - (10) - win32api.GetSystemMetrics(win32con.SM_CXVSCROLL)
numCols = len(self.colHeadings)
for col in self.colHeadings:
itemDetails = (commctrl.LVCFMT_LEFT, width/numCols, col, 0)
self.itemsControl.InsertColumn(index, itemDetails)
index = index + 1
index = 0
for items in self.items:
index = self.itemsControl.InsertItem(index+1, str(items[0]), 0)
for itemno in range(1,numCols):
item = items[itemno]
self.itemsControl.SetItemText(index, itemno, str(item))
def SelectFromList (title, lst):
dlg = ListDialog(title, lst)
if dlg.DoModal()==win32con.IDOK:
return dlg.selecteditem
else:
return None
def SelectFromLists (title, lists, headings):
dlg = ListsDialog(title, lists, headings)
if dlg.DoModal()==win32con.IDOK:
return dlg.selecteditem
else:
return None
def test():
# print SelectFromList('Single list', [1,2,3])
print(SelectFromLists('Multi-List', [ ('1',1, 'a'), ('2',2, 'b'), ('3',3, 'c' )], ['Col 1', 'Col 2']))
if __name__=='__main__':
test()

View File

@ -0,0 +1,121 @@
'''login -- PythonWin user ID and password dialog box
(Adapted from originally distributed with Mark Hammond's PythonWin -
this now replaces it!)
login.GetLogin() displays a modal "OK/Cancel" dialog box with input
fields for a user ID and password. The password field input is masked
with *'s. GetLogin takes two optional parameters, a window title, and a
default user ID. If these parameters are omitted, the title defaults to
"Login", and the user ID is left blank. GetLogin returns a (userid, password)
tuple. GetLogin can be called from scripts running on the console - i.e. you
don't need to write a full-blown GUI app to use it.
login.GetPassword() is similar, except there is no username field.
Example:
import pywin.dialogs.login
title = "FTP Login"
def_user = "fred"
userid, password = pywin.dialogs.login.GetLogin(title, def_user)
Jim Eggleston, 28 August 1996
Merged with dlgpass and moved to pywin.dialogs by Mark Hammond Jan 1998.
'''
import win32ui
import win32api
import win32con
from pywin.mfc import dialog
def MakeLoginDlgTemplate(title):
style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
# Window frame and title
dlg = [ [title, (0, 0, 184, 40), style, None, (8, "MS Sans Serif")], ]
# ID label and text box
dlg.append([130, "User ID:", -1, (7, 9, 69, 9), cs | win32con.SS_LEFT])
s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER
dlg.append(['EDIT', None, win32ui.IDC_EDIT1, (50, 7, 60, 12), s])
# Password label and text box
dlg.append([130, "Password:", -1, (7, 22, 69, 9), cs | win32con.SS_LEFT])
s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER
dlg.append(['EDIT', None, win32ui.IDC_EDIT2, (50, 20, 60, 12), s | win32con.ES_PASSWORD])
# OK/Cancel Buttons
s = cs | win32con.WS_TABSTOP
dlg.append([128, "OK", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON])
s = win32con.BS_PUSHBUTTON | s
dlg.append([128, "Cancel", win32con.IDCANCEL, (124, 20, 50, 14), s])
return dlg
def MakePasswordDlgTemplate(title):
style = win32con.DS_MODALFRAME | win32con.WS_POPUP | win32con.WS_VISIBLE | win32con.WS_CAPTION | win32con.WS_SYSMENU | win32con.DS_SETFONT
cs = win32con.WS_CHILD | win32con.WS_VISIBLE
# Window frame and title
dlg = [ [title, (0, 0, 177, 45), style, None, (8, "MS Sans Serif")], ]
# Password label and text box
dlg.append([130, "Password:", -1, (7, 7, 69, 9), cs | win32con.SS_LEFT])
s = cs | win32con.WS_TABSTOP | win32con.WS_BORDER
dlg.append(['EDIT', None, win32ui.IDC_EDIT1, (50, 7, 60, 12), s | win32con.ES_PASSWORD])
# OK/Cancel Buttons
s = cs | win32con.WS_TABSTOP | win32con.BS_PUSHBUTTON
dlg.append([128, "OK", win32con.IDOK, (124, 5, 50, 14), s | win32con.BS_DEFPUSHBUTTON])
dlg.append([128, "Cancel", win32con.IDCANCEL, (124, 22, 50, 14), s])
return dlg
class LoginDlg(dialog.Dialog):
Cancel = 0
def __init__(self, title):
dialog.Dialog.__init__(self, MakeLoginDlgTemplate(title) )
self.AddDDX(win32ui.IDC_EDIT1,'userid')
self.AddDDX(win32ui.IDC_EDIT2,'password')
def GetLogin(title='Login', userid='', password=''):
d = LoginDlg(title)
d['userid'] = userid
d['password'] = password
if d.DoModal() != win32con.IDOK:
return (None, None)
else:
return (d['userid'], d['password'])
class PasswordDlg(dialog.Dialog):
def __init__(self, title):
dialog.Dialog.__init__(self, MakePasswordDlgTemplate(title) )
self.AddDDX(win32ui.IDC_EDIT1,'password')
def GetPassword(title='Password', password=''):
d = PasswordDlg(title)
d['password'] = password
if d.DoModal()!=win32con.IDOK:
return None
return d['password']
if __name__ == "__main__":
import sys
title = 'Login'
def_user = ''
if len(sys.argv) > 1:
title = sys.argv[1]
if len(sys.argv) > 2:
def_userid = sys.argv[2]
userid, password = GetLogin(title, def_user)
if userid == password == None:
print("User pressed Cancel")
else:
print("User ID: ", userid)
print("Password:", password)
newpassword = GetPassword("Reenter just for fun", password)
if newpassword is None:
print("User cancelled")
else:
what = ""
if newpassword != password:
what = "not "
print("The passwords did %smatch" % (what))

View File

@ -0,0 +1,227 @@
# No cancel button.
from pywin.mfc import dialog
from pywin.mfc.thread import WinThread
import threading
import win32ui
import win32con
import win32api
import time
def MakeProgressDlgTemplate(caption, staticText = ""):
style = (win32con.DS_MODALFRAME |
win32con.WS_POPUP |
win32con.WS_VISIBLE |
win32con.WS_CAPTION |
win32con.WS_SYSMENU |
win32con.DS_SETFONT)
cs = (win32con.WS_CHILD |
win32con.WS_VISIBLE)
w = 215
h = 36 # With button
h = 40
dlg = [[caption,
(0, 0, w, h),
style,
None,
(8, "MS Sans Serif")],
]
s = win32con.WS_TABSTOP | cs
dlg.append([130, staticText, 1000, (7, 7, w-7, h-32), cs | win32con.SS_LEFT])
# dlg.append([128,
# "Cancel",
# win32con.IDCANCEL,
# (w - 60, h - 18, 50, 14), s | win32con.BS_PUSHBUTTON])
return dlg
class CStatusProgressDialog(dialog.Dialog):
def __init__(self, title, msg = "", maxticks = 100, tickincr = 1):
self.initMsg = msg
templ = MakeProgressDlgTemplate(title, msg)
dialog.Dialog.__init__(self, templ)
self.maxticks = maxticks
self.tickincr = tickincr
self.pbar = None
def OnInitDialog(self):
rc = dialog.Dialog.OnInitDialog(self)
self.static = self.GetDlgItem(1000)
self.pbar = win32ui.CreateProgressCtrl()
self.pbar.CreateWindow (win32con.WS_CHILD |
win32con.WS_VISIBLE,
(10, 30, 310, 44),
self, 1001)
self.pbar.SetRange(0, self.maxticks)
self.pbar.SetStep(self.tickincr)
self.progress = 0
self.pincr = 5
return rc
def Close(self):
self.EndDialog(0)
def SetMaxTicks(self, maxticks):
if self.pbar is not None:
self.pbar.SetRange(0, maxticks)
def Tick(self):
if self.pbar is not None:
self.pbar.StepIt()
def SetTitle(self, text):
self.SetWindowText(text)
def SetText(self, text):
self.SetDlgItemText(1000, text)
def Set(self, pos, max = None):
if self.pbar is not None:
self.pbar.SetPos(pos)
if max is not None:
self.pbar.SetRange(0, max)
# a progress dialog created in a new thread - especially suitable for
# console apps with no message loop.
MYWM_SETTITLE = win32con.WM_USER+10
MYWM_SETMSG = win32con.WM_USER+11
MYWM_TICK = win32con.WM_USER+12
MYWM_SETMAXTICKS = win32con.WM_USER+13
MYWM_SET = win32con.WM_USER+14
class CThreadedStatusProcessDialog(CStatusProgressDialog):
def __init__(self, title, msg = "", maxticks = 100, tickincr = 1):
self.title = title
self.msg = msg
self.threadid = win32api.GetCurrentThreadId()
CStatusProgressDialog.__init__(self, title, msg, maxticks, tickincr)
def OnInitDialog(self):
rc = CStatusProgressDialog.OnInitDialog(self)
self.HookMessage(self.OnTitle, MYWM_SETTITLE)
self.HookMessage(self.OnMsg, MYWM_SETMSG)
self.HookMessage(self.OnTick, MYWM_TICK)
self.HookMessage(self.OnMaxTicks, MYWM_SETMAXTICKS)
self.HookMessage(self.OnSet, MYWM_SET)
return rc
def _Send(self, msg):
try:
self.PostMessage(msg)
except win32ui.error:
# the user closed the window - but this does not cancel the
# process - so just ignore it.
pass
def OnTitle(self, msg):
CStatusProgressDialog.SetTitle(self, self.title)
def OnMsg(self, msg):
CStatusProgressDialog.SetText(self, self.msg)
def OnTick(self, msg):
CStatusProgressDialog.Tick(self)
def OnMaxTicks(self, msg):
CStatusProgressDialog.SetMaxTicks(self, self.maxticks)
def OnSet(self, msg):
CStatusProgressDialog.Set(self, self.pos, self.max)
def Close(self):
assert self.threadid, "No thread!"
win32api.PostThreadMessage(self.threadid, win32con.WM_QUIT, 0, 0)
def SetMaxTicks(self, maxticks):
self.maxticks = maxticks
self._Send(MYWM_SETMAXTICKS)
def SetTitle(self, title):
self.title = title
self._Send(MYWM_SETTITLE)
def SetText(self, text):
self.msg = text
self._Send(MYWM_SETMSG)
def Tick(self):
self._Send(MYWM_TICK)
def Set(self, pos, max = None):
self.pos = pos
self.max = max
self._Send(MYWM_SET)
class ProgressThread(WinThread):
def __init__(self, title, msg = "", maxticks = 100, tickincr = 1):
self.title = title
self.msg = msg
self.maxticks = maxticks
self.tickincr = tickincr
self.dialog = None
WinThread.__init__(self)
self.createdEvent = threading.Event()
def InitInstance(self):
self.dialog = CThreadedStatusProcessDialog( self.title, self.msg, self.maxticks, self.tickincr)
self.dialog.CreateWindow()
try:
self.dialog.SetForegroundWindow()
except win32ui.error:
pass
self.createdEvent.set()
return WinThread.InitInstance(self)
def ExitInstance(self):
return 0
def StatusProgressDialog(title, msg = "", maxticks = 100, parent = None):
d = CStatusProgressDialog (title, msg, maxticks)
d.CreateWindow (parent)
return d
def ThreadedStatusProgressDialog(title, msg = "", maxticks = 100):
t = ProgressThread(title, msg, maxticks)
t.CreateThread()
# Need to run a basic "PumpWaitingMessages" loop just incase we are
# running inside Pythonwin.
# Basic timeout incase things go terribly wrong. Ideally we should use
# win32event.MsgWaitForMultipleObjects(), but we use a threading module
# event - so use a dumb strategy
end_time = time.time() + 10
while time.time() < end_time:
if t.createdEvent.isSet():
break
win32ui.PumpWaitingMessages()
time.sleep(0.1)
return t.dialog
def demo():
d = StatusProgressDialog("A Demo", "Doing something...")
import win32api
for i in range(100):
if i == 50:
d.SetText("Getting there...")
if i==90:
d.SetText("Nearly done...")
win32api.Sleep(20)
d.Tick()
d.Close()
def thread_demo():
d = ThreadedStatusProgressDialog("A threaded demo", "Doing something")
import win32api
for i in range(100):
if i == 50:
d.SetText("Getting there...")
if i==90:
d.SetText("Nearly done...")
win32api.Sleep(20)
d.Tick()
d.Close()
if __name__=='__main__':
thread_demo()
#demo()

View File

@ -0,0 +1,541 @@
# DockingBar.py
# Ported directly (comments and all) from the samples at www.codeguru.com
# WARNING: Use at your own risk, as this interface is highly likely to change.
# Currently we support only one child per DockingBar. Later we need to add
# support for multiple children.
import win32api, win32con, win32ui
from pywin.mfc import afxres, window
import struct
clrBtnHilight = win32api.GetSysColor(win32con.COLOR_BTNHILIGHT)
clrBtnShadow = win32api.GetSysColor(win32con.COLOR_BTNSHADOW)
def CenterPoint(rect):
width = rect[2]-rect[0]
height = rect[3]-rect[1]
return rect[0] + width//2, rect[1] + height//2
def OffsetRect(rect, point):
(x, y) = point
return rect[0]+x, rect[1]+y, rect[2]+x, rect[3]+y
def DeflateRect(rect, point):
(x, y) = point
return rect[0]+x, rect[1]+y, rect[2]-x, rect[3]-y
def PtInRect(rect, pt):
return rect[0] <= pt[0] < rect[2] and rect[1] <= pt[1] < rect[3]
class DockingBar(window.Wnd):
def __init__(self, obj=None):
if obj is None:
obj = win32ui.CreateControlBar()
window.Wnd.__init__(self, obj)
self.dialog = None
self.nDockBarID = 0
self.sizeMin = 32, 32
self.sizeHorz = 200, 200
self.sizeVert = 200, 200
self.sizeFloat = 200, 200
self.bTracking = 0
self.bInRecalcNC = 0
self.cxEdge = 6
self.cxBorder = 3
self.cxGripper = 20
self.brushBkgd = win32ui.CreateBrush()
self.brushBkgd.CreateSolidBrush(win32api.GetSysColor(win32con.COLOR_BTNFACE))
# Support for diagonal resizing
self.cyBorder = 3
self.cCaptionSize = win32api.GetSystemMetrics(win32con.SM_CYSMCAPTION)
self.cMinWidth = win32api.GetSystemMetrics(win32con.SM_CXMIN)
self.cMinHeight = win32api.GetSystemMetrics(win32con.SM_CYMIN)
self.rectUndock = (0,0,0,0)
def OnUpdateCmdUI(self, target, bDisableIfNoHndler):
return self.UpdateDialogControls(target, bDisableIfNoHndler)
def CreateWindow(self, parent, childCreator, title, id, style=win32con.WS_CHILD | win32con.WS_VISIBLE | afxres.CBRS_LEFT, childCreatorArgs=()):
assert not ((style & afxres.CBRS_SIZE_FIXED) and (style & afxres.CBRS_SIZE_DYNAMIC)), "Invalid style"
self.rectClose = self.rectBorder = self.rectGripper = self.rectTracker = 0,0,0,0
# save the style
self._obj_.dwStyle = style & afxres.CBRS_ALL
cursor = win32api.LoadCursor(0, win32con.IDC_ARROW)
wndClass = win32ui.RegisterWndClass(win32con.CS_DBLCLKS, cursor, self.brushBkgd.GetSafeHandle(), 0)
self._obj_.CreateWindow(wndClass, title, style, (0,0,0,0), parent, id)
# Create the child dialog
self.dialog = childCreator(*(self,) + childCreatorArgs)
# use the dialog dimensions as default base dimensions
assert self.dialog.IsWindow(), "The childCreator function %s did not create a window!" % childCreator
rect = self.dialog.GetWindowRect()
self.sizeHorz = self.sizeVert = self.sizeFloat = rect[2]-rect[0], rect[3]-rect[1]
self.sizeHorz = self.sizeHorz[0], self.sizeHorz[1] + self.cxEdge + self.cxBorder
self.sizeVert = self.sizeVert[0] + self.cxEdge + self.cxBorder, self.sizeVert[1]
self.HookMessages()
def CalcFixedLayout(self, bStretch, bHorz):
rectTop = self.dockSite.GetControlBar(afxres.AFX_IDW_DOCKBAR_TOP).GetWindowRect()
rectLeft = self.dockSite.GetControlBar(afxres.AFX_IDW_DOCKBAR_LEFT).GetWindowRect()
if bStretch:
nHorzDockBarWidth = 32767
nVertDockBarHeight = 32767
else:
nHorzDockBarWidth = rectTop[2]-rectTop[0] + 4
nVertDockBarHeight = rectLeft[3]-rectLeft[1] + 4
if self.IsFloating():
return self.sizeFloat
if bHorz:
return nHorzDockBarWidth, self.sizeHorz[1]
return self.sizeVert[0], nVertDockBarHeight
def CalcDynamicLayout(self, length, mode):
# Support for diagonal sizing.
if self.IsFloating():
self.GetParent().GetParent().ModifyStyle(win32ui.MFS_4THICKFRAME, 0)
if mode & (win32ui.LM_HORZDOCK | win32ui.LM_VERTDOCK):
flags = win32con.SWP_NOSIZE | win32con.SWP_NOMOVE | win32con.SWP_NOZORDER |\
win32con.SWP_NOACTIVATE | win32con.SWP_FRAMECHANGED
self.SetWindowPos(0, (0, 0, 0, 0,), flags)
self.dockSite.RecalcLayout()
return self._obj_.CalcDynamicLayout(length, mode)
if mode & win32ui.LM_MRUWIDTH:
return self.sizeFloat
if mode & win32ui.LM_COMMIT:
self.sizeFloat = length, self.sizeFloat[1]
return self.sizeFloat
# More diagonal sizing.
if self.IsFloating():
dc = self.dockContext
pt = win32api.GetCursorPos()
windowRect = self.GetParent().GetParent().GetWindowRect()
hittest = dc.nHitTest
if hittest==win32con.HTTOPLEFT:
cx = max(windowRect[2] - pt[0], self.cMinWidth) - self.cxBorder
cy = max(windowRect[3] - self.cCaptionSize - pt[1],self.cMinHeight) - 1
self.sizeFloat = cx, cy
top = min(pt[1], windowRect[3] - self.cCaptionSize - self.cMinHeight) - self.cyBorder
left = min(pt[0], windowRect[2] - self.cMinWidth) - 1
dc.rectFrameDragHorz = left, top, dc.rectFrameDragHorz[2], dc.rectFrameDragHorz[3]
return self.sizeFloat
if hittest==win32con.HTTOPRIGHT:
cx = max(pt[0] - windowRect[0], self.cMinWidth)
cy = max(windowRect[3] - self.cCaptionSize - pt[1], self.cMinHeight) - 1
self.sizeFloat = cx, cy
top = min(pt[1], windowRect[3] - self.cCaptionSize - self.cMinHeight) - self.cyBorder
dc.rectFrameDragHorz = dc.rectFrameDragHorz[0], top, dc.rectFrameDragHorz[2], dc.rectFrameDragHorz[3]
return self.sizeFloat
if hittest==win32con.HTBOTTOMLEFT:
cx = max(windowRect[2] - pt[0], self.cMinWidth) - self.cxBorder
cy = max(pt[1] - windowRect[1] - self.cCaptionSize, self.cMinHeight)
self.sizeFloat = cx, cy
left = min(pt[0], windowRect[2] -self.cMinWidth) - 1
dc.rectFrameDragHorz = left, dc.rectFrameDragHorz[1], dc.rectFrameDragHorz[2], dc.rectFrameDragHorz[3]
return self.sizeFloat
if hittest==win32con.HTBOTTOMRIGHT:
cx = max(pt[0] - windowRect[0], self.cMinWidth)
cy = max(pt[1] - windowRect[1] - self.cCaptionSize, self.cMinHeight)
self.sizeFloat = cx, cy
return self.sizeFloat
if mode & win32ui.LM_LENGTHY:
self.sizeFloat = self.sizeFloat[0], max(self.sizeMin[1], length)
return self.sizeFloat
else:
return max(self.sizeMin[0], length), self.sizeFloat[1]
def OnWindowPosChanged(self, msg):
if self.GetSafeHwnd()==0 or self.dialog is None:
return 0
lparam = msg[3]
""" LPARAM used with WM_WINDOWPOSCHANGED:
typedef struct {
HWND hwnd;
HWND hwndInsertAfter;
int x;
int y;
int cx;
int cy;
UINT flags;} WINDOWPOS;
"""
format = "PPiiiii"
bytes = win32ui.GetBytes( lparam, struct.calcsize(format) )
hwnd, hwndAfter, x, y, cx, cy, flags = struct.unpack(format, bytes)
if self.bInRecalcNC:
rc = self.GetClientRect()
self.dialog.MoveWindow(rc)
return 0
# Find on which side are we docked
nDockBarID = self.GetParent().GetDlgCtrlID()
# Return if dropped at same location
# no docking side change and no size change
if (nDockBarID == self.nDockBarID) and \
(flags & win32con.SWP_NOSIZE) and \
((self._obj_.dwStyle & afxres.CBRS_BORDER_ANY) != afxres.CBRS_BORDER_ANY):
return
self.nDockBarID = nDockBarID
# Force recalc the non-client area
self.bInRecalcNC = 1
try:
swpflags = win32con.SWP_NOSIZE | win32con.SWP_NOMOVE | win32con.SWP_NOZORDER | win32con.SWP_FRAMECHANGED
self.SetWindowPos(0, (0,0,0,0), swpflags)
finally:
self.bInRecalcNC = 0
return 0
# This is a virtual and not a message hook.
def OnSetCursor(self, window, nHitTest, wMouseMsg):
if nHitTest != win32con.HTSIZE or self.bTracking:
return self._obj_.OnSetCursor(window, nHitTest, wMouseMsg)
if self.IsHorz():
win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_SIZENS))
else:
win32api.SetCursor(win32api.LoadCursor(0, win32con.IDC_SIZEWE))
return 1
# Mouse Handling
def OnLButtonUp(self, msg):
if not self.bTracking:
return 1 # pass it on.
self.StopTracking(1)
return 0 # Dont pass on
def OnLButtonDown(self, msg):
# UINT nFlags, CPoint point)
# only start dragging if clicked in "void" space
if self.dockBar is not None:
# start the drag
pt = msg[5]
pt = self.ClientToScreen(pt)
self.dockContext.StartDrag(pt)
return 0
return 1
def OnNcLButtonDown(self, msg):
if self.bTracking: return 0
nHitTest = wparam = msg[2]
pt = msg[5]
if nHitTest==win32con.HTSYSMENU and not self.IsFloating():
self.GetDockingFrame().ShowControlBar(self, 0, 0)
elif nHitTest == win32con.HTMINBUTTON and not self.IsFloating():
self.dockContext.ToggleDocking()
elif nHitTest == win32con.HTCAPTION and not self.IsFloating() and self.dockBar is not None:
self.dockContext.StartDrag(pt)
elif nHitTest == win32con.HTSIZE and not self.IsFloating():
self.StartTracking()
else:
return 1
return 0
def OnLButtonDblClk(self, msg):
# only toggle docking if clicked in "void" space
if self.dockBar is not None:
# toggle docking
self.dockContext.ToggleDocking()
return 0
return 1
def OnNcLButtonDblClk(self, msg):
nHitTest = wparam = msg[2]
# UINT nHitTest, CPoint point)
if self.dockBar is not None and nHitTest == win32con.HTCAPTION:
# toggle docking
self.dockContext.ToggleDocking()
return 0
return 1
def OnMouseMove(self, msg):
flags = wparam = msg[2]
lparam = msg[3]
if self.IsFloating() or not self.bTracking:
return 1
# Convert unsigned 16 bit to signed 32 bit.
x=win32api.LOWORD(lparam)
if x & 32768: x = x | -65536
y = win32api.HIWORD(lparam)
if y & 32768: y = y | -65536
pt = x, y
cpt = CenterPoint(self.rectTracker)
pt = self.ClientToWnd(pt)
if self.IsHorz():
if cpt[1] != pt[1]:
self.OnInvertTracker(self.rectTracker)
self.rectTracker = OffsetRect(self.rectTracker, (0, pt[1] - cpt[1]))
self.OnInvertTracker(self.rectTracker)
else:
if cpt[0] != pt[0]:
self.OnInvertTracker(self.rectTracker)
self.rectTracker = OffsetRect(self.rectTracker, (pt[0]-cpt[0], 0))
self.OnInvertTracker(self.rectTracker)
return 0 # Dont pass it on.
# def OnBarStyleChange(self, old, new):
def OnNcCalcSize(self, bCalcValid, size_info):
(rc0, rc1, rc2, pos) = size_info
self.rectBorder = self.GetWindowRect()
self.rectBorder = OffsetRect( self.rectBorder, (-self.rectBorder[0], -self.rectBorder[1]) )
dwBorderStyle = self._obj_.dwStyle | afxres.CBRS_BORDER_ANY
if self.nDockBarID==afxres.AFX_IDW_DOCKBAR_TOP:
dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_BOTTOM;
rc0.left = rc0.left + self.cxGripper
rc0.bottom = rc0.bottom-self.cxEdge
rc0.top = rc0.top + self.cxBorder
rc0.right = rc0.right - self.cxBorder
self.rectBorder = self.rectBorder[0], self.rectBorder[3]-self.cxEdge, self.rectBorder[2], self.rectBorder[3]
elif self.nDockBarID==afxres.AFX_IDW_DOCKBAR_BOTTOM:
dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_TOP
rc0.left = rc0.left + self.cxGripper
rc0.top = rc0.top + self.cxEdge
rc0.bottom = rc0.bottom - self.cxBorder
rc0.right = rc0.right - self.cxBorder
self.rectBorder = self.rectBorder[0], self.rectBorder[1], self.rectBorder[2], self.rectBorder[1]+self.cxEdge
elif self.nDockBarID==afxres.AFX_IDW_DOCKBAR_LEFT:
dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_RIGHT
rc0.right = rc0.right - self.cxEdge
rc0.left = rc0.left + self.cxBorder
rc0.bottom = rc0.bottom - self.cxBorder
rc0.top = rc0.top + self.cxGripper
self.rectBorder = self.rectBorder[2] - self.cxEdge, self.rectBorder[1], self.rectBorder[2], self.rectBorder[3]
elif self.nDockBarID==afxres.AFX_IDW_DOCKBAR_RIGHT:
dwBorderStyle = dwBorderStyle & ~afxres.CBRS_BORDER_LEFT
rc0.left = rc0.left + self.cxEdge
rc0.right = rc0.right - self.cxBorder
rc0.bottom = rc0.bottom - self.cxBorder
rc0.top = rc0.top + self.cxGripper
self.rectBorder = self.rectBorder[0], self.rectBorder[1], self.rectBorder[0]+self.cxEdge, self.rectBorder[3]
else:
self.rectBorder = 0,0,0,0
self.SetBarStyle(dwBorderStyle)
return 0
def OnNcPaint(self, msg):
self.EraseNonClient()
dc = self.GetWindowDC()
ctl = win32api.GetSysColor(win32con.COLOR_BTNHIGHLIGHT)
cbr = win32api.GetSysColor(win32con.COLOR_BTNSHADOW)
dc.Draw3dRect(self.rectBorder, ctl, cbr)
self.DrawGripper(dc)
rect = self.GetClientRect()
self.InvalidateRect( rect, 1)
return 0
def OnNcHitTest(self, pt): # A virtual, not a hooked message.
if self.IsFloating():
return 1
ptOrig = pt
rect = self.GetWindowRect()
pt = pt[0] - rect[0], pt[1] - rect[1]
if PtInRect(self.rectClose, pt):
return win32con.HTSYSMENU
elif PtInRect(self.rectUndock, pt):
return win32con.HTMINBUTTON
elif PtInRect(self.rectGripper, pt):
return win32con.HTCAPTION
elif PtInRect(self.rectBorder, pt):
return win32con.HTSIZE
else:
return self._obj_.OnNcHitTest(ptOrig)
def StartTracking(self):
self.SetCapture()
# make sure no updates are pending
self.RedrawWindow(None, None, win32con.RDW_ALLCHILDREN | win32con.RDW_UPDATENOW)
self.dockSite.LockWindowUpdate()
self.ptOld = CenterPoint(self.rectBorder)
self.bTracking = 1
self.rectTracker = self.rectBorder;
if not self.IsHorz():
l, t, r, b = self.rectTracker
b = b - 4
self.rectTracker = l, t, r, b
self.OnInvertTracker(self.rectTracker);
def OnCaptureChanged(self, msg):
hwnd = lparam = msg[3]
if self.bTracking and hwnd != self.GetSafeHwnd():
self.StopTracking(0) # cancel tracking
return 1
def StopTracking(self, bAccept):
self.OnInvertTracker(self.rectTracker)
self.dockSite.UnlockWindowUpdate()
self.bTracking = 0
self.ReleaseCapture()
if not bAccept: return
rcc = self.dockSite.GetWindowRect()
if self.IsHorz():
newsize = self.sizeHorz[1]
maxsize = newsize + (rcc[3]-rcc[1])
minsize = self.sizeMin[1]
else:
newsize = self.sizeVert[0]
maxsize = newsize + (rcc[2]-rcc[0])
minsize = self.sizeMin[0]
pt = CenterPoint(self.rectTracker)
if self.nDockBarID== afxres.AFX_IDW_DOCKBAR_TOP:
newsize = newsize + (pt[1] - self.ptOld[1])
elif self.nDockBarID== afxres.AFX_IDW_DOCKBAR_BOTTOM:
newsize = newsize + (- pt[1] + self.ptOld[1])
elif self.nDockBarID== afxres.AFX_IDW_DOCKBAR_LEFT:
newsize = newsize + (pt[0] - self.ptOld[0])
elif self.nDockBarID== afxres.AFX_IDW_DOCKBAR_RIGHT:
newsize = newsize + (- pt[0] + self.ptOld[0])
newsize = max(minsize, min(maxsize, newsize))
if self.IsHorz():
self.sizeHorz = self.sizeHorz[0], newsize
else:
self.sizeVert = newsize, self.sizeVert[1]
self.dockSite.RecalcLayout()
return 0
def OnInvertTracker(self, rect):
assert rect[2]-rect[0]>0 and rect[3]-rect[1]>0, "rect is empty"
assert self.bTracking
rcc = self.GetWindowRect()
rcf = self.dockSite.GetWindowRect()
rect = OffsetRect(rect, (rcc[0] - rcf[0], rcc[1] - rcf[1]))
rect = DeflateRect(rect, (1, 1));
flags = win32con.DCX_WINDOW|win32con.DCX_CACHE|win32con.DCX_LOCKWINDOWUPDATE
dc = self.dockSite.GetDCEx(None, flags)
try:
brush = win32ui.GetHalftoneBrush()
oldBrush = dc.SelectObject(brush)
dc.PatBlt((rect[0], rect[1]), (rect[2]-rect[0], rect[3]-rect[1]), win32con.PATINVERT)
dc.SelectObject(oldBrush)
finally:
self.dockSite.ReleaseDC(dc)
def IsHorz(self):
return self.nDockBarID == afxres.AFX_IDW_DOCKBAR_TOP or \
self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM
def ClientToWnd(self, pt):
x, y=pt
if self.nDockBarID == afxres.AFX_IDW_DOCKBAR_BOTTOM:
y = y + self.cxEdge
elif self.nDockBarID == afxres.AFX_IDW_DOCKBAR_RIGHT:
x = x + self.cxEdge
return x,y
def DrawGripper(self, dc):
# no gripper if floating
if self._obj_.dwStyle & afxres.CBRS_FLOATING:
return
# -==HACK==-
# in order to calculate the client area properly after docking,
# the client area must be recalculated twice (I have no idea why)
self.dockSite.RecalcLayout()
# -==END HACK==-
gripper = self.GetWindowRect()
gripper = self.ScreenToClient( gripper )
gripper = OffsetRect( gripper, (-gripper[0], -gripper[1]) )
gl, gt, gr, gb = gripper
if self._obj_.dwStyle & afxres.CBRS_ORIENT_HORZ:
# gripper at left
self.rectGripper = gl, gt + 40, gl+20, gb
# draw close box
self.rectClose = gl+7, gt + 10, gl+19, gt+22
dc.DrawFrameControl(self.rectClose, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONCLOSE)
# draw docking toggle box
self.rectUndock = OffsetRect(self.rectClose, (0,13))
dc.DrawFrameControl(self.rectUndock, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONMAX);
gt = gt + 38
gb = gb - 10
gl = gl + 10
gr = gl + 3
gripper = gl, gt, gr, gb
dc.Draw3dRect( gripper, clrBtnHilight, clrBtnShadow )
dc.Draw3dRect( OffsetRect(gripper, (4,0)), clrBtnHilight, clrBtnShadow )
else:
# gripper at top
self.rectGripper = gl, gt, gr-40, gt+20
# draw close box
self.rectClose = gr-21, gt+7, gr-10, gt+18
dc.DrawFrameControl(self.rectClose, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONCLOSE)
# draw docking toggle box
self.rectUndock = OffsetRect( self.rectClose, (-13,0) )
dc.DrawFrameControl(self.rectUndock, win32con.DFC_CAPTION, win32con.DFCS_CAPTIONMAX)
gr = gr - 38;
gl = gl + 10
gt = gt + 10
gb = gt + 3
gripper = gl, gt, gr, gb
dc.Draw3dRect( gripper, clrBtnHilight, clrBtnShadow )
dc.Draw3dRect( OffsetRect(gripper, (0,4) ), clrBtnHilight, clrBtnShadow )
def HookMessages(self):
self.HookMessage(self.OnLButtonUp, win32con.WM_LBUTTONUP)
self.HookMessage(self.OnLButtonDown, win32con.WM_LBUTTONDOWN)
self.HookMessage(self.OnLButtonDblClk, win32con.WM_LBUTTONDBLCLK)
self.HookMessage(self.OnNcLButtonDown, win32con.WM_NCLBUTTONDOWN)
self.HookMessage(self.OnNcLButtonDblClk, win32con.WM_NCLBUTTONDBLCLK)
self.HookMessage(self.OnMouseMove, win32con.WM_MOUSEMOVE)
self.HookMessage(self.OnNcPaint, win32con.WM_NCPAINT)
self.HookMessage(self.OnCaptureChanged, win32con.WM_CAPTURECHANGED)
self.HookMessage(self.OnWindowPosChanged, win32con.WM_WINDOWPOSCHANGED)
# self.HookMessage(self.OnSize, win32con.WM_SIZE)
def EditCreator(parent):
d = win32ui.CreateEdit()
es = win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | win32con.ES_MULTILINE | win32con.ES_WANTRETURN
d.CreateWindow( es, (0,0,150,150), parent, 1000)
return d
def test():
import pywin.mfc.dialog
global bar
bar = DockingBar()
creator = EditCreator
bar.CreateWindow(win32ui.GetMainFrame(), creator, "Coolbar Demo",0xfffff)
# win32ui.GetMainFrame().ShowControlBar(bar, 1, 0)
bar.SetBarStyle( bar.GetBarStyle()|afxres.CBRS_TOOLTIPS|afxres.CBRS_FLYBY|afxres.CBRS_SIZE_DYNAMIC)
bar.EnableDocking(afxres.CBRS_ALIGN_ANY)
win32ui.GetMainFrame().DockControlBar(bar, afxres.AFX_IDW_DOCKBAR_BOTTOM)
if __name__=='__main__':
test()

View File

@ -0,0 +1,408 @@
# App.py
# Application stuff.
# The application is responsible for managing the main frame window.
#
# We also grab the FileOpen command, to invoke our Python editor
" The PythonWin application code. Manages most aspects of MDI, etc "
import win32con
import win32api
import win32ui
import sys
import string
import os
from pywin.mfc import window, dialog, afxres
from pywin.mfc.thread import WinApp
import traceback
import regutil
from . import scriptutils
## NOTE: App and AppBuild should NOT be used - instead, you should contruct your
## APP class manually whenever you like (just ensure you leave these 2 params None!)
## Whoever wants the generic "Application" should get it via win32iu.GetApp()
# These are "legacy"
AppBuilder = None
App = None # default - if used, must end up a CApp derived class.
# Helpers that should one day be removed!
def AddIdleHandler(handler):
print("app.AddIdleHandler is deprecated - please use win32ui.GetApp().AddIdleHandler() instead.")
return win32ui.GetApp().AddIdleHandler(handler)
def DeleteIdleHandler(handler):
print("app.DeleteIdleHandler is deprecated - please use win32ui.GetApp().DeleteIdleHandler() instead.")
return win32ui.GetApp().DeleteIdleHandler(handler)
# Helper for writing a Window position by name, and later loading it.
def SaveWindowSize(section,rect,state=""):
""" Writes a rectangle to an INI file
Args: section = section name in the applications INI file
rect = a rectangle in a (cy, cx, y, x) tuple
(same format as CREATESTRUCT position tuples)."""
left, top, right, bottom = rect
if state: state = state + " "
win32ui.WriteProfileVal(section,state+"left",left)
win32ui.WriteProfileVal(section,state+"top",top)
win32ui.WriteProfileVal(section,state+"right",right)
win32ui.WriteProfileVal(section,state+"bottom",bottom)
def LoadWindowSize(section, state=""):
""" Loads a section from an INI file, and returns a rect in a tuple (see SaveWindowSize)"""
if state: state = state + " "
left = win32ui.GetProfileVal(section,state+"left",0)
top = win32ui.GetProfileVal(section,state+"top",0)
right = win32ui.GetProfileVal(section,state+"right",0)
bottom = win32ui.GetProfileVal(section,state+"bottom",0)
return (left, top, right, bottom)
def RectToCreateStructRect(rect):
return (rect[3]-rect[1], rect[2]-rect[0], rect[1], rect[0] )
# Define FrameWindow and Application objects
#
# The Main Frame of the application.
class MainFrame(window.MDIFrameWnd):
sectionPos = "Main Window"
statusBarIndicators = ( afxres.ID_SEPARATOR, #// status line indicator
afxres.ID_INDICATOR_CAPS,
afxres.ID_INDICATOR_NUM,
afxres.ID_INDICATOR_SCRL,
win32ui.ID_INDICATOR_LINENUM,
win32ui.ID_INDICATOR_COLNUM )
def OnCreate(self, cs):
self._CreateStatusBar()
return 0
def _CreateStatusBar(self):
self.statusBar = win32ui.CreateStatusBar(self)
self.statusBar.SetIndicators(self.statusBarIndicators)
self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_LINENUM)
self.HookCommandUpdate(self.OnUpdatePosIndicator, win32ui.ID_INDICATOR_COLNUM)
def OnUpdatePosIndicator(self, cmdui):
editControl = scriptutils.GetActiveEditControl()
value = " " * 5
if editControl is not None:
try:
startChar, endChar = editControl.GetSel()
lineNo = editControl.LineFromChar(startChar)
colNo = endChar - editControl.LineIndex(lineNo)
if cmdui.m_nID==win32ui.ID_INDICATOR_LINENUM:
value = "%0*d" % (5, lineNo + 1)
else:
value = "%0*d" % (3, colNo + 1)
except win32ui.error:
pass
cmdui.SetText(value)
cmdui.Enable()
def PreCreateWindow(self, cc):
cc = self._obj_.PreCreateWindow(cc)
pos = LoadWindowSize(self.sectionPos)
self.startRect = pos
if pos[2] - pos[0]:
rect = RectToCreateStructRect(pos)
cc = cc[0], cc[1], cc[2], cc[3], rect, cc[5], cc[6], cc[7], cc[8]
return cc
def OnDestroy(self, msg):
# use GetWindowPlacement(), as it works even when min'd or max'd
rectNow = self.GetWindowPlacement()[4]
if rectNow != self.startRect:
SaveWindowSize(self.sectionPos, rectNow)
return 0
class CApp(WinApp):
" A class for the application "
def __init__(self):
self.oldCallbackCaller = None
WinApp.__init__(self, win32ui.GetApp() )
self.idleHandlers = []
def InitInstance(self):
" Called to crank up the app "
HookInput()
numMRU = win32ui.GetProfileVal("Settings","Recent File List Size", 10)
win32ui.LoadStdProfileSettings(numMRU)
# self._obj_.InitMDIInstance()
if win32api.GetVersionEx()[0]<4:
win32ui.SetDialogBkColor()
win32ui.Enable3dControls()
# install a "callback caller" - a manager for the callbacks
# self.oldCallbackCaller = win32ui.InstallCallbackCaller(self.CallbackManager)
self.LoadMainFrame()
self.SetApplicationPaths()
def ExitInstance(self):
" Called as the app dies - too late to prevent it here! "
win32ui.OutputDebug("Application shutdown\n")
# Restore the callback manager, if any.
try:
win32ui.InstallCallbackCaller(self.oldCallbackCaller)
except AttributeError:
pass
if self.oldCallbackCaller:
del self.oldCallbackCaller
self.frame=None # clean Python references to the now destroyed window object.
self.idleHandlers = []
# Attempt cleanup if not already done!
if self._obj_: self._obj_.AttachObject(None)
self._obj_ = None
global App
global AppBuilder
App = None
AppBuilder = None
return 0
def HaveIdleHandler(self, handler):
return handler in self.idleHandlers
def AddIdleHandler(self, handler):
self.idleHandlers.append(handler)
def DeleteIdleHandler(self, handler):
self.idleHandlers.remove(handler)
def OnIdle(self, count):
try:
ret = 0
handlers = self.idleHandlers[:] # copy list, as may be modified during loop
for handler in handlers:
try:
thisRet = handler(handler, count)
except:
print("Idle handler %s failed" % (repr(handler)))
traceback.print_exc()
print("Idle handler removed from list")
try:
self.DeleteIdleHandler(handler)
except ValueError: # Item not in list.
pass
thisRet = 0
ret = ret or thisRet
return ret
except KeyboardInterrupt:
pass
def CreateMainFrame(self):
return MainFrame()
def LoadMainFrame(self):
" Create the main applications frame "
self.frame = self.CreateMainFrame()
self.SetMainFrame(self.frame)
self.frame.LoadFrame(win32ui.IDR_MAINFRAME, win32con.WS_OVERLAPPEDWINDOW)
self.frame.DragAcceptFiles() # we can accept these.
self.frame.ShowWindow(win32ui.GetInitialStateRequest())
self.frame.UpdateWindow()
self.HookCommands()
def OnHelp(self,id, code):
try:
if id==win32ui.ID_HELP_GUI_REF:
helpFile = regutil.GetRegisteredHelpFile("Pythonwin Reference")
helpCmd = win32con.HELP_CONTENTS
else:
helpFile = regutil.GetRegisteredHelpFile("Main Python Documentation")
helpCmd = win32con.HELP_FINDER
if helpFile is None:
win32ui.MessageBox("The help file is not registered!")
else:
from . import help
help.OpenHelpFile(helpFile, helpCmd)
except:
t, v, tb = sys.exc_info()
win32ui.MessageBox("Internal error in help file processing\r\n%s: %s" % (t,v))
tb = None # Prevent a cycle
def DoLoadModules(self, modules):
# XXX - this should go, but the debugger uses it :-(
# dont do much checking!
for module in modules:
__import__(module)
def HookCommands(self):
self.frame.HookMessage(self.OnDropFiles,win32con.WM_DROPFILES)
self.HookCommand(self.HandleOnFileOpen,win32ui.ID_FILE_OPEN)
self.HookCommand(self.HandleOnFileNew,win32ui.ID_FILE_NEW)
self.HookCommand(self.OnFileMRU,win32ui.ID_FILE_MRU_FILE1)
self.HookCommand(self.OnHelpAbout,win32ui.ID_APP_ABOUT)
self.HookCommand(self.OnHelp, win32ui.ID_HELP_PYTHON)
self.HookCommand(self.OnHelp, win32ui.ID_HELP_GUI_REF)
# Hook for the right-click menu.
self.frame.GetWindow(win32con.GW_CHILD).HookMessage(self.OnRClick,win32con.WM_RBUTTONDOWN)
def SetApplicationPaths(self):
# Load the users/application paths
new_path = []
apppath=win32ui.GetProfileVal('Python','Application Path','').split(';')
for path in apppath:
if len(path)>0:
new_path.append(win32ui.FullPath(path))
for extra_num in range(1,11):
apppath=win32ui.GetProfileVal('Python','Application Path %d'%extra_num,'').split(';')
if len(apppath) == 0:
break
for path in apppath:
if len(path)>0:
new_path.append(win32ui.FullPath(path))
sys.path = new_path + sys.path
def OnRClick(self,params):
" Handle right click message "
# put up the entire FILE menu!
menu = win32ui.LoadMenu(win32ui.IDR_TEXTTYPE).GetSubMenu(0)
menu.TrackPopupMenu(params[5]) # track at mouse position.
return 0
def OnDropFiles(self,msg):
" Handle a file being dropped from file manager "
hDropInfo = msg[2]
self.frame.SetActiveWindow() # active us
nFiles = win32api.DragQueryFile(hDropInfo)
try:
for iFile in range(0,nFiles):
fileName = win32api.DragQueryFile(hDropInfo, iFile)
win32ui.GetApp().OpenDocumentFile( fileName )
finally:
win32api.DragFinish(hDropInfo);
return 0
# No longer used by Pythonwin, as the C++ code has this same basic functionality
# but handles errors slightly better.
# It all still works, tho, so if you need similar functionality, you can use it.
# Therefore I havent deleted this code completely!
# def CallbackManager( self, ob, args = () ):
# """Manage win32 callbacks. Trap exceptions, report on them, then return 'All OK'
# to the frame-work. """
# import traceback
# try:
# ret = apply(ob, args)
# return ret
# except:
# # take copies of the exception values, else other (handled) exceptions may get
# # copied over by the other fns called.
# win32ui.SetStatusText('An exception occured in a windows command handler.')
# t, v, tb = sys.exc_info()
# traceback.print_exception(t, v, tb.tb_next)
# try:
# sys.stdout.flush()
# except (NameError, AttributeError):
# pass
# Command handlers.
def OnFileMRU( self, id, code ):
" Called when a File 1-n message is recieved "
fileName = win32ui.GetRecentFileList()[id - win32ui.ID_FILE_MRU_FILE1]
win32ui.GetApp().OpenDocumentFile(fileName)
def HandleOnFileOpen( self, id, code ):
" Called when FileOpen message is received "
win32ui.GetApp().OnFileOpen()
def HandleOnFileNew( self, id, code ):
" Called when FileNew message is received "
win32ui.GetApp().OnFileNew()
def OnHelpAbout( self, id, code ):
" Called when HelpAbout message is received. Displays the About dialog. "
win32ui.InitRichEdit()
dlg=AboutBox()
dlg.DoModal()
def _GetRegistryValue(key, val, default = None):
# val is registry value - None for default val.
try:
hkey = win32api.RegOpenKey(win32con.HKEY_CURRENT_USER, key)
return win32api.RegQueryValueEx(hkey, val)[0]
except win32api.error:
try:
hkey = win32api.RegOpenKey(win32con.HKEY_LOCAL_MACHINE, key)
return win32api.RegQueryValueEx(hkey, val)[0]
except win32api.error:
return default
scintilla = "Scintilla is Copyright 1998-2008 Neil Hodgson (http://www.scintilla.org)"
idle = "This program uses IDLE extensions by Guido van Rossum, Tim Peters and others."
contributors = "Thanks to the following people for making significant contributions: Roger Upole, Sidnei da Silva, Sam Rushing, Curt Hagenlocher, Dave Brennan, Roger Burnham, Gordon McMillan, Neil Hodgson, Laramie Leavitt. (let me know if I have forgotten you!)"
# The About Box
class AboutBox(dialog.Dialog):
def __init__(self, idd=win32ui.IDD_ABOUTBOX):
dialog.Dialog.__init__(self, idd)
def OnInitDialog(self):
text = "Pythonwin - Python IDE and GUI Framework for Windows.\n\n%s\n\nPython is %s\n\n%s\n\n%s\n\n%s" % (win32ui.copyright, sys.copyright, scintilla, idle, contributors)
self.SetDlgItemText(win32ui.IDC_EDIT1, text)
# Get the build number - written by installers.
# For distutils build, read pywin32.version.txt
import distutils.sysconfig
site_packages = distutils.sysconfig.get_python_lib(plat_specific=1)
try:
build_no = open(os.path.join(site_packages, "pywin32.version.txt")).read().strip()
ver = "pywin32 build %s" % build_no
except EnvironmentError:
ver = None
if ver is None:
# See if we are Part of Active Python
ver = _GetRegistryValue("SOFTWARE\\ActiveState\\ActivePython", "CurrentVersion")
if ver is not None:
ver = "ActivePython build %s" % (ver,)
if ver is None:
ver = ""
self.SetDlgItemText(win32ui.IDC_ABOUT_VERSION, ver)
self.HookCommand(self.OnButHomePage, win32ui.IDC_BUTTON1)
def OnButHomePage(self, id, code):
if code == win32con.BN_CLICKED:
win32api.ShellExecute(0, "open", "http://starship.python.net/crew/mhammond/win32", None, "", 1)
def Win32RawInput(prompt=None):
"Provide raw_input() for gui apps"
# flush stderr/out first.
try:
sys.stdout.flush()
sys.stderr.flush()
except:
pass
if prompt is None: prompt = ""
ret=dialog.GetSimpleInput(prompt)
if ret==None:
raise KeyboardInterrupt("operation cancelled")
return ret
def Win32Input(prompt=None):
"Provide input() for gui apps"
return eval(input(prompt))
def HookInput():
try:
raw_input
# must be py2x...
sys.modules['__builtin__'].raw_input=Win32RawInput
sys.modules['__builtin__'].input=Win32Input
except NameError:
# must be py3k
import code
sys.modules['builtins'].input=Win32RawInput
def HaveGoodGUI():
"""Returns true if we currently have a good gui available.
"""
return "pywin.framework.startup" in sys.modules
def CreateDefaultGUI( appClass = None):
"""Creates a default GUI environment
"""
if appClass is None:
from . import intpyapp # Bring in the default app - could be param'd later.
appClass = intpyapp.InteractivePythonApp
# Create and init the app.
appClass().InitInstance()
def CheckCreateDefaultGUI():
"""Checks and creates if necessary a default GUI environment.
"""
rc = HaveGoodGUI()
if not rc:
CreateDefaultGUI()
return rc

View File

@ -0,0 +1,143 @@
import win32ui
import win32con
import win32api
import string
import os
from . import app
import sys
from pywin.mfc import docview, window
bStretch = 1
class BitmapDocument(docview.Document):
"A bitmap document. Holds the bitmap data itself."
def __init__(self, template):
docview.Document.__init__(self, template)
self.bitmap=None
def OnNewDocument(self):
# I can not create new bitmaps.
win32ui.MessageBox("Bitmaps can not be created.")
def OnOpenDocument(self, filename):
self.bitmap=win32ui.CreateBitmap()
# init data members
f = open(filename, 'rb')
try:
try:
self.bitmap.LoadBitmapFile(f)
except IOError:
win32ui.MessageBox("Could not load the bitmap from %s" % filename)
return 0
finally:
f.close()
self.size = self.bitmap.GetSize()
return 1
def DeleteContents(self):
self.bitmap=None
class BitmapView(docview.ScrollView):
"A view of a bitmap. Obtains data from document."
def __init__(self, doc):
docview.ScrollView.__init__(self, doc)
self.width = self.height = 0
# set up message handlers
self.HookMessage (self.OnSize, win32con.WM_SIZE)
def OnInitialUpdate(self):
doc = self.GetDocument()
if doc.bitmap:
bitmapSize = doc.bitmap.GetSize()
self.SetScrollSizes(win32con.MM_TEXT, bitmapSize)
def OnSize (self, params):
lParam = params[3]
self.width = win32api.LOWORD(lParam)
self.height = win32api.HIWORD(lParam)
def OnDraw (self, dc):
# set sizes used for "non stretch" mode.
doc = self.GetDocument()
if doc.bitmap is None: return
bitmapSize = doc.bitmap.GetSize()
if bStretch:
# stretch BMP.
viewRect = (0,0,self.width, self.height)
bitmapRect = (0,0,bitmapSize[0], bitmapSize[1])
doc.bitmap.Paint(dc, viewRect, bitmapRect)
else:
# non stretch.
doc.bitmap.Paint(dc)
class BitmapFrame(window.MDIChildWnd):
def OnCreateClient( self, createparams, context ):
borderX = win32api.GetSystemMetrics(win32con.SM_CXFRAME)
borderY = win32api.GetSystemMetrics(win32con.SM_CYFRAME)
titleY = win32api.GetSystemMetrics(win32con.SM_CYCAPTION) # includes border
# try and maintain default window pos, else adjust if cant fit
# get the main client window dimensions.
mdiClient = win32ui.GetMainFrame().GetWindow(win32con.GW_CHILD)
clientWindowRect=mdiClient.ScreenToClient(mdiClient.GetWindowRect())
clientWindowSize=(clientWindowRect[2]-clientWindowRect[0],clientWindowRect[3]-clientWindowRect[1])
left, top, right, bottom=mdiClient.ScreenToClient(self.GetWindowRect())
# width, height=context.doc.size[0], context.doc.size[1]
# width = width+borderX*2
# height= height+titleY+borderY*2-1
# if (left+width)>clientWindowSize[0]:
# left = clientWindowSize[0] - width
# if left<0:
# left = 0
# width = clientWindowSize[0]
# if (top+height)>clientWindowSize[1]:
# top = clientWindowSize[1] - height
# if top<0:
# top = 0
# height = clientWindowSize[1]
# self.frame.MoveWindow((left, top, left+width, top+height),0)
window.MDIChildWnd.OnCreateClient(self, createparams, context)
return 1
class BitmapTemplate(docview.DocTemplate):
def __init__(self):
docview.DocTemplate.__init__(self, win32ui.IDR_PYTHONTYPE, BitmapDocument, BitmapFrame, BitmapView)
def MatchDocType(self, fileName, fileType):
doc = self.FindOpenDocument(fileName)
if doc: return doc
ext = os.path.splitext(fileName)[1].lower()
if ext =='.bmp': # removed due to PIL! or ext=='.ppm':
return win32ui.CDocTemplate_Confidence_yesAttemptNative
return win32ui.CDocTemplate_Confidence_maybeAttemptForeign
# return win32ui.CDocTemplate_Confidence_noAttempt
# For debugging purposes, when this module may be reloaded many times.
try:
win32ui.GetApp().RemoveDocTemplate(bitmapTemplate)
except NameError:
pass
bitmapTemplate = BitmapTemplate()
bitmapTemplate.SetDocStrings('\nBitmap\nBitmap\nBitmap (*.bmp)\n.bmp\nPythonBitmapFileType\nPython Bitmap File')
win32ui.GetApp().AddDocTemplate(bitmapTemplate)
# This works, but just didnt make it through the code reorg.
#class PPMBitmap(Bitmap):
# def LoadBitmapFile(self, file ):
# magic=file.readline()
# if magic <> "P6\n":
# raise TypeError, "The file is not a PPM format file"
# rowcollist=string.split(file.readline())
# cols=string.atoi(rowcollist[0])
# rows=string.atoi(rowcollist[1])
# file.readline() # whats this one?
# self.bitmap.LoadPPMFile(file,(cols,rows))
def t():
bitmapTemplate.OpenDocumentFile('d:\\winnt\\arcade.bmp')
#OpenBMPFile( 'd:\\winnt\\arcade.bmp')
def demo():
import glob
winDir=win32api.GetWindowsDirectory()
for fileName in glob.glob1(winDir, '*.bmp')[:2]:
bitmapTemplate.OpenDocumentFile(os.path.join(winDir, fileName))

View File

@ -0,0 +1,49 @@
# cmdline - command line utilities.
import sys
import win32ui
import string
def ParseArgs( str ):
import string
ret=[]
pos = 0
length=len(str)
while pos<length:
try:
while str[pos] in string.whitespace: pos = pos+1
except IndexError:
break
if pos>=length:
break
if str[pos]=='"':
pos=pos+1
try:
endPos = str.index('"', pos)-1
nextPos = endPos+2
except ValueError:
endPos=length
nextPos=endPos+1
else:
endPos = pos
while endPos<length and not str[endPos] in string.whitespace: endPos = endPos+1
nextPos=endPos+1
ret.append(str[pos:endPos+1].strip())
pos = nextPos
return ret
def FixArgFileName(fileName):
"""Convert a filename on the commandline to something useful.
Given an automatic filename on the commandline, turn it a python module name,
with the path added to sys.path. """
import os
path, fname = os.path.split(fileName)
if len(path)==0:
path = os.curdir
path=os.path.abspath(path)
# must check that the command line arg's path is in sys.path
for syspath in sys.path:
if os.path.abspath(syspath)==path:
break
else:
sys.path.append(path)
return os.path.splitext(fname)[0]

View File

@ -0,0 +1,170 @@
# Command Handlers for the debugger.
# Not in the debugger package, as I always want these interfaces to be
# available, even if the debugger has not yet been (or can not be)
# imported
import win32ui, win32con
from . import scriptutils
import warnings
from pywin.scintilla.control import CScintillaEditInterface
IdToBarNames = {
win32ui.IDC_DBG_STACK : ("Stack",0),
win32ui.IDC_DBG_BREAKPOINTS : ("Breakpoints",0),
win32ui.IDC_DBG_WATCH : ("Watch",1),
}
class DebuggerCommandHandler:
def HookCommands(self):
commands = ( (self.OnStep, None, win32ui.IDC_DBG_STEP),
(self.OnStepOut, self.OnUpdateOnlyBreak, win32ui.IDC_DBG_STEPOUT),
(self.OnStepOver, None, win32ui.IDC_DBG_STEPOVER),
(self.OnGo, None, win32ui.IDC_DBG_GO),
(self.OnClose, self.OnUpdateClose, win32ui.IDC_DBG_CLOSE),
(self.OnAdd, self.OnUpdateAddBreakpoints, win32ui.IDC_DBG_ADD),
(self.OnClearAll, self.OnUpdateClearAllBreakpoints, win32ui.IDC_DBG_CLEAR),
# (self.OnDebuggerToolbar, self.OnUpdateDebuggerToolbar, win32ui.ID_DEBUGGER_TOOLBAR),
)
frame = win32ui.GetMainFrame()
for methHandler, methUpdate, id in commands:
frame.HookCommand(methHandler, id);
if not methUpdate is None:
frame.HookCommandUpdate(methUpdate, id)
for id in list(IdToBarNames.keys()):
frame.HookCommand( self.OnDebuggerBar, id)
frame.HookCommandUpdate(self.OnUpdateDebuggerBar, id)
def OnDebuggerToolbar(self, id, code):
if code==0:
return not win32ui.GetMainFrame().OnBarCheck(id)
def OnUpdateDebuggerToolbar(self, cmdui):
win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui)
cmdui.Enable(1)
def _GetDebugger(self):
try:
import pywin.debugger
return pywin.debugger.currentDebugger
except ImportError:
return None
def _DoOrStart(self, doMethod, startFlag):
d=self._GetDebugger()
if d is not None and d.IsDebugging():
method = getattr(d, doMethod)
method()
else:
scriptutils.RunScript(defName=None, defArgs=None, bShowDialog = 0, debuggingType=startFlag)
def OnStep(self, msg, code):
self._DoOrStart("do_set_step", scriptutils.RS_DEBUGGER_STEP)
def OnStepOver(self, msg, code):
self._DoOrStart("do_set_next", scriptutils.RS_DEBUGGER_STEP)
def OnStepOut(self, msg, code):
d=self._GetDebugger()
if d is not None and d.IsDebugging():
d.do_set_return()
def OnGo(self, msg, code):
self._DoOrStart("do_set_continue", scriptutils.RS_DEBUGGER_GO)
def OnClose(self, msg, code):
d=self._GetDebugger()
if d is not None:
if d.IsDebugging():
d.set_quit()
else:
d.close()
def OnUpdateClose(self, cmdui):
d=self._GetDebugger()
if d is not None and d.inited:
cmdui.Enable(1)
else:
cmdui.Enable(0)
def OnAdd(self, msg, code):
doc, view = scriptutils.GetActiveEditorDocument()
if doc is None:
## Don't do a messagebox, as this could be triggered from the app's
## idle loop whenever the debug toolbar is visible, giving a never-ending
## series of dialogs. This can happen when the OnUpdate handler
## for the toolbar button IDC_DBG_ADD fails, since MFC falls back to
## sending a normal command if the UI update command fails.
## win32ui.MessageBox('There is no active window - no breakpoint can be added')
warnings.warn('There is no active window - no breakpoint can be added')
return None
pathName = doc.GetPathName()
lineNo = view.LineFromChar(view.GetSel()[0])+1
# If I have a debugger, then tell it, otherwise just add a marker
d=self._GetDebugger()
if d is None:
import pywin.framework.editor.color.coloreditor
doc.MarkerToggle(lineNo, pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT)
else:
if d.get_break(pathName, lineNo):
win32ui.SetStatusText('Clearing breakpoint',1)
rc = d.clear_break(pathName, lineNo)
else:
win32ui.SetStatusText('Setting breakpoint',1)
rc = d.set_break(pathName, lineNo)
if rc:
win32ui.MessageBox(rc)
d.GUIRespondDebuggerData()
def OnClearAll(self, msg, code):
win32ui.SetStatusText('Clearing all breakpoints')
d=self._GetDebugger()
if d is None:
import pywin.framework.editor
import pywin.framework.editor.color.coloreditor
for doc in pywin.framework.editor.editorTemplate.GetDocumentList():
doc.MarkerDeleteAll(pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT)
else:
d.clear_all_breaks()
d.UpdateAllLineStates()
d.GUIRespondDebuggerData()
def OnUpdateOnlyBreak(self, cmdui):
d=self._GetDebugger()
ok = d is not None and d.IsBreak()
cmdui.Enable(ok)
def OnUpdateAddBreakpoints(self, cmdui):
doc, view = scriptutils.GetActiveEditorDocument()
if doc is None or not isinstance(view, CScintillaEditInterface):
enabled = 0
else:
enabled = 1
lineNo = view.LineFromChar(view.GetSel()[0])+1
import pywin.framework.editor.color.coloreditor
cmdui.SetCheck(doc.MarkerAtLine(lineNo, pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT) != 0)
cmdui.Enable(enabled)
def OnUpdateClearAllBreakpoints(self, cmdui):
d=self._GetDebugger()
cmdui.Enable(d is None or len(d.breaks)!=0)
def OnUpdateDebuggerBar(self, cmdui):
name, always = IdToBarNames.get(cmdui.m_nID)
enabled = always
d=self._GetDebugger()
if d is not None and d.IsDebugging() and name is not None:
enabled = 1
bar = d.GetDebuggerBar(name)
cmdui.SetCheck(bar.IsWindowVisible())
cmdui.Enable(enabled)
def OnDebuggerBar(self, id, code):
name = IdToBarNames.get(id)[0]
d=self._GetDebugger()
if d is not None and name is not None:
bar = d.GetDebuggerBar(name)
newState = not bar.IsWindowVisible()
win32ui.GetMainFrame().ShowControlBar(bar, newState, 1)

View File

@ -0,0 +1,69 @@
# dlgappcore.
#
# base classes for dialog based apps.
from . import app
import win32ui
import win32con
import win32api
import sys
from pywin.mfc import dialog
error = "Dialog Application Error"
class AppDialog(dialog.Dialog):
"The dialog box for the application"
def __init__(self, id, dll=None):
self.iconId = win32ui.IDR_MAINFRAME
dialog.Dialog.__init__(self, id, dll)
def OnInitDialog(self):
return dialog.Dialog.OnInitDialog(self)
# Provide support for a dlg app using an icon
def OnPaint(self):
if not self.IsIconic(): return self._obj_.OnPaint()
self.DefWindowProc(win32con.WM_ICONERASEBKGND, dc.GetHandleOutput(), 0)
left, top, right, bottom = self.GetClientRect()
left = (right - win32api.GetSystemMetrics(win32con.SM_CXICON)) >> 1
top = (bottom - win32api.GetSystemMetrics(win32con.SM_CYICON)) >> 1
hIcon = win32ui.GetApp().LoadIcon(self.iconId)
self.GetDC().DrawIcon((left, top), hIcon)
# Only needed to provide a minimized icon (and this seems
# less important under win95/NT4
def OnEraseBkgnd(self, dc):
if self.IsIconic():
return 1
else:
return self._obj_.OnEraseBkgnd(dc)
def OnQueryDragIcon(self):
return win32ui.GetApp().LoadIcon(self.iconId)
def PreDoModal(self):
pass
class DialogApp(app.CApp):
"An application class, for an app with main dialog box"
def InitInstance(self):
# win32ui.SetProfileFileName('dlgapp.ini')
win32ui.LoadStdProfileSettings()
win32ui.EnableControlContainer()
win32ui.Enable3dControls()
self.dlg = self.frame = self.CreateDialog()
if self.frame is None:
raise error("No dialog was created by CreateDialog()")
return
self._obj_.InitDlgInstance(self.dlg)
self.PreDoModal()
self.dlg.PreDoModal()
self.dlg.DoModal()
def CreateDialog(self):
pass
def PreDoModal(self):
pass

View File

@ -0,0 +1,200 @@
# ModuleBrowser.py - A view that provides a module browser for an editor document.
import pywin.mfc.docview
import win32ui
import win32con
import commctrl
import win32api
from pywin.tools import hierlist, browser
import pywin.framework.scriptutils
import afxres
import pyclbr
class HierListCLBRModule(hierlist.HierListItem):
def __init__(self, modName, clbrdata):
self.modName = modName
self.clbrdata = clbrdata
def GetText(self):
return self.modName
def GetSubList(self):
ret = []
for item in self.clbrdata.values():
if item.__class__ != pyclbr.Class: # ie, it is a pyclbr Function instance (only introduced post 1.5.2)
ret.append(HierListCLBRFunction( item ) )
else:
ret.append(HierListCLBRClass( item) )
ret.sort()
return ret
def IsExpandable(self):
return 1
class HierListCLBRItem(hierlist.HierListItem):
def __init__(self, name, file, lineno, suffix = ""):
self.name = str(name)
self.file = file
self.lineno = lineno
self.suffix = suffix
def __lt__(self, other):
return self.name < other.name
def __eq__(self, other):
return self.name == other.name
def GetText(self):
return self.name + self.suffix
def TakeDefaultAction(self):
if self.file:
pywin.framework.scriptutils.JumpToDocument(self.file, self.lineno, bScrollToTop = 1)
else:
win32ui.SetStatusText("Can not locate the source code for this object.")
def PerformItemSelected(self):
if self.file is None:
msg = "%s - source can not be located." % (self.name, )
else:
msg = "%s defined at line %d of %s" % (self.name, self.lineno, self.file)
win32ui.SetStatusText(msg)
class HierListCLBRClass(HierListCLBRItem):
def __init__(self, clbrclass, suffix = ""):
try:
name = clbrclass.name
file = clbrclass.file
lineno = clbrclass.lineno
self.super = clbrclass.super
self.methods = clbrclass.methods
except AttributeError:
name = clbrclass
file = lineno = None
self.super = []; self.methods = {}
HierListCLBRItem.__init__(self, name, file, lineno, suffix)
def GetSubList(self):
r1 = []
for c in self.super:
r1.append(HierListCLBRClass(c, " (Parent class)"))
r1.sort()
r2=[]
for meth, lineno in self.methods.items():
r2.append(HierListCLBRMethod(meth, self.file, lineno))
r2.sort()
return r1+r2
def IsExpandable(self):
return len(self.methods) + len(self.super)
def GetBitmapColumn(self):
return 21
class HierListCLBRFunction(HierListCLBRItem):
def __init__(self, clbrfunc, suffix = ""):
name = clbrfunc.name
file = clbrfunc.file
lineno = clbrfunc.lineno
HierListCLBRItem.__init__(self, name, file, lineno, suffix)
def GetBitmapColumn(self):
return 22
class HierListCLBRMethod(HierListCLBRItem):
def GetBitmapColumn(self):
return 22
class HierListCLBRErrorItem(hierlist.HierListItem):
def __init__(self, text):
self.text = text
def GetText(self):
return self.text
def GetSubList(self):
return [HierListCLBRErrorItem(self.text)]
def IsExpandable(self):
return 0
class HierListCLBRErrorRoot(HierListCLBRErrorItem):
def IsExpandable(self):
return 1
class BrowserView(pywin.mfc.docview.TreeView):
def OnInitialUpdate(self):
self.list = None
rc = self._obj_.OnInitialUpdate()
self.HookMessage(self.OnSize, win32con.WM_SIZE)
self.bDirty = 0
self.destroying = 0
return rc
def DestroyBrowser(self):
self.DestroyList()
def OnActivateView(self, activate, av, dv):
# print "AV", self.bDirty, activate
if activate:
self.CheckRefreshList()
return self._obj_.OnActivateView(activate, av, dv)
def _MakeRoot(self):
path = self.GetDocument().GetPathName()
if not path:
return HierListCLBRErrorRoot("Error: Can not browse a file until it is saved")
else:
mod, path = pywin.framework.scriptutils.GetPackageModuleName(path)
if self.bDirty:
what = "Refreshing"
# Hack for pyclbr being too smart
try:
del pyclbr._modules[mod]
except (KeyError, AttributeError):
pass
else:
what = "Building"
win32ui.SetStatusText("%s class list - please wait..." % (what,), 1)
win32ui.DoWaitCursor(1)
try:
reader = pyclbr.readmodule_ex # new version post 1.5.2
except AttributeError:
reader = pyclbr.readmodule
try:
data = reader(mod, [path])
if data:
return HierListCLBRModule(mod, data)
else:
return HierListCLBRErrorRoot("No Python classes in module.")
finally:
win32ui.DoWaitCursor(0)
win32ui.SetStatusText(win32ui.LoadString(afxres.AFX_IDS_IDLEMESSAGE))
def DestroyList(self):
self.destroying = 1
list = getattr(self, "list", None) # If the document was not successfully opened, we may not have a list.
self.list = None
if list is not None:
list.HierTerm()
self.destroying = 0
def CheckMadeList(self):
if self.list is not None or self.destroying: return
self.rootitem = root = self._MakeRoot()
self.list = list = hierlist.HierListWithItems( root, win32ui.IDB_BROWSER_HIER)
list.HierInit(self.GetParentFrame(), self)
list.SetStyle(commctrl.TVS_HASLINES | commctrl.TVS_LINESATROOT | commctrl.TVS_HASBUTTONS)
def CheckRefreshList(self):
if self.bDirty:
if self.list is None:
self.CheckMadeList()
else:
new_root = self._MakeRoot()
if self.rootitem.__class__==new_root.__class__==HierListCLBRModule:
self.rootitem.modName = new_root.modName
self.rootitem.clbrdata = new_root.clbrdata
self.list.Refresh()
else:
self.list.AcceptRoot(self._MakeRoot())
self.bDirty = 0
def OnSize(self, params):
lparam = params[3]
w = win32api.LOWORD(lparam)
h = win32api.HIWORD(lparam)
if w != 0:
self.CheckMadeList()
elif w == 0:
self.DestroyList()
return 1
def _UpdateUIForState(self):
self.bDirty = 1

View File

@ -0,0 +1,90 @@
# __init__ for the Pythonwin editor package.
#
# We used to support optional editors - eg, color or non-color.
#
# This really isnt necessary with Scintilla, and scintilla
# is getting so deeply embedded that it was too much work.
import win32ui, sys, win32con
defaultCharacterFormat = (-402653169, 0, 200, 0, 0, 0, 49, 'Courier New')
##def GetDefaultEditorModuleName():
## import pywin
## # If someone has set pywin.editormodulename, then this is what we use
## try:
## prefModule = pywin.editormodulename
## except AttributeError:
## prefModule = win32ui.GetProfileVal("Editor","Module", "")
## return prefModule
##
##def WriteDefaultEditorModule(module):
## try:
## module = module.__name__
## except:
## pass
## win32ui.WriteProfileVal("Editor", "Module", module)
def LoadDefaultEditor():
pass
## prefModule = GetDefaultEditorModuleName()
## restorePrefModule = None
## mod = None
## if prefModule:
## try:
## mod = __import__(prefModule)
## except 'xx':
## msg = "Importing your preferred editor ('%s') failed.\n\nError %s: %s\n\nAn attempt will be made to load the default editor.\n\nWould you like this editor disabled in the future?" % (prefModule, sys.exc_info()[0], sys.exc_info()[1])
## rc = win32ui.MessageBox(msg, "Error importing editor", win32con.MB_YESNO)
## if rc == win32con.IDNO:
## restorePrefModule = prefModule
## WriteDefaultEditorModule("")
## del rc
##
## try:
## # Try and load the default one - dont catch errors here.
## if mod is None:
## prefModule = "pywin.framework.editor.color.coloreditor"
## mod = __import__(prefModule)
##
## # Get at the real module.
## mod = sys.modules[prefModule]
##
## # Do a "from mod import *"
## globals().update(mod.__dict__)
##
## finally:
## # Restore the users default editor if it failed and they requested not to disable it.
## if restorePrefModule:
## WriteDefaultEditorModule(restorePrefModule)
def GetEditorOption(option, defaultValue, min=None, max = None):
rc = win32ui.GetProfileVal("Editor", option, defaultValue)
if min is not None and rc < min: rc = defaultValue
if max is not None and rc > max: rc = defaultValue
return rc
def SetEditorOption(option, newValue):
win32ui.WriteProfileVal("Editor", option, newValue)
def DeleteEditorOption(option):
try:
win32ui.WriteProfileVal("Editor", option, None)
except win32ui.error:
pass
# Load and save font tuples
def GetEditorFontOption(option, default = None):
if default is None: default = defaultCharacterFormat
fmt = GetEditorOption( option, "" )
if fmt == "": return default
try:
return eval(fmt)
except:
print("WARNING: Invalid font setting in registry - setting ignored")
return default
def SetEditorFontOption(option, newValue):
SetEditorOption(option, str(newValue))
from pywin.framework.editor.color.coloreditor import editorTemplate

View File

@ -0,0 +1,525 @@
# Color Editor originally by Neil Hodgson, but restructured by mh to integrate
# even tighter into Pythonwin.
import win32ui
import win32con
import win32api
import sys
import pywin.scintilla.keycodes
from pywin.scintilla import bindings
from pywin.framework.editor import GetEditorOption, SetEditorOption, GetEditorFontOption, SetEditorFontOption, defaultCharacterFormat
#from pywin.framework.editor import EditorPropertyPage
MSG_CHECK_EXTERNAL_FILE = win32con.WM_USER+1999 ## WARNING: Duplicated in document.py and editor.py
# Define a few common markers
MARKER_BOOKMARK = 0
MARKER_BREAKPOINT = 1
MARKER_CURRENT = 2
from pywin.debugger import dbgcon
from pywin.scintilla.document import CScintillaDocument
from pywin.framework.editor.document import EditorDocumentBase
from pywin.scintilla import scintillacon # For the marker definitions
import pywin.scintilla.view
class SyntEditDocument(EditorDocumentBase):
"A SyntEdit document. "
def OnDebuggerStateChange(self, state):
self._ApplyOptionalToViews("OnDebuggerStateChange", state)
def HookViewNotifications(self, view):
EditorDocumentBase.HookViewNotifications(self, view)
view.SCISetUndoCollection(1)
def FinalizeViewCreation(self, view):
EditorDocumentBase.FinalizeViewCreation(self, view)
if view==self.GetFirstView():
self.GetDocTemplate().CheckIDLEMenus(view.idle)
SyntEditViewParent=pywin.scintilla.view.CScintillaView
class SyntEditView(SyntEditViewParent):
"A view of a SyntEdit. Obtains data from document."
def __init__(self, doc):
SyntEditViewParent.__init__(self, doc)
self.bCheckingFile = 0
def OnInitialUpdate(self):
SyntEditViewParent.OnInitialUpdate(self)
self.HookMessage(self.OnRClick,win32con.WM_RBUTTONDOWN)
for id in [win32ui.ID_VIEW_FOLD_COLLAPSE, win32ui.ID_VIEW_FOLD_COLLAPSE_ALL,
win32ui.ID_VIEW_FOLD_EXPAND, win32ui.ID_VIEW_FOLD_EXPAND_ALL]:
self.HookCommand(self.OnCmdViewFold, id)
self.HookCommandUpdate(self.OnUpdateViewFold, id)
self.HookCommand(self.OnCmdViewFoldTopLevel, win32ui.ID_VIEW_FOLD_TOPLEVEL)
# Define the markers
# self.SCIMarkerDeleteAll()
self.SCIMarkerDefineAll(MARKER_BOOKMARK, scintillacon.SC_MARK_ROUNDRECT, win32api.RGB(0x0, 0x0, 0x0), win32api.RGB(0, 0xff, 0xff))
self.SCIMarkerDefine(MARKER_CURRENT, scintillacon.SC_MARK_ARROW)
self.SCIMarkerSetBack(MARKER_CURRENT, win32api.RGB(0xff, 0xff, 0x00))
# Define the folding markers
if 1: #traditional markers
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDEROPEN, scintillacon.SC_MARK_MINUS, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDER, scintillacon.SC_MARK_PLUS, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDERSUB, scintillacon.SC_MARK_EMPTY, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDERTAIL, scintillacon.SC_MARK_EMPTY, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDEREND, scintillacon.SC_MARK_EMPTY, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDEROPENMID, scintillacon.SC_MARK_EMPTY, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDERMIDTAIL, scintillacon.SC_MARK_EMPTY, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
else: # curved markers
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDEROPEN, scintillacon.SC_MARK_CIRCLEMINUS, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDER, scintillacon.SC_MARK_CIRCLEPLUS, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDERSUB, scintillacon.SC_MARK_VLINE, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDERTAIL, scintillacon.SC_MARK_LCORNERCURVE, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDEREND, scintillacon.SC_MARK_CIRCLEPLUSCONNECTED, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDEROPENMID, scintillacon.SC_MARK_CIRCLEMINUSCONNECTED, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefineAll(scintillacon.SC_MARKNUM_FOLDERMIDTAIL, scintillacon.SC_MARK_TCORNERCURVE, win32api.RGB(0xff, 0xff, 0xff), win32api.RGB(0, 0, 0))
self.SCIMarkerDefine(MARKER_BREAKPOINT, scintillacon.SC_MARK_CIRCLE)
# Marker background depends on debugger state
self.SCIMarkerSetFore(MARKER_BREAKPOINT, win32api.RGB(0x0, 0, 0))
# Get the current debugger state.
try:
import pywin.debugger
if pywin.debugger.currentDebugger is None:
state = dbgcon.DBGSTATE_NOT_DEBUGGING
else:
state = pywin.debugger.currentDebugger.debuggerState
except ImportError:
state = dbgcon.DBGSTATE_NOT_DEBUGGING
self.OnDebuggerStateChange(state)
def _GetSubConfigNames(self):
return ["editor"] # Allow [Keys:Editor] sections to be specific to us
def DoConfigChange(self):
SyntEditViewParent.DoConfigChange(self)
tabSize = GetEditorOption("Tab Size", 4, 2)
indentSize = GetEditorOption("Indent Size", 4, 2)
bUseTabs = GetEditorOption("Use Tabs", 0)
bSmartTabs = GetEditorOption("Smart Tabs", 1)
ext = self.idle.IDLEExtension("AutoIndent") # Required extension.
self.SCISetViewWS( GetEditorOption("View Whitespace", 0) )
self.SCISetViewEOL( GetEditorOption("View EOL", 0) )
self.SCISetIndentationGuides( GetEditorOption("View Indentation Guides", 0) )
if GetEditorOption("Right Edge Enabled", 0):
mode = scintillacon.EDGE_BACKGROUND
else:
mode = scintillacon.EDGE_NONE
self.SCISetEdgeMode(mode)
self.SCISetEdgeColumn( GetEditorOption("Right Edge Column", 75) )
self.SCISetEdgeColor( GetEditorOption("Right Edge Color", win32api.RGB(0xef, 0xef, 0xef)))
width = GetEditorOption("Marker Margin Width", 16)
self.SCISetMarginWidthN(1, width)
width = GetEditorOption("Fold Margin Width", 12)
self.SCISetMarginWidthN(2, width)
width = GetEditorOption("Line Number Margin Width", 0)
self.SCISetMarginWidthN(0, width)
self.bFolding = GetEditorOption("Enable Folding", 1)
fold_flags = 0
self.SendScintilla(scintillacon.SCI_SETMODEVENTMASK, scintillacon.SC_MOD_CHANGEFOLD);
if self.bFolding:
if GetEditorOption("Fold Lines", 1):
fold_flags = 16
self.SCISetProperty("fold", self.bFolding)
self.SCISetFoldFlags(fold_flags)
tt_color = GetEditorOption("Tab Timmy Color", win32api.RGB(0xff, 0, 0))
self.SendScintilla(scintillacon.SCI_INDICSETFORE, 1, tt_color)
tt_use = GetEditorOption("Use Tab Timmy", 1)
if tt_use:
self.SCISetProperty("tab.timmy.whinge.level", "1")
# Auto-indent has very complicated behaviour. In a nutshell, the only
# way to get sensible behaviour from it is to ensure tabwidth != indentsize.
# Further, usetabs will only ever go from 1->0, never 0->1.
# This is _not_ the behaviour Pythonwin wants:
# * Tab width is arbitary, so should have no impact on smarts.
# * bUseTabs setting should reflect how new files are created, and
# if Smart Tabs disabled, existing files are edited
# * If "Smart Tabs" is enabled, bUseTabs should have no bearing
# for existing files (unless of course no context can be determined)
#
# So for smart tabs we configure the widget with completely dummy
# values (ensuring tabwidth != indentwidth), ask it to guess, then
# look at the values it has guessed, and re-configure
if bSmartTabs:
ext.config(usetabs=1, tabwidth=5, indentwidth=4)
ext.set_indentation_params(1)
if ext.indentwidth==5:
# Either 5 literal spaces, or a single tab character. Assume a tab
usetabs = 1
indentwidth = tabSize
else:
# Either Indented with spaces, and indent size has been guessed or
# an empty file (or no context found - tough!)
if self.GetTextLength()==0: # emtpy
usetabs = bUseTabs
indentwidth = indentSize
else: # guessed.
indentwidth = ext.indentwidth
usetabs = 0
# Tab size can never be guessed - set at user preference.
ext.config(usetabs=usetabs, indentwidth=indentwidth, tabwidth=tabSize)
else:
# Dont want smart-tabs - just set the options!
ext.config(usetabs=bUseTabs, tabwidth=tabSize, indentwidth=indentSize)
self.SCISetIndent(indentSize)
self.SCISetTabWidth(tabSize)
def OnDebuggerStateChange(self, state):
if state == dbgcon.DBGSTATE_NOT_DEBUGGING:
# Indicate breakpoints arent really usable.
# Not quite white - useful when no marker margin, so set as background color.
self.SCIMarkerSetBack(MARKER_BREAKPOINT, win32api.RGB(0xef, 0xef, 0xef))
else:
# A light-red, so still readable when no marker margin.
self.SCIMarkerSetBack(MARKER_BREAKPOINT, win32api.RGB(0xff, 0x80, 0x80))
def HookDocumentHandlers(self):
SyntEditViewParent.HookDocumentHandlers(self)
self.HookMessage(self.OnCheckExternalDocumentUpdated,MSG_CHECK_EXTERNAL_FILE)
def HookHandlers(self):
SyntEditViewParent.HookHandlers(self)
self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
def _PrepareUserStateChange(self):
return self.GetSel(), self.GetFirstVisibleLine()
def _EndUserStateChange(self, info):
scrollOff = info[1] - self.GetFirstVisibleLine()
if scrollOff:
self.LineScroll(scrollOff)
# Make sure we dont reset the cursor beyond the buffer.
max = self.GetTextLength()
newPos = min(info[0][0], max), min(info[0][1], max)
self.SetSel(newPos)
#######################################
# The Windows Message or Notify handlers.
#######################################
def OnMarginClick(self, std, extra):
notify = self.SCIUnpackNotifyMessage(extra)
if notify.margin==2: # Our fold margin
line_click = self.LineFromChar(notify.position)
# max_line = self.GetLineCount()
if self.SCIGetFoldLevel(line_click) & scintillacon.SC_FOLDLEVELHEADERFLAG:
# If a fold point.
self.SCIToggleFold(line_click)
return 1
def OnSetFocus(self,msg):
# Even though we use file change notifications, we should be very sure about it here.
self.OnCheckExternalDocumentUpdated(msg)
return 1
def OnCheckExternalDocumentUpdated(self, msg):
if self.bCheckingFile: return
self.bCheckingFile = 1
self.GetDocument().CheckExternalDocumentUpdated()
self.bCheckingFile = 0
def OnRClick(self,params):
menu = win32ui.CreatePopupMenu()
self.AppendMenu(menu, "&Locate module", "LocateModule")
self.AppendMenu(menu, flags=win32con.MF_SEPARATOR)
self.AppendMenu(menu, "&Undo", "EditUndo")
self.AppendMenu(menu, '&Redo', 'EditRedo')
self.AppendMenu(menu, flags=win32con.MF_SEPARATOR)
self.AppendMenu(menu, 'Cu&t', 'EditCut')
self.AppendMenu(menu, '&Copy', 'EditCopy')
self.AppendMenu(menu, '&Paste', 'EditPaste')
self.AppendMenu(menu, flags=win32con.MF_SEPARATOR)
self.AppendMenu(menu, '&Select all', 'EditSelectAll')
self.AppendMenu(menu, 'View &Whitespace', 'ViewWhitespace', checked=self.SCIGetViewWS())
self.AppendMenu(menu, "&Fixed Font", "ViewFixedFont", checked = self._GetColorizer().bUseFixed)
self.AppendMenu(menu, flags=win32con.MF_SEPARATOR)
self.AppendMenu(menu, "&Goto line...", "GotoLine")
submenu = win32ui.CreatePopupMenu()
newitems = self.idle.GetMenuItems("edit")
for text, event in newitems:
self.AppendMenu(submenu, text, event)
flags=win32con.MF_STRING|win32con.MF_ENABLED|win32con.MF_POPUP
menu.AppendMenu(flags, submenu.GetHandle(), "&Source code")
flags = win32con.TPM_LEFTALIGN|win32con.TPM_LEFTBUTTON|win32con.TPM_RIGHTBUTTON
menu.TrackPopupMenu(params[5], flags, self)
return 0
def OnCmdViewFold(self, cid, code): # Handle the menu command
if cid == win32ui.ID_VIEW_FOLD_EXPAND_ALL:
self.FoldExpandAllEvent(None)
elif cid == win32ui.ID_VIEW_FOLD_EXPAND:
self.FoldExpandEvent(None)
elif cid == win32ui.ID_VIEW_FOLD_COLLAPSE_ALL:
self.FoldCollapseAllEvent(None)
elif cid == win32ui.ID_VIEW_FOLD_COLLAPSE:
self.FoldCollapseEvent(None)
else:
print("Unknown collapse/expand ID")
def OnUpdateViewFold(self, cmdui): # Update the tick on the UI.
if not self.bFolding:
cmdui.Enable(0)
return
id = cmdui.m_nID
if id in [win32ui.ID_VIEW_FOLD_EXPAND_ALL, win32ui.ID_VIEW_FOLD_COLLAPSE_ALL]:
cmdui.Enable()
else:
enable = 0
lineno = self.LineFromChar(self.GetSel()[0])
foldable = self.SCIGetFoldLevel(lineno) & scintillacon.SC_FOLDLEVELHEADERFLAG
is_expanded = self.SCIGetFoldExpanded(lineno)
if id == win32ui.ID_VIEW_FOLD_EXPAND:
if foldable and not is_expanded:
enable = 1
elif id == win32ui.ID_VIEW_FOLD_COLLAPSE:
if foldable and is_expanded:
enable = 1
cmdui.Enable(enable)
def OnCmdViewFoldTopLevel(self, cid, code): # Handle the menu command
self.FoldTopLevelEvent(None)
#######################################
# The Events
#######################################
def ToggleBookmarkEvent(self, event, pos = -1):
"""Toggle a bookmark at the specified or current position
"""
if pos==-1:
pos, end = self.GetSel()
startLine = self.LineFromChar(pos)
self.GetDocument().MarkerToggle(startLine+1, MARKER_BOOKMARK)
return 0
def GotoNextBookmarkEvent(self, event, fromPos=-1):
""" Move to the next bookmark
"""
if fromPos==-1:
fromPos, end = self.GetSel()
startLine = self.LineFromChar(fromPos)+1 # Zero based line to start
nextLine = self.GetDocument().MarkerGetNext(startLine+1, MARKER_BOOKMARK)-1
if nextLine<0:
nextLine = self.GetDocument().MarkerGetNext(0, MARKER_BOOKMARK)-1
if nextLine <0 or nextLine == startLine-1:
win32api.MessageBeep()
else:
self.SCIEnsureVisible(nextLine)
self.SCIGotoLine(nextLine)
return 0
def TabKeyEvent(self, event):
"""Insert an indent. If no selection, a single indent, otherwise a block indent
"""
# Handle auto-complete first.
if self.SCIAutoCActive():
self.SCIAutoCComplete()
return 0
# Call the IDLE event.
return self.bindings.fire("<<smart-indent>>", event)
def EnterKeyEvent(self, event):
"""Handle the enter key with special handling for auto-complete
"""
# Handle auto-complete first.
if self.SCIAutoCActive():
self.SCIAutoCComplete()
self.SCIAutoCCancel()
# Call the IDLE event.
return self.bindings.fire("<<newline-and-indent>>", event)
def ShowInteractiveWindowEvent(self, event):
import pywin.framework.interact
pywin.framework.interact.ShowInteractiveWindow()
def FoldTopLevelEvent(self, event = None):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
try:
self.Colorize()
maxLine = self.GetLineCount()
# Find the first line, and check out its state.
for lineSeek in range(maxLine):
if self.SCIGetFoldLevel(lineSeek) & scintillacon.SC_FOLDLEVELHEADERFLAG:
expanding = not self.SCIGetFoldExpanded(lineSeek)
break
else:
# no folds here!
return
for lineSeek in range(lineSeek, maxLine):
level = self.SCIGetFoldLevel(lineSeek)
level_no = level & scintillacon.SC_FOLDLEVELNUMBERMASK - scintillacon.SC_FOLDLEVELBASE
is_header = level & scintillacon.SC_FOLDLEVELHEADERFLAG
# print lineSeek, level_no, is_header
if level_no == 0 and is_header:
if (expanding and not self.SCIGetFoldExpanded(lineSeek)) or \
(not expanding and self.SCIGetFoldExpanded(lineSeek)):
self.SCIToggleFold(lineSeek)
finally:
win32ui.DoWaitCursor(-1)
def FoldExpandSecondLevelEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
## I think this is needed since Scintilla may not have
## already formatted parts of file outside visible window.
self.Colorize()
levels=[scintillacon.SC_FOLDLEVELBASE]
## Scintilla's level number is based on amount of whitespace indentation
for lineno in range(self.GetLineCount()):
level = self.SCIGetFoldLevel(lineno)
if not level & scintillacon.SC_FOLDLEVELHEADERFLAG:
continue
curr_level = level & scintillacon.SC_FOLDLEVELNUMBERMASK
if curr_level > levels[-1]:
levels.append(curr_level)
try:
level_ind=levels.index(curr_level)
except ValueError:
## probably syntax error in source file, bail
break
levels=levels[:level_ind+1]
if level_ind == 1 and not self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldCollapseSecondLevelEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
## I think this is needed since Scintilla may not have
## already formatted parts of file outside visible window.
self.Colorize()
levels=[scintillacon.SC_FOLDLEVELBASE]
## Scintilla's level number is based on amount of whitespace indentation
for lineno in range(self.GetLineCount()):
level = self.SCIGetFoldLevel(lineno)
if not level & scintillacon.SC_FOLDLEVELHEADERFLAG:
continue
curr_level = level & scintillacon.SC_FOLDLEVELNUMBERMASK
if curr_level > levels[-1]:
levels.append(curr_level)
try:
level_ind=levels.index(curr_level)
except ValueError:
## probably syntax error in source file, bail
break
levels=levels[:level_ind+1]
if level_ind == 1 and self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldExpandEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
lineno = self.LineFromChar(self.GetSel()[0])
if self.SCIGetFoldLevel(lineno) & scintillacon.SC_FOLDLEVELHEADERFLAG and \
not self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldExpandAllEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
for lineno in range(0, self.GetLineCount()):
if self.SCIGetFoldLevel(lineno) & scintillacon.SC_FOLDLEVELHEADERFLAG and \
not self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldCollapseEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
lineno = self.LineFromChar(self.GetSel()[0])
if self.SCIGetFoldLevel(lineno) & scintillacon.SC_FOLDLEVELHEADERFLAG and \
self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
def FoldCollapseAllEvent(self, event):
if not self.bFolding:
return 1
win32ui.DoWaitCursor(1)
self.Colorize()
for lineno in range(0, self.GetLineCount()):
if self.SCIGetFoldLevel(lineno) & scintillacon.SC_FOLDLEVELHEADERFLAG and \
self.SCIGetFoldExpanded(lineno):
self.SCIToggleFold(lineno)
win32ui.DoWaitCursor(-1)
from pywin.framework.editor.frame import EditorFrame
class SplitterFrame(EditorFrame):
def OnCreate(self, cs):
self.HookCommand(self.OnWindowSplit, win32ui.ID_WINDOW_SPLIT)
return 1
def OnWindowSplit(self, id, code):
self.GetDlgItem(win32ui.AFX_IDW_PANE_FIRST).DoKeyboardSplit()
return 1
from pywin.framework.editor.template import EditorTemplateBase
class SyntEditTemplate(EditorTemplateBase):
def __init__(self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None):
if makeDoc is None: makeDoc = SyntEditDocument
if makeView is None: makeView = SyntEditView
if makeFrame is None: makeFrame = SplitterFrame
self.bSetMenus = 0
EditorTemplateBase.__init__(self, res, makeDoc, makeFrame, makeView)
def CheckIDLEMenus(self, idle):
if self.bSetMenus: return
self.bSetMenus = 1
submenu = win32ui.CreatePopupMenu()
newitems = idle.GetMenuItems("edit")
flags=win32con.MF_STRING|win32con.MF_ENABLED
for text, event in newitems:
id = bindings.event_to_commands.get(event)
if id is not None:
keyname = pywin.scintilla.view.configManager.get_key_binding( event, ["editor"] )
if keyname is not None:
text = text + "\t" + keyname
submenu.AppendMenu(flags, id, text)
mainMenu = self.GetSharedMenu()
editMenu = mainMenu.GetSubMenu(1)
editMenu.AppendMenu(win32con.MF_SEPARATOR, 0, "")
editMenu.AppendMenu(win32con.MF_STRING | win32con.MF_POPUP | win32con.MF_ENABLED, submenu.GetHandle(), "&Source Code")
def _CreateDocTemplate(self, resourceId):
return win32ui.CreateDocTemplate(resourceId)
def CreateWin32uiDocument(self):
return self.DoCreateDoc()
def GetPythonPropertyPages(self):
"""Returns a list of property pages
"""
from pywin.scintilla import configui
return EditorTemplateBase.GetPythonPropertyPages(self) + [configui.ScintillaFormatPropertyPage()]
# For debugging purposes, when this module may be reloaded many times.
try:
win32ui.GetApp().RemoveDocTemplate(editorTemplate)
except NameError:
pass
editorTemplate = SyntEditTemplate()
win32ui.GetApp().AddDocTemplate(editorTemplate)

View File

@ -0,0 +1,257 @@
from pywin.mfc import dialog
from . import document
import win32ui
import win32con
import win32api
from pywin.framework.editor import GetEditorOption, SetEditorOption, DeleteEditorOption, GetEditorFontOption, SetEditorFontOption, defaultCharacterFormat, editorTemplate
import pywin.scintilla.config
# The standard 16 color VGA palette should always be possible
paletteVGA = ( ("Black",0,0,0), ("Navy",0,0,128), ("Green",0,128,0), ("Cyan",0,128,128),
("Maroon",128,0,0), ("Purple",128,0,128), ("Olive",128,128,0), ("Gray",128,128,128),
("Silver",192,192,192), ("Blue",0,0,255), ("Lime",0,255,0), ("Aqua",0,255,255),
("Red",255,0,0), ("Fuchsia",255,0,255), ("Yellow",255,255,0), ("White",255,255,255) )
######################################################
#
# Property Page for editor options
#
class EditorPropertyPage(dialog.PropertyPage):
def __init__(self):
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_EDITOR)
self.autooptions = []
self._AddEditorOption(win32ui.IDC_AUTO_RELOAD, "i", "Auto Reload", 1)
self._AddEditorOption(win32ui.IDC_COMBO1, "i", "Backup Type", document.BAK_DOT_BAK_BAK_DIR)
self._AddEditorOption(win32ui.IDC_AUTOCOMPLETE, "i", "Autocomplete Attributes", 1)
self._AddEditorOption(win32ui.IDC_CALLTIPS, "i", "Show Call Tips", 1)
self._AddEditorOption(win32ui.IDC_MARGIN_LINENUMBER, "i", "Line Number Margin Width", 0)
self._AddEditorOption(win32ui.IDC_RADIO1, "i", "MarkersInMargin", None)
self._AddEditorOption(win32ui.IDC_MARGIN_MARKER, "i", "Marker Margin Width", None)
self["Marker Margin Width"] = GetEditorOption("Marker Margin Width", 16)
# Folding
self._AddEditorOption(win32ui.IDC_MARGIN_FOLD, "i", "Fold Margin Width", 12)
self._AddEditorOption(win32ui.IDC_FOLD_ENABLE, "i", "Enable Folding", 1)
self._AddEditorOption(win32ui.IDC_FOLD_ON_OPEN, "i", "Fold On Open", 0)
self._AddEditorOption(win32ui.IDC_FOLD_SHOW_LINES, "i", "Fold Lines", 1)
# Right edge.
self._AddEditorOption(win32ui.IDC_RIGHTEDGE_ENABLE, "i", "Right Edge Enabled", 0)
self._AddEditorOption(win32ui.IDC_RIGHTEDGE_COLUMN, "i", "Right Edge Column", 75)
# Source control, etc
self.AddDDX(win32ui.IDC_VSS_INTEGRATE, "bVSS")
self.AddDDX(win32ui.IDC_KEYBOARD_CONFIG, "Configs", "l")
self["Configs"] = pywin.scintilla.config.find_config_files()
def _AddEditorOption(self, idd, typ, optionName, defaultVal):
self.AddDDX(idd, optionName, typ)
# some options are "derived" - ie, can be implied from others
# (eg, "view markers in background" is implied from "markerMarginWidth==0"
# So we don't actually store these values, but they do still get DDX support.
if defaultVal is not None:
self[optionName] = GetEditorOption(optionName, defaultVal)
self.autooptions.append((optionName, defaultVal))
def OnInitDialog(self):
for name, val in self.autooptions:
self[name] = GetEditorOption(name, val)
# Note that these MUST be in the same order as the BAK constants.
cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
cbo.AddString("None")
cbo.AddString(".BAK File")
cbo.AddString("TEMP dir")
cbo.AddString("Own dir")
# Source Safe
bVSS = GetEditorOption("Source Control Module", "") == "pywin.framework.editor.vss"
self['bVSS'] = bVSS
edit = self.GetDlgItem(win32ui.IDC_RIGHTEDGE_SAMPLE)
edit.SetWindowText("Sample Color")
rc = dialog.PropertyPage.OnInitDialog(self)
try:
self.GetDlgItem(win32ui.IDC_KEYBOARD_CONFIG).SelectString(-1, GetEditorOption("Keyboard Config", "default"))
except win32ui.error:
import traceback
traceback.print_exc()
pass
self.HookCommand(self.OnButSimple, win32ui.IDC_FOLD_ENABLE)
self.HookCommand(self.OnButSimple, win32ui.IDC_RADIO1)
self.HookCommand(self.OnButSimple, win32ui.IDC_RADIO2)
self.HookCommand(self.OnButSimple, win32ui.IDC_RIGHTEDGE_ENABLE)
self.HookCommand(self.OnButEdgeColor, win32ui.IDC_RIGHTEDGE_DEFINE)
butMarginEnabled = self['Marker Margin Width'] > 0
self.GetDlgItem(win32ui.IDC_RADIO1).SetCheck(butMarginEnabled)
self.GetDlgItem(win32ui.IDC_RADIO2).SetCheck(not butMarginEnabled)
self.edgeColor = self.initialEdgeColor = GetEditorOption("Right Edge Color", win32api.RGB(0xef, 0xef, 0xef))
for spinner_id in (win32ui.IDC_SPIN1, win32ui.IDC_SPIN2, win32ui.IDC_SPIN3):
spinner=self.GetDlgItem(spinner_id)
spinner.SetRange(0,100)
self.UpdateUIForState()
return rc
def OnButSimple(self, id, code):
if code == win32con.BN_CLICKED:
self.UpdateUIForState()
def OnButEdgeColor(self, id, code):
if code == win32con.BN_CLICKED:
d = win32ui.CreateColorDialog(self.edgeColor, 0, self)
# Ensure the current color is a custom color (as it may not be in the swatch)
# plus some other nice gray scales.
ccs = [self.edgeColor]
for c in range(0xef, 0x4f, -0x10):
ccs.append(win32api.RGB(c,c,c))
d.SetCustomColors( ccs )
if d.DoModal() == win32con.IDOK:
self.edgeColor = d.GetColor()
self.UpdateUIForState()
def UpdateUIForState(self):
folding = self.GetDlgItem(win32ui.IDC_FOLD_ENABLE).GetCheck()
self.GetDlgItem(win32ui.IDC_FOLD_ON_OPEN).EnableWindow(folding)
self.GetDlgItem(win32ui.IDC_FOLD_SHOW_LINES).EnableWindow(folding)
widthEnabled = self.GetDlgItem(win32ui.IDC_RADIO1).GetCheck()
self.GetDlgItem(win32ui.IDC_MARGIN_MARKER).EnableWindow(widthEnabled)
self.UpdateData() # Ensure self[] is up to date with the control data.
if widthEnabled and self["Marker Margin Width"] == 0:
self["Marker Margin Width"] = 16
self.UpdateData(0) # Ensure control up to date with self[]
# Right edge
edgeEnabled = self.GetDlgItem(win32ui.IDC_RIGHTEDGE_ENABLE).GetCheck()
self.GetDlgItem(win32ui.IDC_RIGHTEDGE_COLUMN).EnableWindow(edgeEnabled)
self.GetDlgItem(win32ui.IDC_RIGHTEDGE_SAMPLE).EnableWindow(edgeEnabled)
self.GetDlgItem(win32ui.IDC_RIGHTEDGE_DEFINE).EnableWindow(edgeEnabled)
edit = self.GetDlgItem(win32ui.IDC_RIGHTEDGE_SAMPLE)
edit.SetBackgroundColor(0, self.edgeColor)
def OnOK(self):
for name, defVal in self.autooptions:
SetEditorOption(name, self[name])
# Margin width gets handled differently.
if self['MarkersInMargin'] == 0:
SetEditorOption("Marker Margin Width", self["Marker Margin Width"])
else:
SetEditorOption("Marker Margin Width", 0)
if self.edgeColor != self.initialEdgeColor:
SetEditorOption("Right Edge Color", self.edgeColor)
if self['bVSS']:
SetEditorOption("Source Control Module", "pywin.framework.editor.vss")
else:
if GetEditorOption("Source Control Module", "")=='pywin.framework.editor.vss':
SetEditorOption("Source Control Module", "")
# Keyboard config
configname = self.GetDlgItem(win32ui.IDC_KEYBOARD_CONFIG).GetWindowText()
if configname:
if configname == "default":
DeleteEditorOption("Keyboard Config")
else:
SetEditorOption("Keyboard Config", configname)
import pywin.scintilla.view
pywin.scintilla.view.LoadConfiguration()
# Now tell all views we have changed.
## for doc in editorTemplate.GetDocumentList():
## for view in doc.GetAllViews():
## try:
## fn = view.OnConfigChange
## except AttributeError:
## continue
## fn()
return 1
class EditorWhitespacePropertyPage(dialog.PropertyPage):
def __init__(self):
dialog.PropertyPage.__init__(self, win32ui.IDD_PP_TABS)
self.autooptions = []
self._AddEditorOption(win32ui.IDC_TAB_SIZE, "i", "Tab Size", 4)
self._AddEditorOption(win32ui.IDC_INDENT_SIZE, "i", "Indent Size", 4)
self._AddEditorOption(win32ui.IDC_USE_SMART_TABS, "i", "Smart Tabs", 1)
self._AddEditorOption(win32ui.IDC_VIEW_WHITESPACE, "i", "View Whitespace", 0)
self._AddEditorOption(win32ui.IDC_VIEW_EOL, "i", "View EOL", 0)
self._AddEditorOption(win32ui.IDC_VIEW_INDENTATIONGUIDES, "i", "View Indentation Guides", 0)
def _AddEditorOption(self, idd, typ, optionName, defaultVal):
self.AddDDX(idd, optionName, typ)
self[optionName] = GetEditorOption(optionName, defaultVal)
self.autooptions.append((optionName, defaultVal))
def OnInitDialog(self):
for name, val in self.autooptions:
self[name] = GetEditorOption(name, val)
rc = dialog.PropertyPage.OnInitDialog(self)
idc = win32ui.IDC_TABTIMMY_NONE
if GetEditorOption("Use Tab Timmy", 1):
idc = win32ui.IDC_TABTIMMY_IND
self.GetDlgItem(idc).SetCheck(1)
idc = win32ui.IDC_RADIO1
if GetEditorOption("Use Tabs", 0):
idc = win32ui.IDC_USE_TABS
self.GetDlgItem(idc).SetCheck(1)
tt_color = GetEditorOption("Tab Timmy Color", win32api.RGB(0xff, 0, 0))
self.cbo = self.GetDlgItem(win32ui.IDC_COMBO1)
for c in paletteVGA:
self.cbo.AddString(c[0])
sel = 0
for c in paletteVGA:
if tt_color == win32api.RGB(c[1], c[2], c[3]):
break
sel = sel + 1
else:
sel = -1
self.cbo.SetCurSel(sel)
self.HookCommand(self.OnButSimple, win32ui.IDC_TABTIMMY_NONE)
self.HookCommand(self.OnButSimple, win32ui.IDC_TABTIMMY_IND)
self.HookCommand(self.OnButSimple, win32ui.IDC_TABTIMMY_BG)
# Set ranges for the spinners.
for spinner_id in [win32ui.IDC_SPIN1, win32ui.IDC_SPIN2]:
spinner = self.GetDlgItem(spinner_id)
spinner.SetRange(1, 16)
return rc
def OnButSimple(self, id, code):
if code == win32con.BN_CLICKED:
self.UpdateUIForState()
def UpdateUIForState(self):
timmy = self.GetDlgItem(win32ui.IDC_TABTIMMY_NONE).GetCheck()
self.GetDlgItem(win32ui.IDC_COMBO1).EnableWindow(not timmy)
def OnOK(self):
for name, defVal in self.autooptions:
SetEditorOption(name, self[name])
SetEditorOption("Use Tabs", self.GetDlgItem(win32ui.IDC_USE_TABS).GetCheck())
SetEditorOption("Use Tab Timmy", self.GetDlgItem(win32ui.IDC_TABTIMMY_IND).GetCheck())
c = paletteVGA[self.cbo.GetCurSel()]
SetEditorOption("Tab Timmy Color", win32api.RGB(c[1], c[2], c[3]))
return 1
def testpp():
ps = dialog.PropertySheet("Editor Options")
ps.AddPage(EditorWhitespacePropertyPage())
ps.DoModal()
if __name__=='__main__':
testpp()

View File

@ -0,0 +1,332 @@
# We no longer support the old, non-colour editor!
from pywin.mfc import docview, object
from pywin.framework.editor import GetEditorOption
import win32ui
import os
import win32con
import string
import traceback
import win32api
import shutil
BAK_NONE=0
BAK_DOT_BAK=1
BAK_DOT_BAK_TEMP_DIR=2
BAK_DOT_BAK_BAK_DIR=3
MSG_CHECK_EXTERNAL_FILE = win32con.WM_USER+1999 ## WARNING: Duplicated in editor.py and coloreditor.py
import pywin.scintilla.document
ParentEditorDocument=pywin.scintilla.document.CScintillaDocument
class EditorDocumentBase(ParentEditorDocument):
def __init__(self, template):
self.bAutoReload = GetEditorOption("Auto Reload", 1)
self.bDeclinedReload = 0 # Has the user declined to reload.
self.fileStat = None
self.bReportedFileNotFound = 0
# what sort of bak file should I create.
# default to write to %temp%/bak/filename.ext
self.bakFileType=GetEditorOption("Backup Type", BAK_DOT_BAK_BAK_DIR)
self.watcherThread = FileWatchingThread(self)
self.watcherThread.CreateThread()
# Should I try and use VSS integration?
self.scModuleName=GetEditorOption("Source Control Module", "")
self.scModule = None # Loaded when first used.
ParentEditorDocument.__init__(self, template, template.CreateWin32uiDocument())
def OnCloseDocument(self ):
self.watcherThread.SignalStop()
return self._obj_.OnCloseDocument()
# def OnOpenDocument(self, name):
# rc = ParentEditorDocument.OnOpenDocument(self, name)
# self.GetFirstView()._SetLoadedText(self.text)
# self._DocumentStateChanged()
# return rc
def OnSaveDocument( self, fileName ):
win32ui.SetStatusText("Saving file...",1)
# rename to bak if required.
dir, basename = os.path.split(fileName)
if self.bakFileType==BAK_DOT_BAK:
bakFileName=dir+'\\'+os.path.splitext(basename)[0]+'.bak'
elif self.bakFileType==BAK_DOT_BAK_TEMP_DIR:
bakFileName=win32api.GetTempPath()+'\\'+os.path.splitext(basename)[0]+'.bak'
elif self.bakFileType==BAK_DOT_BAK_BAK_DIR:
tempPath=os.path.join(win32api.GetTempPath(),'bak')
try:
os.mkdir(tempPath,0)
except os.error:
pass
bakFileName=os.path.join(tempPath,basename)
try:
os.unlink(bakFileName) # raise NameError if no bakups wanted.
except (os.error, NameError):
pass
try:
# Do a copy as it might be on different volumes,
# and the file may be a hard-link, causing the link
# to follow the backup.
shutil.copy2(fileName, bakFileName)
except (os.error, NameError, IOError):
pass
try:
self.SaveFile(fileName)
except IOError as details:
win32ui.MessageBox("Error - could not save file\r\n\r\n%s"%details)
return 0
except (UnicodeEncodeError, LookupError) as details:
rc = win32ui.MessageBox("Encoding failed: \r\n%s"%details +
'\r\nPlease add desired source encoding as first line of file, eg \r\n' +
'# -*- coding: mbcs -*-\r\n\r\n' +
'If you continue, the file will be saved as binary and will\r\n' +
'not be valid in the declared encoding.\r\n\r\n' +
'Save the file as binary with an invalid encoding?',
"File save failed",
win32con.MB_YESNO | win32con.MB_DEFBUTTON2)
if rc==win32con.IDYES:
try:
self.SaveFile(fileName, encoding="latin-1")
except IOError as details:
win32ui.MessageBox("Error - could not save file\r\n\r\n%s"%details)
return 0
else:
return 0
self.SetModifiedFlag(0) # No longer dirty
self.bDeclinedReload = 0 # They probably want to know if it changes again!
win32ui.AddToRecentFileList(fileName)
self.SetPathName(fileName)
win32ui.SetStatusText("Ready")
self._DocumentStateChanged()
return 1
def FinalizeViewCreation(self, view):
ParentEditorDocument.FinalizeViewCreation(self, view)
if view == self.GetFirstView():
self._DocumentStateChanged()
if view.bFolding and GetEditorOption("Fold On Open", 0):
view.FoldTopLevelEvent()
def HookViewNotifications(self, view):
ParentEditorDocument.HookViewNotifications(self, view)
# Support for reloading the document from disk - presumably after some
# external application has modified it (or possibly source control has
# checked it out.
def ReloadDocument(self):
"""Reloads the document from disk. Assumes the file has
been saved and user has been asked if necessary - it just does it!
"""
win32ui.SetStatusText("Reloading document. Please wait...", 1)
self.SetModifiedFlag(0)
# Loop over all views, saving their state, then reload the document
views = self.GetAllViews()
states = []
for view in views:
try:
info = view._PrepareUserStateChange()
except AttributeError: # Not our editor view?
info = None
states.append(info)
self.OnOpenDocument(self.GetPathName())
for view, info in zip(views, states):
if info is not None:
view._EndUserStateChange(info)
self._DocumentStateChanged()
win32ui.SetStatusText("Document reloaded.")
# Reloading the file
def CheckExternalDocumentUpdated(self):
if self.bDeclinedReload or not self.GetPathName():
return
try:
newstat = os.stat(self.GetPathName())
except os.error as exc:
if not self.bReportedFileNotFound:
print("The file '%s' is open for editing, but\nchecking it for changes caused the error: %s" % (self.GetPathName(), exc.strerror))
self.bReportedFileNotFound = 1
return
if self.bReportedFileNotFound:
print("The file '%s' has re-appeared - continuing to watch for changes..." % (self.GetPathName(),))
self.bReportedFileNotFound = 0 # Once found again we want to start complaining.
changed = (self.fileStat is None) or \
self.fileStat[0] != newstat[0] or \
self.fileStat[6] != newstat[6] or \
self.fileStat[8] != newstat[8] or \
self.fileStat[9] != newstat[9]
if changed:
question = None
if self.IsModified():
question = "%s\r\n\r\nThis file has been modified outside of the source editor.\r\nDo you want to reload it and LOSE THE CHANGES in the source editor?" % self.GetPathName()
mbStyle = win32con.MB_YESNO | win32con.MB_DEFBUTTON2 # Default to "No"
else:
if not self.bAutoReload:
question = "%s\r\n\r\nThis file has been modified outside of the source editor.\r\nDo you want to reload it?" % self.GetPathName()
mbStyle = win32con.MB_YESNO # Default to "Yes"
if question:
rc = win32ui.MessageBox(question, None, mbStyle)
if rc!=win32con.IDYES:
self.bDeclinedReload = 1
return
self.ReloadDocument()
def _DocumentStateChanged(self):
"""Called whenever the documents state (on disk etc) has been changed
by the editor (eg, as the result of a save operation)
"""
if self.GetPathName():
try:
self.fileStat = os.stat(self.GetPathName())
except os.error:
self.fileStat = None
else:
self.fileStat = None
self.watcherThread._DocumentStateChanged()
self._UpdateUIForState()
self._ApplyOptionalToViews("_UpdateUIForState")
self._ApplyOptionalToViews("SetReadOnly", self._IsReadOnly())
self._ApplyOptionalToViews("SCISetSavePoint")
# Allow the debugger to reset us too.
import pywin.debugger
if pywin.debugger.currentDebugger is not None:
pywin.debugger.currentDebugger.UpdateDocumentLineStates(self)
# Read-only document support - make it obvious to the user
# that the file is read-only.
def _IsReadOnly(self):
return self.fileStat is not None and (self.fileStat[0] & 128)==0
def _UpdateUIForState(self):
"""Change the title to reflect the state of the document -
eg ReadOnly, Dirty, etc
"""
filename = self.GetPathName()
if not filename: return # New file - nothing to do
try:
# This seems necessary so the internal state of the window becomes
# "visible". without it, it is still shown, but certain functions
# (such as updating the title) dont immediately work?
self.GetFirstView().ShowWindow(win32con.SW_SHOW)
title = win32ui.GetFileTitle(filename)
except win32ui.error:
title = filename
if self._IsReadOnly():
title = title + " (read-only)"
self.SetTitle(title)
def MakeDocumentWritable(self):
pretend_ss = 0 # Set to 1 to test this without source safe :-)
if not self.scModuleName and not pretend_ss: # No Source Control support.
win32ui.SetStatusText("Document is read-only, and no source-control system is configured")
win32api.MessageBeep()
return 0
# We have source control support - check if the user wants to use it.
msg = "Would you like to check this file out?"
defButton = win32con.MB_YESNO
if self.IsModified():
msg = msg + "\r\n\r\nALL CHANGES IN THE EDITOR WILL BE LOST"
defButton = win32con.MB_YESNO
if win32ui.MessageBox(msg, None, defButton)!=win32con.IDYES:
return 0
if pretend_ss:
print("We are only pretending to check it out!")
win32api.SetFileAttributes(self.GetPathName(), win32con.FILE_ATTRIBUTE_NORMAL)
self.ReloadDocument()
return 1
# Now call on the module to do it.
if self.scModule is None:
try:
self.scModule = __import__(self.scModuleName)
for part in self.scModuleName.split('.')[1:]:
self.scModule = getattr(self.scModule, part)
except:
traceback.print_exc()
print("Error loading source control module.")
return 0
if self.scModule.CheckoutFile(self.GetPathName()):
self.ReloadDocument()
return 1
return 0
def CheckMakeDocumentWritable(self):
if self._IsReadOnly():
return self.MakeDocumentWritable()
return 1
def SaveModified(self):
# Called as the document is closed. If we are about
# to prompt for a save, bring the document to the foreground.
if self.IsModified():
frame = self.GetFirstView().GetParentFrame()
try:
frame.MDIActivate()
frame.AutoRestore()
except:
print("Could not bring document to foreground")
return self._obj_.SaveModified()
# NOTE - I DONT use the standard threading module,
# as this waits for all threads to terminate at shutdown.
# When using the debugger, it is possible shutdown will
# occur without Pythonwin getting a complete shutdown,
# so we deadlock at the end - threading is waiting for
import pywin.mfc.thread
import win32event
class FileWatchingThread(pywin.mfc.thread.WinThread):
def __init__(self, doc):
self.doc = doc
self.adminEvent = win32event.CreateEvent(None, 0, 0, None)
self.stopEvent = win32event.CreateEvent(None, 0, 0, None)
self.watchEvent = None
pywin.mfc.thread.WinThread.__init__(self)
def _DocumentStateChanged(self):
win32event.SetEvent(self.adminEvent)
def RefreshEvent(self):
self.hwnd = self.doc.GetFirstView().GetSafeHwnd()
if self.watchEvent is not None:
win32api.FindCloseChangeNotification(self.watchEvent)
self.watchEvent = None
path = self.doc.GetPathName()
if path: path = os.path.dirname(path)
if path:
filter = win32con.FILE_NOTIFY_CHANGE_FILE_NAME | \
win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES | \
win32con.FILE_NOTIFY_CHANGE_LAST_WRITE
try:
self.watchEvent = win32api.FindFirstChangeNotification(path, 0, filter)
except win32api.error as exc:
print("Can not watch file", path, "for changes -", exc.strerror)
def SignalStop(self):
win32event.SetEvent(self.stopEvent)
def Run(self):
while 1:
handles = [self.stopEvent, self.adminEvent]
if self.watchEvent is not None:
handles.append(self.watchEvent)
rc = win32event.WaitForMultipleObjects(handles, 0, win32event.INFINITE)
if rc == win32event.WAIT_OBJECT_0:
break
elif rc == win32event.WAIT_OBJECT_0+1:
self.RefreshEvent()
else:
win32api.PostMessage(self.hwnd, MSG_CHECK_EXTERNAL_FILE, 0, 0)
try:
# If the directory has been removed underneath us, we get this error.
win32api.FindNextChangeNotification(self.watchEvent)
except win32api.error as exc:
print("Can not watch file", self.doc.GetPathName(), "for changes -", exc.strerror)
break
# close a circular reference
self.doc = None
if self.watchEvent:
win32api.FindCloseChangeNotification(self.watchEvent)

View File

@ -0,0 +1,465 @@
#####################################################################
#
# editor.py
#
# A general purpose text editor, built on top of the win32ui edit
# type, which is built on an MFC CEditView
#
#
# We now support reloading of externally modified documented
# (eg, presumably by some other process, such as source control or
# another editor.
# We also suport auto-loading of externally modified files.
# - if the current document has not been modified in this
# editor, but has been modified on disk, then the file
# can be automatically reloaded.
#
# Note that it will _always_ prompt you if the file in the editor has been modified.
import win32ui
import win32api
import win32con
import regex
import re
import string
import sys, os
import traceback
from pywin.mfc import docview, dialog, afxres
from pywin.framework.editor import GetEditorOption, SetEditorOption, GetEditorFontOption, SetEditorFontOption, defaultCharacterFormat
patImport=regex.symcomp('import \(<name>.*\)')
patIndent=regex.compile('^\\([ \t]*[~ \t]\\)')
ID_LOCATE_FILE = 0xe200
ID_GOTO_LINE = 0xe2001
MSG_CHECK_EXTERNAL_FILE = win32con.WM_USER+1999 ## WARNING: Duplicated in document.py and coloreditor.py
# Key Codes that modify the bufffer when Ctrl or Alt are NOT pressed.
MODIFYING_VK_KEYS = [win32con.VK_BACK, win32con.VK_TAB, win32con.VK_RETURN, win32con.VK_SPACE, win32con.VK_DELETE]
for k in range(48, 91):
MODIFYING_VK_KEYS.append(k)
# Key Codes that modify the bufffer when Ctrl is pressed.
MODIFYING_VK_KEYS_CTRL = [win32con.VK_BACK, win32con.VK_RETURN, win32con.VK_SPACE, win32con.VK_DELETE]
# Key Codes that modify the bufffer when Alt is pressed.
MODIFYING_VK_KEYS_ALT = [win32con.VK_BACK, win32con.VK_RETURN, win32con.VK_SPACE, win32con.VK_DELETE]
# The editor itself starts here.
# Using the MFC Document/View model, we have an EditorDocument, which is responsible for
# managing the contents of the file, and a view which is responsible for rendering it.
#
# Due to a limitation in the Windows edit controls, we are limited to one view
# per document, although nothing in this code assumes this (I hope!)
isRichText=1 # We are using the Rich Text control. This has not been tested with value "0" for quite some time!
#ParentEditorDocument=docview.Document
from .document import EditorDocumentBase
ParentEditorDocument=EditorDocumentBase
class EditorDocument(ParentEditorDocument):
#
# File loading and saving operations
#
def OnOpenDocument(self, filename):
#
# handle Unix and PC text file format.
#
# Get the "long name" of the file name, as it may have been translated
# to short names by the shell.
self.SetPathName(filename) # Must set this early!
# Now do the work!
self.BeginWaitCursor()
win32ui.SetStatusText("Loading file...",1)
try:
f = open(filename,"rb")
except IOError:
win32ui.MessageBox(filename + '\nCan not find this file\nPlease verify that the correct path and file name are given')
self.EndWaitCursor()
return 0
raw=f.read()
f.close()
contents = self.TranslateLoadedData(raw)
rc = 0
if win32ui.IsWin32s() and len(contents)>62000: # give or take a few bytes
win32ui.MessageBox("This file is too big for Python on Windows 3.1\r\nPlease use another editor to view this file.")
else:
try:
self.GetFirstView().SetWindowText(contents)
rc = 1
except TypeError: # Null byte in file.
win32ui.MessageBox("This file contains NULL bytes, and can not be edited")
rc = 0
self.EndWaitCursor()
self.SetModifiedFlag(0) # No longer dirty
self._DocumentStateChanged()
return rc
def TranslateLoadedData(self, data):
"""Given raw data read from a file, massage it suitable for the edit window"""
# if a CR in the first 250 chars, then perform the expensive translate
if data[:250].find('\r')==-1:
win32ui.SetStatusText("Translating from Unix file format - please wait...",1)
return re.sub('\r*\n','\r\n',data)
else:
return data
def SaveFile(self, fileName, encoding=None):
if isRichText:
view = self.GetFirstView()
view.SaveTextFile(fileName, encoding=encoding)
else: # Old style edit view window.
self.GetFirstView().SaveFile(fileName)
try:
# Make sure line cache has updated info about me!
import linecache
linecache.checkcache()
except:
pass
#
# Color state stuff
#
def SetAllLineColors(self, color = None):
for view in self.GetAllViews():
view.SetAllLineColors(color)
def SetLineColor(self, lineNo, color):
"Color a line of all views"
for view in self.GetAllViews():
view.SetLineColor(lineNo, color)
# def StreamTextOut(self, data): ### This seems unreliable???
# self.saveFileHandle.write(data)
# return 1 # keep em coming!
#ParentEditorView=docview.EditView
ParentEditorView=docview.RichEditView
class EditorView(ParentEditorView):
def __init__(self, doc):
ParentEditorView.__init__(self, doc)
if isRichText:
self.SetWordWrap(win32ui.CRichEditView_WrapNone)
self.addToMRU = 1
self.HookHandlers()
self.bCheckingFile = 0
self.defCharFormat = GetEditorFontOption("Default Font", defaultCharacterFormat)
# Smart tabs override everything else if context can be worked out.
self.bSmartTabs = GetEditorOption("Smart Tabs", 1)
self.tabSize = GetEditorOption("Tab Size", 8)
self.indentSize = GetEditorOption("Indent Size", 8)
# If next indent is at a tab position, and useTabs is set, a tab will be inserted.
self.bUseTabs = GetEditorOption("Use Tabs", 1)
def OnInitialUpdate(self):
rc = self._obj_.OnInitialUpdate()
self.SetDefaultCharFormat(self.defCharFormat)
return rc
def CutCurLine(self):
curLine = self._obj_.LineFromChar()
nextLine = curLine+1
start = self._obj_.LineIndex(curLine)
end = self._obj_.LineIndex(nextLine)
if end==0: # must be last line.
end = start + self.end.GetLineLength(curLine)
self._obj_.SetSel(start,end)
self._obj_.Cut()
def _PrepareUserStateChange(self):
"Return selection, lineindex, etc info, so it can be restored"
self.SetRedraw(0)
return self.GetModify(), self.GetSel(), self.GetFirstVisibleLine()
def _EndUserStateChange(self, info):
scrollOff = info[2] - self.GetFirstVisibleLine()
if scrollOff:
self.LineScroll(scrollOff)
self.SetSel(info[1])
self.SetModify(info[0])
self.SetRedraw(1)
self.InvalidateRect()
self.UpdateWindow()
def _UpdateUIForState(self):
self.SetReadOnly(self.GetDocument()._IsReadOnly())
def SetAllLineColors(self, color = None):
if isRichText:
info = self._PrepareUserStateChange()
try:
if color is None: color = self.defCharFormat[4]
self.SetSel(0,-1)
self.SetSelectionCharFormat((win32con.CFM_COLOR, 0,0,0,color))
finally:
self._EndUserStateChange(info)
def SetLineColor(self, lineNo, color):
"lineNo is the 1 based line number to set. If color is None, default color is used."
if isRichText:
info = self._PrepareUserStateChange()
try:
if color is None: color = self.defCharFormat[4]
lineNo = lineNo-1
startIndex = self.LineIndex(lineNo)
if startIndex!=-1:
self.SetSel(startIndex, self.LineIndex(lineNo+1))
self.SetSelectionCharFormat((win32con.CFM_COLOR, 0,0,0,color))
finally:
self._EndUserStateChange(info)
def Indent(self):
"""Insert an indent to move the cursor to the next tab position.
Honors the tab size and 'use tabs' settings. Assumes the cursor is already at the
position to be indented, and the selection is a single character (ie, not a block)
"""
start, end = self._obj_.GetSel()
startLine = self._obj_.LineFromChar(start)
line = self._obj_.GetLine(startLine)
realCol = start - self._obj_.LineIndex(startLine)
# Calulate the next tab stop.
# Expand existing tabs.
curCol = 0
for ch in line[:realCol]:
if ch=='\t':
curCol = ((curCol / self.tabSize) + 1) * self.tabSize
else:
curCol = curCol + 1
nextColumn = ((curCol / self.indentSize) + 1) * self.indentSize
# print "curCol is", curCol, "nextColumn is", nextColumn
ins = None
if self.bSmartTabs:
# Look for some context.
if realCol==0: # Start of the line - see if the line above can tell us
lookLine = startLine-1
while lookLine >= 0:
check = self._obj_.GetLine(lookLine)[0:1]
if check in ['\t', ' ']:
ins = check
break
lookLine = lookLine - 1
else: # See if the previous char can tell us
check = line[realCol-1]
if check in ['\t', ' ']:
ins = check
# Either smart tabs off, or not smart enough!
# Use the "old style" settings.
if ins is None:
if self.bUseTabs and nextColumn % self.tabSize==0:
ins = '\t'
else:
ins = ' '
if ins == ' ':
# Calc the number of spaces to take us to the next stop
ins = ins * (nextColumn - curCol)
self._obj_.ReplaceSel(ins)
def BlockDent(self, isIndent, startLine, endLine):
" Indent/Undent all lines specified "
if not self.GetDocument().CheckMakeDocumentWritable(): return 0
tabSize=self.tabSize # hard-code for now!
info = self._PrepareUserStateChange()
try:
for lineNo in range(startLine, endLine):
pos=self._obj_.LineIndex(lineNo)
self._obj_.SetSel(pos, pos)
if isIndent:
self.Indent()
else:
line = self._obj_.GetLine(lineNo)
try:
noToDel = 0
if line[0]=='\t':
noToDel = 1
elif line[0]==' ':
for noToDel in range(0,tabSize):
if line[noToDel]!=' ':
break
else:
noToDel=tabSize
if noToDel:
self._obj_.SetSel(pos, pos+noToDel)
self._obj_.Clear()
except IndexError:
pass
finally:
self._EndUserStateChange(info)
self.GetDocument().SetModifiedFlag(1) # Now dirty
self._obj_.SetSel(self.LineIndex(startLine), self.LineIndex(endLine))
def GotoLine(self, lineNo = None):
try:
if lineNo is None:
lineNo = int(input("Enter Line Number"))
except (ValueError, KeyboardInterrupt):
return 0
self.GetLineCount() # Seems to be needed when file first opened???
charNo = self.LineIndex(lineNo-1)
self.SetSel(charNo)
def HookHandlers(self): # children can override, but should still call me!
# self.HookAllKeyStrokes(self.OnKey)
self.HookMessage(self.OnCheckExternalDocumentUpdated,MSG_CHECK_EXTERNAL_FILE)
self.HookMessage(self.OnRClick,win32con.WM_RBUTTONDOWN)
self.HookMessage(self.OnSetFocus, win32con.WM_SETFOCUS)
self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN)
self.HookKeyStroke(self.OnKeyCtrlY, 25) # ^Y
self.HookKeyStroke(self.OnKeyCtrlG, 7) # ^G
self.HookKeyStroke(self.OnKeyTab, 9) # TAB
self.HookKeyStroke(self.OnKeyEnter, 13) # Enter
self.HookCommand(self.OnCmdLocateFile, ID_LOCATE_FILE)
self.HookCommand(self.OnCmdGotoLine, ID_GOTO_LINE)
self.HookCommand(self.OnEditPaste, afxres.ID_EDIT_PASTE)
self.HookCommand(self.OnEditCut, afxres.ID_EDIT_CUT)
# Hook Handlers
def OnSetFocus(self,msg):
# Even though we use file change notifications, we should be very sure about it here.
self.OnCheckExternalDocumentUpdated(msg)
def OnRClick(self,params):
menu = win32ui.CreatePopupMenu()
# look for a module name
line=self._obj_.GetLine().strip()
flags=win32con.MF_STRING|win32con.MF_ENABLED
if patImport.match(line)==len(line):
menu.AppendMenu(flags, ID_LOCATE_FILE, "&Locate %s.py"%patImport.group('name'))
menu.AppendMenu(win32con.MF_SEPARATOR);
menu.AppendMenu(flags, win32ui.ID_EDIT_UNDO, '&Undo')
menu.AppendMenu(win32con.MF_SEPARATOR);
menu.AppendMenu(flags, win32ui.ID_EDIT_CUT, 'Cu&t')
menu.AppendMenu(flags, win32ui.ID_EDIT_COPY, '&Copy')
menu.AppendMenu(flags, win32ui.ID_EDIT_PASTE, '&Paste')
menu.AppendMenu(flags, win32con.MF_SEPARATOR);
menu.AppendMenu(flags, win32ui.ID_EDIT_SELECT_ALL, '&Select all')
menu.AppendMenu(flags, win32con.MF_SEPARATOR);
menu.AppendMenu(flags, ID_GOTO_LINE, '&Goto line...')
menu.TrackPopupMenu(params[5])
return 0
def OnCmdGotoLine(self, cmd, code):
self.GotoLine()
return 0
def OnCmdLocateFile(self, cmd, code):
modName = patImport.group('name')
if not modName:
return 0
import pywin.framework.scriptutils
fileName = pywin.framework.scriptutils.LocatePythonFile(modName)
if fileName is None:
win32ui.SetStatusText("Can't locate module %s" % modName)
else:
win32ui.GetApp().OpenDocumentFile(fileName)
return 0
# Key handlers
def OnKeyEnter(self, key):
if not self.GetDocument().CheckMakeDocumentWritable(): return 0
curLine = self._obj_.GetLine()
self._obj_.ReplaceSel('\r\n') # insert the newline
# If the current line indicates the next should be indented,
# then copy the current indentation to this line.
res = patIndent.match(curLine,0)
if res>0 and curLine.strip():
curIndent = patIndent.group(1)
self._obj_.ReplaceSel(curIndent)
return 0 # dont pass on
def OnKeyCtrlY(self, key):
if not self.GetDocument().CheckMakeDocumentWritable(): return 0
self.CutCurLine()
return 0 # dont let him have it!
def OnKeyCtrlG(self, key):
self.GotoLine()
return 0 # dont let him have it!
def OnKeyTab(self, key):
if not self.GetDocument().CheckMakeDocumentWritable(): return 0
start, end = self._obj_.GetSel()
if start==end: # normal TAB key
self.Indent()
return 0 # we handled this.
# Otherwise it is a block indent/dedent.
if start>end:
start, end = end, start # swap them.
startLine = self._obj_.LineFromChar(start)
endLine = self._obj_.LineFromChar(end)
self.BlockDent(win32api.GetKeyState(win32con.VK_SHIFT)>=0, startLine, endLine)
return 0
def OnEditPaste(self, id, code):
# Return 1 if we can make the file editable.(or it already is!)
return self.GetDocument().CheckMakeDocumentWritable()
def OnEditCut(self, id, code):
# Return 1 if we can make the file editable.(or it already is!)
return self.GetDocument().CheckMakeDocumentWritable()
def OnKeyDown(self, msg):
key = msg[2]
if win32api.GetKeyState(win32con.VK_CONTROL) & 0x8000:
modList = MODIFYING_VK_KEYS_CTRL
elif win32api.GetKeyState(win32con.VK_MENU) & 0x8000:
modList = MODIFYING_VK_KEYS_ALT
else:
modList = MODIFYING_VK_KEYS
if key in modList:
# Return 1 if we can make the file editable.(or it already is!)
return self.GetDocument().CheckMakeDocumentWritable()
return 1 # Pass it on OK
# def OnKey(self, key):
# return self.GetDocument().CheckMakeDocumentWritable()
def OnCheckExternalDocumentUpdated(self, msg):
if self._obj_ is None or self.bCheckingFile: return
self.bCheckingFile = 1
self.GetDocument().CheckExternalDocumentUpdated()
self.bCheckingFile = 0
from .template import EditorTemplateBase
class EditorTemplate(EditorTemplateBase):
def __init__(self, res=win32ui.IDR_TEXTTYPE, makeDoc=None, makeFrame=None, makeView=None):
if makeDoc is None: makeDoc = EditorDocument
if makeView is None: makeView = EditorView
EditorTemplateBase.__init__(self, res, makeDoc, makeFrame, makeView)
def _CreateDocTemplate(self, resourceId):
return win32ui.CreateRichEditDocTemplate(resourceId)
def CreateWin32uiDocument(self):
return self.DoCreateRichEditDoc()
def Create(fileName = None, title=None, template = None):
return editorTemplate.OpenDocumentFile(fileName)
from pywin.framework.editor import GetDefaultEditorModuleName
prefModule = GetDefaultEditorModuleName()
# Initialize only if this is the "default" editor.
if __name__==prefModule:
# For debugging purposes, when this module may be reloaded many times.
try:
win32ui.GetApp().RemoveDocTemplate(editorTemplate)
except (NameError, win32ui.error):
pass
editorTemplate = EditorTemplate()
win32ui.GetApp().AddDocTemplate(editorTemplate)

Some files were not shown because too many files have changed in this diff Show More