]> git.saurik.com Git - wxWidgets.git/blame - wxPython/wx/lib/intctrl.py
1.
[wxWidgets.git] / wxPython / wx / lib / intctrl.py
CommitLineData
d14a1e28
RD
1#----------------------------------------------------------------------------
2# Name: wxPython.lib.intctrl.py
3# Author: Will Sadkin
4# Created: 01/16/2003
5# Copyright: (c) 2003 by Will Sadkin
6# RCS-ID: $Id$
7# License: wxWindows license
8#----------------------------------------------------------------------------
9# NOTE:
10# This was written to provide a standard integer edit control for wxPython.
11#
12# wxIntCtrl permits integer (long) values to be retrieved or set via
13# .GetValue() and .SetValue(), and provides an EVT_INT() event function
14# for trapping changes to the control.
15#
16# It supports negative integers as well as the naturals, and does not
17# permit leading zeros or an empty control; attempting to delete the
18# contents of the control will result in a (selected) value of zero,
19# thus preserving a legitimate integer value, or an empty control
20# (if a value of None is allowed for the control.) Similarly, replacing the
21# contents of the control with '-' will result in a selected (absolute)
22# value of -1.
23#
24# wxIntCtrl also supports range limits, with the option of either
25# enforcing them or simply coloring the text of the control if the limits
26# are exceeded.
b881fc78
RD
27#----------------------------------------------------------------------------
28# 12/08/2003 - Jeff Grimmett (grimmtooth@softhome.net)
29#
30# o 2.5 Compatability changes
31#
32
33import string
34import types
35
36import wx
37
38#----------------------------------------------------------------------------
1fded56b 39
d14a1e28
RD
40from sys import maxint
41MAXINT = maxint # (constants should be in upper case)
42MININT = -maxint-1
1fded56b 43
d14a1e28
RD
44#----------------------------------------------------------------------------
45
b881fc78
RD
46# Used to trap events indicating that the current
47# integer value of the control has been changed.
48wxEVT_COMMAND_INT_UPDATED = wx.NewEventType()
49EVT_INT = wx.PyEventBinder(wxEVT_COMMAND_INT_UPDATED, 1)
50
51#----------------------------------------------------------------------------
d14a1e28
RD
52
53# wxWindows' wxTextCtrl translates Composite "control key"
54# events into single events before returning them to its OnChar
55# routine. The doc says that this results in 1 for Ctrl-A, 2 for
56# Ctrl-B, etc. However, there are no wxPython or wxWindows
57# symbols for them, so I'm defining codes for Ctrl-X (cut) and
58# Ctrl-V (paste) here for readability:
59WXK_CTRL_X = (ord('X')+1) - ord('A')
60WXK_CTRL_V = (ord('V')+1) - ord('A')
61
b881fc78 62class wxIntUpdatedEvent(wx.PyCommandEvent):
d14a1e28 63 def __init__(self, id, value = 0, object=None):
b881fc78 64 wx.PyCommandEvent.__init__(self, wxEVT_COMMAND_INT_UPDATED, id)
d14a1e28
RD
65
66 self.__value = value
67 self.SetEventObject(object)
68
69 def GetValue(self):
70 """Retrieve the value of the control at the time
71 this event was generated."""
72 return self.__value
73
74
75#----------------------------------------------------------------------------
76
b881fc78 77class wxIntValidator( wx.PyValidator ):
d14a1e28
RD
78 """
79 Validator class used with wxIntCtrl; handles all validation of input
b881fc78 80 prior to changing the value of the underlying wx.TextCtrl.
d14a1e28
RD
81 """
82 def __init__(self):
b881fc78
RD
83 wx.PyValidator.__init__(self)
84 self.Bind(wx.EVT_CHAR, self.OnChar)
d14a1e28
RD
85
86 def Clone (self):
87 return self.__class__()
88
89 def Validate(self, window): # window here is the *parent* of the ctrl
90 """
91 Because each operation on the control is vetted as it's made,
92 the value of the control is always valid.
93 """
94 return 1
95
96
97 def OnChar(self, event):
98 """
99 Validates keystrokes to make sure the resulting value will a legal
100 value. Erasing the value causes it to be set to 0, with the value
101 selected, so it can be replaced. Similarly, replacing the value
102 with a '-' sign causes the value to become -1, with the value
103 selected. Leading zeros are removed if introduced by selection,
104 and are prevented from being inserted.
105 """
106 key = event.KeyCode()
107 ctrl = event.GetEventObject()
108
109
110 value = ctrl.GetValue()
b881fc78 111 textval = wx.TextCtrl.GetValue(ctrl)
d14a1e28
RD
112 allow_none = ctrl.IsNoneAllowed()
113
114 pos = ctrl.GetInsertionPoint()
115 sel_start, sel_to = ctrl.GetSelection()
116 select_len = sel_to - sel_start
117
118# (Uncomment for debugging:)
119## print 'keycode:', key
120## print 'pos:', pos
121## print 'sel_start, sel_to:', sel_start, sel_to
122## print 'select_len:', select_len
123## print 'textval:', textval
124
125 # set defaults for processing:
126 allow_event = 1
127 set_to_none = 0
128 set_to_zero = 0
129 set_to_minus_one = 0
130 paste = 0
131 internally_set = 0
132
133 new_value = value
134 new_text = textval
135 new_pos = pos
136
137 # Validate action, and predict resulting value, so we can
138 # range check the result and validate that too.
139
b881fc78 140 if key in (wx.WXK_DELETE, wx.WXK_BACK, WXK_CTRL_X):
d14a1e28
RD
141 if select_len:
142 new_text = textval[:sel_start] + textval[sel_to:]
b881fc78 143 elif key == wx.WXK_DELETE and pos < len(textval):
d14a1e28 144 new_text = textval[:pos] + textval[pos+1:]
b881fc78 145 elif key == wx.WXK_BACK and pos > 0:
d14a1e28
RD
146 new_text = textval[:pos-1] + textval[pos:]
147 # (else value shouldn't change)
148
149 if new_text in ('', '-'):
150 # Deletion of last significant digit:
151 if allow_none and new_text == '':
152 new_value = None
153 set_to_none = 1
154 else:
155 new_value = 0
156 set_to_zero = 1
157 else:
158 try:
159 new_value = ctrl._fromGUI(new_text)
160 except ValueError:
161 allow_event = 0
162
163
164 elif key == WXK_CTRL_V: # (see comments at top of file)
165 # Only allow paste if number:
166 paste_text = ctrl._getClipboardContents()
167 new_text = textval[:sel_start] + paste_text + textval[sel_to:]
168 if new_text == '' and allow_none:
169 new_value = None
170 set_to_none = 1
171 else:
172 try:
173 # Convert the resulting strings, verifying they
174 # are legal integers and will fit in proper
175 # size if ctrl limited to int. (if not,
176 # disallow event.)
177 new_value = ctrl._fromGUI(new_text)
b881fc78 178
d14a1e28
RD
179 if paste_text:
180 paste_value = ctrl._fromGUI(paste_text)
181 else:
182 paste_value = 0
b881fc78 183
d14a1e28
RD
184 new_pos = sel_start + len(str(paste_value))
185
186 # if resulting value is 0, truncate and highlight value:
187 if new_value == 0 and len(new_text) > 1:
188 set_to_zero = 1
189
190 elif paste_value == 0:
191 # Disallow pasting a leading zero with nothing selected:
192 if( select_len == 0
193 and value is not None
194 and ( (value >= 0 and pos == 0)
195 or (value < 0 and pos in [0,1]) ) ):
196 allow_event = 0
b881fc78 197
d14a1e28
RD
198 paste = 1
199
200 except ValueError:
201 allow_event = 0
202
203
b881fc78 204 elif key < wx.WXK_SPACE or key > 255:
d14a1e28
RD
205 pass # event ok
206
207
208 elif chr(key) == '-':
209 # Allow '-' to result in -1 if replacing entire contents:
210 if( value is None
211 or (value == 0 and pos == 0)
212 or (select_len >= len(str(abs(value)))) ):
213 new_value = -1
214 set_to_minus_one = 1
215
216 # else allow negative sign only at start, and only if
217 # number isn't already zero or negative:
218 elif pos != 0 or (value is not None and value < 0):
219 allow_event = 0
220 else:
221 new_text = '-' + textval
222 new_pos = 1
223 try:
224 new_value = ctrl._fromGUI(new_text)
225 except ValueError:
226 allow_event = 0
227
228
229 elif chr(key) in string.digits:
230 # disallow inserting a leading zero with nothing selected
231 if( chr(key) == '0'
232 and select_len == 0
233 and value is not None
234 and ( (value >= 0 and pos == 0)
235 or (value < 0 and pos in [0,1]) ) ):
236 allow_event = 0
237 # disallow inserting digits before the minus sign:
238 elif value is not None and value < 0 and pos == 0:
239 allow_event = 0
240 else:
241 new_text = textval[:sel_start] + chr(key) + textval[sel_to:]
242 try:
243 new_value = ctrl._fromGUI(new_text)
244 except ValueError:
245 allow_event = 0
246
247 else:
248 # not a legal char
249 allow_event = 0
250
251
252 if allow_event:
253 # Do range checking for new candidate value:
254 if ctrl.IsLimited() and not ctrl.IsInBounds(new_value):
255 allow_event = 0
256 elif new_value is not None:
257 # ensure resulting text doesn't result in a leading 0:
258 if not set_to_zero and not set_to_minus_one:
259 if( (new_value > 0 and new_text[0] == '0')
260 or (new_value < 0 and new_text[1] == '0')
261 or (new_value == 0 and select_len > 1 ) ):
262
263 # Allow replacement of leading chars with
264 # zero, but remove the leading zero, effectively
265 # making this like "remove leading digits"
266
267 # Account for leading zero when positioning cursor:
b881fc78 268 if( key == wx.WXK_BACK
d14a1e28
RD
269 or (paste and paste_value == 0 and new_pos > 0) ):
270 new_pos = new_pos - 1
271
b881fc78
RD
272 wx.CallAfter(ctrl.SetValue, new_value)
273 wx.CallAfter(ctrl.SetInsertionPoint, new_pos)
d14a1e28
RD
274 internally_set = 1
275
276 elif paste:
277 # Always do paste numerically, to remove
278 # leading/trailing spaces
b881fc78
RD
279 wx.CallAfter(ctrl.SetValue, new_value)
280 wx.CallAfter(ctrl.SetInsertionPoint, new_pos)
d14a1e28
RD
281 internally_set = 1
282
283 elif (new_value == 0 and len(new_text) > 1 ):
284 allow_event = 0
285
286 if allow_event:
287 ctrl._colorValue(new_value) # (one way or t'other)
288
289# (Uncomment for debugging:)
290## if allow_event:
291## print 'new value:', new_value
292## if paste: print 'paste'
293## if set_to_none: print 'set_to_none'
294## if set_to_zero: print 'set_to_zero'
295## if set_to_minus_one: print 'set_to_minus_one'
296## if internally_set: print 'internally_set'
297## else:
298## print 'new text:', new_text
299## print 'disallowed'
300## print
301
302 if allow_event:
303 if set_to_none:
b881fc78 304 wx.CallAfter(ctrl.SetValue, new_value)
d14a1e28
RD
305
306 elif set_to_zero:
307 # select to "empty" numeric value
b881fc78
RD
308 wx.CallAfter(ctrl.SetValue, new_value)
309 wx.CallAfter(ctrl.SetInsertionPoint, 0)
310 wx.CallAfter(ctrl.SetSelection, 0, 1)
d14a1e28
RD
311
312 elif set_to_minus_one:
b881fc78
RD
313 wx.CallAfter(ctrl.SetValue, new_value)
314 wx.CallAfter(ctrl.SetInsertionPoint, 1)
315 wx.CallAfter(ctrl.SetSelection, 1, 2)
d14a1e28
RD
316
317 elif not internally_set:
318 event.Skip() # allow base wxTextCtrl to finish processing
319
b881fc78
RD
320 elif not wx.Validator_IsSilent():
321 wx.Bell()
d14a1e28
RD
322
323
324 def TransferToWindow(self):
325 """ Transfer data from validator to window.
326
327 The default implementation returns False, indicating that an error
328 occurred. We simply return True, as we don't do any data transfer.
329 """
b881fc78 330 return True # Prevent wx.Dialog from complaining.
d14a1e28
RD
331
332
333 def TransferFromWindow(self):
334 """ Transfer data from window to validator.
335
336 The default implementation returns False, indicating that an error
337 occurred. We simply return True, as we don't do any data transfer.
338 """
b881fc78 339 return True # Prevent wx.Dialog from complaining.
d14a1e28
RD
340
341
342#----------------------------------------------------------------------------
343
b881fc78 344class wxIntCtrl(wx.TextCtrl):
d14a1e28
RD
345 """
346 This class provides a control that takes and returns integers as
347 value, and provides bounds support and optional value limiting.
348
349 wxIntCtrl(
350 parent, id = -1,
351 value = 0,
352 pos = wxDefaultPosition,
353 size = wxDefaultSize,
354 style = 0,
355 validator = wxDefaultValidator,
356 name = "integer",
357 min = None,
358 max = None,
359 limited = False,
360 allow_none = False,
361 allow_long = False,
362 default_color = wxBLACK,
363 oob_color = wxRED )
364
365 value
366 If no initial value is set, the default will be zero, or
367 the minimum value, if specified. If an illegal string is specified,
368 a ValueError will result. (You can always later set the initial
369 value with SetValue() after instantiation of the control.)
370 min
371 The minimum value that the control should allow. This can be
372 adjusted with SetMin(). If the control is not limited, any value
373 below this bound will be colored with the current out-of-bounds color.
374 If min < -sys.maxint-1 and the control is configured to not allow long
375 values, the minimum bound will still be set to the long value, but
376 the implicit bound will be -sys.maxint-1.
377 max
378 The maximum value that the control should allow. This can be
379 adjusted with SetMax(). If the control is not limited, any value
380 above this bound will be colored with the current out-of-bounds color.
381 if max > sys.maxint and the control is configured to not allow long
382 values, the maximum bound will still be set to the long value, but
383 the implicit bound will be sys.maxint.
384
385 limited
386 Boolean indicating whether the control prevents values from
387 exceeding the currently set minimum and maximum values (bounds).
388 If False and bounds are set, out-of-bounds values will
389 be colored with the current out-of-bounds color.
390
391 allow_none
392 Boolean indicating whether or not the control is allowed to be
393 empty, representing a value of None for the control.
394
395 allow_long
396 Boolean indicating whether or not the control is allowed to hold
397 and return a long as well as an int.
398
399 default_color
400 Color value used for in-bounds values of the control.
401
402 oob_color
403 Color value used for out-of-bounds values of the control
404 when the bounds are set but the control is not limited.
405
406 validator
407 Normally None, wxIntCtrl uses its own validator to do value
408 validation and input control. However, a validator derived
409 from wxIntValidator can be supplied to override the data
410 transfer methods for the wxIntValidator class.
411 """
412
413 def __init__ (
414 self, parent, id=-1, value = 0,
b881fc78
RD
415 pos = wx.DefaultPosition, size = wx.DefaultSize,
416 style = 0, validator = wx.DefaultValidator,
d14a1e28
RD
417 name = "integer",
418 min=None, max=None,
419 limited = 0, allow_none = 0, allow_long = 0,
b881fc78 420 default_color = wx.BLACK, oob_color = wx.RED,
d14a1e28
RD
421 ):
422
423 # Establish attrs required for any operation on value:
424 self.__min = None
425 self.__max = None
426 self.__limited = 0
b881fc78
RD
427 self.__default_color = wx.BLACK
428 self.__oob_color = wx.RED
d14a1e28
RD
429 self.__allow_none = 0
430 self.__allow_long = 0
431 self.__oldvalue = None
432
b881fc78 433 if validator == wx.DefaultValidator:
d14a1e28
RD
434 validator = wxIntValidator()
435
b881fc78 436 wx.TextCtrl.__init__(
d14a1e28
RD
437 self, parent, id, self._toGUI(0),
438 pos, size, style, validator, name )
439
440 # The following lets us set out our "integer update" events:
b881fc78 441 self.Bind(wx.EVT_TEXT, self.OnText )
d14a1e28
RD
442
443 # Establish parameters, with appropriate error checking
444
445 self.SetBounds(min, max)
446 self.SetLimited(limited)
447 self.SetColors(default_color, oob_color)
448 self.SetNoneAllowed(allow_none)
449 self.SetLongAllowed(allow_long)
450 self.SetValue(value)
451 self.__oldvalue = 0
452
453
454 def OnText( self, event ):
455 """
456 Handles an event indicating that the text control's value
457 has changed, and issue EVT_INT event.
b881fc78
RD
458 NOTE: using wx.TextCtrl.SetValue() to change the control's
459 contents from within a wx.EVT_CHAR handler can cause double
d14a1e28
RD
460 text events. So we check for actual changes to the text
461 before passing the events on.
462 """
463 value = self.GetValue()
464 if value != self.__oldvalue:
465 try:
466 self.GetEventHandler().ProcessEvent(
467 wxIntUpdatedEvent( self.GetId(), self.GetValue(), self ) )
468 except ValueError:
469 return
470 # let normal processing of the text continue
471 event.Skip()
472 self.__oldvalue = value # record for next event
473
474
475 def GetValue(self):
476 """
477 Returns the current integer (long) value of the control.
478 """
b881fc78 479 return self._fromGUI( wx.TextCtrl.GetValue(self) )
d14a1e28
RD
480
481 def SetValue(self, value):
482 """
483 Sets the value of the control to the integer value specified.
484 The resulting actual value of the control may be altered to
485 conform with the bounds set on the control if limited,
486 or colored if not limited but the value is out-of-bounds.
487 A ValueError exception will be raised if an invalid value
488 is specified.
489 """
b881fc78 490 wx.TextCtrl.SetValue( self, self._toGUI(value) )
d14a1e28
RD
491 self._colorValue()
492
493
494 def SetMin(self, min=None):
495 """
496 Sets the minimum value of the control. If a value of None
497 is provided, then the control will have no explicit minimum value.
498 If the value specified is greater than the current maximum value,
499 then the function returns 0 and the minimum will not change from
500 its current setting. On success, the function returns 1.
501
502 If successful and the current value is lower than the new lower
503 bound, if the control is limited, the value will be automatically
504 adjusted to the new minimum value; if not limited, the value in the
505 control will be colored with the current out-of-bounds color.
506
507 If min > -sys.maxint-1 and the control is configured to not allow longs,
508 the function will return 0, and the min will not be set.
509 """
510 if( self.__max is None
511 or min is None
512 or (self.__max is not None and self.__max >= min) ):
513 self.__min = min
514
515 if self.IsLimited() and min is not None and self.GetValue() < min:
516 self.SetValue(min)
517 else:
518 self._colorValue()
519 return 1
520 else:
521 return 0
522
523
524 def GetMin(self):
525 """
526 Gets the minimum value of the control. It will return the current
527 minimum integer, or None if not specified.
528 """
529 return self.__min
530
531
532 def SetMax(self, max=None):
533 """
534 Sets the maximum value of the control. If a value of None
535 is provided, then the control will have no explicit maximum value.
536 If the value specified is less than the current minimum value, then
537 the function returns 0 and the maximum will not change from its
538 current setting. On success, the function returns 1.
539
540 If successful and the current value is greater than the new upper
541 bound, if the control is limited the value will be automatically
542 adjusted to this maximum value; if not limited, the value in the
543 control will be colored with the current out-of-bounds color.
544
545 If max > sys.maxint and the control is configured to not allow longs,
546 the function will return 0, and the max will not be set.
547 """
548 if( self.__min is None
549 or max is None
550 or (self.__min is not None and self.__min <= max) ):
551 self.__max = max
552
553 if self.IsLimited() and max is not None and self.GetValue() > max:
554 self.SetValue(max)
555 else:
556 self._colorValue()
557 return 1
558 else:
559 return 0
560
561
562 def GetMax(self):
563 """
564 Gets the maximum value of the control. It will return the current
565 maximum integer, or None if not specified.
566 """
567 return self.__max
568
569
570 def SetBounds(self, min=None, max=None):
571 """
572 This function is a convenience function for setting the min and max
573 values at the same time. The function only applies the maximum bound
574 if setting the minimum bound is successful, and returns True
575 only if both operations succeed.
576 NOTE: leaving out an argument will remove the corresponding bound.
577 """
578 ret = self.SetMin(min)
579 return ret and self.SetMax(max)
580
581
582 def GetBounds(self):
583 """
584 This function returns a two-tuple (min,max), indicating the
585 current bounds of the control. Each value can be None if
586 that bound is not set.
587 """
588 return (self.__min, self.__max)
589
590
591 def SetLimited(self, limited):
592 """
593 If called with a value of True, this function will cause the control
594 to limit the value to fall within the bounds currently specified.
595 If the control's value currently exceeds the bounds, it will then
596 be limited accordingly.
597
598 If called with a value of 0, this function will disable value
599 limiting, but coloring of out-of-bounds values will still take
600 place if bounds have been set for the control.
601 """
602 self.__limited = limited
603 if limited:
604 min = self.GetMin()
605 max = self.GetMax()
606 if not min is None and self.GetValue() < min:
607 self.SetValue(min)
608 elif not max is None and self.GetValue() > max:
609 self.SetValue(max)
610 else:
611 self._colorValue()
612
613
614 def IsLimited(self):
615 """
616 Returns True if the control is currently limiting the
617 value to fall within the current bounds.
618 """
619 return self.__limited
620
621
622 def IsInBounds(self, value=None):
623 """
624 Returns True if no value is specified and the current value
625 of the control falls within the current bounds. This function can
626 also be called with a value to see if that value would fall within
627 the current bounds of the given control.
628 """
629 if value is None:
630 value = self.GetValue()
631
632 if( not (value is None and self.IsNoneAllowed())
633 and type(value) not in (types.IntType, types.LongType) ):
634 raise ValueError (
635 'wxIntCtrl requires integer values, passed %s'% repr(value) )
636
637 min = self.GetMin()
638 max = self.GetMax()
639 if min is None: min = value
640 if max is None: max = value
641
642 # if bounds set, and value is None, return False
643 if value == None and (min is not None or max is not None):
644 return 0
645 else:
646 return min <= value <= max
647
648
649 def SetNoneAllowed(self, allow_none):
650 """
651 Change the behavior of the validation code, allowing control
652 to have a value of None or not, as appropriate. If the value
653 of the control is currently None, and allow_none is 0, the
654 value of the control will be set to the minimum value of the
655 control, or 0 if no lower bound is set.
656 """
657 self.__allow_none = allow_none
658 if not allow_none and self.GetValue() is None:
659 min = self.GetMin()
660 if min is not None: self.SetValue(min)
661 else: self.SetValue(0)
662
663
664 def IsNoneAllowed(self):
665 return self.__allow_none
666
667
668 def SetLongAllowed(self, allow_long):
669 """
670 Change the behavior of the validation code, allowing control
671 to have a long value or not, as appropriate. If the value
672 of the control is currently long, and allow_long is 0, the
673 value of the control will be adjusted to fall within the
674 size of an integer type, at either the sys.maxint or -sys.maxint-1,
675 for positive and negative values, respectively.
676 """
677 current_value = self.GetValue()
678 if not allow_long and type(current_value) is types.LongType:
679 if current_value > 0:
680 self.SetValue(MAXINT)
681 else:
682 self.SetValue(MININT)
683 self.__allow_long = allow_long
684
685
686 def IsLongAllowed(self):
687 return self.__allow_long
688
689
690
b881fc78 691 def SetColors(self, default_color=wx.BLACK, oob_color=wx.RED):
d14a1e28
RD
692 """
693 Tells the control what colors to use for normal and out-of-bounds
694 values. If the value currently exceeds the bounds, it will be
695 recolored accordingly.
696 """
697 self.__default_color = default_color
698 self.__oob_color = oob_color
699 self._colorValue()
700
701
702 def GetColors(self):
703 """
704 Returns a tuple of (default_color, oob_color), indicating
705 the current color settings for the control.
706 """
707 return self.__default_color, self.__oob_color
708
709
710 def _colorValue(self, value=None):
711 """
712 Colors text with oob_color if current value exceeds bounds
713 set for control.
714 """
715 if not self.IsInBounds(value):
716 self.SetForegroundColour(self.__oob_color)
717 else:
718 self.SetForegroundColour(self.__default_color)
719 self.Refresh()
720
721
722 def _toGUI( self, value ):
723 """
724 Conversion function used to set the value of the control; does
725 type and bounds checking and raises ValueError if argument is
726 not a valid value.
727 """
728 if value is None and self.IsNoneAllowed():
729 return ''
730 elif type(value) == types.LongType and not self.IsLongAllowed():
731 raise ValueError (
732 'wxIntCtrl requires integer value, passed long' )
733 elif type(value) not in (types.IntType, types.LongType):
734 raise ValueError (
735 'wxIntCtrl requires integer value, passed %s'% repr(value) )
736
737 elif self.IsLimited():
738 min = self.GetMin()
739 max = self.GetMax()
740 if not min is None and value < min:
741 raise ValueError (
742 'value is below minimum value of control %d'% value )
743 if not max is None and value > max:
744 raise ValueError (
745 'value exceeds value of control %d'% value )
746
747 return str(value)
748
749
750 def _fromGUI( self, value ):
751 """
752 Conversion function used in getting the value of the control.
753 """
754
755 # One or more of the underlying text control implementations
756 # issue an intermediate EVT_TEXT when replacing the control's
757 # value, where the intermediate value is an empty string.
758 # So, to ensure consistency and to prevent spurious ValueErrors,
759 # we make the following test, and react accordingly:
760 #
761 if value == '':
762 if not self.IsNoneAllowed():
763 return 0
764 else:
765 return None
766 else:
767 try:
768 return int( value )
769 except ValueError:
770 if self.IsLongAllowed():
771 return long( value )
772 else:
773 raise
774
775
776 def Cut( self ):
777 """
778 Override the wxTextCtrl's .Cut function, with our own
779 that does validation. Will result in a value of 0
780 if entire contents of control are removed.
781 """
782 sel_start, sel_to = self.GetSelection()
783 select_len = sel_to - sel_start
b881fc78 784 textval = wx.TextCtrl.GetValue(self)
d14a1e28 785
b881fc78 786 do = wx.TextDataObject()
d14a1e28 787 do.SetText(textval[sel_start:sel_to])
b881fc78
RD
788 wx.TheClipboard.Open()
789 wx.TheClipboard.SetData(do)
790 wx.TheClipboard.Close()
d14a1e28
RD
791 if select_len == len(wxTextCtrl.GetValue(self)):
792 if not self.IsNoneAllowed():
793 self.SetValue(0)
794 self.SetInsertionPoint(0)
795 self.SetSelection(0,1)
796 else:
797 self.SetValue(None)
798 else:
799 new_value = self._fromGUI(textval[:sel_start] + textval[sel_to:])
800 self.SetValue(new_value)
801
802
803 def _getClipboardContents( self ):
804 """
805 Subroutine for getting the current contents of the clipboard.
806 """
b881fc78
RD
807 do = wx.TextDataObject()
808 wx.TheClipboard.Open()
809 success = wx.TheClipboard.GetData(do)
810 wx.TheClipboard.Close()
d14a1e28
RD
811
812 if not success:
813 return None
814 else:
815 # Remove leading and trailing spaces before evaluating contents
816 return do.GetText().strip()
817
818
819 def Paste( self ):
820 """
821 Override the wxTextCtrl's .Paste function, with our own
822 that does validation. Will raise ValueError if not a
823 valid integerizable value.
824 """
825 paste_text = self._getClipboardContents()
826 if paste_text:
827 # (conversion will raise ValueError if paste isn't legal)
828 sel_start, sel_to = self.GetSelection()
b881fc78 829 text = wx.TextCtrl.GetValue( self )
d14a1e28
RD
830 new_text = text[:sel_start] + paste_text + text[sel_to:]
831 if new_text == '' and self.IsNoneAllowed():
832 self.SetValue(None)
833 else:
834 value = self._fromGUI(new_text)
835 self.SetValue(value)
836 new_pos = sel_start + len(paste_text)
b881fc78 837 wx.CallAfter(self.SetInsertionPoint, new_pos)
d14a1e28
RD
838
839
840
841#===========================================================================
842
843if __name__ == '__main__':
844
845 import traceback
846
b881fc78 847 class myDialog(wx.Dialog):
d14a1e28 848 def __init__(self, parent, id, title,
b881fc78
RD
849 pos = wx.DefaultPosition, size = wx.DefaultSize,
850 style = wx.DEFAULT_DIALOG_STYLE ):
851 wx.Dialog.__init__(self, parent, id, title, pos, size, style)
d14a1e28 852
b881fc78
RD
853 self.int_ctrl = wxIntCtrl(self, wx.NewId(), size=(55,20))
854 self.OK = wx.Button( self, wx.ID_OK, "OK")
855 self.Cancel = wx.Button( self, wx.ID_CANCEL, "Cancel")
d14a1e28 856
b881fc78
RD
857 vs = wx.BoxSizer( wx.VERTICAL )
858 vs.Add( self.int_ctrl, 0, wx.ALIGN_CENTRE|wx.ALL, 5 )
859 hs = wx.BoxSizer( wx.HORIZONTAL )
860 hs.Add( self.OK, 0, wx.ALIGN_CENTRE|wx.ALL, 5 )
861 hs.Add( self.Cancel, 0, wx.ALIGN_CENTRE|wx.ALL, 5 )
862 vs.Add(hs, 0, wx.ALIGN_CENTRE|wx.ALL, 5 )
d14a1e28
RD
863
864 self.SetAutoLayout( True )
865 self.SetSizer( vs )
866 vs.Fit( self )
867 vs.SetSizeHints( self )
b881fc78 868 self.Bind(EVT_INT, self.OnInt, self.int_ctrl)
d14a1e28
RD
869
870 def OnInt(self, event):
871 print 'int now', event.GetValue()
872
b881fc78 873 class TestApp(wx.App):
d14a1e28
RD
874 def OnInit(self):
875 try:
b881fc78
RD
876 self.frame = wx.Frame(None, -1, "Test", (20,20), (120,100) )
877 self.panel = wx.Panel(self.frame, -1)
878 button = wx.Button(self.panel, 10, "Push Me", (20, 20))
879 self.Bind(wx.EVT_BUTTON, self.OnClick, button)
d14a1e28
RD
880 except:
881 traceback.print_exc()
882 return False
883 return True
884
885 def OnClick(self, event):
886 dlg = myDialog(self.panel, -1, "test wxIntCtrl")
887 dlg.int_ctrl.SetValue(501)
888 dlg.int_ctrl.SetInsertionPoint(1)
889 dlg.int_ctrl.SetSelection(1,2)
890 rc = dlg.ShowModal()
891 print 'final value', dlg.int_ctrl.GetValue()
892 del dlg
893 self.frame.Destroy()
894
895 def Show(self):
896 self.frame.Show(True)
897
898 try:
899 app = TestApp(0)
900 app.Show()
901 app.MainLoop()
902 except:
903 traceback.print_exc()