##// END OF EJS Templates
s/view_name/_view_name
Jonathan Frederic -
Show More
@@ -1,197 +1,197 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // WidgetModel, WidgetView, and WidgetManager
10 10 //============================================================================
11 11 /**
12 12 * Base Widget classes
13 13 * @module IPython
14 14 * @namespace IPython
15 15 * @submodule widget
16 16 */
17 17
18 18 (function () {
19 19 "use strict";
20 20
21 21 // Use require.js 'define' method so that require.js is intelligent enough to
22 22 // syncronously load everything within this file when it is being 'required'
23 23 // elsewhere.
24 24 define(["underscore",
25 25 "backbone",
26 26 ], function (Underscore, Backbone) {
27 27
28 28 //--------------------------------------------------------------------
29 29 // WidgetManager class
30 30 //--------------------------------------------------------------------
31 31 var WidgetManager = function (comm_manager) {
32 32 // Public constructor
33 33 WidgetManager._managers.push(this);
34 34
35 35 // Attach a comm manager to the
36 36 this.comm_manager = comm_manager;
37 37 this._models = {}; /* Dictionary of model ids and model instances */
38 38
39 39 // Register already-registered widget model types with the comm manager.
40 40 var that = this;
41 41 _.each(WidgetManager._model_types, function(model_type, model_name) {
42 42 that.comm_manager.register_target(model_name, $.proxy(that._handle_comm_open, that));
43 43 });
44 44 };
45 45
46 46 //--------------------------------------------------------------------
47 47 // Class level
48 48 //--------------------------------------------------------------------
49 49 WidgetManager._model_types = {}; /* Dictionary of model type names (target_name) and model types. */
50 50 WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
51 51 WidgetManager._managers = []; /* List of widget managers */
52 52
53 53 WidgetManager.register_widget_model = function (model_name, model_type) {
54 54 // Registers a widget model by name.
55 55 WidgetManager._model_types[model_name] = model_type;
56 56
57 57 // Register the widget with the comm manager. Make sure to pass this object's context
58 58 // in so `this` works in the call back.
59 59 _.each(WidgetManager._managers, function(instance, i) {
60 60 if (instance.comm_manager !== null) {
61 61 instance.comm_manager.register_target(model_name, $.proxy(instance._handle_comm_open, instance));
62 62 }
63 63 });
64 64 };
65 65
66 66 WidgetManager.register_widget_view = function (view_name, view_type) {
67 67 // Registers a widget view by name.
68 68 WidgetManager._view_types[view_name] = view_type;
69 69 };
70 70
71 71 //--------------------------------------------------------------------
72 72 // Instance level
73 73 //--------------------------------------------------------------------
74 74 WidgetManager.prototype.display_view = function(msg, model) {
75 75 var cell = this.get_msg_cell(msg.parent_header.msg_id);
76 76 if (cell === null) {
77 77 console.log("Could not determine where the display" +
78 78 " message was from. Widget will not be displayed");
79 79 } else {
80 80 var view = this.create_view(model, {cell: cell});
81 81 if (view === null) {
82 82 console.error("View creation failed", model);
83 83 }
84 84 if (cell.widget_subarea !== undefined
85 85 && cell.widget_subarea !== null) {
86 86
87 87 cell.widget_area.show();
88 88 cell.widget_subarea.append(view.$el);
89 89 }
90 90 }
91 91 },
92 92
93 93 WidgetManager.prototype.create_view = function(model, options, view) {
94 var view_name = model.get('view_name');
94 var view_name = model.get('_view_name');
95 95 var ViewType = WidgetManager._view_types[view_name];
96 96 if (ViewType !== undefined && ViewType !== null) {
97 97
98 98 // If a view is passed into the method, use that view's cell as
99 99 // the cell for the view that is created.
100 100 options = options || {};
101 101 if (view !== undefined) {
102 102 options.cell = view.options.cell;
103 103 }
104 104
105 105 var parameters = {model: model, options: options};
106 106 var view = new ViewType(parameters);
107 107 view.render();
108 108 IPython.keyboard_manager.register_events(view.$el);
109 109 model.views.push(view);
110 110 model.on('destroy', view.remove, view);
111 111 return view;
112 112 }
113 113 return null;
114 114 },
115 115
116 116 WidgetManager.prototype.get_msg_cell = function (msg_id) {
117 117 var cell = null;
118 118 // First, check to see if the msg was triggered by cell execution.
119 119 if (IPython.notebook !== undefined && IPython.notebook !== null) {
120 120 cell = IPython.notebook.get_msg_cell(msg_id);
121 121 }
122 122 if (cell !== null) {
123 123 return cell
124 124 }
125 125 // Second, check to see if a get_cell callback was defined
126 126 // for the message. get_cell callbacks are registered for
127 127 // widget messages, so this block is actually checking to see if the
128 128 // message was triggered by a widget.
129 129 var kernel = this.comm_manager.kernel;
130 130 if (kernel !== undefined && kernel !== null) {
131 131 var callbacks = kernel.get_callbacks_for_msg(msg_id);
132 132 if (callbacks !== undefined &&
133 133 callbacks.iopub !== undefined &&
134 134 callbacks.iopub.get_cell !== undefined) {
135 135
136 136 return callbacks.iopub.get_cell();
137 137 }
138 138 }
139 139
140 140 // Not triggered by a cell or widget (no get_cell callback
141 141 // exists).
142 142 return null;
143 143 };
144 144
145 145 WidgetManager.prototype.callbacks = function (view) {
146 146 // callback handlers specific a view
147 147 var callbacks = {};
148 148 var cell = view.options.cell;
149 149 if (cell !== null) {
150 150 // Try to get output handlers
151 151 var handle_output = null;
152 152 var handle_clear_output = null;
153 153 if (cell.output_area !== undefined && cell.output_area !== null) {
154 154 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
155 155 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
156 156 }
157 157
158 158 // Create callback dict using what is known
159 159 var that = this;
160 160 callbacks = {
161 161 iopub : {
162 162 output : handle_output,
163 163 clear_output : handle_clear_output,
164 164
165 165 // Special function only registered by widget messages.
166 166 // Allows us to get the cell for a message so we know
167 167 // where to add widgets if the code requires it.
168 168 get_cell : function () {
169 169 return cell;
170 170 },
171 171 },
172 172 };
173 173 }
174 174 return callbacks;
175 175 };
176 176
177 177 WidgetManager.prototype.get_model = function (model_id) {
178 178 // Look-up a model instance by its id.
179 179 var model = this._models[model_id];
180 180 if (model !== undefined && model.id == model_id) {
181 181 return model;
182 182 }
183 183 return null;
184 184 };
185 185
186 186 WidgetManager.prototype._handle_comm_open = function (comm, msg) {
187 187 // Handle when a comm is opened.
188 188 var model_id = comm.comm_id;
189 189 var widget_type_name = msg.content.target_name;
190 190 var widget_model = new WidgetManager._model_types[widget_type_name](this, model_id, comm);
191 191 this._models[model_id] = widget_model;
192 192 };
193 193
194 194 IPython.WidgetManager = WidgetManager;
195 195 return IPython.WidgetManager;
196 196 });
197 197 }());
@@ -1,483 +1,483 b''
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 inspect
17 17 import types
18 18
19 19 from IPython.kernel.comm import Comm
20 20 from IPython.config import LoggingConfigurable
21 21 from IPython.utils.traitlets import Unicode, Dict, Instance, Bool, List
22 22 from IPython.utils.py3compat import string_types
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Classes
26 26 #-----------------------------------------------------------------------------
27 27 class CallbackDispatcher(LoggingConfigurable):
28 28 acceptable_nargs = List([], help="""List of integers.
29 29 The number of arguments in the callbacks registered must match one of
30 30 the integers in this list. If this list is empty or None, it will be
31 31 ignored.""")
32 32
33 33 def __init__(self, *pargs, **kwargs):
34 34 """Constructor"""
35 35 LoggingConfigurable.__init__(self, *pargs, **kwargs)
36 36 self.callbacks = {}
37 37
38 38 def __call__(self, *pargs, **kwargs):
39 39 """Call all of the registered callbacks that have the same number of
40 40 positional arguments."""
41 41 nargs = len(pargs)
42 42 self._validate_nargs(nargs)
43 43 if nargs in self.callbacks:
44 44 for callback in self.callbacks[nargs]:
45 45 callback(*pargs, **kwargs)
46 46
47 47 def register_callback(self, callback, remove=False):
48 48 """(Un)Register a callback
49 49
50 50 Parameters
51 51 ----------
52 52 callback: method handle
53 53 Method to be registered or unregisted.
54 54 remove=False: bool
55 55 Whether or not to unregister the callback."""
56 56
57 57 # Validate the number of arguments that the callback accepts.
58 58 nargs = self._get_nargs(callback)
59 59 self._validate_nargs(nargs)
60 60
61 61 # Get/create the appropriate list of callbacks.
62 62 if nargs not in self.callbacks:
63 63 self.callbacks[nargs] = []
64 64 callback_list = self.callbacks[nargs]
65 65
66 66 # (Un)Register the callback.
67 67 if remove and callback in callback_list:
68 68 callback_list.remove(callback)
69 69 elif not remove and callback not in callback_list:
70 70 callback_list.append(callback)
71 71
72 72 def _validate_nargs(self, nargs):
73 73 if self.acceptable_nargs is not None and \
74 74 len(self.acceptable_nargs) > 0 and \
75 75 nargs not in self.acceptable_nargs:
76 76
77 77 raise TypeError('Invalid number of positional arguments. See acceptable_nargs list.')
78 78
79 79 def _get_nargs(self, callback):
80 80 """Gets the number of arguments in a callback"""
81 81 if callable(callback):
82 82 argspec = inspect.getargspec(callback)
83 83 if argspec[0] is None:
84 84 nargs = 0
85 85 elif argspec[3] is None:
86 86 nargs = len(argspec[0]) # Only count vargs!
87 87 else:
88 88 nargs = len(argspec[0]) - len(argspec[3]) # Subtract number of defaults.
89 89
90 90 # Bound methods have an additional 'self' argument
91 91 if isinstance(callback, types.MethodType):
92 92 nargs -= 1
93 93 return nargs
94 94 else:
95 95 raise TypeError('Callback must be callable.')
96 96
97 97
98 98 class Widget(LoggingConfigurable):
99 99 #-------------------------------------------------------------------------
100 100 # Class attributes
101 101 #-------------------------------------------------------------------------
102 102 widget_construction_callback = None
103 103 widgets = {}
104 104
105 105 def on_widget_constructed(callback):
106 106 """Registers a callback to be called when a widget is constructed.
107 107
108 108 The callback must have the following signature:
109 109 callback(widget)"""
110 110 Widget.widget_construction_callback = callback
111 111
112 112 def _call_widget_constructed(widget):
113 113 """Class method, called when a widget is constructed."""
114 114 if Widget.widget_construction_callback is not None and callable(Widget.widget_construction_callback):
115 115 Widget.widget_construction_callback(widget)
116 116
117 117 #-------------------------------------------------------------------------
118 118 # Traits
119 119 #-------------------------------------------------------------------------
120 120 model_name = Unicode('WidgetModel', help="""Name of the backbone model
121 121 registered in the front-end to create and sync this widget with.""")
122 view_name = Unicode(help="""Default view registered in the front-end
122 _view_name = Unicode(help="""Default view registered in the front-end
123 123 to use to represent the widget.""", sync=True)
124 124 _comm = Instance('IPython.kernel.comm.Comm')
125 125
126 126 #-------------------------------------------------------------------------
127 127 # (Con/de)structor
128 128 #-------------------------------------------------------------------------
129 129 def __init__(self, **kwargs):
130 130 """Public constructor"""
131 131 self.closed = False
132 132
133 133 self._property_lock = (None, None)
134 134 self._keys = None
135 135
136 136 self._display_callbacks = CallbackDispatcher(acceptable_nargs=[0])
137 137 self._msg_callbacks = CallbackDispatcher(acceptable_nargs=[1, 2])
138 138
139 139 super(Widget, self).__init__(**kwargs)
140 140
141 141 self.on_trait_change(self._handle_property_changed, self.keys)
142 142 Widget._call_widget_constructed(self)
143 143
144 144 def __del__(self):
145 145 """Object disposal"""
146 146 self.close()
147 147
148 148 #-------------------------------------------------------------------------
149 149 # Properties
150 150 #-------------------------------------------------------------------------
151 151 @property
152 152 def keys(self):
153 153 """Gets a list of the traitlets that should be synced with the front-end."""
154 154 if self._keys is None:
155 155 self._keys = []
156 156 for trait_name in self.trait_names():
157 157 if self.trait_metadata(trait_name, 'sync'):
158 158 self._keys.append(trait_name)
159 159 return self._keys
160 160
161 161 @property
162 162 def comm(self):
163 163 """Gets the Comm associated with this widget.
164 164
165 165 If a Comm doesn't exist yet, a Comm will be created automagically."""
166 166 if self._comm is None:
167 167 # Create a comm.
168 168 self._comm = Comm(target_name=self.model_name)
169 169 self._comm.on_msg(self._handle_msg)
170 170 self._comm.on_close(self._close)
171 171 Widget.widgets[self.model_id] = self
172 172
173 173 # first update
174 174 self.send_state()
175 175 return self._comm
176 176
177 177 @property
178 178 def model_id(self):
179 179 """Gets the model id of this widget.
180 180
181 181 If a Comm doesn't exist yet, a Comm will be created automagically."""
182 182 return self.comm.comm_id
183 183
184 184 #-------------------------------------------------------------------------
185 185 # Methods
186 186 #-------------------------------------------------------------------------
187 187 def close(self):
188 188 """Close method.
189 189
190 190 Closes the widget which closes the underlying comm.
191 191 When the comm is closed, all of the widget views are automatically
192 192 removed from the front-end."""
193 193 if not self.closed:
194 194 self._comm.close()
195 195 self._close()
196 196
197 197 def send_state(self, key=None):
198 198 """Sends the widget state, or a piece of it, to the front-end.
199 199
200 200 Parameters
201 201 ----------
202 202 key : unicode (optional)
203 203 A single property's name to sync with the front-end.
204 204 """
205 205 self._send({
206 206 "method" : "update",
207 207 "state" : self.get_state()
208 208 })
209 209
210 210 def get_state(self, key=None):
211 211 """Gets the widget state, or a piece of it.
212 212
213 213 Parameters
214 214 ----------
215 215 key : unicode (optional)
216 216 A single property's name to get.
217 217 """
218 218 keys = self.keys if key is None else [key]
219 219 return {k: self._pack_widgets(getattr(self, k)) for k in keys}
220 220
221 221 def send(self, content):
222 222 """Sends a custom msg to the widget model in the front-end.
223 223
224 224 Parameters
225 225 ----------
226 226 content : dict
227 227 Content of the message to send.
228 228 """
229 229 self._send({"method": "custom", "content": content})
230 230
231 231 def on_msg(self, callback, remove=False):
232 232 """(Un)Register a custom msg recieve callback.
233 233
234 234 Parameters
235 235 ----------
236 236 callback: method handler
237 237 Can have a signature of:
238 238 - callback(content) Signature 1
239 239 - callback(sender, content) Signature 2
240 240 remove: bool
241 241 True if the callback should be unregistered."""
242 242 self._msg_callbacks.register_callback(callback, remove=remove)
243 243
244 244 def on_displayed(self, callback, remove=False):
245 245 """(Un)Register a widget displayed callback.
246 246
247 247 Parameters
248 248 ----------
249 249 callback: method handler
250 250 Can have a signature of:
251 251 - callback(sender, **kwargs)
252 252 kwargs from display call passed through without modification.
253 253 remove: bool
254 254 True if the callback should be unregistered."""
255 255 self._display_callbacks.register_callback(callback, remove=remove)
256 256
257 257 #-------------------------------------------------------------------------
258 258 # Support methods
259 259 #-------------------------------------------------------------------------
260 260 @contextmanager
261 261 def _lock_property(self, key, value):
262 262 """Lock a property-value pair.
263 263
264 264 NOTE: This, in addition to the single lock for all state changes, is
265 265 flawed. In the future we may want to look into buffering state changes
266 266 back to the front-end."""
267 267 self._property_lock = (key, value)
268 268 try:
269 269 yield
270 270 finally:
271 271 self._property_lock = (None, None)
272 272
273 273 def _should_send_property(self, key, value):
274 274 """Check the property lock (property_lock)"""
275 275 return key != self._property_lock[0] or \
276 276 value != self._property_lock[1]
277 277
278 278 def _close(self):
279 279 """Unsafe close"""
280 280 del Widget.widgets[self.model_id]
281 281 self._comm = None
282 282 self.closed = True
283 283
284 284 # Event handlers
285 285 def _handle_msg(self, msg):
286 286 """Called when a msg is received from the front-end"""
287 287 data = msg['content']['data']
288 288 method = data['method']
289 289 if not method in ['backbone', 'custom']:
290 290 self.log.error('Unknown front-end to back-end widget msg with method "%s"' % method)
291 291
292 292 # Handle backbone sync methods CREATE, PATCH, and UPDATE all in one.
293 293 if method == 'backbone' and 'sync_data' in data:
294 294 sync_data = data['sync_data']
295 295 self._handle_receive_state(sync_data) # handles all methods
296 296
297 297 # Handle a custom msg from the front-end
298 298 elif method == 'custom':
299 299 if 'content' in data:
300 300 self._handle_custom_msg(data['content'])
301 301
302 302 def _handle_receive_state(self, sync_data):
303 303 """Called when a state is received from the front-end."""
304 304 for name in self.keys:
305 305 if name in sync_data:
306 306 value = self._unpack_widgets(sync_data[name])
307 307 with self._lock_property(name, value):
308 308 setattr(self, name, value)
309 309
310 310 def _handle_custom_msg(self, content):
311 311 """Called when a custom msg is received."""
312 312 self._msg_callbacks(content) # Signature 1
313 313 self._msg_callbacks(self, content) # Signature 2
314 314
315 315 def _handle_property_changed(self, name, old, new):
316 316 """Called when a property has been changed."""
317 317 # Make sure this isn't information that the front-end just sent us.
318 318 if self._should_send_property(name, new):
319 319 # Send new state to front-end
320 320 self.send_state(key=name)
321 321
322 322 def _handle_displayed(self, **kwargs):
323 323 """Called when a view has been displayed for this widget instance"""
324 324 self._display_callbacks(**kwargs)
325 325
326 326 def _pack_widgets(self, x):
327 327 """Recursively converts all widget instances to model id strings.
328 328
329 329 Children widgets will be stored and transmitted to the front-end by
330 330 their model ids. Return value must be JSON-able."""
331 331 if isinstance(x, dict):
332 332 return {k: self._pack_widgets(v) for k, v in x.items()}
333 333 elif isinstance(x, list):
334 334 return [self._pack_widgets(v) for v in x]
335 335 elif isinstance(x, Widget):
336 336 return x.model_id
337 337 else:
338 338 return x # Value must be JSON-able
339 339
340 340 def _unpack_widgets(self, x):
341 341 """Recursively converts all model id strings to widget instances.
342 342
343 343 Children widgets will be stored and transmitted to the front-end by
344 344 their model ids."""
345 345 if isinstance(x, dict):
346 346 return {k: self._unpack_widgets(v) for k, v in x.items()}
347 347 elif isinstance(x, list):
348 348 return [self._unpack_widgets(v) for v in x]
349 349 elif isinstance(x, string_types):
350 350 return x if x not in Widget.widgets else Widget.widgets[x]
351 351 else:
352 352 return x
353 353
354 354 def _ipython_display_(self, **kwargs):
355 355 """Called when `IPython.display.display` is called on the widget."""
356 356 # Show view. By sending a display message, the comm is opened and the
357 357 # initial state is sent.
358 358 self._send({"method": "display"})
359 359 self._handle_displayed(**kwargs)
360 360
361 361 def _send(self, msg):
362 362 """Sends a message to the model in the front-end."""
363 363 self.comm.send(msg)
364 364
365 365
366 366 class DOMWidget(Widget):
367 367 visible = Bool(True, help="Whether or not the widget is visible.", sync=True)
368 368 _css = Dict(sync=True) # Internal CSS property dict
369 369
370 370 def get_css(self, key, selector=""):
371 371 """Get a CSS property of the widget.
372 372
373 373 Note: This function does not actually request the CSS from the
374 374 front-end; Only properties that have been set with set_css can be read.
375 375
376 376 Parameters
377 377 ----------
378 378 key: unicode
379 379 CSS key
380 380 selector: unicode (optional)
381 381 JQuery selector used when the CSS key/value was set.
382 382 """
383 383 if selector in self._css and key in self._css[selector]:
384 384 return self._css[selector][key]
385 385 else:
386 386 return None
387 387
388 388 def set_css(self, *args, **kwargs):
389 389 """Set one or more CSS properties of the widget.
390 390
391 391 This function has two signatures:
392 392 - set_css(css_dict, selector='')
393 393 - set_css(key, value, selector='')
394 394
395 395 Parameters
396 396 ----------
397 397 css_dict : dict
398 398 CSS key/value pairs to apply
399 399 key: unicode
400 400 CSS key
401 401 value
402 402 CSS value
403 403 selector: unicode (optional)
404 404 JQuery selector to use to apply the CSS key/value. If no selector
405 405 is provided, an empty selector is used. An empty selector makes the
406 406 front-end try to apply the css to a default element. The default
407 407 element is an attribute unique to each view, which is a DOM element
408 408 of the view that should be styled with common CSS (see
409 409 `$el_to_style` in the Javascript code).
410 410 """
411 411 selector = kwargs.get('selector', '')
412 412 if not selector in self._css:
413 413 self._css[selector] = {}
414 414
415 415 # Signature 1: set_css(css_dict, selector='')
416 416 if len(args) == 1:
417 417 if isinstance(args[0], dict):
418 418 for (key, value) in args[0].items():
419 419 if not (key in self._css[selector] and value == self._css[selector][key]):
420 420 self._css[selector][key] = value
421 421 self.send_state('_css')
422 422 else:
423 423 raise Exception('css_dict must be a dict.')
424 424
425 425 # Signature 2: set_css(key, value, selector='')
426 426 elif len(args) == 2 or len(args) == 3:
427 427
428 428 # Selector can be a positional arg if it's the 3rd value
429 429 if len(args) == 3:
430 430 selector = args[2]
431 431 if selector not in self._css:
432 432 self._css[selector] = {}
433 433
434 434 # Only update the property if it has changed.
435 435 key = args[0]
436 436 value = args[1]
437 437 if not (key in self._css[selector] and value == self._css[selector][key]):
438 438 self._css[selector][key] = value
439 439 self.send_state('_css') # Send new state to client.
440 440 else:
441 441 raise Exception('set_css only accepts 1-3 arguments')
442 442
443 443 def add_class(self, class_names, selector=""):
444 444 """Add class[es] to a DOM element.
445 445
446 446 Parameters
447 447 ----------
448 448 class_names: unicode or list
449 449 Class name(s) to add to the DOM element(s).
450 450 selector: unicode (optional)
451 451 JQuery selector to select the DOM element(s) that the class(es) will
452 452 be added to.
453 453 """
454 454 class_list = class_names
455 455 if isinstance(class_list, list):
456 456 class_list = ' '.join(class_list)
457 457
458 458 self.send({
459 459 "msg_type" : "add_class",
460 460 "class_list" : class_list,
461 461 "selector" : selector
462 462 })
463 463
464 464 def remove_class(self, class_names, selector=""):
465 465 """Remove class[es] from a DOM element.
466 466
467 467 Parameters
468 468 ----------
469 469 class_names: unicode or list
470 470 Class name(s) to remove from the DOM element(s).
471 471 selector: unicode (optional)
472 472 JQuery selector to select the DOM element(s) that the class(es) will
473 473 be removed from.
474 474 """
475 475 class_list = class_names
476 476 if isinstance(class_list, list):
477 477 class_list = ' '.join(class_list)
478 478
479 479 self.send({
480 480 "msg_type" : "remove_class",
481 481 "class_list" : class_list,
482 482 "selector" : selector,
483 483 })
@@ -1,34 +1,34 b''
1 1 """BoolWidget class.
2 2
3 3 Represents a boolean using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from .widget import DOMWidget
17 17 from IPython.utils.traitlets import Unicode, Bool, List
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Classes
21 21 #-----------------------------------------------------------------------------
22 22 class _BoolWidget(DOMWidget):
23 23 value = Bool(False, help="Bool value", sync=True)
24 24 description = Unicode('', help="Description of the boolean (label).", sync=True)
25 25 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
26 26
27 27
28 28 class CheckBoxWidget(_BoolWidget):
29 view_name = Unicode('CheckBoxView', sync=True)
29 _view_name = Unicode('CheckBoxView', sync=True)
30 30
31 31
32 32 class ToggleButtonWidget(_BoolWidget):
33 view_name = Unicode('ToggleButtonView', sync=True)
33 _view_name = Unicode('ToggleButtonView', sync=True)
34 34 No newline at end of file
@@ -1,60 +1,60 b''
1 1 """ButtonWidget class.
2 2
3 3 Represents a button in the frontend using a widget. Allows user to listen for
4 4 click events on the button and trigger backend code when the clicks are fired.
5 5 """
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (c) 2013, the IPython Development Team.
8 8 #
9 9 # Distributed under the terms of the Modified BSD License.
10 10 #
11 11 # The full license is in the file COPYING.txt, distributed with this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17 from .widget import DOMWidget, CallbackDispatcher
18 18 from IPython.utils.traitlets import Unicode, Bool
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Classes
22 22 #-----------------------------------------------------------------------------
23 23 class ButtonWidget(DOMWidget):
24 view_name = Unicode('ButtonView', sync=True)
24 _view_name = Unicode('ButtonView', sync=True)
25 25
26 26 # Keys
27 27 description = Unicode('', help="Description of the button (label).", sync=True)
28 28 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
29 29
30 30 def __init__(self, **kwargs):
31 31 """Constructor"""
32 32 super(ButtonWidget, self).__init__(**kwargs)
33 33 self._click_handlers = CallbackDispatcher(acceptable_nargs=[0, 1])
34 34 self.on_msg(self._handle_button_msg)
35 35
36 36 def on_click(self, callback, remove=False):
37 37 """Register a callback to execute when the button is clicked.
38 38
39 39 The callback can either accept no parameters or one sender parameter:
40 40 - callback()
41 41 - callback(sender)
42 42 If the callback has a sender parameter, the ButtonWidget instance that
43 43 called the callback will be passed into the method as the sender.
44 44
45 45 Parameters
46 46 ----------
47 47 remove : bool (optional)
48 48 Set to true to remove the callback from the list of callbacks."""
49 49 self._click_handlers.register_callback(callback, remove=remove)
50 50
51 51 def _handle_button_msg(self, content):
52 52 """Handle a msg from the front-end.
53 53
54 54 Parameters
55 55 ----------
56 56 content: dict
57 57 Content of the msg."""
58 58 if 'event' in content and content['event'] == 'click':
59 59 self._click_handlers()
60 60 self._click_handlers(self)
@@ -1,56 +1,56 b''
1 1 """ContainerWidget class.
2 2
3 3 Represents a container that can be used to group other widgets.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from .widget import DOMWidget
17 17 from IPython.utils.traitlets import Unicode, Bool, List, Instance
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Classes
21 21 #-----------------------------------------------------------------------------
22 22 class ContainerWidget(DOMWidget):
23 view_name = Unicode('ContainerView', sync=True)
23 _view_name = Unicode('ContainerView', sync=True)
24 24
25 25 # Keys, all private and managed by helper methods. Flexible box model
26 26 # classes...
27 27 children = List(Instance(DOMWidget))
28 28 _children = List(Instance(DOMWidget), sync=True)
29 29
30 30 def __init__(self, *pargs, **kwargs):
31 31 """Constructor"""
32 32 DOMWidget.__init__(self, *pargs, **kwargs)
33 33 self.on_trait_change(self._validate, ['children'])
34 34
35 35 def _validate(self, name, old, new):
36 36 """Validate children list.
37 37
38 38 Makes sure only one instance of any given model can exist in the
39 39 children list.
40 40 An excellent post on uniqifiers is available at
41 41 http://www.peterbe.com/plog/uniqifiers-benchmark
42 42 which provides the inspiration for using this implementation. Below
43 43 I've implemented the `f5` algorithm using Python comprehensions."""
44 44 if new is not None and isinstance(new, list):
45 45 seen = {}
46 46 def add_item(i):
47 47 seen[i.model_id] = True
48 48 return i
49 49 return [add_item(i) for i in new if not i.model_id in seen]
50 50
51 51
52 52 class PopupWidget(ContainerWidget):
53 view_name = Unicode('PopupView', sync=True)
53 _view_name = Unicode('PopupView', sync=True)
54 54
55 55 description = Unicode(sync=True)
56 56 button_text = Unicode(sync=True)
@@ -1,59 +1,59 b''
1 1 """FloatWidget class.
2 2
3 3 Represents an unbounded float using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from .widget import DOMWidget
17 17 from IPython.utils.traitlets import Unicode, CFloat, Bool, List, Enum
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Classes
21 21 #-----------------------------------------------------------------------------
22 22 class _FloatWidget(DOMWidget):
23 23 value = CFloat(0.0, help="Float value", sync=True)
24 24 disabled = Bool(False, help="Enable or disable user changes", sync=True)
25 25 description = Unicode(help="Description of the value this widget represents", sync=True)
26 26
27 27
28 28 class _BoundedFloatWidget(_FloatWidget):
29 29 max = CFloat(100.0, help="Max value", sync=True)
30 30 min = CFloat(0.0, help="Min value", sync=True)
31 31 step = CFloat(0.1, help="Minimum step that the value can take (ignored by some views)", sync=True)
32 32
33 33 def __init__(self, *pargs, **kwargs):
34 34 """Constructor"""
35 35 DOMWidget.__init__(self, *pargs, **kwargs)
36 36 self.on_trait_change(self._validate, ['value', 'min', 'max'])
37 37
38 38 def _validate(self, name, old, new):
39 39 """Validate value, max, min."""
40 40 if self.min > new or new > self.max:
41 41 self.value = min(max(new, self.min), self.max)
42 42
43 43
44 44 class FloatTextWidget(_FloatWidget):
45 view_name = Unicode('FloatTextView', sync=True)
45 _view_name = Unicode('FloatTextView', sync=True)
46 46
47 47
48 48 class BoundedFloatTextWidget(_BoundedFloatWidget):
49 view_name = Unicode('FloatTextView', sync=True)
49 _view_name = Unicode('FloatTextView', sync=True)
50 50
51 51
52 52 class FloatSliderWidget(_BoundedFloatWidget):
53 view_name = Unicode('FloatSliderView', sync=True)
53 _view_name = Unicode('FloatSliderView', sync=True)
54 54 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
55 55 help="Vertical or horizontal.", sync=True)
56 56
57 57
58 58 class FloatProgressWidget(_BoundedFloatWidget):
59 view_name = Unicode('ProgressView', sync=True)
59 _view_name = Unicode('ProgressView', sync=True)
@@ -1,36 +1,36 b''
1 1 """ButtonWidget class.
2 2
3 3 Represents a button in the frontend using a widget. Allows user to listen for
4 4 click events on the button and trigger backend code when the clicks are fired.
5 5 """
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (c) 2013, the IPython Development Team.
8 8 #
9 9 # Distributed under the terms of the Modified BSD License.
10 10 #
11 11 # The full license is in the file COPYING.txt, distributed with this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17 import base64
18 18
19 19 from .widget import DOMWidget
20 20 from IPython.utils.traitlets import Unicode, CUnicode, Bytes
21 21
22 22 #-----------------------------------------------------------------------------
23 23 # Classes
24 24 #-----------------------------------------------------------------------------
25 25 class ImageWidget(DOMWidget):
26 view_name = Unicode('ImageView', sync=True)
26 _view_name = Unicode('ImageView', sync=True)
27 27
28 28 # Define the custom state properties to sync with the front-end
29 29 format = Unicode('png', sync=True)
30 30 width = CUnicode(sync=True)
31 31 height = CUnicode(sync=True)
32 32 _b64value = Unicode(sync=True)
33 33
34 34 value = Bytes()
35 35 def _value_changed(self, name, old, new):
36 36 self._b64value = base64.b64encode(new) No newline at end of file
@@ -1,59 +1,59 b''
1 1 """IntWidget class.
2 2
3 3 Represents an unbounded int using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from .widget import DOMWidget
17 17 from IPython.utils.traitlets import Unicode, CInt, Bool, List, Enum
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Classes
21 21 #-----------------------------------------------------------------------------
22 22 class _IntWidget(DOMWidget):
23 23 value = CInt(0, help="Int value", sync=True)
24 24 disabled = Bool(False, help="Enable or disable user changes", sync=True)
25 25 description = Unicode(help="Description of the value this widget represents", sync=True)
26 26
27 27
28 28 class _BoundedIntWidget(_IntWidget):
29 29 step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
30 30 max = CInt(100, help="Max value", sync=True)
31 31 min = CInt(0, help="Min value", sync=True)
32 32
33 33 def __init__(self, *pargs, **kwargs):
34 34 """Constructor"""
35 35 DOMWidget.__init__(self, *pargs, **kwargs)
36 36 self.on_trait_change(self._validate, ['value', 'min', 'max'])
37 37
38 38 def _validate(self, name, old, new):
39 39 """Validate value, max, min."""
40 40 if self.min > new or new > self.max:
41 41 self.value = min(max(new, self.min), self.max)
42 42
43 43
44 44 class IntTextWidget(_IntWidget):
45 view_name = Unicode('IntTextView', sync=True)
45 _view_name = Unicode('IntTextView', sync=True)
46 46
47 47
48 48 class BoundedIntTextWidget(_BoundedIntWidget):
49 view_name = Unicode('IntTextView', sync=True)
49 _view_name = Unicode('IntTextView', sync=True)
50 50
51 51
52 52 class IntSliderWidget(_BoundedIntWidget):
53 view_name = Unicode('IntSliderView', sync=True)
53 _view_name = Unicode('IntSliderView', sync=True)
54 54 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
55 55 help="Vertical or horizontal.", sync=True)
56 56
57 57
58 58 class IntProgressWidget(_BoundedIntWidget):
59 view_name = Unicode('ProgressView', sync=True)
59 _view_name = Unicode('ProgressView', sync=True)
@@ -1,100 +1,100 b''
1 1 """SelectionWidget class.
2 2
3 3 Represents an enumeration using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from threading import Lock
17 17
18 18 from .widget import DOMWidget
19 19 from IPython.utils.traitlets import Unicode, List, Bool, Any, Dict
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # SelectionWidget
23 23 #-----------------------------------------------------------------------------
24 24 class _SelectionWidget(DOMWidget):
25 25 value = Any(help="Selected value")
26 26 values = List(help="List of values the user can select")
27 27 value_names = Dict(help="""List of string representations for each value.
28 28 These string representations are used to display the values in the
29 29 front-end.""")
30 30 disabled = Bool(False, help="Enable or disable user changes", sync=True)
31 31 description = Unicode(help="Description of the value this widget represents", sync=True)
32 32
33 33 _value = Unicode(sync=True) # Bi-directionally synced.
34 34 _values = List(sync=True) # Only back-end to front-end synced.
35 35 _reverse_value_names = Dict()
36 36
37 37 def __init__(self, *pargs, **kwargs):
38 38 """Constructor"""
39 39 DOMWidget.__init__(self, *pargs, **kwargs)
40 40 self.value_lock = Lock()
41 41 self.on_trait_change(self._string_value_set, ['_value'])
42 42
43 43 def _value_names_changed(self, name=None, old=None, new=None):
44 44 """Handles when the value_names Dict has been changed.
45 45
46 46 This method sets the _reverse_value_names Dict to the inverse of the new
47 47 value for the value_names Dict."""
48 48 self._reverse_value_names = {v:k for k, v in self.value_names.items()}
49 49 self._values_changed()
50 50
51 51 def _values_changed(self, name=None, old=None, new=None):
52 52 """Called when values has been changed"""
53 53 self._values = [self._get_string_repr(v) for v in self.values]
54 54
55 55 def _value_changed(self, name, old, new):
56 56 """Called when value has been changed"""
57 57 if self.value_lock.acquire(False):
58 58 try:
59 59 # Make sure the value is in the list of values.
60 60 if new in self.values:
61 61 # Set the string version of the value.
62 62 self._value = self._get_string_repr(new)
63 63 else:
64 64 raise TypeError('Value must be a value in the values list.')
65 65 finally:
66 66 self.value_lock.release()
67 67
68 68 def _get_string_repr(self, value):
69 69 """Get the string repr of a value"""
70 70 if value not in self.value_names:
71 71 self.value_names[value] = str(value)
72 72 self._value_names_changed()
73 73 return self.value_names[value]
74 74
75 75 def _string_value_set(self, name, old, new):
76 76 """Called when _value has been changed."""
77 77 if self.value_lock.acquire(False):
78 78 try:
79 79 if new in self._reverse_value_names:
80 80 self.value = self._reverse_value_names[new]
81 81 else:
82 82 self.value = None
83 83 finally:
84 84 self.value_lock.release()
85 85
86 86
87 87 class ToggleButtonsWidget(_SelectionWidget):
88 view_name = Unicode('ToggleButtonsView', sync=True)
88 _view_name = Unicode('ToggleButtonsView', sync=True)
89 89
90 90
91 91 class DropdownWidget(_SelectionWidget):
92 view_name = Unicode('DropdownView', sync=True)
92 _view_name = Unicode('DropdownView', sync=True)
93 93
94 94
95 95 class RadioButtonsWidget(_SelectionWidget):
96 view_name = Unicode('RadioButtonsView', sync=True)
96 _view_name = Unicode('RadioButtonsView', sync=True)
97 97
98 98
99 99 class ListBoxWidget(_SelectionWidget):
100 view_name = Unicode('ListBoxView', sync=True)
100 _view_name = Unicode('ListBoxView', sync=True)
@@ -1,58 +1,58 b''
1 1 """SelectionContainerWidget class.
2 2
3 3 Represents a multipage container that can be used to group other widgets into
4 4 pages.
5 5 """
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (c) 2013, the IPython Development Team.
8 8 #
9 9 # Distributed under the terms of the Modified BSD License.
10 10 #
11 11 # The full license is in the file COPYING.txt, distributed with this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17 from .widget_container import ContainerWidget
18 18 from IPython.utils.traitlets import Unicode, Dict, CInt, List, Instance
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Classes
22 22 #-----------------------------------------------------------------------------
23 23 class _SelectionContainerWidget(ContainerWidget):
24 24 _titles = Dict(help="Titles of the pages", sync=True)
25 25 selected_index = CInt(0, sync=True)
26 26
27 27 # Public methods
28 28 def set_title(self, index, title):
29 29 """Sets the title of a container page.
30 30
31 31 Parameters
32 32 ----------
33 33 index : int
34 34 Index of the container page
35 35 title : unicode
36 36 New title"""
37 37 self._titles[index] = title
38 38 self.send_state('_titles')
39 39
40 40 def get_title(self, index):
41 41 """Gets the title of a container pages.
42 42
43 43 Parameters
44 44 ----------
45 45 index : int
46 46 Index of the container page"""
47 47 if index in self._titles:
48 48 return self._titles[index]
49 49 else:
50 50 return None
51 51
52 52
53 53 class AccordionWidget(_SelectionContainerWidget):
54 view_name = Unicode('AccordionView', sync=True)
54 _view_name = Unicode('AccordionView', sync=True)
55 55
56 56
57 57 class TabWidget(_SelectionContainerWidget):
58 view_name = Unicode('TabView', sync=True)
58 _view_name = Unicode('TabView', sync=True)
@@ -1,75 +1,75 b''
1 1 """StringWidget class.
2 2
3 3 Represents a unicode string using a widget.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from .widget import DOMWidget, CallbackDispatcher
17 17 from IPython.utils.traitlets import Unicode, Bool, List
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Classes
21 21 #-----------------------------------------------------------------------------
22 22 class _StringWidget(DOMWidget):
23 23 value = Unicode(help="String value", sync=True)
24 24 disabled = Bool(False, help="Enable or disable user changes", sync=True)
25 25 description = Unicode(help="Description of the value this widget represents", sync=True)
26 26
27 27
28 28 class HTMLWidget(_StringWidget):
29 view_name = Unicode('HTMLView', sync=True)
29 _view_name = Unicode('HTMLView', sync=True)
30 30
31 31
32 32 class LatexWidget(_StringWidget):
33 view_name = Unicode('LatexView', sync=True)
33 _view_name = Unicode('LatexView', sync=True)
34 34
35 35
36 36 class TextAreaWidget(_StringWidget):
37 view_name = Unicode('TextAreaView', sync=True)
37 _view_name = Unicode('TextAreaView', sync=True)
38 38
39 39 def scroll_to_bottom(self):
40 40 self.send({"method": "scroll_to_bottom"})
41 41
42 42
43 43 class TextBoxWidget(_StringWidget):
44 view_name = Unicode('TextBoxView', sync=True)
44 _view_name = Unicode('TextBoxView', sync=True)
45 45
46 46 def __init__(self, **kwargs):
47 47 super(TextBoxWidget, self).__init__(**kwargs)
48 48 self._submission_callbacks = CallbackDispatcher(acceptable_nargs=[0, 1])
49 49 self.on_msg(self._handle_string_msg)
50 50
51 51 def _handle_string_msg(self, content):
52 52 """Handle a msg from the front-end.
53 53
54 54 Parameters
55 55 ----------
56 56 content: dict
57 57 Content of the msg."""
58 58 if 'event' in content and content['event'] == 'submit':
59 59 self._submission_callbacks()
60 60 self._submission_callbacks(self)
61 61
62 62 def on_submit(self, callback, remove=False):
63 63 """(Un)Register a callback to handle text submission.
64 64
65 65 Triggered when the user clicks enter.
66 66
67 67 Parameters
68 68 callback: Method handle
69 69 Function to be called when the text has been submitted. Function
70 70 can have two possible signatures:
71 71 callback()
72 72 callback(sender)
73 73 remove: bool (optional)
74 74 Whether or not to unregister the callback"""
75 75 self._submission_callbacks.register_callback(callback, remove=remove)
General Comments 0
You need to be logged in to leave comments. Login now