install pywin32-220
This commit is contained in:
parent
15d9012f5a
commit
93f7d415a0
581 changed files with 100203 additions and 0 deletions
56
Lib/site-packages/adodbapi/__init__.py
Normal file
56
Lib/site-packages/adodbapi/__init__.py
Normal 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__
|
276
Lib/site-packages/adodbapi/ado_consts.py
Normal file
276
Lib/site-packages/adodbapi/ado_consts.py
Normal 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'
|
||||||
|
}
|
1010
Lib/site-packages/adodbapi/adodbapi.py
Normal file
1010
Lib/site-packages/adodbapi/adodbapi.py
Normal file
File diff suppressed because it is too large
Load diff
70
Lib/site-packages/adodbapi/examples/db_print.py
Normal file
70
Lib/site-packages/adodbapi/examples/db_print.py
Normal 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()
|
19
Lib/site-packages/adodbapi/examples/db_table_names.py
Normal file
19
Lib/site-packages/adodbapi/examples/db_table_names.py
Normal 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)
|
38
Lib/site-packages/adodbapi/examples/xls_read.py
Normal file
38
Lib/site-packages/adodbapi/examples/xls_read.py
Normal 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()
|
32
Lib/site-packages/adodbapi/examples/xls_write.py
Normal file
32
Lib/site-packages/adodbapi/examples/xls_write.py
Normal 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')))
|
33
Lib/site-packages/adodbapi/is64bit.py
Normal file
33
Lib/site-packages/adodbapi/is64bit.py
Normal 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()))
|
506
Lib/site-packages/adodbapi/license.txt
Normal file
506
Lib/site-packages/adodbapi/license.txt
Normal 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!
|
||||||
|
|
92
Lib/site-packages/adodbapi/readme.txt
Normal file
92
Lib/site-packages/adodbapi/readme.txt
Normal 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).
|
14
Lib/site-packages/adodbapi/schema_table.py
Normal file
14
Lib/site-packages/adodbapi/schema_table.py
Normal 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
|
1348
Lib/site-packages/adodbapi/test/adodbapitest.py
Normal file
1348
Lib/site-packages/adodbapi/test/adodbapitest.py
Normal file
File diff suppressed because it is too large
Load diff
161
Lib/site-packages/adodbapi/test/adodbapitestconfig.py
Normal file
161
Lib/site-packages/adodbapi/test/adodbapitestconfig.py
Normal 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'
|
899
Lib/site-packages/adodbapi/test/dbapi20.py
Normal file
899
Lib/site-packages/adodbapi/test/dbapi20.py
Normal 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.'
|
||||||
|
)
|
33
Lib/site-packages/adodbapi/test/is64bit.py
Normal file
33
Lib/site-packages/adodbapi/test/is64bit.py
Normal 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()))
|
113
Lib/site-packages/adodbapi/test/setuptestframework.py
Normal file
113
Lib/site-packages/adodbapi/test/setuptestframework.py
Normal 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')
|
181
Lib/site-packages/adodbapi/test/test_adodbapi_dbapi20.py
Normal file
181
Lib/site-packages/adodbapi/test/test_adodbapi_dbapi20.py
Normal 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)
|
46
Lib/site-packages/adodbapi/test/tryconnection2.py
Normal file
46
Lib/site-packages/adodbapi/test/tryconnection2.py
Normal 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'
|
48
Lib/site-packages/adodbapi/test/tryconnection3.py
Normal file
48
Lib/site-packages/adodbapi/test/tryconnection3.py
Normal 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'
|
BIN
Lib/site-packages/isapi/PyISAPI_loader.dll
Normal file
BIN
Lib/site-packages/isapi/PyISAPI_loader.dll
Normal file
Binary file not shown.
7
Lib/site-packages/isapi/README.txt
Normal file
7
Lib/site-packages/isapi/README.txt
Normal 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.
|
33
Lib/site-packages/isapi/__init__.py
Normal file
33
Lib/site-packages/isapi/__init__.py
Normal 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
|
92
Lib/site-packages/isapi/doc/isapi.html
Normal file
92
Lib/site-packages/isapi/doc/isapi.html
Normal 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.
|
730
Lib/site-packages/isapi/install.py
Normal file
730
Lib/site-packages/isapi/install.py
Normal 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)
|
120
Lib/site-packages/isapi/isapicon.py
Normal file
120
Lib/site-packages/isapi/isapicon.py
Normal 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
|
20
Lib/site-packages/isapi/samples/README.txt
Normal file
20
Lib/site-packages/isapi/samples/README.txt
Normal 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.
|
196
Lib/site-packages/isapi/samples/advanced.py
Normal file
196
Lib/site-packages/isapi/samples/advanced.py
Normal 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, '<no such variable>')
|
||||||
|
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)
|
109
Lib/site-packages/isapi/samples/redirector.py
Normal file
109
Lib/site-packages/isapi/samples/redirector.py
Normal 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)
|
78
Lib/site-packages/isapi/samples/redirector_asynch.py
Normal file
78
Lib/site-packages/isapi/samples/redirector_asynch.py
Normal 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)
|
155
Lib/site-packages/isapi/samples/redirector_with_filter.py
Normal file
155
Lib/site-packages/isapi/samples/redirector_with_filter.py
Normal 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)
|
154
Lib/site-packages/isapi/samples/test.py
Normal file
154
Lib/site-packages/isapi/samples/test.py
Normal 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)
|
68
Lib/site-packages/isapi/simple.py
Normal file
68
Lib/site-packages/isapi/simple.py
Normal 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
|
3
Lib/site-packages/isapi/test/README.txt
Normal file
3
Lib/site-packages/isapi/test/README.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
This is a directory for tests of the PyISAPI framework.
|
||||||
|
|
||||||
|
For demos, please see the pyisapi 'samples' directory.
|
111
Lib/site-packages/isapi/test/extension_simple.py
Normal file
111
Lib/site-packages/isapi/test/extension_simple.py
Normal 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)
|
171
Lib/site-packages/isapi/threaded_extension.py
Normal file
171
Lib/site-packages/isapi/threaded_extension.py
Normal 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()
|
3
Lib/site-packages/pythoncom.py
Normal file
3
Lib/site-packages/pythoncom.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# Magic utility that "redirects" to pythoncomxx.dll
|
||||||
|
import pywintypes
|
||||||
|
pywintypes.__import_pywin32_system_module__("pythoncom", globals())
|
BIN
Lib/site-packages/pythonwin/Pythonwin.exe
Normal file
BIN
Lib/site-packages/pythonwin/Pythonwin.exe
Normal file
Binary file not shown.
BIN
Lib/site-packages/pythonwin/dde.pyd
Normal file
BIN
Lib/site-packages/pythonwin/dde.pyd
Normal file
Binary file not shown.
30
Lib/site-packages/pythonwin/license.txt
Normal file
30
Lib/site-packages/pythonwin/license.txt
Normal 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.
|
BIN
Lib/site-packages/pythonwin/mfc140u.dll
Normal file
BIN
Lib/site-packages/pythonwin/mfc140u.dll
Normal file
Binary file not shown.
BIN
Lib/site-packages/pythonwin/mfcm140u.dll
Normal file
BIN
Lib/site-packages/pythonwin/mfcm140u.dll
Normal file
Binary file not shown.
221
Lib/site-packages/pythonwin/pywin/Demos/app/basictimerapp.py
Normal file
221
Lib/site-packages/pythonwin/pywin/Demos/app/basictimerapp.py
Normal 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()
|
194
Lib/site-packages/pythonwin/pywin/Demos/app/customprint.py
Normal file
194
Lib/site-packages/pythonwin/pywin/Demos/app/customprint.py
Normal 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()
|
||||||
|
|
52
Lib/site-packages/pythonwin/pywin/Demos/app/demoutils.py
Normal file
52
Lib/site-packages/pythonwin/pywin/Demos/app/demoutils.py
Normal 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()
|
46
Lib/site-packages/pythonwin/pywin/Demos/app/dlgappdemo.py
Normal file
46
Lib/site-packages/pythonwin/pywin/Demos/app/dlgappdemo.py
Normal 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()
|
62
Lib/site-packages/pythonwin/pywin/Demos/app/dojobapp.py
Normal file
62
Lib/site-packages/pythonwin/pywin/Demos/app/dojobapp.py
Normal 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()
|
45
Lib/site-packages/pythonwin/pywin/Demos/app/helloapp.py
Normal file
45
Lib/site-packages/pythonwin/pywin/Demos/app/helloapp.py
Normal 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()
|
108
Lib/site-packages/pythonwin/pywin/Demos/cmdserver.py
Normal file
108
Lib/site-packages/pythonwin/pywin/Demos/cmdserver.py
Normal 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()
|
||||||
|
|
98
Lib/site-packages/pythonwin/pywin/Demos/createwin.py
Normal file
98
Lib/site-packages/pythonwin/pywin/Demos/createwin.py
Normal 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)
|
||||||
|
|
54
Lib/site-packages/pythonwin/pywin/Demos/demoutils.py
Normal file
54
Lib/site-packages/pythonwin/pywin/Demos/demoutils.py
Normal 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()
|
69
Lib/site-packages/pythonwin/pywin/Demos/dibdemo.py
Normal file
69
Lib/site-packages/pythonwin/pywin/Demos/dibdemo.py
Normal 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()
|
137
Lib/site-packages/pythonwin/pywin/Demos/dlgtest.py
Normal file
137
Lib/site-packages/pythonwin/pywin/Demos/dlgtest.py
Normal 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)
|
73
Lib/site-packages/pythonwin/pywin/Demos/dyndlg.py
Normal file
73
Lib/site-packages/pythonwin/pywin/Demos/dyndlg.py
Normal 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()
|
79
Lib/site-packages/pythonwin/pywin/Demos/fontdemo.py
Normal file
79
Lib/site-packages/pythonwin/pywin/Demos/fontdemo.py
Normal 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()
|
68
Lib/site-packages/pythonwin/pywin/Demos/guidemo.py
Normal file
68
Lib/site-packages/pythonwin/pywin/Demos/guidemo.py
Normal 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()
|
104
Lib/site-packages/pythonwin/pywin/Demos/hiertest.py
Normal file
104
Lib/site-packages/pythonwin/pywin/Demos/hiertest.py
Normal 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()
|
12
Lib/site-packages/pythonwin/pywin/Demos/menutest.py
Normal file
12
Lib/site-packages/pythonwin/pywin/Demos/menutest.py
Normal 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.")
|
49
Lib/site-packages/pythonwin/pywin/Demos/objdoc.py
Normal file
49
Lib/site-packages/pythonwin/pywin/Demos/objdoc.py
Normal 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()
|
0
Lib/site-packages/pythonwin/pywin/Demos/ocx/__init__.py
Normal file
0
Lib/site-packages/pythonwin/pywin/Demos/ocx/__init__.py
Normal file
54
Lib/site-packages/pythonwin/pywin/Demos/ocx/demoutils.py
Normal file
54
Lib/site-packages/pythonwin/pywin/Demos/ocx/demoutils.py
Normal 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()
|
84
Lib/site-packages/pythonwin/pywin/Demos/ocx/flash.py
Normal file
84
Lib/site-packages/pythonwin/pywin/Demos/ocx/flash.py
Normal 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()
|
127
Lib/site-packages/pythonwin/pywin/Demos/ocx/msoffice.py
Normal file
127
Lib/site-packages/pythonwin/pywin/Demos/ocx/msoffice.py
Normal 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()
|
101
Lib/site-packages/pythonwin/pywin/Demos/ocx/ocxserialtest.py
Normal file
101
Lib/site-packages/pythonwin/pywin/Demos/ocx/ocxserialtest.py
Normal 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()
|
186
Lib/site-packages/pythonwin/pywin/Demos/ocx/ocxtest.py
Normal file
186
Lib/site-packages/pythonwin/pywin/Demos/ocx/ocxtest.py
Normal 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()
|
55
Lib/site-packages/pythonwin/pywin/Demos/ocx/webbrowser.py
Normal file
55
Lib/site-packages/pythonwin/pywin/Demos/ocx/webbrowser.py
Normal 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()
|
358
Lib/site-packages/pythonwin/pywin/Demos/openGLDemo.py
Normal file
358
Lib/site-packages/pythonwin/pywin/Demos/openGLDemo.py
Normal 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()
|
88
Lib/site-packages/pythonwin/pywin/Demos/progressbar.py
Normal file
88
Lib/site-packages/pythonwin/pywin/Demos/progressbar.py
Normal 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)
|
54
Lib/site-packages/pythonwin/pywin/Demos/sliderdemo.py
Normal file
54
Lib/site-packages/pythonwin/pywin/Demos/sliderdemo.py
Normal 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()
|
72
Lib/site-packages/pythonwin/pywin/Demos/splittst.py
Normal file
72
Lib/site-packages/pythonwin/pywin/Demos/splittst.py
Normal 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()
|
174
Lib/site-packages/pythonwin/pywin/Demos/threadedgui.py
Normal file
174
Lib/site-packages/pythonwin/pywin/Demos/threadedgui.py
Normal 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()
|
93
Lib/site-packages/pythonwin/pywin/Demos/toolbar.py
Normal file
93
Lib/site-packages/pythonwin/pywin/Demos/toolbar.py
Normal 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()
|
29
Lib/site-packages/pythonwin/pywin/IDLE.cfg
Normal file
29
Lib/site-packages/pythonwin/pywin/IDLE.cfg
Normal 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>>
|
||||||
|
|
10
Lib/site-packages/pythonwin/pywin/__init__.py
Normal file
10
Lib/site-packages/pythonwin/pywin/__init__.py
Normal 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
|
113
Lib/site-packages/pythonwin/pywin/debugger/__init__.py
Normal file
113
Lib/site-packages/pythonwin/pywin/debugger/__init__.py
Normal 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)
|
31
Lib/site-packages/pythonwin/pywin/debugger/configui.py
Normal file
31
Lib/site-packages/pythonwin/pywin/debugger/configui.py
Normal 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
|
28
Lib/site-packages/pythonwin/pywin/debugger/dbgcon.py
Normal file
28
Lib/site-packages/pythonwin/pywin/debugger/dbgcon.py
Normal 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)
|
47
Lib/site-packages/pythonwin/pywin/debugger/dbgpyapp.py
Normal file
47
Lib/site-packages/pythonwin/pywin/debugger/dbgpyapp.py
Normal 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()
|
985
Lib/site-packages/pythonwin/pywin/debugger/debugger.py
Normal file
985
Lib/site-packages/pythonwin/pywin/debugger/debugger.py
Normal 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
|
48
Lib/site-packages/pythonwin/pywin/debugger/fail.py
Normal file
48
Lib/site-packages/pythonwin/pywin/debugger/fail.py
Normal 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()
|
215
Lib/site-packages/pythonwin/pywin/default.cfg
Normal file
215
Lib/site-packages/pythonwin/pywin/default.cfg
Normal 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
|
||||||
|
|
0
Lib/site-packages/pythonwin/pywin/dialogs/__init__.py
Normal file
0
Lib/site-packages/pythonwin/pywin/dialogs/__init__.py
Normal file
116
Lib/site-packages/pythonwin/pywin/dialogs/ideoptions.py
Normal file
116
Lib/site-packages/pythonwin/pywin/dialogs/ideoptions.py
Normal 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()
|
122
Lib/site-packages/pythonwin/pywin/dialogs/list.py
Normal file
122
Lib/site-packages/pythonwin/pywin/dialogs/list.py
Normal 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()
|
121
Lib/site-packages/pythonwin/pywin/dialogs/login.py
Normal file
121
Lib/site-packages/pythonwin/pywin/dialogs/login.py
Normal 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))
|
227
Lib/site-packages/pythonwin/pywin/dialogs/status.py
Normal file
227
Lib/site-packages/pythonwin/pywin/dialogs/status.py
Normal 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()
|
541
Lib/site-packages/pythonwin/pywin/docking/DockingBar.py
Normal file
541
Lib/site-packages/pythonwin/pywin/docking/DockingBar.py
Normal 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()
|
0
Lib/site-packages/pythonwin/pywin/docking/__init__.py
Normal file
0
Lib/site-packages/pythonwin/pywin/docking/__init__.py
Normal file
0
Lib/site-packages/pythonwin/pywin/framework/__init__.py
Normal file
0
Lib/site-packages/pythonwin/pywin/framework/__init__.py
Normal file
408
Lib/site-packages/pythonwin/pywin/framework/app.py
Normal file
408
Lib/site-packages/pythonwin/pywin/framework/app.py
Normal 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
|
143
Lib/site-packages/pythonwin/pywin/framework/bitmap.py
Normal file
143
Lib/site-packages/pythonwin/pywin/framework/bitmap.py
Normal 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))
|
49
Lib/site-packages/pythonwin/pywin/framework/cmdline.py
Normal file
49
Lib/site-packages/pythonwin/pywin/framework/cmdline.py
Normal 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]
|
170
Lib/site-packages/pythonwin/pywin/framework/dbgcommands.py
Normal file
170
Lib/site-packages/pythonwin/pywin/framework/dbgcommands.py
Normal 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)
|
69
Lib/site-packages/pythonwin/pywin/framework/dlgappcore.py
Normal file
69
Lib/site-packages/pythonwin/pywin/framework/dlgappcore.py
Normal 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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
257
Lib/site-packages/pythonwin/pywin/framework/editor/configui.py
Normal file
257
Lib/site-packages/pythonwin/pywin/framework/editor/configui.py
Normal 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()
|
332
Lib/site-packages/pythonwin/pywin/framework/editor/document.py
Normal file
332
Lib/site-packages/pythonwin/pywin/framework/editor/document.py
Normal 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)
|
465
Lib/site-packages/pythonwin/pywin/framework/editor/editor.py
Normal file
465
Lib/site-packages/pythonwin/pywin/framework/editor/editor.py
Normal 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
Loading…
Reference in a new issue