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