##// END OF EJS Templates
Allow parent to be set after construction......
Jonathan Frederic -
Show More
@@ -21,7 +21,7 import os
21 21 import IPython
22 22 from IPython.kernel.comm import Comm
23 23 from IPython.config import LoggingConfigurable
24 from IPython.utils.traitlets import Unicode, Dict, List
24 from IPython.utils.traitlets import Unicode, Dict, List, Instance
25 25 from IPython.display import Javascript, display
26 26 from IPython.utils.py3compat import string_types
27 27
@@ -49,17 +49,30 class Widget(LoggingConfigurable):
49 49 registered in the frontend to create and sync this widget with.""")
50 50 default_view_name = Unicode(help="""Default view registered in the frontend
51 51 to use to represent the widget.""")
52
52 parent = Instance('IPython.html.widgets.widget.Widget')
53
54 def _parent_changed(self, name, old, new):
55 if self._displayed:
56 raise Exception('Parent cannot be set because widget has been displayed.')
57 elif new == self:
58 raise Exception('Parent cannot be set to self.')
59 else:
60
61 # Parent/child association
62 if new is not None and not self in new._children:
63 new._children.append(self)
64 if old is not None and self in old._children:
65 old._children.remove(self)
53 66
54 67 # Private/protected declarations
55 68 _keys = []
56 69 _property_lock = False
57 _parent = None
58 _children = []
59 70 _css = Dict()
71 _displayed = False
72 _comm = None
60 73
61 74
62 def __init__(self, parent=None, **kwargs):
75 def __init__(self, **kwargs):
63 76 """Public constructor
64 77
65 78 Parameters
@@ -72,15 +85,9 class Widget(LoggingConfigurable):
72 85 widget's default view as it's child. The default view can be set
73 86 via the default_view_name property.
74 87 """
75 super(Widget, self).__init__(**kwargs)
76
77 # Parent/child association
78 88 self._children = []
79 if parent is not None:
80 parent._children.append(self)
81 self._parent = parent
82 self.comm = None
83
89 super(Widget, self).__init__(**kwargs)
90
84 91 # Register after init to allow default values to be specified
85 92 self.on_trait_change(self._handle_property_changed, self.keys)
86 93
@@ -94,21 +101,11 class Widget(LoggingConfigurable):
94 101 """Close method. Closes the widget which closes the underlying comm.
95 102 When the comm is closed, all of the widget views are automatically
96 103 removed from the frontend."""
97 self.comm.close()
98 del self.comm
99
100
101 # Properties
102 def _get_parent(self):
103 return self._parent
104 parent = property(_get_parent)
105
106
107 def _get_children(self):
108 return copy(self._children)
109 children = property(_get_children)
104 self._comm.close()
105 del self._comm
110 106
111 107
108 # Properties
112 109 def _get_keys(self):
113 110 keys = ['_css']
114 111 keys.extend(self._keys)
@@ -140,7 +137,7 class Widget(LoggingConfigurable):
140 137
141 138 def _handle_property_changed(self, name, old, new):
142 139 """Called when a proeprty has been changed."""
143 if not self._property_lock and self.comm is not None:
140 if not self._property_lock and self._comm is not None:
144 141 # TODO: Validate properties.
145 142 # Send new state to frontend
146 143 self.send_state(key=name)
@@ -148,7 +145,7 class Widget(LoggingConfigurable):
148 145
149 146 def _handle_close(self):
150 147 """Called when the comm is closed by the frontend."""
151 self.comm = None
148 self._comm = None
152 149
153 150
154 151 # Public methods
@@ -160,7 +157,7 class Widget(LoggingConfigurable):
160 157 key : unicode (optional)
161 158 A single property's name to sync with the frontend.
162 159 """
163 if self.comm is not None:
160 if self._comm is not None:
164 161 state = {}
165 162
166 163 # If a key is provided, just send the state of that key.
@@ -174,7 +171,7 class Widget(LoggingConfigurable):
174 171 state[key] = getattr(self, key)
175 172 except Exception as e:
176 173 pass # Eat errors, nom nom nom
177 self.comm.send({"method": "update",
174 self._comm.send({"method": "update",
178 175 "state": state})
179 176
180 177
@@ -226,27 +223,30 class Widget(LoggingConfigurable):
226 223 ----------
227 224 view_name: unicode (optional)
228 225 View to display in the frontend. Overrides default_view_name."""
226
229 227 if not view_name:
230 228 view_name = self.default_view_name
231 229
232 230 # Create a comm.
233 if self.comm is None:
234 self.comm = Comm(target_name=self.target_name)
235 self.comm.on_msg(self._handle_msg)
236 self.comm.on_close(self._handle_close)
231 if self._comm is None:
232 self._comm = Comm(target_name=self.target_name)
233 self._comm.on_msg(self._handle_msg)
234 self._comm.on_close(self._handle_close)
237 235
238 236 # Make sure model is syncronized
239 237 self.send_state()
240 238
241 239 # Show view.
242 if self.parent is None:
243 self.comm.send({"method": "display", "view_name": view_name})
240 if self.parent is None or self.parent._comm is None:
241 self._comm.send({"method": "display", "view_name": view_name})
244 242 else:
245 self.comm.send({"method": "display",
243 self._comm.send({"method": "display",
246 244 "view_name": view_name,
247 "parent": self.parent.comm.comm_id})
248
245 "parent": self.parent._comm.comm_id})
246 self._displayed = True
247
249 248 # Now display children if any.
250 for child in self.children:
251 child._repr_widget_()
249 for child in self._children:
250 if child != self:
251 child._repr_widget_()
252 252 return None
General Comments 0
You need to be logged in to leave comments. Login now