Show More
@@ -15,7 +15,8 b' Represents an unbounded float using a widget.' | |||
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | 16 | from .widget import DOMWidget, register |
|
17 | 17 | from .trait_types import Color |
|
18 |
from IPython.utils.traitlets import Unicode, CFloat, Bool, CaselessStrEnum, |
|
|
18 | from IPython.utils.traitlets import (Unicode, CFloat, Bool, CaselessStrEnum, | |
|
19 | Tuple, TraitError) | |
|
19 | 20 | from IPython.utils.warn import DeprecatedClass |
|
20 | 21 | |
|
21 | 22 | #----------------------------------------------------------------------------- |
@@ -31,39 +32,37 b' class _Float(DOMWidget):' | |||
|
31 | 32 | kwargs['value'] = value |
|
32 | 33 | super(_Float, self).__init__(**kwargs) |
|
33 | 34 | |
|
35 | ||
|
34 | 36 | class _BoundedFloat(_Float): |
|
35 | 37 | max = CFloat(100.0, help="Max value", sync=True) |
|
36 | 38 | min = CFloat(0.0, help="Min value", sync=True) |
|
37 |
step = CFloat(0.1, help="Minimum step t |
|
|
39 | step = CFloat(0.1, help="Minimum step to increment the value (ignored by some views)", sync=True) | |
|
38 | 40 | |
|
39 | 41 | def __init__(self, *pargs, **kwargs): |
|
40 | 42 | """Constructor""" |
|
41 | 43 | super(_BoundedFloat, self).__init__(*pargs, **kwargs) |
|
42 | self._handle_value_changed('value', None, self.value) | |
|
43 | self._handle_max_changed('max', None, self.max) | |
|
44 | self._handle_min_changed('min', None, self.min) | |
|
45 | self.on_trait_change(self._handle_value_changed, 'value') | |
|
46 | self.on_trait_change(self._handle_max_changed, 'max') | |
|
47 | self.on_trait_change(self._handle_min_changed, 'min') | |
|
48 | ||
|
49 | def _handle_value_changed(self, name, old, new): | |
|
50 | """Validate value.""" | |
|
51 | if self.min > new or new > self.max: | |
|
52 | self.value = min(max(new, self.min), self.max) | |
|
53 | ||
|
54 | def _handle_max_changed(self, name, old, new): | |
|
55 | """Make sure the min is always <= the max.""" | |
|
56 | if new < self.min: | |
|
57 | raise ValueError("setting max < min") | |
|
58 | if new < self.value: | |
|
59 | self.value = new | |
|
60 | 44 | |
|
61 |
def _ |
|
|
62 | """Make sure the max is always >= the min.""" | |
|
63 |
if n |
|
|
64 | raise ValueError("setting min > max") | |
|
65 | if new > self.value: | |
|
66 | self.value = new | |
|
45 | def _value_validate(self, value, trait): | |
|
46 | """Cap and floor value""" | |
|
47 | if self.min > value or self.max < value: | |
|
48 | value = min(max(value, self.min), self.max) | |
|
49 | return value | |
|
50 | ||
|
51 | def _min_validate(self, min, trait): | |
|
52 | """Enforce min <= value <= max""" | |
|
53 | if min > self.max: | |
|
54 | raise TraitError("Setting min > max") | |
|
55 | if min > self.value: | |
|
56 | self.value = min | |
|
57 | return min | |
|
58 | ||
|
59 | def _max_validate(self, max, trait): | |
|
60 | """Enforce min <= value <= max""" | |
|
61 | if max < self.min: | |
|
62 | raise TraitError("setting max < min") | |
|
63 | if max < self.value: | |
|
64 | self.value = max | |
|
65 | return max | |
|
67 | 66 | |
|
68 | 67 | |
|
69 | 68 | @register('IPython.FloatText') |
@@ -15,7 +15,8 b' Represents an unbounded int using a widget.' | |||
|
15 | 15 | #----------------------------------------------------------------------------- |
|
16 | 16 | from .widget import DOMWidget, register |
|
17 | 17 | from .trait_types import Color |
|
18 |
from IPython.utils.traitlets import Unicode, CInt, Bool, CaselessStrEnum, |
|
|
18 | from IPython.utils.traitlets import (Unicode, CInt, Bool, CaselessStrEnum, | |
|
19 | Tuple, TraitError) | |
|
19 | 20 | from IPython.utils.warn import DeprecatedClass |
|
20 | 21 | |
|
21 | 22 | #----------------------------------------------------------------------------- |
@@ -32,41 +33,39 b' class _Int(DOMWidget):' | |||
|
32 | 33 | kwargs['value'] = value |
|
33 | 34 | super(_Int, self).__init__(**kwargs) |
|
34 | 35 | |
|
36 | ||
|
35 | 37 | class _BoundedInt(_Int): |
|
36 | 38 | """Base class used to create widgets that represent a int that is bounded |
|
37 | 39 | by a minium and maximum.""" |
|
38 |
step = CInt(1, help="Minimum step t |
|
|
40 | step = CInt(1, help="Minimum step to increment the value (ignored by some views)", sync=True) | |
|
39 | 41 | max = CInt(100, help="Max value", sync=True) |
|
40 | 42 | min = CInt(0, help="Min value", sync=True) |
|
41 | 43 | |
|
42 | 44 | def __init__(self, *pargs, **kwargs): |
|
43 | 45 | """Constructor""" |
|
44 | 46 | super(_BoundedInt, self).__init__(*pargs, **kwargs) |
|
45 | self._handle_value_changed('value', None, self.value) | |
|
46 | self._handle_max_changed('max', None, self.max) | |
|
47 | self._handle_min_changed('min', None, self.min) | |
|
48 | self.on_trait_change(self._handle_value_changed, 'value') | |
|
49 | self.on_trait_change(self._handle_max_changed, 'max') | |
|
50 | self.on_trait_change(self._handle_min_changed, 'min') | |
|
51 | ||
|
52 | def _handle_value_changed(self, name, old, new): | |
|
53 | """Validate value.""" | |
|
54 | if self.min > new or new > self.max: | |
|
55 | self.value = min(max(new, self.min), self.max) | |
|
56 | ||
|
57 | def _handle_max_changed(self, name, old, new): | |
|
58 | """Make sure the min is always <= the max.""" | |
|
59 | if new < self.min: | |
|
60 | raise ValueError("setting max < min") | |
|
61 | if new < self.value: | |
|
62 | self.value = new | |
|
63 | 47 | |
|
64 |
def _ |
|
|
65 | """Make sure the max is always >= the min.""" | |
|
66 |
if n |
|
|
67 | raise ValueError("setting min > max") | |
|
68 | if new > self.value: | |
|
69 | self.value = new | |
|
48 | def _value_validate(self, value, trait): | |
|
49 | """Cap and floor value""" | |
|
50 | if self.min > value or self.max < value: | |
|
51 | value = min(max(value, self.min), self.max) | |
|
52 | return value | |
|
53 | ||
|
54 | def _min_validate(self, min, trait): | |
|
55 | """Enforce min <= value <= max""" | |
|
56 | if min > self.max: | |
|
57 | raise TraitError("Setting min > max") | |
|
58 | if min > self.value: | |
|
59 | self.value = min | |
|
60 | return min | |
|
61 | ||
|
62 | def _max_validate(self, max, trait): | |
|
63 | """Enforce min <= value <= max""" | |
|
64 | if max < self.min: | |
|
65 | raise TraitError("setting max < min") | |
|
66 | if max < self.value: | |
|
67 | self.value = max | |
|
68 | return max | |
|
70 | 69 | |
|
71 | 70 | @register('IPython.IntText') |
|
72 | 71 | class IntText(_Int): |
@@ -319,7 +319,6 b' class TraitType(object):' | |||
|
319 | 319 | accept superclasses for :class:`This` values. |
|
320 | 320 | """ |
|
321 | 321 | |
|
322 | ||
|
323 | 322 | metadata = {} |
|
324 | 323 | default_value = Undefined |
|
325 | 324 | allow_none = False |
@@ -447,7 +446,7 b' class TraitType(object):' | |||
|
447 | 446 | try: |
|
448 | 447 | old_value = obj._trait_values[self.name] |
|
449 | 448 | except KeyError: |
|
450 |
old_value = |
|
|
449 | old_value = Undefined | |
|
451 | 450 | |
|
452 | 451 | obj._trait_values[self.name] = new_value |
|
453 | 452 | try: |
@@ -465,13 +464,14 b' class TraitType(object):' | |||
|
465 | 464 | return value |
|
466 | 465 | if hasattr(self, 'validate'): |
|
467 | 466 | value = self.validate(obj, value) |
|
468 | try: | |
|
469 |
|
|
|
470 | except (AttributeError, RuntimeError): | |
|
471 | # Qt mixins raise RuntimeError on missing attrs accessed before __init__ | |
|
472 | pass | |
|
473 | else: | |
|
474 | value = obj_validate(value, self) | |
|
467 | if obj._cross_validation_lock is False: | |
|
468 | value = self._cross_validate(obj, value) | |
|
469 | return value | |
|
470 | ||
|
471 | def _cross_validate(self, obj, value): | |
|
472 | if hasattr(obj, '_%s_validate' % self.name): | |
|
473 | cross_validate = getattr(obj, '_%s_validate' % self.name) | |
|
474 | value = cross_validate(value, self) | |
|
475 | 475 | return value |
|
476 | 476 | |
|
477 | 477 | def __or__(self, other): |
@@ -542,6 +542,7 b' class MetaHasTraits(type):' | |||
|
542 | 542 | v.this_class = cls |
|
543 | 543 | super(MetaHasTraits, cls).__init__(name, bases, classdict) |
|
544 | 544 | |
|
545 | ||
|
545 | 546 | class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)): |
|
546 | 547 | |
|
547 | 548 | def __new__(cls, *args, **kw): |
@@ -555,6 +556,7 b' class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)):' | |||
|
555 | 556 | inst._trait_values = {} |
|
556 | 557 | inst._trait_notifiers = {} |
|
557 | 558 | inst._trait_dyn_inits = {} |
|
559 | inst._cross_validation_lock = True | |
|
558 | 560 | # Here we tell all the TraitType instances to set their default |
|
559 | 561 | # values on the instance. |
|
560 | 562 | for key in dir(cls): |
@@ -570,33 +572,60 b' class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)):' | |||
|
570 | 572 | value.instance_init() |
|
571 | 573 | if key not in kw: |
|
572 | 574 | value.set_default_value(inst) |
|
573 | ||
|
575 | inst._cross_validation_lock = False | |
|
574 | 576 | return inst |
|
575 | 577 | |
|
576 | 578 | def __init__(self, *args, **kw): |
|
577 | 579 | # Allow trait values to be set using keyword arguments. |
|
578 | 580 | # We need to use setattr for this to trigger validation and |
|
579 | 581 | # notifications. |
|
580 | ||
|
581 | 582 | with self.hold_trait_notifications(): |
|
582 | 583 | for key, value in iteritems(kw): |
|
583 | 584 | setattr(self, key, value) |
|
584 | 585 | |
|
585 | 586 | @contextlib.contextmanager |
|
586 | 587 | def hold_trait_notifications(self): |
|
587 | """Context manager for bundling trait change notifications | |
|
588 | """Context manager for bundling trait change notifications and cross | |
|
589 | validation. | |
|
588 | 590 | |
|
589 | Use this when doing multiple trait assignments (init, config), | |
|
590 |
|
|
|
591 | Use this when doing multiple trait assignments (init, config), to avoid | |
|
592 | race conditions in trait notifiers requesting other trait values. | |
|
591 | 593 | All trait notifications will fire after all values have been assigned. |
|
592 | 594 | """ |
|
595 | if self._cross_validation_lock is True: | |
|
596 | yield | |
|
597 | else: | |
|
598 | self._cross_validation_lock = True | |
|
599 | cache = {} | |
|
600 | notifications = {} | |
|
593 | 601 | _notify_trait = self._notify_trait |
|
594 | notifications = [] | |
|
595 | self._notify_trait = lambda *a: notifications.append(a) | |
|
602 | ||
|
603 | def cache_values(*a): | |
|
604 | cache[a[0]] = a | |
|
605 | ||
|
606 | def hold_notifications(*a): | |
|
607 | notifications[a[0]] = a | |
|
608 | ||
|
609 | self._notify_trait = cache_values | |
|
596 | 610 | |
|
597 | 611 | try: |
|
598 | 612 | yield |
|
599 | 613 | finally: |
|
614 | try: | |
|
615 | self._notify_trait = hold_notifications | |
|
616 | for name in cache: | |
|
617 | if hasattr(self, '_%s_validate' % name): | |
|
618 | cross_validate = getattr(self, '_%s_validate' % name) | |
|
619 | setattr(self, name, cross_validate(getattr(self, name), self)) | |
|
620 | except TraitError as e: | |
|
621 | self._notify_trait = lambda *x: None | |
|
622 | for name in cache: | |
|
623 | if cache[name][1] is not Undefined: | |
|
624 | setattr(self, name, cache[name][1]) | |
|
625 | notifications = {} | |
|
626 | raise e | |
|
627 | finally: | |
|
628 | self._cross_validation_lock = False | |
|
600 | 629 | self._notify_trait = _notify_trait |
|
601 | 630 | if isinstance(_notify_trait, types.MethodType): |
|
602 | 631 | # FIXME: remove when support is bumped to 3.4. |
@@ -604,9 +633,10 b' class HasTraits(py3compat.with_metaclass(MetaHasTraits, object)):' | |||
|
604 | 633 | # remove the redundant value from __dict__ |
|
605 | 634 | # (only used to preserve pickleability on Python < 3.4) |
|
606 | 635 | self.__dict__.pop('_notify_trait', None) |
|
636 | ||
|
607 | 637 | # trigger delayed notifications |
|
608 |
for |
|
|
609 |
self._notify_trait(* |
|
|
638 | for name in notifications: | |
|
639 | self._notify_trait(*(notifications[name])) | |
|
610 | 640 | |
|
611 | 641 | def _notify_trait(self, name, old_value, new_value): |
|
612 | 642 |
General Comments 0
You need to be logged in to leave comments.
Login now