# Based on iwidgets2.2.0/entryfield.itk code.
# Possible return values of validation functions.
class EntryField(Pmw
.MegaWidget
):
_classBindingsDefinedFor
= 0
def __init__(self
, parent
= None, **kw
):
# Define the megawidget options.
('errorbackground', 'pink', None),
('invalidcommand', self
.bell
, None),
('labelmargin', 0, INITOPT
),
('labelpos', None, INITOPT
),
('modifiedcommand', None, None),
('sticky', 'ew', INITOPT
),
('validate', None, self
._validate
),
('extravalidators', {}, None),
self
.defineoptions(kw
, optiondefs
)
# Initialise the base class (after defining the options).
Pmw
.MegaWidget
.__init
__(self
, parent
)
interior
= self
.interior()
self
._entryFieldEntry
= self
.createcomponent('entry',
Tkinter
.Entry
, (interior
,))
self
._entryFieldEntry
.grid(column
=2, row
=2, sticky
=self
['sticky'])
self
.__setEntry
(self
['value'])
interior
.grid_columnconfigure(2, weight
=1)
interior
.grid_rowconfigure(2, weight
=1)
self
.createlabel(interior
)
# Initialise instance variables.
self
.normalBackground
= None
self
._previousText
= None
_registerEntryField(self
._entryFieldEntry
, self
)
# Establish the special class bindings if not already done.
# Also create bindings if the Tkinter default interpreter has
# changed. Use Tkinter._default_root to create class
# bindings, so that a reference to root is created by
# bind_class rather than a reference to self, which would
# prevent object cleanup.
if EntryField
._classBindingsDefinedFor
!= Tkinter
._default
_root
:
tagList
= self
._entryFieldEntry
.bindtags()
root
= Tkinter
._default
_root
sequences
= root
.bind_class(tag
)
if type(sequences
) is types
.StringType
:
# In old versions of Tkinter, bind_class returns a string
sequences
= root
.tk
.splitlist(sequences
)
for sequence
in sequences
:
allSequences
[sequence
] = None
for sequence
in allSequences
.keys():
root
.bind_class('EntryFieldPre', sequence
, _preProcess
)
root
.bind_class('EntryFieldPost', sequence
, _postProcess
)
EntryField
._classBindingsDefinedFor
= root
self
._entryFieldEntry
.bindtags(('EntryFieldPre',) +
self
._entryFieldEntry
.bindtags() + ('EntryFieldPost',))
self
._entryFieldEntry
.bind('<Return>', self
._executeCommand
)
# Check keywords and initialise options.
_deregisterEntryField(self
._entryFieldEntry
)
Pmw
.MegaWidget
.destroy(self
)
def _getValidatorFunc(self
, validator
, index
):
# Search the extra and standard validator lists for the
# given 'validator'. If 'validator' is an alias, then
# continue the search using the alias. Make sure that
# self-referencial aliases do not cause infinite loops.
extraValidators
= self
['extravalidators']
traversedValidators
.append(validator
)
if extraValidators
.has_key(validator
):
validator
= extraValidators
[validator
][index
]
elif _standardValidators
.has_key(validator
):
validator
= _standardValidators
[validator
][index
]
if validator
in traversedValidators
:
if type(opt
) is types
.DictionaryType
:
# Look up validator maps and replace 'validator' field with
# the corresponding function.
validator
= dict['validator']
valFunction
= self
._getValidatorFunc
(validator
, 0)
self
._checkValidateFunction
(valFunction
, 'validate', validator
)
dict['validator'] = valFunction
# Look up validator maps and replace 'stringtovalue' field
# with the corresponding function.
if dict.has_key('stringtovalue'):
stringtovalue
= dict['stringtovalue']
strFunction
= self
._getValidatorFunc
(stringtovalue
, 1)
self
._checkValidateFunction
(
strFunction
, 'stringtovalue', stringtovalue
)
strFunction
= self
._getValidatorFunc
(validator
, 1)
if strFunction
== validator
:
dict['stringtovalue'] = strFunction
self
._validationInfo
= dict
del args
['stringtovalue']
self
._validationArgs
= args
self
._previousText
= None
if type(dict['min']) == types
.StringType
and strFunction
is not None:
dict['min'] = apply(strFunction
, (dict['min'],), args
)
if type(dict['max']) == types
.StringType
and strFunction
is not None:
dict['max'] = apply(strFunction
, (dict['max'],), args
)
def _checkValidateFunction(self
, function
, option
, validator
):
# Raise an error if 'function' is not a function or None.
if function
is not None and not callable(function
):
extraValidators
= self
['extravalidators']
extra
= extraValidators
.keys()
standard
= _standardValidators
.keys()
standard
= tuple(standard
)
msg
= 'bad %s value "%s": must be a function or one of ' \
'the standard validators %s or extra validators %s'
raise ValueError, msg
% (option
, validator
, standard
, extra
)
def _executeCommand(self
, event
= None):
# Return result of command for invoke() method.
self
._previousText
= self
._entryFieldEntry
.get()
self
._previousICursor
= self
._entryFieldEntry
.index('insert')
self
._previousXview
= self
._entryFieldEntry
.index('@0')
if self
._entryFieldEntry
.selection_present():
self
._previousSel
= (self
._entryFieldEntry
.index('sel.first'),
self
._entryFieldEntry
.index('sel.last'))
# No need to check if text has not changed.
previousText
= self
._previousText
if previousText
== self
._entryFieldEntry
.get():
valid
= self
._checkValidity
()
# The invalidcommand called by _checkValidity() destroyed us.
cmd
= self
['modifiedcommand']
if callable(cmd
) and previousText
!= self
._entryFieldEntry
.get():
# If there is a variable specified by the entry_textvariable
# option, checkentry() should be called after the set() method
# of the variable is called.
self
._previousText
= None
return self
._postProcess
()
text
= self
._entryFieldEntry
.get()
dict = self
._validationInfo
args
= self
._validationArgs
if dict['validator'] is not None:
status
= apply(dict['validator'], (text
,), args
)
# Check for out of (min, max) range.
if dict['stringtovalue'] is not None:
if min is None and max is None:
val
= apply(dict['stringtovalue'], (text
,), args
)
if min is not None and val
< min:
if max is not None and val
> max:
def _checkValidity(self
):
valid
= self
._getValidity
()
cmd
= self
['invalidcommand']
# The invalidcommand destroyed us.
# Restore the entry to its previous value.
if self
._previousText
is not None:
self
.__setEntry
(self
._previousText
)
self
._entryFieldEntry
.icursor(self
._previousICursor
)
self
._entryFieldEntry
.xview(self
._previousXview
)
if self
._previousSel
is not None:
self
._entryFieldEntry
.selection_range(self
._previousSel
[0],
# Check if the saved text is valid as well.
valid
= self
._getValidity
()
# The validator or stringtovalue commands called by
# _checkValidity() destroyed us.
if self
.normalBackground
is not None:
self
._entryFieldEntry
.configure(
background
= self
.normalBackground
)
self
.normalBackground
= None
if self
.normalBackground
is None:
self
.normalBackground
= self
._entryFieldEntry
.cget('background')
self
._entryFieldEntry
.configure(
background
= self
['errorbackground'])
return self
._executeCommand
()
def __setEntry(self
, text
):
oldState
= str(self
._entryFieldEntry
.cget('state'))
self
._entryFieldEntry
.configure(state
='normal')
self
._entryFieldEntry
.delete(0, 'end')
self
._entryFieldEntry
.insert(0, text
)
self
._entryFieldEntry
.configure(state
=oldState
)
def setentry(self
, text
):
return self
._postProcess
()
return self
._entryFieldEntry
.get()
def setvalue(self
, text
):
return self
.setentry(text
)
Pmw
.forwardmethods(EntryField
, Tkinter
.Entry
, '_entryFieldEntry')
# ======================================================================
# Entry field validation functions
_numericregex
= re
.compile('^[0-9]*$')
_alphabeticregex
= re
.compile('^[a-z]*$', re
.IGNORECASE
)
_alphanumericregex
= re
.compile('^[0-9a-z]*$', re
.IGNORECASE
)
def numericvalidator(text
):
if _numericregex
.match(text
) is None:
def integervalidator(text
):
if text
in ('', '-', '+'):
def alphabeticvalidator(text
):
if _alphabeticregex
.match(text
) is None:
def alphanumericvalidator(text
):
if _alphanumericregex
.match(text
) is None:
def hexadecimalvalidator(text
):
if text
in ('', '0x', '0X', '+', '+0x', '+0X', '-', '-0x', '-0X'):
def realvalidator(text
, separator
= '.'):
if string
.find(text
, '.') >= 0:
index
= string
.find(text
, separator
)
text
= text
[:index
] + '.' + text
[index
+ 1:]
# Check if the string could be made valid by appending a digit
# eg ('-', '+', '.', '-.', '+.', '1.23e', '1E-').
if text
[-1] in string
.digits
:
def timevalidator(text
, separator
= ':'):
Pmw
.timestringtoseconds(text
, separator
)
if len(text
) > 0 and text
[0] in ('+', '-'):
if re
.search('[^0-9' + separator
+ ']', text
) is not None:
def datevalidator(text
, format
= 'ymd', separator
= '/'):
Pmw
.datestringtojdn(text
, format
, separator
)
if re
.search('[^0-9' + separator
+ ']', text
) is not None:
'numeric' : (numericvalidator
, string
.atol
),
'integer' : (integervalidator
, string
.atol
),
'hexadecimal' : (hexadecimalvalidator
, lambda s
: string
.atol(s
, 16)),
'real' : (realvalidator
, Pmw
.stringtoreal
),
'alphabetic' : (alphabeticvalidator
, len),
'alphanumeric' : (alphanumericvalidator
, len),
'time' : (timevalidator
, Pmw
.timestringtoseconds
),
'date' : (datevalidator
, Pmw
.datestringtojdn
),
def _registerEntryField(entry
, entryField
):
# Register an EntryField widget for an Entry widget
_entryCache
[entry
] = entryField
def _deregisterEntryField(entry
):
# Deregister an Entry widget
# Forward preprocess events for an Entry to it's EntryField
_entryCache
[event
.widget
]._preProcess
()
# Forward postprocess events for an Entry to it's EntryField
# The function specified by the 'command' option may have destroyed
# the megawidget in a binding earlier in bindtags, so need to check.
if _entryCache
.has_key(event
.widget
):
_entryCache
[event
.widget
]._postProcess
()