Show More
@@ -213,7 +213,7 b' define(["widgets/js/manager",' | |||||
213 | var that = this; |
|
213 | var that = this; | |
214 | var packed; |
|
214 | var packed; | |
215 | if (value instanceof Backbone.Model) { |
|
215 | if (value instanceof Backbone.Model) { | |
216 | return value.id; |
|
216 | return "IPY_MODEL_" + value.id; | |
217 |
|
217 | |||
218 | } else if ($.isArray(value)) { |
|
218 | } else if ($.isArray(value)) { | |
219 | packed = []; |
|
219 | packed = []; | |
@@ -252,13 +252,15 b' define(["widgets/js/manager",' | |||||
252 | }); |
|
252 | }); | |
253 | return unpacked; |
|
253 | return unpacked; | |
254 |
|
254 | |||
|
255 | } else if (typeof value === 'string' && value.slice(0,10) === "IPY_MODEL_") { | |||
|
256 | var model = this.widget_manager.get_model(value.slice(10, value.length)); | |||
|
257 | if (model) { | |||
|
258 | return model; | |||
|
259 | } else { | |||
|
260 | return value; | |||
|
261 | } | |||
255 | } else { |
|
262 | } else { | |
256 | var model = this.widget_manager.get_model(value); |
|
|||
257 | if (model) { |
|
|||
258 | return model; |
|
|||
259 | } else { |
|
|||
260 | return value; |
|
263 | return value; | |
261 | } |
|
|||
262 | } |
|
264 | } | |
263 | }, |
|
265 | }, | |
264 |
|
266 |
@@ -195,8 +195,15 b' class Widget(LoggingConfigurable):' | |||||
195 | A single property's name to get. |
|
195 | A single property's name to get. | |
196 | """ |
|
196 | """ | |
197 | keys = self.keys if key is None else [key] |
|
197 | keys = self.keys if key is None else [key] | |
198 | return {k: self._pack_widgets(getattr(self, k)) for k in keys} |
|
198 | state = {} | |
199 |
|
199 | for k in keys: | ||
|
200 | f = self.trait_metadata(k, 'to_json') | |||
|
201 | if f is None: | |||
|
202 | f = self._trait_to_json | |||
|
203 | value = getattr(self, k) | |||
|
204 | state[k] = f(value) | |||
|
205 | return state | |||
|
206 | ||||
200 | def send(self, content): |
|
207 | def send(self, content): | |
201 | """Sends a custom msg to the widget model in the front-end. |
|
208 | """Sends a custom msg to the widget model in the front-end. | |
202 |
|
209 | |||
@@ -280,7 +287,10 b' class Widget(LoggingConfigurable):' | |||||
280 | """Called when a state is received from the front-end.""" |
|
287 | """Called when a state is received from the front-end.""" | |
281 | for name in self.keys: |
|
288 | for name in self.keys: | |
282 | if name in sync_data: |
|
289 | if name in sync_data: | |
283 |
|
|
290 | f = self.trait_metadata(name, 'from_json') | |
|
291 | if f is None: | |||
|
292 | f = self._trait_from_json | |||
|
293 | value = f(sync_data[name]) | |||
284 | with self._lock_property(name, value): |
|
294 | with self._lock_property(name, value): | |
285 | setattr(self, name, value) |
|
295 | setattr(self, name, value) | |
286 |
|
296 | |||
@@ -299,31 +309,34 b' class Widget(LoggingConfigurable):' | |||||
299 | """Called when a view has been displayed for this widget instance""" |
|
309 | """Called when a view has been displayed for this widget instance""" | |
300 | self._display_callbacks(self, **kwargs) |
|
310 | self._display_callbacks(self, **kwargs) | |
301 |
|
311 | |||
302 |
def _ |
|
312 | def _trait_to_json(self, x): | |
303 | """Recursively converts all widget instances to model id strings. |
|
313 | """Convert a trait value to json | |
304 |
|
314 | |||
305 | Children widgets will be stored and transmitted to the front-end by |
|
315 | Traverse lists/tuples and dicts and serialize their values as well. | |
306 | their model ids. Return value must be JSON-able.""" |
|
316 | Replace any widgets with their model_id | |
|
317 | """ | |||
307 | if isinstance(x, dict): |
|
318 | if isinstance(x, dict): | |
308 |
return {k: self._ |
|
319 | return {k: self._trait_to_json(v) for k, v in x.items()} | |
309 | elif isinstance(x, (list, tuple)): |
|
320 | elif isinstance(x, (list, tuple)): | |
310 |
return [self._ |
|
321 | return [self._trait_to_json(v) for v in x] | |
311 | elif isinstance(x, Widget): |
|
322 | elif isinstance(x, Widget): | |
312 | return x.model_id |
|
323 | return "IPY_MODEL_" + x.model_id | |
313 | else: |
|
324 | else: | |
314 | return x # Value must be JSON-able |
|
325 | return x # Value must be JSON-able | |
315 |
|
326 | |||
316 | def _unpack_widgets(self, x): |
|
327 | def _trait_from_json(self, x): | |
317 | """Recursively converts all model id strings to widget instances. |
|
328 | """Convert json values to objects | |
318 |
|
329 | |||
319 | Children widgets will be stored and transmitted to the front-end by |
|
330 | Replace any strings representing valid model id values to Widget references. | |
320 | their model ids.""" |
|
331 | """ | |
321 | if isinstance(x, dict): |
|
332 | if isinstance(x, dict): | |
322 |
return {k: self._ |
|
333 | return {k: self._trait_from_json(v) for k, v in x.items()} | |
323 | elif isinstance(x, (list, tuple)): |
|
334 | elif isinstance(x, (list, tuple)): | |
324 |
return [self._ |
|
335 | return [self._trait_from_json(v) for v in x] | |
325 | elif isinstance(x, string_types): |
|
336 | elif isinstance(x, string_types) and x.startswith('IPY_MODEL_') and x[10:] in Widget.widgets: | |
326 | return x if x not in Widget.widgets else Widget.widgets[x] |
|
337 | # we want to support having child widgets at any level in a hierarchy | |
|
338 | # trusting that a widget UUID will not appear out in the wild | |||
|
339 | return Widget.widgets[x] | |||
327 | else: |
|
340 | else: | |
328 | return x |
|
341 | return x | |
329 |
|
342 |
@@ -560,6 +560,27 b' class TestInstance(TestCase):' | |||||
560 | self.assertRaises(TraitError, setattr, a, 'inst', Bar) |
|
560 | self.assertRaises(TraitError, setattr, a, 'inst', Bar) | |
561 | self.assertRaises(TraitError, setattr, a, 'inst', Bah()) |
|
561 | self.assertRaises(TraitError, setattr, a, 'inst', Bah()) | |
562 |
|
562 | |||
|
563 | def test_default_klass(self): | |||
|
564 | class Foo(object): pass | |||
|
565 | class Bar(Foo): pass | |||
|
566 | class Bah(object): pass | |||
|
567 | ||||
|
568 | class FooInstance(Instance): | |||
|
569 | klass = Foo | |||
|
570 | ||||
|
571 | class A(HasTraits): | |||
|
572 | inst = FooInstance() | |||
|
573 | ||||
|
574 | a = A() | |||
|
575 | self.assertTrue(a.inst is None) | |||
|
576 | a.inst = Foo() | |||
|
577 | self.assertTrue(isinstance(a.inst, Foo)) | |||
|
578 | a.inst = Bar() | |||
|
579 | self.assertTrue(isinstance(a.inst, Foo)) | |||
|
580 | self.assertRaises(TraitError, setattr, a, 'inst', Foo) | |||
|
581 | self.assertRaises(TraitError, setattr, a, 'inst', Bar) | |||
|
582 | self.assertRaises(TraitError, setattr, a, 'inst', Bah()) | |||
|
583 | ||||
563 | def test_unique_default_value(self): |
|
584 | def test_unique_default_value(self): | |
564 | class Foo(object): pass |
|
585 | class Foo(object): pass | |
565 | class A(HasTraits): |
|
586 | class A(HasTraits): |
@@ -809,8 +809,12 b' class Instance(ClassBasedTraitType):' | |||||
809 | """A trait whose value must be an instance of a specified class. |
|
809 | """A trait whose value must be an instance of a specified class. | |
810 |
|
810 | |||
811 | The value can also be an instance of a subclass of the specified class. |
|
811 | The value can also be an instance of a subclass of the specified class. | |
|
812 | ||||
|
813 | Subclasses can declare default classes by overriding the klass attribute | |||
812 | """ |
|
814 | """ | |
813 |
|
815 | |||
|
816 | klass = None | |||
|
817 | ||||
814 | def __init__(self, klass=None, args=None, kw=None, |
|
818 | def __init__(self, klass=None, args=None, kw=None, | |
815 | allow_none=True, **metadata ): |
|
819 | allow_none=True, **metadata ): | |
816 | """Construct an Instance trait. |
|
820 | """Construct an Instance trait. | |
@@ -836,14 +840,17 b' class Instance(ClassBasedTraitType):' | |||||
836 | ----- |
|
840 | ----- | |
837 | If both ``args`` and ``kw`` are None, then the default value is None. |
|
841 | If both ``args`` and ``kw`` are None, then the default value is None. | |
838 | If ``args`` is a tuple and ``kw`` is a dict, then the default is |
|
842 | If ``args`` is a tuple and ``kw`` is a dict, then the default is | |
839 |
created as ``klass(*args, **kw)``. If e |
|
843 | created as ``klass(*args, **kw)``. If exactly one of ``args`` or ``kw`` is | |
840 |
|
|
844 | None, the None is replaced by ``()`` or ``{}``, respectively. | |
841 | """ |
|
845 | """ | |
842 |
|
846 | if klass is None: | ||
843 | if (klass is None) or (not (inspect.isclass(klass) or isinstance(klass, py3compat.string_types))): |
|
847 | klass = self.klass | |
844 | raise TraitError('The klass argument must be a class' |
|
848 | ||
845 | ' you gave: %r' % klass) |
|
849 | if (klass is not None) and (inspect.isclass(klass) or isinstance(klass, py3compat.string_types)): | |
846 | self.klass = klass |
|
850 | self.klass = klass | |
|
851 | else: | |||
|
852 | raise TraitError('The klass attribute must be a class' | |||
|
853 | ' not: %r' % klass) | |||
847 |
|
854 | |||
848 | # self.klass is a class, so handle default_value |
|
855 | # self.klass is a class, so handle default_value | |
849 | if args is None and kw is None: |
|
856 | if args is None and kw is None: |
General Comments 0
You need to be logged in to leave comments.
Login now