1 #----------------------------------------------------------------------------
2 # Name: wxPython.lib.maskednumctrl.py
5 # Copyright: (c) 2003 by Will Sadkin
7 # License: wxWindows license
8 #----------------------------------------------------------------------------
10 # This was written to provide a numeric edit control for wxPython that
11 # does things like right-insert (like a calculator), and does grouping, etc.
12 # (ie. the features of wxMaskedTextCtrl), but allows Get/Set of numeric
13 # values, rather than text.
15 # wxMaskedNumCtrl permits integer, and floating point values to be set
16 # retrieved or set via .GetValue() and .SetValue() (type chosen based on
17 # fraction width, and provides an EVT_MASKEDNUM() event function for trapping
18 # changes to the control.
20 # It supports negative numbers as well as the naturals, and has the option
21 # of not permitting leading zeros or an empty control; if an empty value is
22 # not allowed, attempting to delete the contents of the control will result
23 # in a (selected) value of zero, thus preserving a legitimate numeric value.
24 # Similarly, replacing the contents of the control with '-' will result in
25 # a selected (absolute) value of -1.
27 # wxMaskedNumCtrl also supports range limits, with the option of either
28 # enforcing them or simply coloring the text of the control if the limits
31 # wxMaskedNumCtrl is intended to support fixed-point numeric entry, and
32 # is derived from wxMaskedTextCtrl. As such, it supports a limited range
33 # of values to comply with a fixed-width entry mask.
36 <B>wxMaskedNumCtrl:</B>
38 <LI>allows you to get and set integer or floating point numbers as value,</LI>
39 <LI>provides bounds support and optional value limiting,</LI>
40 <LI>has the right-insert input style that wxMaskedTextCtrl supports,</LI>
41 <LI>provides optional automatic grouping, sign control and format, grouping and decimal
42 character selection, etc. etc.</LI>
45 Being derived from wxMaskedTextCtrl, the control only allows
46 fixed-point notation. That is, it has a fixed (though reconfigurable)
47 maximum width for the integer portion and optional fixed width
52 <B>wxMaskedNumCtrl</B>(
55 pos = wxDefaultPosition,
58 validator = wxDefaultValidator,
59 name = "maskednumber",
60 <B>integerWidth</B> = 10,
61 <B>fractionWidth</B> = 0,
62 <B>allowNone</B> = False,
63 <B>allowNegative</B> = True,
64 <B>useParensForNegatives</B> = False,
65 <B>groupDigits</B> = False,
66 <B>groupChar</B> = ',',
67 <B>decimalChar</B> = '.',
70 <B>limited</B> = False,
71 <B>selectOnEntry</b> = True,
72 <B>foregroundColour</B> = "Black",
73 <B>signedForegroundColour</B> = "Red",
74 <B>emptyBackgroundColour</B> = "White",
75 <B>validBackgroundColour</B> = "White",
76 <B>invalidBackgroundColour</B> = "Yellow",
81 <DD>If no initial value is set, the default will be zero, or
82 the minimum value, if specified. If an illegal string is specified,
83 a ValueError will result. (You can always later set the initial
84 value with SetValue() after instantiation of the control.)
86 <DL><B>integerWidth</B>
87 <DD>Indicates how many places to the right of any decimal point
88 should be allowed in the control. This will, perforce, limit
89 the size of the values that can be entered. This number need
90 not include space for grouping characters or the sign, if either
91 of these options are enabled, as the resulting underlying
92 mask is automatically by the control. The default of 10
93 will allow any 32 bit integer value. The minimum value
94 for integerWidth is 1.
96 <DL><B>fractionWidth</B>
97 <DD>Indicates how many decimal places to show for numeric value.
98 If default (0), then the control will display and return only
99 integer or long values.
102 <DD>Boolean indicating whether or not the control is allowed to be
103 empty, representing a value of None for the control.
105 <DT><B>allowNegative</B>
106 <DD>Boolean indicating whether or not control is allowed to hold
109 <DT><B>useParensForNegatives</B>
110 <DD>If true, this will cause negative numbers to be displayed with ()s
111 rather than -, (although '-' will still trigger a negative number.)
113 <DT><B>groupDigits</B>
114 <DD>Indicates whether or not grouping characters should be allowed and/or
115 inserted when leaving the control or the decimal character is entered.
118 <DD>What grouping character will be used if allowed. (By default ',')
120 <DT><B>decimalChar</B>
121 <DD>If fractionWidth is > 0, what character will be used to represent
122 the decimal point. (By default '.')
125 <DD>The minimum value that the control should allow. This can be also be
126 adjusted with SetMin(). If the control is not limited, any value
127 below this bound will result in a background colored with the current
128 invalidBackgroundColour. If the min specified will not fit into the
129 control, the min setting will be ignored.
132 <DD>The maximum value that the control should allow. This can be
133 adjusted with SetMax(). If the control is not limited, any value
134 above this bound will result in a background colored with the current
135 invalidBackgroundColour. If the max specified will not fit into the
136 control, the max setting will be ignored.
139 <DD>Boolean indicating whether the control prevents values from
140 exceeding the currently set minimum and maximum values (bounds).
141 If False and bounds are set, out-of-bounds values will
142 result in a background colored with the current invalidBackgroundColour.
144 <DT><B>selectOnEntry</B>
145 <DD>Boolean indicating whether or not the value in each field of the
146 control should be automatically selected (for replacement) when
147 that field is entered, either by cursor movement or tabbing.
148 This can be desirable when using these controls for rapid data entry.
150 <DT><B>foregroundColour</B>
151 <DD>Color value used for positive values of the control.
153 <DT><B>signedForegroundColour</B>
154 <DD>Color value used for negative values of the control.
156 <DT><B>emptyBackgroundColour</B>
157 <DD>What background color to use when the control is considered
158 "empty." (allow_none must be set to trigger this behavior.)
160 <DT><B>validBackgroundColour</B>
161 <DD>What background color to use when the control value is
164 <DT><B>invalidBackgroundColour</B>
165 <DD>Color value used for illegal values or values out-of-bounds of the
166 control when the bounds are set but the control is not limited.
170 <DT><B>EVT_MASKEDNUM(win, id, func)</B>
171 <DD>Respond to a wxEVT_COMMAND_MASKED_NUMBER_UPDATED event, generated when
172 the value changes. Notice that this event will always be sent when the
173 control's contents changes - whether this is due to user input or
174 comes from the program itself (for example, if SetValue() is called.)
177 <DT><B>SetValue(int|long|float|string)</B>
178 <DD>Sets the value of the control to the value specified, if
179 possible. The resulting actual value of the control may be
180 altered to conform to the format of the control, changed
181 to conform with the bounds set on the control if limited,
182 or colored if not limited but the value is out-of-bounds.
183 A ValueError exception will be raised if an invalid value
186 <DT><B>GetValue()</B>
187 <DD>Retrieves the numeric value from the control. The value
188 retrieved will be either be returned as a long if the
189 fractionWidth is 0, or a float otherwise.
192 <DT><B>SetParameters(**kwargs)</B>
193 <DD>Allows simultaneous setting of various attributes
194 of the control after construction. Keyword arguments
195 allowed are the same parameters as supported in the constructor.
198 <DT><B>SetIntegerWidth(value)</B>
199 <DD>Resets the width of the integer portion of the control. The
200 value must be >= 1, or an AttributeError exception will result.
201 This value should account for any grouping characters that might
202 be inserted (if grouping is enabled), but does not need to account
203 for the sign, as that is handled separately by the control.
204 <DT><B>GetIntegerWidth()</B>
205 <DD>Returns the current width of the integer portion of the control,
206 not including any reserved sign position.
209 <DT><B>SetFractionWidth(value)</B>
210 <DD>Resets the width of the fractional portion of the control. The
211 value must be >= 0, or an AttributeError exception will result. If
212 0, the current value of the control will be truncated to an integer
214 <DT><B>GetFractionWidth()</B>
215 <DD>Returns the current width of the fractional portion of the control.
218 <DT><B>SetMin(min=None)</B>
219 <DD>Resets the minimum value of the control. If a value of <I>None</I>
220 is provided, then the control will have no explicit minimum value.
221 If the value specified is greater than the current maximum value,
222 then the function returns False and the minimum will not change from
223 its current setting. On success, the function returns True.
225 If successful and the current value is lower than the new lower
226 bound, if the control is limited, the value will be automatically
227 adjusted to the new minimum value; if not limited, the value in the
228 control will be colored as invalid.
230 If min > the max value allowed by the width of the control,
231 the function will return False, and the min will not be set.
234 <DD>Gets the current lower bound value for the control.
235 It will return None if no lower bound is currently specified.
238 <DT><B>SetMax(max=None)</B>
239 <DD>Resets the maximum value of the control. If a value of <I>None</I>
240 is provided, then the control will have no explicit maximum value.
241 If the value specified is less than the current minimum value, then
242 the function returns False and the maximum will not change from its
243 current setting. On success, the function returns True.
245 If successful and the current value is greater than the new upper
246 bound, if the control is limited the value will be automatically
247 adjusted to this maximum value; if not limited, the value in the
248 control will be colored as invalid.
250 If max > the max value allowed by the width of the control,
251 the function will return False, and the max will not be set.
254 <DD>Gets the current upper bound value for the control.
255 It will return None if no upper bound is currently specified.
258 <DT><B>SetBounds(min=None,max=None)</B>
259 <DD>This function is a convenience function for setting the min and max
260 values at the same time. The function only applies the maximum bound
261 if setting the minimum bound is successful, and returns True
262 only if both operations succeed. <B><I>Note:</I></B> leaving out an argument
263 will remove the corresponding bound.
264 <DT><B>GetBounds()</B>
265 <DD>This function returns a two-tuple (min,max), indicating the
266 current bounds of the control. Each value can be None if
267 that bound is not set.
270 <DT><B>IsInBounds(value=None)</B>
271 <DD>Returns <I>True</I> if no value is specified and the current value
272 of the control falls within the current bounds. This function can also
273 be called with a value to see if that value would fall within the current
274 bounds of the given control.
277 <DT><B>SetLimited(bool)</B>
278 <DD>If called with a value of True, this function will cause the control
279 to limit the value to fall within the bounds currently specified.
280 If the control's value currently exceeds the bounds, it will then
281 be limited accordingly.
282 If called with a value of False, this function will disable value
283 limiting, but coloring of out-of-bounds values will still take
284 place if bounds have been set for the control.
285 <DT><B>GetLimited()</B>
286 <DT><B>IsLimited()</B>
287 <DD>Returns <I>True</I> if the control is currently limiting the
288 value to fall within the current bounds.
291 <DT><B>SetAllowNone(bool)</B>
292 <DD>If called with a value of True, this function will cause the control
293 to allow the value to be empty, representing a value of None.
294 If called with a value of False, this function will prevent the value
295 from being None. If the value of the control is currently None,
296 ie. the control is empty, then the value will be changed to that
297 of the lower bound of the control, or 0 if no lower bound is set.
298 <DT><B>GetAllowNone()</B>
299 <DT><B>IsNoneAllowed()</B>
300 <DD>Returns <I>True</I> if the control currently allows its
304 <DT><B>SetAllowNegative(bool)</B>
305 <DD>If called with a value of True, this function will cause the
306 control to allow the value to be negative (and reserve space for
307 displaying the sign. If called with a value of False, and the
308 value of the control is currently negative, the value of the
309 control will be converted to the absolute value, and then
310 limited appropriately based on the existing bounds of the control
312 <DT><B>GetAllowNegative()</B>
313 <DT><B>IsNegativeAllowed()</B>
314 <DD>Returns <I>True</I> if the control currently permits values
318 <DT><B>SetGroupDigits(bool)</B>
319 <DD>If called with a value of True, this will make the control
320 automatically add and manage grouping characters to the presented
321 value in integer portion of the control.
322 <DT><B>GetGroupDigits()</B>
323 <DT><B>IsGroupingAllowed()</B>
324 <DD>Returns <I>True</I> if the control is currently set to group digits.
327 <DT><B>SetGroupChar()</B>
328 <DD>Sets the grouping character for the integer portion of the
329 control. (The default grouping character this is ','.
330 <DT><B>GetGroupChar()</B>
331 <DD>Returns the current grouping character for the control.
334 <DT><B>SetSelectOnEntry()</B>
335 <DD>If called with a value of <I>True</I>, this will make the control
336 automatically select the contents of each field as it is entered
337 within the control. (The default is True.)
338 <DT><B>GetSelectOnEntry()</B>
339 <DD>Returns <I>True</I> if the control currently auto selects
340 the field values on entry.
347 from wxPython
.wx
import *
348 import types
, string
, copy
349 from sys
import maxint
350 MAXINT
= maxint
# (constants should be in upper case)
353 from wxPython
.tools
.dbg
import Logger
354 from wxPython
.lib
.maskededit
import wxMaskedEditMixin
, wxMaskedTextCtrl
, Field
359 #----------------------------------------------------------------------------
361 wxEVT_COMMAND_MASKED_NUMBER_UPDATED
= wxNewEventType()
364 def EVT_MASKEDNUM(win
, id, func
):
365 """Used to trap events indicating that the current
366 integer value of the control has been changed."""
367 win
.Connect(id, -1, wxEVT_COMMAND_MASKED_NUMBER_UPDATED
, func
)
370 class wxMaskedNumNumberUpdatedEvent(wxPyCommandEvent
):
371 def __init__(self
, id, value
= 0, object=None):
372 wxPyCommandEvent
.__init
__(self
, wxEVT_COMMAND_MASKED_NUMBER_UPDATED
, id)
375 self
.SetEventObject(object)
378 """Retrieve the value of the control at the time
379 this event was generated."""
383 #----------------------------------------------------------------------------
385 class wxMaskedNumCtrl(wxMaskedTextCtrl
):
387 valid_ctrl_params
= {
388 'integerWidth': 10, # by default allow all 32-bit integers
389 'fractionWidth': 0, # by default, use integers
390 'decimalChar': '.', # by default, use '.' for decimal point
391 'allowNegative': True, # by default, allow negative numbers
392 'useParensForNegatives': False, # by default, use '-' to indicate negatives
393 'groupDigits': True, # by default, don't insert grouping
394 'groupChar': ',', # by default, use ',' for grouping
395 'min': None, # by default, no bounds set
397 'limited': False, # by default, no limiting even if bounds set
398 'allowNone': False, # by default, don't allow empty value
399 'selectOnEntry': True, # by default, select the value of each field on entry
400 'foregroundColour': "Black",
401 'signedForegroundColour': "Red",
402 'emptyBackgroundColour': "White",
403 'validBackgroundColour': "White",
404 'invalidBackgroundColour': "Yellow",
405 'useFixedWidthFont': True, # by default, use a fixed-width font
410 self
, parent
, id=-1, value
= 0,
411 pos
= wxDefaultPosition
, size
= wxDefaultSize
,
412 style
= wxTE_PROCESS_TAB
, validator
= wxDefaultValidator
,
416 dbg('wxMaskedNumCtrl::__init__', indent
=1)
418 # Set defaults for control:
419 dbg('setting defaults:')
420 for key
, param_value
in wxMaskedNumCtrl
.valid_ctrl_params
.items():
421 # This is done this way to make setattr behave consistently with
422 # "private attribute" name mangling
423 setattr(self
, '_' + key
, copy
.copy(param_value
))
425 # Assign defaults for all attributes:
426 init_args
= copy
.deepcopy(wxMaskedNumCtrl
.valid_ctrl_params
)
427 dbg('kwargs:', kwargs
)
428 for key
, param_value
in kwargs
.items():
429 key
= key
.replace('Color', 'Colour')
430 if key
not in wxMaskedNumCtrl
.valid_ctrl_params
.keys():
431 raise AttributeError('invalid keyword argument "%s"' % key
)
433 init_args
[key
] = param_value
434 dbg('init_args:', indent
=1)
435 for key
, param_value
in init_args
.items():
436 dbg('%s:' % key
, param_value
)
439 # Process initial fields for the control, as part of construction:
440 if type(init_args
['integerWidth']) != types
.IntType
:
441 raise AttributeError('invalid integerWidth (%s) specified; expected integer' % repr(init_args
['integerWidth']))
442 elif init_args
['integerWidth'] < 1:
443 raise AttributeError('invalid integerWidth (%s) specified; must be > 0' % repr(init_args
['integerWidth']))
447 if init_args
.has_key('fractionWidth'):
448 if type(init_args
['fractionWidth']) != types
.IntType
:
449 raise AttributeError('invalid fractionWidth (%s) specified; expected integer' % repr(self
._fractionWidth
))
450 elif init_args
['fractionWidth'] < 0:
451 raise AttributeError('invalid fractionWidth (%s) specified; must be >= 0' % repr(init_args
['fractionWidth']))
452 self
._fractionWidth
= init_args
['fractionWidth']
454 if self
._fractionWidth
:
455 fracmask
= '.' + '#{%d}' % self
._fractionWidth
456 dbg('fracmask:', fracmask
)
457 fields
[1] = Field(defaultValue
='0'*self
._fractionWidth
)
461 self
._integerWidth
= init_args
['integerWidth']
462 if init_args
['groupDigits']:
463 self
._groupSpace
= (self
._integerWidth
- 1) / 3
466 intmask
= '#{%d}' % (self
._integerWidth
+ self
._groupSpace
)
467 if self
._fractionWidth
:
471 fields
[0] = Field(formatcodes
='r<>', emptyInvalid
=emptyInvalid
)
472 dbg('intmask:', intmask
)
474 # don't bother to reprocess these arguments:
475 del init_args
['integerWidth']
476 del init_args
['fractionWidth']
479 mask
= intmask
+fracmask
481 # initial value of state vars
484 self
._typedSign
= False
486 # Construct the base control:
487 wxMaskedTextCtrl
.__init
__(
488 self
, parent
, id, '',
489 pos
, size
, style
, validator
, name
,
493 validFunc
=self
.IsInBounds
,
494 setupEventHandling
= False)
496 EVT_SET_FOCUS( self
, self
._OnFocus
) ## defeat automatic full selection
497 EVT_KILL_FOCUS( self
, self
._OnKillFocus
) ## run internal validator
498 EVT_LEFT_DCLICK(self
, self
._OnDoubleClick
) ## select field under cursor on dclick
499 EVT_RIGHT_UP(self
, self
._OnContextMenu
) ## bring up an appropriate context menu
500 EVT_KEY_DOWN( self
, self
._OnKeyDown
) ## capture control events not normally seen, eg ctrl-tab.
501 EVT_CHAR( self
, self
._OnChar
) ## handle each keypress
502 EVT_TEXT( self
, self
.GetId(), self
.OnTextChange
) ## color control appropriately & keep
503 ## track of previous value for undo
505 # Establish any additional parameters, with appropriate error checking
506 self
.SetParameters(**init_args
)
508 # Set the value requested (if possible)
509 ## wxCallAfter(self.SetValue, value)
512 # Ensure proper coloring:
514 dbg('finished wxMaskedNumCtrl::__init__', indent
=0)
517 def SetParameters(self
, **kwargs
):
519 This routine is used to initialize and reconfigure the control:
521 dbg('wxMaskedNumCtrl::SetParameters', indent
=1)
522 maskededit_kwargs
= {}
523 reset_fraction_width
= False
526 if( (kwargs
.has_key('integerWidth') and kwargs
['integerWidth'] != self
._integerWidth
)
527 or (kwargs
.has_key('fractionWidth') and kwargs
['fractionWidth'] != self
._fractionWidth
)
528 or (kwargs
.has_key('groupDigits') and kwargs
['groupDigits'] != self
._groupDigits
) ):
532 if kwargs
.has_key('fractionWidth'):
533 if type(kwargs
['fractionWidth']) != types
.IntType
:
534 raise AttributeError('invalid fractionWidth (%s) specified; expected integer' % repr(kwargs
['fractionWidth']))
535 elif kwargs
['fractionWidth'] < 0:
536 raise AttributeError('invalid fractionWidth (%s) specified; must be >= 0' % repr(kwargs
['fractionWidth']))
538 if self
._fractionWidth
!= kwargs
['fractionWidth']:
539 self
._fractionWidth
= kwargs
['fractionWidth']
541 if self
._fractionWidth
:
542 fracmask
= '.' + '#{%d}' % self
._fractionWidth
543 fields
[1] = Field(defaultValue
='0'*self
._fractionWidth
)
548 dbg('fracmask:', fracmask
)
550 if kwargs
.has_key('integerWidth'):
551 if type(kwargs
['integerWidth']) != types
.IntType
:
553 raise AttributeError('invalid integerWidth (%s) specified; expected integer' % repr(kwargs
['integerWidth']))
554 elif kwargs
['integerWidth'] < 0:
556 raise AttributeError('invalid integerWidth (%s) specified; must be > 0' % repr(kwargs
['integerWidth']))
558 self
._integerWidth
= kwargs
['integerWidth']
560 if kwargs
.has_key('groupDigits'):
561 self
._groupDigits
= kwargs
['groupDigits']
563 if self
._groupDigits
:
564 self
._groupSpace
= (self
._integerWidth
- 1) / 3
568 intmask
= '#{%d}' % (self
._integerWidth
+ self
._groupSpace
)
569 dbg('intmask:', intmask
)
570 fields
[0] = Field(formatcodes
='r<>', emptyInvalid
=emptyInvalid
)
571 maskededit_kwargs
['fields'] = fields
573 # don't bother to reprocess these arguments:
574 if kwargs
.has_key('integerWidth'):
575 del kwargs
['integerWidth']
576 if kwargs
.has_key('fractionWidth'):
577 del kwargs
['fractionWidth']
579 maskededit_kwargs
['mask'] = intmask
+fracmask
581 if kwargs
.has_key('groupChar'):
582 old_groupchar
= self
._groupChar
# save so we can reformat properly
583 dbg("old_groupchar: '%s'" % old_groupchar
)
584 maskededit_kwargs
['groupChar'] = kwargs
['groupChar']
585 if kwargs
.has_key('decimalChar'):
586 old_decimalchar
= self
._decimalChar
587 dbg("old_decimalchar: '%s'" % old_decimalchar
)
588 maskededit_kwargs
['decimalChar'] = kwargs
['decimalChar']
590 # for all other parameters, assign keyword args as appropriate:
591 for key
, param_value
in kwargs
.items():
592 key
= key
.replace('Color', 'Colour')
593 if key
not in wxMaskedNumCtrl
.valid_ctrl_params
.keys():
594 raise AttributeError('invalid keyword argument "%s"' % key
)
595 elif key
not in wxMaskedEditMixin
.valid_ctrl_params
.keys():
596 setattr(self
, '_' + key
, param_value
)
597 elif key
in ('mask', 'autoformat'): # disallow explicit setting of mask
598 raise AttributeError('invalid keyword argument "%s"' % key
)
600 maskededit_kwargs
[key
] = param_value
601 dbg('kwargs:', kwargs
)
603 # reprocess existing format codes to ensure proper resulting format:
604 formatcodes
= self
.GetFormatcodes()
605 if kwargs
.has_key('allowNegative'):
606 if kwargs
['allowNegative'] and '-' not in formatcodes
:
608 maskededit_kwargs
['formatcodes'] = formatcodes
609 elif not kwargs
['allowNegative'] and '-' in formatcodes
:
610 formatcodes
= formatcodes
.replace('-','')
611 maskededit_kwargs
['formatcodes'] = formatcodes
613 if kwargs
.has_key('groupDigits'):
614 if kwargs
['groupDigits'] and ',' not in formatcodes
:
616 maskededit_kwargs
['formatcodes'] = formatcodes
617 elif not kwargs
['groupDigits'] and ',' in formatcodes
:
618 formatcodes
= formatcodes
.replace(',','')
619 maskededit_kwargs
['formatcodes'] = formatcodes
621 if kwargs
.has_key('selectOnEntry'):
622 self
._selectOnEntry
= kwargs
['selectOnEntry']
623 dbg("kwargs['selectOnEntry']?", kwargs
['selectOnEntry'], "'S' in formatcodes?", 'S' in formatcodes
)
624 if kwargs
['selectOnEntry'] and 'S' not in formatcodes
:
626 maskededit_kwargs
['formatcodes'] = formatcodes
627 elif not kwargs
['selectOnEntry'] and 'S' in formatcodes
:
628 formatcodes
= formatcodes
.replace('S','')
629 maskededit_kwargs
['formatcodes'] = formatcodes
631 if 'r' in formatcodes
and self
._fractionWidth
:
632 # top-level mask should only be right insert if no fractional
633 # part will be shown; ie. if reconfiguring control, remove
634 # previous "global" setting.
635 formatcodes
= formatcodes
.replace('r', '')
636 maskededit_kwargs
['formatcodes'] = formatcodes
638 if kwargs
.has_key('limited'):
639 if kwargs
['limited'] and not self
._limited
:
640 maskededit_kwargs
['validRequired'] = True
641 elif not kwargs
['limited'] and self
._limited
:
642 maskededit_kwargs
['validRequired'] = False
643 self
._limited
= kwargs
['limited']
645 dbg('maskededit_kwargs:', maskededit_kwargs
)
646 if maskededit_kwargs
.keys():
647 self
.SetCtrlParameters(**maskededit_kwargs
)
649 # Record end of integer and place cursor there:
650 integerEnd
= self
._fields
[0]._extent
[1]
651 self
.SetInsertionPoint(integerEnd
)
652 self
.SetSelection(integerEnd
, integerEnd
)
654 # Go ensure all the format codes necessary are present:
655 orig_intformat
= intformat
= self
.GetFieldParameter(0, 'formatcodes')
656 if 'r' not in intformat
:
658 if '>' not in intformat
:
660 if intformat
!= orig_intformat
:
661 if self
._fractionWidth
:
662 self
.SetFieldParameters(0, formatcodes
=intformat
)
664 self
.SetCtrlParameters(formatcodes
=intformat
)
666 # Set min and max as appropriate:
667 if kwargs
.has_key('min'):
669 if( self
._max
is None
671 or (self
._max
is not None and self
._max
>= min) ):
675 textmin
= self
._toGUI
(min, apply_limits
= False)
677 dbg('min will not fit into control; ignoring', indent
=0)
685 if kwargs
.has_key('max'):
687 if( self
._min
is None
689 or (self
._min
is not None and self
._min
<= max) ):
693 textmax
= self
._toGUI
(max, apply_limits
= False)
695 dbg('max will not fit into control; ignoring', indent
=0)
702 if kwargs
.has_key('allowNegative'):
703 self
._allowNegative
= kwargs
['allowNegative']
705 # Ensure current value of control obeys any new restrictions imposed:
706 text
= self
._GetValue
()
707 dbg('text value: "%s"' % text
)
708 if kwargs
.has_key('groupChar') and text
.find(old_groupchar
) != -1:
709 text
= text
.replace(old_groupchar
, self
._groupChar
)
710 if kwargs
.has_key('decimalChar') and text
.find(old_decimalchar
) != -1:
711 text
= text
.replace(old_decimalchar
, self
._decimalChar
)
712 if text
!= self
._GetValue
():
713 wxTextCtrl
.SetValue(self
, text
)
715 value
= self
.GetValue()
717 dbg('self._allowNegative?', self
._allowNegative
)
718 if not self
._allowNegative
and self
._isNeg
:
720 dbg('abs(value):', value
)
723 elif not self
._allowNone
and wxMaskedTextCtrl
.GetValue(self
) == '':
729 sel_start
, sel_to
= self
.GetSelection()
730 if self
.IsLimited() and self
._min
is not None and value
< self
._min
:
731 dbg('Set to min value:', self
._min
)
732 self
._SetValue
(self
._toGUI
(self
._min
))
734 elif self
.IsLimited() and self
._max
is not None and value
> self
._max
:
735 dbg('Setting to max value:', self
._max
)
736 self
._SetValue
(self
._toGUI
(self
._max
))
738 # reformat current value as appropriate to possibly new conditions
739 dbg('Reformatting value:', value
)
740 sel_start
, sel_to
= self
.GetSelection()
741 self
._SetValue
(self
._toGUI
(value
))
742 self
.Refresh() # recolor as appropriate
743 dbg('finished wxMaskedNumCtrl::SetParameters', indent
=0)
747 def _GetNumValue(self
, value
):
749 This function attempts to "clean up" a text value, providing a regularized
750 convertable string, via atol() or atof(), for any well-formed numeric text value.
752 return value
.replace(self
._groupChar
, '').replace(self
._decimalChar
, '.').replace('(', '-').replace(')','').strip()
755 def GetFraction(self
, candidate
=None):
757 Returns the fractional portion of the value as a float. If there is no
758 fractional portion, the value returned will be 0.0.
760 if not self
._fractionWidth
:
763 fracstart
, fracend
= self
._fields
[1]._extent
764 if candidate
is None:
765 value
= self
._toGUI
(wxMaskedTextCtrl
.GetValue(self
))
767 value
= self
._toGUI
(candidate
)
768 fracstring
= value
[fracstart
:fracend
].strip()
772 return string
.atof(fracstring
)
774 def _OnChangeSign(self
, event
):
775 dbg('wxMaskedNumCtrl::_OnChangeSign', indent
=1)
776 self
._typedSign
= True
777 wxMaskedEditMixin
._OnChangeSign
(self
, event
)
781 def _disallowValue(self
):
782 dbg('wxMaskedNumCtrl::_disallowValue')
783 # limited and -1 is out of bounds
786 if not wxValidator_IsSilent():
788 sel_start
, sel_to
= self
._GetSelection
()
789 dbg('queuing reselection of (%d, %d)' % (sel_start
, sel_to
))
790 wxCallAfter(self
.SetInsertionPoint
, sel_start
) # preserve current selection/position
791 wxCallAfter(self
.SetSelection
, sel_start
, sel_to
)
793 def _SetValue(self
, value
):
795 This routine supersedes the base masked control _SetValue(). It is
796 needed to ensure that the value of the control is always representable/convertable
797 to a numeric return value (via GetValue().) This routine also handles
798 automatic adjustment and grouping of the value without explicit intervention
802 dbg('wxMaskedNumCtrl::_SetValue("%s")' % value
, indent
=1)
804 if( (self
._fractionWidth
and value
.find(self
._decimalChar
) == -1) or
805 (self
._fractionWidth
== 0 and value
.find(self
._decimalChar
) != -1) ) :
806 value
= self
._toGUI
(value
)
808 numvalue
= self
._GetNumValue
(value
)
809 dbg('cleansed value: "%s"' % numvalue
)
814 dbg('calling base wxMaskedTextCtrl._SetValue(self, "%s")' % value
)
815 wxMaskedTextCtrl
._SetValue
(self
, value
)
818 elif self
._min
> 0 and self
.IsLimited():
819 replacement
= self
._min
822 dbg('empty value; setting replacement:', replacement
)
824 if replacement
is None:
825 # Go get the integer portion about to be set and verify its validity
826 intstart
, intend
= self
._fields
[0]._extent
827 dbg('intstart, intend:', intstart
, intend
)
828 dbg('raw integer:"%s"' % value
[intstart
:intend
])
829 int = self
._GetNumValue
(value
[intstart
:intend
])
830 numval
= self
._fromGUI
(value
)
832 dbg('integer: "%s"' % int)
834 fracval
= self
.GetFraction(value
)
835 except ValueError, e
:
836 dbg('Exception:', e
, 'must be out of bounds; disallow value')
837 self
._disallowValue
()
842 dbg('self._isNeg?', self
._isNeg
)
843 if int == '-' and self
._oldvalue
< 0 and not self
._typedSign
:
844 dbg('just a negative sign; old value < 0; setting replacement of 0')
847 elif int[:2] == '-0' and self
._fractionWidth
== 0:
848 if self
._oldvalue
< 0:
849 dbg('-0; setting replacement of 0')
852 elif not self
._limited
or (self
._min
< -1 and self
._max
>= -1):
853 dbg('-0; setting replacement of -1')
857 # limited and -1 is out of bounds
858 self
._disallowValue
()
862 elif int == '-' and (self
._oldvalue
>= 0 or self
._typedSign
) and self
._fractionWidth
== 0:
863 if not self
._limited
or (self
._min
< -1 and self
._max
>= -1):
864 dbg('just a negative sign; setting replacement of -1')
867 # limited and -1 is out of bounds
868 self
._disallowValue
()
872 elif( self
._typedSign
873 and int.find('-') != -1
875 and not self
._min
<= numval
<= self
._max
):
876 # changed sign resulting in value that's now out-of-bounds;
878 self
._disallowValue
()
882 if replacement
is None:
883 if int and int != '-':
887 # integer requested is not legal. This can happen if the user
888 # is attempting to insert a digit in the middle of the control
889 # resulting in something like " 3 45". Disallow such actions:
890 dbg('>>>>>>>>>>>>>>>> "%s" does not convert to a long!' % int)
891 if not wxValidator_IsSilent():
893 sel_start
, sel_to
= self
._GetSelection
()
894 dbg('queuing reselection of (%d, %d)' % (sel_start
, sel_to
))
895 wxCallAfter(self
.SetInsertionPoint
, sel_start
) # preserve current selection/position
896 wxCallAfter(self
.SetSelection
, sel_start
, sel_to
)
900 if int[0] == '0' and len(int) > 1:
901 dbg('numvalue: "%s"' % numvalue
.replace(' ', ''))
902 if self
._fractionWidth
:
903 value
= self
._toGUI
(string
.atof(numvalue
))
905 value
= self
._toGUI
(string
.atol(numvalue
))
906 dbg('modified value: "%s"' % value
)
908 self
._typedSign
= False # reset state var
910 if replacement
is not None:
911 # Value presented wasn't a legal number, but control should do something
912 # reasonable instead:
913 dbg('setting replacement value:', replacement
)
914 self
._SetValue
(self
._toGUI
(replacement
))
915 sel_start
= wxMaskedTextCtrl
.GetValue(self
).find(str(abs(replacement
))) # find where it put the 1, so we can select it
916 sel_to
= sel_start
+ len(str(abs(replacement
)))
917 dbg('queuing selection of (%d, %d)' %(sel_start
, sel_to
))
918 wxCallAfter(self
.SetInsertionPoint
, sel_start
)
919 wxCallAfter(self
.SetSelection
, sel_start
, sel_to
)
923 # Otherwise, apply appropriate formatting to value:
925 # Because we're intercepting the value and adjusting it
926 # before a sign change is detected, we need to do this here:
927 if '-' in value
or '(' in value
:
932 dbg('value:"%s"' % value
, 'self._useParens:', self
._useParens
)
933 if self
._fractionWidth
:
934 adjvalue
= self
._adjustFloat
(self
._GetNumValue
(value
).replace('.',self
._decimalChar
))
936 adjvalue
= self
._adjustInt
(self
._GetNumValue
(value
))
937 dbg('adjusted value: "%s"' % adjvalue
)
940 sel_start
, sel_to
= self
._GetSelection
() # record current insertion point
941 dbg('calling base wxMaskedTextCtrl._SetValue(self, "%s")' % adjvalue
)
942 wxMaskedTextCtrl
._SetValue
(self
, adjvalue
)
943 # After all actions so far scheduled, check that resulting cursor
944 # position is appropriate, and move if not:
945 wxCallAfter(self
._CheckInsertionPoint
)
947 dbg('finished wxMaskedNumCtrl::_SetValue', indent
=0)
949 def _CheckInsertionPoint(self
):
950 # If current insertion point is before the end of the integer and
951 # its before the 1st digit, place it just after the sign position:
952 dbg('wxMaskedNumCtrl::CheckInsertionPoint', indent
=1)
953 sel_start
, sel_to
= self
._GetSelection
()
954 text
= self
._GetValue
()
955 if sel_to
< self
._fields
[0]._extent
[1] and text
[sel_to
] in (' ', '-', '('):
956 text
, signpos
, right_signpos
= self
._getSignedValue
()
957 dbg('setting selection(%d, %d)' % (signpos
+1, signpos
+1))
958 self
.SetInsertionPoint(signpos
+1)
959 self
.SetSelection(signpos
+1, signpos
+1)
963 def _OnErase( self
, event
):
965 This overrides the base control _OnErase, so that erasing around
966 grouping characters auto selects the digit before or after the
967 grouping character, so that the erasure does the right thing.
969 dbg('wxMaskedNumCtrl::_OnErase', indent
=1)
971 #if grouping digits, make sure deletes next to group char always
972 # delete next digit to appropriate side:
973 if self
._groupDigits
:
974 key
= event
.GetKeyCode()
975 value
= wxMaskedTextCtrl
.GetValue(self
)
976 sel_start
, sel_to
= self
._GetSelection
()
979 # if 1st selected char is group char, select to previous digit
980 if sel_start
> 0 and sel_start
< len(self
._mask
) and value
[sel_start
:sel_to
] == self
._groupChar
:
981 self
.SetInsertionPoint(sel_start
-1)
982 self
.SetSelection(sel_start
-1, sel_to
)
984 # elif previous char is group char, select to previous digit
985 elif sel_start
> 1 and sel_start
== sel_to
and value
[sel_start
-1:sel_start
] == self
._groupChar
:
986 self
.SetInsertionPoint(sel_start
-2)
987 self
.SetSelection(sel_start
-2, sel_to
)
989 elif key
== WXK_DELETE
:
990 if( sel_to
< len(self
._mask
) - 2 + (1 *self
._useParens
)
991 and sel_start
== sel_to
992 and value
[sel_to
] == self
._groupChar
):
993 self
.SetInsertionPoint(sel_start
)
994 self
.SetSelection(sel_start
, sel_to
+2)
996 elif( sel_to
< len(self
._mask
) - 2 + (1 *self
._useParens
)
997 and value
[sel_start
:sel_to
] == self
._groupChar
):
998 self
.SetInsertionPoint(sel_start
)
999 self
.SetSelection(sel_start
, sel_to
+1)
1001 wxMaskedTextCtrl
._OnErase
(self
, event
)
1005 def OnTextChange( self
, event
):
1007 Handles an event indicating that the text control's value
1008 has changed, and issue EVT_MaskedNum event.
1009 NOTE: using wxTextCtrl.SetValue() to change the control's
1010 contents from within a EVT_CHAR handler can cause double
1011 text events. So we check for actual changes to the text
1012 before passing the events on.
1014 dbg('wxMaskedNumCtrl::OnTextChange', indent
=1)
1015 if not wxMaskedTextCtrl
._OnTextChange
(self
, event
):
1019 # else... legal value
1021 value
= self
.GetValue()
1022 if value
!= self
._oldvalue
:
1024 self
.GetEventHandler().ProcessEvent(
1025 wxMaskedNumNumberUpdatedEvent( self
.GetId(), self
.GetValue(), self
) )
1029 # let normal processing of the text continue
1031 self
._oldvalue
= value
# record for next event
1034 def _GetValue(self
):
1036 Override of wxMaskedTextCtrl to allow amixin to get the raw text value of the
1037 control with this function.
1039 return wxTextCtrl
.GetValue(self
)
1044 Returns the current numeric value of the control.
1046 return self
._fromGUI
( wxMaskedTextCtrl
.GetValue(self
) )
1048 def SetValue(self
, value
):
1050 Sets the value of the control to the value specified.
1051 The resulting actual value of the control may be altered to
1052 conform with the bounds set on the control if limited,
1053 or colored if not limited but the value is out-of-bounds.
1054 A ValueError exception will be raised if an invalid value
1057 wxMaskedTextCtrl
.SetValue( self
, self
._toGUI
(value
) )
1060 def SetIntegerWidth(self
, value
):
1061 self
.SetCtrlParameters(integerWidth
=value
)
1062 def GetIntegerWidth(self
):
1063 return self
._integerWidth
1065 def SetFractionWidth(self
, value
):
1066 self
.SetCtrlParameters(fractionWidth
=value
)
1067 def GetFractionWidth(self
):
1068 return self
._fractionWidth
1072 def SetMin(self
, min=None):
1074 Sets the minimum value of the control. If a value of None
1075 is provided, then the control will have no explicit minimum value.
1076 If the value specified is greater than the current maximum value,
1077 then the function returns False and the minimum will not change from
1078 its current setting. On success, the function returns True.
1080 If successful and the current value is lower than the new lower
1081 bound, if the control is limited, the value will be automatically
1082 adjusted to the new minimum value; if not limited, the value in the
1083 control will be colored as invalid.
1085 If min > the max value allowed by the width of the control,
1086 the function will return False, and the min will not be set.
1088 dbg('wxMaskedNumCtrl::SetMin(%s)' % repr(min), indent
=1)
1089 if( self
._max
is None
1091 or (self
._max
is not None and self
._max
>= min) ):
1093 self
.SetParameters(min=min)
1104 Gets the lower bound value of the control. It will return
1105 None if not specified.
1110 def SetMax(self
, max=None):
1112 Sets the maximum value of the control. If a value of None
1113 is provided, then the control will have no explicit maximum value.
1114 If the value specified is less than the current minimum value, then
1115 the function returns False and the maximum will not change from its
1116 current setting. On success, the function returns True.
1118 If successful and the current value is greater than the new upper
1119 bound, if the control is limited the value will be automatically
1120 adjusted to this maximum value; if not limited, the value in the
1121 control will be colored as invalid.
1123 If max > the max value allowed by the width of the control,
1124 the function will return False, and the max will not be set.
1126 if( self
._min
is None
1128 or (self
._min
is not None and self
._min
<= max) ):
1130 self
.SetParameters(max=max)
1142 Gets the maximum value of the control. It will return the current
1143 maximum integer, or None if not specified.
1148 def SetBounds(self
, min=None, max=None):
1150 This function is a convenience function for setting the min and max
1151 values at the same time. The function only applies the maximum bound
1152 if setting the minimum bound is successful, and returns True
1153 only if both operations succeed.
1154 NOTE: leaving out an argument will remove the corresponding bound.
1156 ret
= self
.SetMin(min)
1157 return ret
and self
.SetMax(max)
1160 def GetBounds(self
):
1162 This function returns a two-tuple (min,max), indicating the
1163 current bounds of the control. Each value can be None if
1164 that bound is not set.
1166 return (self
._min
, self
._max
)
1169 def SetLimited(self
, limited
):
1171 If called with a value of True, this function will cause the control
1172 to limit the value to fall within the bounds currently specified.
1173 If the control's value currently exceeds the bounds, it will then
1174 be limited accordingly.
1176 If called with a value of False, this function will disable value
1177 limiting, but coloring of out-of-bounds values will still take
1178 place if bounds have been set for the control.
1180 self
.SetParameters(limited
= limited
)
1183 def IsLimited(self
):
1185 Returns True if the control is currently limiting the
1186 value to fall within the current bounds.
1188 return self
._limited
1190 def GetLimited(self
):
1191 """ (For regularization of property accessors) """
1192 return self
.IsLimited
1195 def IsInBounds(self
, value
=None):
1197 Returns True if no value is specified and the current value
1198 of the control falls within the current bounds. This function can
1199 also be called with a value to see if that value would fall within
1200 the current bounds of the given control.
1202 dbg('IsInBounds(%s)' % repr(value
), indent
=1)
1204 value
= self
.GetValue()
1207 value
= self
._GetNumValue
(self
._toGUI
(value
))
1208 except ValueError, e
:
1209 dbg('error getting NumValue(self._toGUI(value)):', e
, indent
=0)
1213 elif self
._fractionWidth
:
1214 value
= float(value
)
1220 if min is None: min = value
1221 if max is None: max = value
1223 # if bounds set, and value is None, return False
1224 if value
== None and (min is not None or max is not None):
1225 dbg('finished IsInBounds', indent
=0)
1228 dbg('finished IsInBounds', indent
=0)
1229 return min <= value
<= max
1232 def SetAllowNone(self
, allow_none
):
1234 Change the behavior of the validation code, allowing control
1235 to have a value of None or not, as appropriate. If the value
1236 of the control is currently None, and allow_none is False, the
1237 value of the control will be set to the minimum value of the
1238 control, or 0 if no lower bound is set.
1240 self
._allowNone
= allow_none
1241 if not allow_none
and self
.GetValue() is None:
1243 if min is not None: self
.SetValue(min)
1244 else: self
.SetValue(0)
1247 def IsNoneAllowed(self
):
1248 return self
._allowNone
1249 def GetAllowNone(self
):
1250 """ (For regularization of property accessors) """
1251 return self
.IsNoneAllowed()
1253 def SetAllowNegative(self
, value
):
1254 self
.SetParameters(allowNegative
=value
)
1255 def IsNegativeAllowed(self
):
1256 return self
._allowNegative
1257 def GetAllowNegative(self
):
1258 """ (For regularization of property accessors) """
1259 return self
.IsNegativeAllowed()
1261 def SetGroupDigits(self
, value
):
1262 self
.SetParameters(groupDigits
=value
)
1263 def IsGroupingAllowed(self
):
1264 return self
._groupDigits
1265 def GetGroupDigits(self
):
1266 """ (For regularization of property accessors) """
1267 return self
.IsGroupingAllowed()
1269 def SetGroupChar(self
, value
):
1270 self
.SetParameters(groupChar
=value
)
1271 def GetGroupChar(self
):
1272 return self
._groupChar
1274 def SetDecimalChar(self
, value
):
1275 self
.SetParameters(decimalChar
=value
)
1276 def GetDecimalChar(self
):
1277 return self
._decimalChar
1279 def SetSelectOnEntry(self
, value
):
1280 self
.SetParameters(selectOnEntry
=value
)
1281 def GetSelectOnEntry(self
):
1282 return self
._selectOnEntry
1284 # (Other parameter accessors are inherited from base class)
1287 def _toGUI( self
, value
, apply_limits
= True ):
1289 Conversion function used to set the value of the control; does
1290 type and bounds checking and raises ValueError if argument is
1293 dbg('wxMaskedNumCtrl::_toGUI(%s)' % repr(value
), indent
=1)
1294 if value
is None and self
.IsNoneAllowed():
1296 return self
._template
1298 elif type(value
) in (types
.StringType
, types
.UnicodeType
):
1299 value
= self
._GetNumValue
(value
)
1300 dbg('cleansed num value: "%s"' % value
)
1302 if self
._fractionWidth
or value
.find('.') != -1:
1303 value
= float(value
)
1306 except Exception, e
:
1307 dbg('exception raised:', e
, indent
=0)
1308 raise ValueError ('wxMaskedNumCtrl requires numeric value, passed %s'% repr(value
) )
1310 elif type(value
) not in (types
.IntType
, types
.LongType
, types
.FloatType
):
1313 'wxMaskedNumCtrl requires numeric value, passed %s'% repr(value
) )
1315 if not self
._allowNegative
and value
< 0:
1317 'control configured to disallow negative values, passed %s'% repr(value
) )
1319 if self
.IsLimited() and apply_limits
:
1322 if not min is None and value
< min:
1325 'value %d is below minimum value of control'% value
)
1326 if not max is None and value
> max:
1329 'value %d exceeds value of control'% value
)
1331 adjustwidth
= len(self
._mask
) - (1 * self
._useParens
* self
._signOk
)
1332 dbg('len(%s):' % self
._mask
, len(self
._mask
))
1333 dbg('adjustwidth - groupSpace:', adjustwidth
- self
._groupSpace
)
1334 dbg('adjustwidth:', adjustwidth
)
1335 if self
._fractionWidth
== 0:
1336 s
= str(long(value
)).rjust(self
._integerWidth
)
1338 format
= '%' + '%d.%df' % (self
._integerWidth
+self
._fractionWidth
+1, self
._fractionWidth
)
1339 s
= format
% float(value
)
1340 dbg('s:"%s"' % s
, 'len(s):', len(s
))
1341 if len(s
) > (adjustwidth
- self
._groupSpace
):
1343 raise ValueError ('value %s exceeds the integer width of the control (%d)' % (s
, self
._integerWidth
))
1344 elif s
[0] not in ('-', ' ') and self
._allowNegative
and len(s
) == (adjustwidth
- self
._groupSpace
):
1346 raise ValueError ('value %s exceeds the integer width of the control (%d)' % (s
, self
._integerWidth
))
1348 s
= s
.rjust(adjustwidth
).replace('.', self
._decimalChar
)
1349 if self
._signOk
and self
._useParens
:
1350 if s
.find('-') != -1:
1351 s
= s
.replace('-', '(') + ')'
1354 dbg('returned: "%s"' % s
, indent
=0)
1358 def _fromGUI( self
, value
):
1360 Conversion function used in getting the value of the control.
1363 dbg('wxMaskedNumCtrl::_fromGUI(%s)' % value
, indent
=1)
1364 # One or more of the underlying text control implementations
1365 # issue an intermediate EVT_TEXT when replacing the control's
1366 # value, where the intermediate value is an empty string.
1367 # So, to ensure consistency and to prevent spurious ValueErrors,
1368 # we make the following test, and react accordingly:
1371 if not self
.IsNoneAllowed():
1372 dbg('empty value; not allowed,returning 0', indent
= 0)
1373 if self
._fractionWidth
:
1378 dbg('empty value; returning None', indent
= 0)
1381 value
= self
._GetNumValue
(value
)
1382 dbg('Num value: "%s"' % value
)
1383 if self
._fractionWidth
:
1386 return float( value
)
1388 dbg("couldn't convert to float; returning None")
1399 return long( value
)
1401 dbg("couldn't convert to long; returning None")
1407 dbg('exception occurred; returning None')
1411 def _Paste( self
, value
=None, raise_on_invalid
=False, just_return_value
=False ):
1413 Preprocessor for base control paste; if value needs to be right-justified
1414 to fit in control, do so prior to paste:
1416 dbg('wxMaskedNumCtrl::_Paste (value = "%s")' % value
)
1418 paste_text
= self
._getClipboardContents
()
1422 # treat paste as "replace number", if appropriate:
1423 sel_start
, sel_to
= self
._GetSelection
()
1424 if sel_start
== sel_to
or self
._selectOnEntry
and (sel_start
, sel_to
) == self
._fields
[0]._extent
:
1425 paste_text
= self
._toGUI
(paste_text
)
1426 self
._SetSelection
(0, len(self
._mask
))
1428 return wxMaskedEditMixin
._Paste
(self
,
1430 raise_on_invalid
=raise_on_invalid
,
1431 just_return_value
=just_return_value
)
1435 #===========================================================================
1437 if __name__
== '__main__':
1441 class myDialog(wxDialog
):
1442 def __init__(self
, parent
, id, title
,
1443 pos
= wxPyDefaultPosition
, size
= wxPyDefaultSize
,
1444 style
= wxDEFAULT_DIALOG_STYLE
):
1445 wxDialog
.__init
__(self
, parent
, id, title
, pos
, size
, style
)
1447 self
.int_ctrl
= wxMaskedNumCtrl(self
, wxNewId(), size
=(55,20))
1448 self
.OK
= wxButton( self
, wxID_OK
, "OK")
1449 self
.Cancel
= wxButton( self
, wxID_CANCEL
, "Cancel")
1451 vs
= wxBoxSizer( wxVERTICAL
)
1452 vs
.AddWindow( self
.int_ctrl
, 0, wxALIGN_CENTRE|wxALL
, 5 )
1453 hs
= wxBoxSizer( wxHORIZONTAL
)
1454 hs
.AddWindow( self
.OK
, 0, wxALIGN_CENTRE|wxALL
, 5 )
1455 hs
.AddWindow( self
.Cancel
, 0, wxALIGN_CENTRE|wxALL
, 5 )
1456 vs
.AddSizer(hs
, 0, wxALIGN_CENTRE|wxALL
, 5 )
1458 self
.SetAutoLayout( True )
1461 vs
.SetSizeHints( self
)
1462 EVT_MASKEDNUM(self
, self
.int_ctrl
.GetId(), self
.OnChange
)
1464 def OnChange(self
, event
):
1465 print 'value now', event
.GetValue()
1467 class TestApp(wxApp
):
1470 self
.frame
= wxFrame(NULL
, -1, "Test",
1471 wxPoint(20,20), wxSize(120,100) )
1472 self
.panel
= wxPanel(self
.frame
, -1)
1473 button
= wxButton(self
.panel
, 10, "Push Me",
1475 EVT_BUTTON(self
, 10, self
.OnClick
)
1477 traceback
.print_exc()
1481 def OnClick(self
, event
):
1482 dlg
= myDialog(self
.panel
, -1, "test wxMaskedNumCtrl")
1483 dlg
.int_ctrl
.SetValue(501)
1484 dlg
.int_ctrl
.SetInsertionPoint(1)
1485 dlg
.int_ctrl
.SetSelection(1,2)
1486 rc
= dlg
.ShowModal()
1487 print 'final value', dlg
.int_ctrl
.GetValue()
1489 self
.frame
.Destroy()
1492 self
.frame
.Show(True)
1499 traceback
.print_exc()
1503 ## =============================##
1504 ## 1. Add support for printf-style format specification.
1505 ## 2. Add option for repositioning on 'illegal' insertion point.