##// END OF EJS Templates
s/target_name/widget_class
Jonathan Frederic -
Show More
@@ -1,262 +1,262
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 "underscore",
6 6 "backbone",
7 7 "jquery",
8 8 "base/js/namespace"
9 9 ], function (_, Backbone, $, IPython) {
10 10 "use strict";
11 11 //--------------------------------------------------------------------
12 12 // WidgetManager class
13 13 //--------------------------------------------------------------------
14 14 var WidgetManager = function (comm_manager, notebook) {
15 15 // Public constructor
16 16 WidgetManager._managers.push(this);
17 17
18 18 // Attach a comm manager to the
19 19 this.keyboard_manager = notebook.keyboard_manager;
20 20 this.notebook = notebook;
21 21 this.comm_manager = comm_manager;
22 22 this._models = {}; /* Dictionary of model ids and model instances */
23 23
24 24 // Register with the comm manager.
25 25 this.comm_manager.register_target('ipython.widget', $.proxy(this._handle_comm_open, this));
26 26 };
27 27
28 28 //--------------------------------------------------------------------
29 29 // Class level
30 30 //--------------------------------------------------------------------
31 31 WidgetManager._model_types = {}; /* Dictionary of model type names (target_name) and model types. */
32 32 WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
33 33 WidgetManager._managers = []; /* List of widget managers */
34 34
35 35 WidgetManager.register_widget_model = function (model_name, model_type) {
36 36 // Registers a widget model by name.
37 37 WidgetManager._model_types[model_name] = model_type;
38 38 };
39 39
40 40 WidgetManager.register_widget_view = function (view_name, view_type) {
41 41 // Registers a widget view by name.
42 42 WidgetManager._view_types[view_name] = view_type;
43 43 };
44 44
45 45 //--------------------------------------------------------------------
46 46 // Instance level
47 47 //--------------------------------------------------------------------
48 48 WidgetManager.prototype.display_view = function(msg, model) {
49 49 // Displays a view for a particular model.
50 50 var cell = this.get_msg_cell(msg.parent_header.msg_id);
51 51 if (cell === null) {
52 52 console.log("Could not determine where the display" +
53 53 " message was from. Widget will not be displayed");
54 54 } else {
55 55 var that = this;
56 56 this.create_view(model, {cell: cell, callback: function(view) {
57 57 that._handle_display_view(view);
58 58 if (cell.widget_subarea) {
59 59 cell.widget_subarea.append(view.$el);
60 60 }
61 61 view.trigger('displayed');
62 62 }});
63 63 }
64 64 };
65 65
66 66 WidgetManager.prototype._handle_display_view = function (view) {
67 67 // Have the IPython keyboard manager disable its event
68 68 // handling so the widget can capture keyboard input.
69 69 // Note, this is only done on the outer most widgets.
70 70 if (this.keyboard_manager) {
71 71 this.keyboard_manager.register_events(view.$el);
72 72
73 73 if (view.additional_elements) {
74 74 for (var i = 0; i < view.additional_elements.length; i++) {
75 75 this.keyboard_manager.register_events(view.additional_elements[i]);
76 76 }
77 77 }
78 78 }
79 79 };
80 80
81 81
82 82 WidgetManager.prototype.create_view = function(model, options) {
83 83 // Creates a view for a particular model.
84 84
85 85 var view_name = model.get('_view_name');
86 86 var view_mod = model.get('_view_module');
87 87 var errback = options.errback || function(err) {console.log(err);};
88 88
89 89 var instantiate_view = function(ViewType) {
90 90 if (ViewType) {
91 91 // If a view is passed into the method, use that view's cell as
92 92 // the cell for the view that is created.
93 93 options = options || {};
94 94 if (options.parent !== undefined) {
95 95 options.cell = options.parent.options.cell;
96 96 }
97 97
98 98 // Create and render the view...
99 99 var parameters = {model: model, options: options};
100 100 var view = new ViewType(parameters);
101 101 view.render();
102 102 model.on('destroy', view.remove, view);
103 103 options.callback(view);
104 104 } else {
105 105 errback({unknown_view: true, view_name: view_name,
106 106 view_module: view_mod});
107 107 }
108 108 };
109 109
110 110 if (view_mod) {
111 111 require([view_mod], function(module) {
112 112 instantiate_view(module[view_name]);
113 113 }, errback);
114 114 } else {
115 115 instantiate_view(WidgetManager._view_types[view_name]);
116 116 }
117 117 };
118 118
119 119 WidgetManager.prototype.get_msg_cell = function (msg_id) {
120 120 var cell = null;
121 121 // First, check to see if the msg was triggered by cell execution.
122 122 if (this.notebook) {
123 123 cell = this.notebook.get_msg_cell(msg_id);
124 124 }
125 125 if (cell !== null) {
126 126 return cell;
127 127 }
128 128 // Second, check to see if a get_cell callback was defined
129 129 // for the message. get_cell callbacks are registered for
130 130 // widget messages, so this block is actually checking to see if the
131 131 // message was triggered by a widget.
132 132 var kernel = this.comm_manager.kernel;
133 133 if (kernel) {
134 134 var callbacks = kernel.get_callbacks_for_msg(msg_id);
135 135 if (callbacks && callbacks.iopub &&
136 136 callbacks.iopub.get_cell !== undefined) {
137 137 return callbacks.iopub.get_cell();
138 138 }
139 139 }
140 140
141 141 // Not triggered by a cell or widget (no get_cell callback
142 142 // exists).
143 143 return null;
144 144 };
145 145
146 146 WidgetManager.prototype.callbacks = function (view) {
147 147 // callback handlers specific a view
148 148 var callbacks = {};
149 149 if (view && view.options.cell) {
150 150
151 151 // Try to get output handlers
152 152 var cell = view.options.cell;
153 153 var handle_output = null;
154 154 var handle_clear_output = null;
155 155 if (cell.output_area) {
156 156 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
157 157 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
158 158 }
159 159
160 160 // Create callback dict using what is known
161 161 var that = this;
162 162 callbacks = {
163 163 iopub : {
164 164 output : handle_output,
165 165 clear_output : handle_clear_output,
166 166
167 167 // Special function only registered by widget messages.
168 168 // Allows us to get the cell for a message so we know
169 169 // where to add widgets if the code requires it.
170 170 get_cell : function () {
171 171 return cell;
172 172 },
173 173 },
174 174 };
175 175 }
176 176 return callbacks;
177 177 };
178 178
179 179 WidgetManager.prototype.get_model = function (model_id) {
180 180 // Look-up a model instance by its id.
181 181 var model = this._models[model_id];
182 182 if (model !== undefined && model.id == model_id) {
183 183 return model;
184 184 }
185 185 return null;
186 186 };
187 187
188 188 WidgetManager.prototype._handle_comm_open = function (comm, msg) {
189 189 // Handle when a comm is opened.
190 190 return this.create_model({model_name: msg.content.data.model_name, comm: comm});
191 191 };
192 192
193 193 WidgetManager.prototype.create_model = function (options) {
194 194 // Create and return a new widget model.
195 195 //
196 // Minimally, one must provide the model_name and target_name
196 // Minimally, one must provide the model_name and widget_class
197 197 // parameters to create a model from Javascript.
198 198 //
199 199 // Example
200 200 // --------
201 201 // JS:
202 202 // window.slider = IPython.notebook.kernel.widget_manager.create_model({
203 203 // model_name: 'WidgetModel',
204 // target_name: 'IPython.html.widgets.widget_int.IntSlider',
204 // widget_class: 'IPython.html.widgets.widget_int.IntSlider',
205 205 // init_state_callback: function(model) { console.log('Create success!', model); }});
206 206 //
207 207 // Parameters
208 208 // ----------
209 209 // options: dictionary
210 210 // Dictionary of options with the following contents:
211 211 // model_name: string
212 212 // Target name of the widget model to create.
213 // target_name: (optional) string
213 // widget_class: (optional) string
214 214 // Target name of the widget in the back-end.
215 215 // comm: (optional) Comm
216 216 // init_state_callback: (optional) callback
217 217 // Called when the first state push from the back-end is
218 218 // recieved. Allows you to modify the model after it's
219 219 // complete state is filled and synced.
220 220
221 221 // Create a comm if it wasn't provided.
222 222 var comm = options.comm;
223 223 if (!comm) {
224 comm = this.comm_manager.new_comm('ipython.widget', {'target_name': options.target_name});
224 comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class});
225 225 }
226 226
227 227 // Create and return a new model that is connected to the comm.
228 228 var that = this;
229 229
230 230 var instantiate_model = function(ModelType) {
231 231 var model_id = comm.comm_id;
232 232 var widget_model = new ModelType(that, model_id, comm, options.init_state_callback);
233 233 widget_model.on('comm:close', function () {
234 234 delete that._models[model_id];
235 235 });
236 236 that._models[model_id] = widget_model;
237 237 };
238 238
239 239 var widget_type_name = msg.content.data.model_name;
240 240 var widget_module = msg.content.data.model_module;
241 241
242 242 if (widget_module) {
243 243 // Load the module containing the widget model
244 244 require([widget_module], function(mod) {
245 245 if (mod[widget_type_name]) {
246 246 instantiate_model(mod[widget_type_name]);
247 247 } else {
248 248 console.log("Error creating widget model: " + widget_type_name
249 249 + " not found in " + widget_module);
250 250 }
251 251 }, function(err) { console.log(err); });
252 252 } else {
253 253 // No module specified, load from the global models registry
254 254 instantiate_model(WidgetManager._model_types[widget_type_name]);
255 255 }
256 256 };
257 257
258 258 // Backwards compatability.
259 259 IPython.WidgetManager = WidgetManager;
260 260
261 261 return {'WidgetManager': WidgetManager};
262 262 });
@@ -1,467 +1,466
1 1 """Base Widget class. Allows user to create widgets in the back-end that render
2 2 in the IPython notebook front-end.
3 3 """
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (c) 2013, the IPython Development Team.
6 6 #
7 7 # Distributed under the terms of the Modified BSD License.
8 8 #
9 9 # The full license is in the file COPYING.txt, distributed with this software.
10 10 #-----------------------------------------------------------------------------
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15 from contextlib import contextmanager
16 16 import collections
17 17
18 18 from IPython.core.getipython import get_ipython
19 19 from IPython.kernel.comm import Comm
20 20 from IPython.config import LoggingConfigurable
21 21 from IPython.utils.importstring import import_item
22 22 from IPython.utils.traitlets import Unicode, Dict, Instance, Bool, List, \
23 23 CaselessStrEnum, Tuple, CUnicode, Int, Set
24 24 from IPython.utils.py3compat import string_types
25 25
26 26 #-----------------------------------------------------------------------------
27 27 # Classes
28 28 #-----------------------------------------------------------------------------
29 29 class CallbackDispatcher(LoggingConfigurable):
30 30 """A structure for registering and running callbacks"""
31 31 callbacks = List()
32 32
33 33 def __call__(self, *args, **kwargs):
34 34 """Call all of the registered callbacks."""
35 35 value = None
36 36 for callback in self.callbacks:
37 37 try:
38 38 local_value = callback(*args, **kwargs)
39 39 except Exception as e:
40 40 ip = get_ipython()
41 41 if ip is None:
42 42 self.log.warn("Exception in callback %s: %s", callback, e, exc_info=True)
43 43 else:
44 44 ip.showtraceback()
45 45 else:
46 46 value = local_value if local_value is not None else value
47 47 return value
48 48
49 49 def register_callback(self, callback, remove=False):
50 50 """(Un)Register a callback
51 51
52 52 Parameters
53 53 ----------
54 54 callback: method handle
55 55 Method to be registered or unregistered.
56 56 remove=False: bool
57 57 Whether to unregister the callback."""
58 58
59 59 # (Un)Register the callback.
60 60 if remove and callback in self.callbacks:
61 61 self.callbacks.remove(callback)
62 62 elif not remove and callback not in self.callbacks:
63 63 self.callbacks.append(callback)
64 64
65 65 def _show_traceback(method):
66 66 """decorator for showing tracebacks in IPython"""
67 67 def m(self, *args, **kwargs):
68 68 try:
69 69 return(method(self, *args, **kwargs))
70 70 except Exception as e:
71 71 ip = get_ipython()
72 72 if ip is None:
73 73 self.log.warn("Exception in widget method %s: %s", method, e, exc_info=True)
74 74 else:
75 75 ip.showtraceback()
76 76 return m
77 77
78 78 class Widget(LoggingConfigurable):
79 79 #-------------------------------------------------------------------------
80 80 # Class attributes
81 81 #-------------------------------------------------------------------------
82 82 _widget_construction_callback = None
83 83 widgets = {}
84 84
85 85 @staticmethod
86 86 def on_widget_constructed(callback):
87 87 """Registers a callback to be called when a widget is constructed.
88 88
89 89 The callback must have the following signature:
90 90 callback(widget)"""
91 91 Widget._widget_construction_callback = callback
92 92
93 93 @staticmethod
94 94 def _call_widget_constructed(widget):
95 95 """Static method, called when a widget is constructed."""
96 96 if Widget._widget_construction_callback is not None and callable(Widget._widget_construction_callback):
97 97 Widget._widget_construction_callback(widget)
98 98
99 99 @staticmethod
100 100 def handle_comm_opened(comm, msg):
101 101 """Static method, called when a widget is constructed."""
102 target_name = msg['content']['data']['target_name']
103 widget_class = import_item(target_name)
102 widget_class = import_item(msg['content']['data']['widget_class'])
104 103 widget = widget_class(comm=comm)
105 104
106 105
107 106 #-------------------------------------------------------------------------
108 107 # Traits
109 108 #-------------------------------------------------------------------------
110 109 _model_module = Unicode(None, allow_none=True, help="""A requirejs module name
111 110 in which to find _model_name. If empty, look in the global registry.""")
112 111 _model_name = Unicode('WidgetModel', help="""Name of the backbone model
113 112 registered in the front-end to create and sync this widget with.""")
114 113 _view_module = Unicode(help="""A requirejs module in which to find _view_name.
115 114 If empty, look in the global registry.""", sync=True)
116 115 _view_name = Unicode(None, allow_none=True, help="""Default view registered in the front-end
117 116 to use to represent the widget.""", sync=True)
118 117 comm = Instance('IPython.kernel.comm.Comm')
119 118
120 119 msg_throttle = Int(3, sync=True, help="""Maximum number of msgs the
121 120 front-end can send before receiving an idle msg from the back-end.""")
122 121
123 122 version = Int(0, sync=True, help="""Widget's version""")
124 123 keys = List()
125 124 def _keys_default(self):
126 125 return [name for name in self.traits(sync=True)]
127 126
128 127 _property_lock = Tuple((None, None))
129 128 _send_state_lock = Int(0)
130 129 _states_to_send = Set(allow_none=False)
131 130 _display_callbacks = Instance(CallbackDispatcher, ())
132 131 _msg_callbacks = Instance(CallbackDispatcher, ())
133 132
134 133 #-------------------------------------------------------------------------
135 134 # (Con/de)structor
136 135 #-------------------------------------------------------------------------
137 136 def __init__(self, open_comm=True, **kwargs):
138 137 """Public constructor"""
139 138 self._model_id = kwargs.pop('model_id', None)
140 139 super(Widget, self).__init__(**kwargs)
141 140
142 141 Widget._call_widget_constructed(self)
143 142 self.open()
144 143
145 144 def __del__(self):
146 145 """Object disposal"""
147 146 self.close()
148 147
149 148 #-------------------------------------------------------------------------
150 149 # Properties
151 150 #-------------------------------------------------------------------------
152 151
153 152 def open(self):
154 153 """Open a comm to the frontend if one isn't already open."""
155 154 if self.comm is None:
156 155 args = dict(target_name='ipython.widget',
157 156 data={'model_name': self._model_name,
158 157 'model_module': self._model_module})
159 158 if self._model_id is not None:
160 159 args['comm_id'] = self._model_id
161 160 self.comm = Comm(**args)
162 161
163 162 def _comm_changed(self, name, new):
164 163 """Called when the comm is changed."""
165 164 self.comm = new
166 165 self._model_id = self.model_id
167 166
168 167 self.comm.on_msg(self._handle_msg)
169 168 Widget.widgets[self.model_id] = self
170 169
171 170 # first update
172 171 self.send_state()
173 172
174 173 @property
175 174 def model_id(self):
176 175 """Gets the model id of this widget.
177 176
178 177 If a Comm doesn't exist yet, a Comm will be created automagically."""
179 178 return self.comm.comm_id
180 179
181 180 #-------------------------------------------------------------------------
182 181 # Methods
183 182 #-------------------------------------------------------------------------
184 183
185 184 def close(self):
186 185 """Close method.
187 186
188 187 Closes the underlying comm.
189 188 When the comm is closed, all of the widget views are automatically
190 189 removed from the front-end."""
191 190 if self.comm is not None:
192 191 Widget.widgets.pop(self.model_id, None)
193 192 self.comm.close()
194 193 self.comm = None
195 194
196 195 def send_state(self, key=None):
197 196 """Sends the widget state, or a piece of it, to the front-end.
198 197
199 198 Parameters
200 199 ----------
201 200 key : unicode, or iterable (optional)
202 201 A single property's name or iterable of property names to sync with the front-end.
203 202 """
204 203 self._send({
205 204 "method" : "update",
206 205 "state" : self.get_state(key=key)
207 206 })
208 207
209 208 def get_state(self, key=None):
210 209 """Gets the widget state, or a piece of it.
211 210
212 211 Parameters
213 212 ----------
214 213 key : unicode or iterable (optional)
215 214 A single property's name or iterable of property names to get.
216 215 """
217 216 if key is None:
218 217 keys = self.keys
219 218 elif isinstance(key, string_types):
220 219 keys = [key]
221 220 elif isinstance(key, collections.Iterable):
222 221 keys = key
223 222 else:
224 223 raise ValueError("key must be a string, an iterable of keys, or None")
225 224 state = {}
226 225 for k in keys:
227 226 f = self.trait_metadata(k, 'to_json', self._trait_to_json)
228 227 value = getattr(self, k)
229 228 state[k] = f(value)
230 229 return state
231 230
232 231 def set_state(self, sync_data):
233 232 """Called when a state is received from the front-end."""
234 233 for name in self.keys:
235 234 if name in sync_data:
236 235 json_value = sync_data[name]
237 236 from_json = self.trait_metadata(name, 'from_json', self._trait_from_json)
238 237 with self._lock_property(name, json_value):
239 238 setattr(self, name, from_json(json_value))
240 239
241 240 def send(self, content):
242 241 """Sends a custom msg to the widget model in the front-end.
243 242
244 243 Parameters
245 244 ----------
246 245 content : dict
247 246 Content of the message to send.
248 247 """
249 248 self._send({"method": "custom", "content": content})
250 249
251 250 def on_msg(self, callback, remove=False):
252 251 """(Un)Register a custom msg receive callback.
253 252
254 253 Parameters
255 254 ----------
256 255 callback: callable
257 256 callback will be passed two arguments when a message arrives::
258 257
259 258 callback(widget, content)
260 259
261 260 remove: bool
262 261 True if the callback should be unregistered."""
263 262 self._msg_callbacks.register_callback(callback, remove=remove)
264 263
265 264 def on_displayed(self, callback, remove=False):
266 265 """(Un)Register a widget displayed callback.
267 266
268 267 Parameters
269 268 ----------
270 269 callback: method handler
271 270 Must have a signature of::
272 271
273 272 callback(widget, **kwargs)
274 273
275 274 kwargs from display are passed through without modification.
276 275 remove: bool
277 276 True if the callback should be unregistered."""
278 277 self._display_callbacks.register_callback(callback, remove=remove)
279 278
280 279 #-------------------------------------------------------------------------
281 280 # Support methods
282 281 #-------------------------------------------------------------------------
283 282 @contextmanager
284 283 def _lock_property(self, key, value):
285 284 """Lock a property-value pair.
286 285
287 286 The value should be the JSON state of the property.
288 287
289 288 NOTE: This, in addition to the single lock for all state changes, is
290 289 flawed. In the future we may want to look into buffering state changes
291 290 back to the front-end."""
292 291 self._property_lock = (key, value)
293 292 try:
294 293 yield
295 294 finally:
296 295 self._property_lock = (None, None)
297 296
298 297 @contextmanager
299 298 def hold_sync(self):
300 299 """Hold syncing any state until the context manager is released"""
301 300 # We increment a value so that this can be nested. Syncing will happen when
302 301 # all levels have been released.
303 302 self._send_state_lock += 1
304 303 try:
305 304 yield
306 305 finally:
307 306 self._send_state_lock -=1
308 307 if self._send_state_lock == 0:
309 308 self.send_state(self._states_to_send)
310 309 self._states_to_send.clear()
311 310
312 311 def _should_send_property(self, key, value):
313 312 """Check the property lock (property_lock)"""
314 313 to_json = self.trait_metadata(key, 'to_json', self._trait_to_json)
315 314 if (key == self._property_lock[0]
316 315 and to_json(value) == self._property_lock[1]):
317 316 return False
318 317 elif self._send_state_lock > 0:
319 318 self._states_to_send.add(key)
320 319 return False
321 320 else:
322 321 return True
323 322
324 323 # Event handlers
325 324 @_show_traceback
326 325 def _handle_msg(self, msg):
327 326 """Called when a msg is received from the front-end"""
328 327 data = msg['content']['data']
329 328 method = data['method']
330 329 if not method in ['backbone', 'custom']:
331 330 self.log.error('Unknown front-end to back-end widget msg with method "%s"' % method)
332 331
333 332 # Handle backbone sync methods CREATE, PATCH, and UPDATE all in one.
334 333 if method == 'backbone' and 'sync_data' in data:
335 334 sync_data = data['sync_data']
336 335 self.set_state(sync_data) # handles all methods
337 336
338 337 # Handle a custom msg from the front-end
339 338 elif method == 'custom':
340 339 if 'content' in data:
341 340 self._handle_custom_msg(data['content'])
342 341
343 342 def _handle_custom_msg(self, content):
344 343 """Called when a custom msg is received."""
345 344 self._msg_callbacks(self, content)
346 345
347 346 def _notify_trait(self, name, old_value, new_value):
348 347 """Called when a property has been changed."""
349 348 # Trigger default traitlet callback machinery. This allows any user
350 349 # registered validation to be processed prior to allowing the widget
351 350 # machinery to handle the state.
352 351 LoggingConfigurable._notify_trait(self, name, old_value, new_value)
353 352
354 353 # Send the state after the user registered callbacks for trait changes
355 354 # have all fired (allows for user to validate values).
356 355 if self.comm is not None and name in self.keys:
357 356 # Make sure this isn't information that the front-end just sent us.
358 357 if self._should_send_property(name, new_value):
359 358 # Send new state to front-end
360 359 self.send_state(key=name)
361 360
362 361 def _handle_displayed(self, **kwargs):
363 362 """Called when a view has been displayed for this widget instance"""
364 363 self._display_callbacks(self, **kwargs)
365 364
366 365 def _trait_to_json(self, x):
367 366 """Convert a trait value to json
368 367
369 368 Traverse lists/tuples and dicts and serialize their values as well.
370 369 Replace any widgets with their model_id
371 370 """
372 371 if isinstance(x, dict):
373 372 return {k: self._trait_to_json(v) for k, v in x.items()}
374 373 elif isinstance(x, (list, tuple)):
375 374 return [self._trait_to_json(v) for v in x]
376 375 elif isinstance(x, Widget):
377 376 return "IPY_MODEL_" + x.model_id
378 377 else:
379 378 return x # Value must be JSON-able
380 379
381 380 def _trait_from_json(self, x):
382 381 """Convert json values to objects
383 382
384 383 Replace any strings representing valid model id values to Widget references.
385 384 """
386 385 if isinstance(x, dict):
387 386 return {k: self._trait_from_json(v) for k, v in x.items()}
388 387 elif isinstance(x, (list, tuple)):
389 388 return [self._trait_from_json(v) for v in x]
390 389 elif isinstance(x, string_types) and x.startswith('IPY_MODEL_') and x[10:] in Widget.widgets:
391 390 # we want to support having child widgets at any level in a hierarchy
392 391 # trusting that a widget UUID will not appear out in the wild
393 392 return Widget.widgets[x[10:]]
394 393 else:
395 394 return x
396 395
397 396 def _ipython_display_(self, **kwargs):
398 397 """Called when `IPython.display.display` is called on the widget."""
399 398 # Show view.
400 399 if self._view_name is not None:
401 400 self._send({"method": "display"})
402 401 self._handle_displayed(**kwargs)
403 402
404 403 def _send(self, msg):
405 404 """Sends a message to the model in the front-end."""
406 405 self.comm.send(msg)
407 406
408 407
409 408 class DOMWidget(Widget):
410 409 visible = Bool(True, help="Whether the widget is visible.", sync=True)
411 410 _css = Tuple(sync=True, help="CSS property list: (selector, key, value)")
412 411 _dom_classes = Tuple(sync=True, help="DOM classes applied to widget.$el.")
413 412
414 413 width = CUnicode(sync=True)
415 414 height = CUnicode(sync=True)
416 415 padding = CUnicode(sync=True)
417 416 margin = CUnicode(sync=True)
418 417
419 418 color = Unicode(sync=True)
420 419 background_color = Unicode(sync=True)
421 420 border_color = Unicode(sync=True)
422 421
423 422 border_width = CUnicode(sync=True)
424 423 border_radius = CUnicode(sync=True)
425 424 border_style = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_border-style.asp
426 425 'none',
427 426 'hidden',
428 427 'dotted',
429 428 'dashed',
430 429 'solid',
431 430 'double',
432 431 'groove',
433 432 'ridge',
434 433 'inset',
435 434 'outset',
436 435 'initial',
437 436 'inherit', ''],
438 437 default_value='', sync=True)
439 438
440 439 font_style = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_font_font-style.asp
441 440 'normal',
442 441 'italic',
443 442 'oblique',
444 443 'initial',
445 444 'inherit', ''],
446 445 default_value='', sync=True)
447 446 font_weight = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_font_weight.asp
448 447 'normal',
449 448 'bold',
450 449 'bolder',
451 450 'lighter',
452 451 'initial',
453 452 'inherit', ''] + [str(100 * (i+1)) for i in range(9)],
454 453 default_value='', sync=True)
455 454 font_size = CUnicode(sync=True)
456 455 font_family = Unicode(sync=True)
457 456
458 457 def __init__(self, *pargs, **kwargs):
459 458 super(DOMWidget, self).__init__(*pargs, **kwargs)
460 459
461 460 def _validate_border(name, old, new):
462 461 if new is not None and new != '':
463 462 if name != 'border_width' and not self.border_width:
464 463 self.border_width = 1
465 464 if name != 'border_style' and self.border_style == '':
466 465 self.border_style = 'solid'
467 466 self.on_trait_change(_validate_border, ['border_width', 'border_style', 'border_color'])
General Comments 0
You need to be logged in to leave comments. Login now