##// END OF EJS Templates
More PEP8 changes
Jonathan Frederic -
Show More
@@ -1,449 +1,429 b''
1 """Base Widget class. Allows user to create widgets in the back-end that render
1 """Base Widget class. Allows user to create widgets in the back-end that render
2 in the IPython notebook front-end.
2 in the IPython notebook front-end.
3 """
3 """
4 #-----------------------------------------------------------------------------
4 #-----------------------------------------------------------------------------
5 # Copyright (c) 2013, the IPython Development Team.
5 # Copyright (c) 2013, the IPython Development Team.
6 #
6 #
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8 #
8 #
9 # The full license is in the file COPYING.txt, distributed with this software.
9 # The full license is in the file COPYING.txt, distributed with this software.
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Imports
13 # Imports
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 from contextlib import contextmanager
15 from contextlib import contextmanager
16 import inspect
16 import inspect
17 import types
17 import types
18
18
19 from IPython.kernel.comm import Comm
19 from IPython.kernel.comm import Comm
20 from IPython.config import LoggingConfigurable
20 from IPython.config import LoggingConfigurable
21 from IPython.utils.traitlets import Unicode, Dict, Instance, Bool
21 from IPython.utils.traitlets import Unicode, Dict, Instance, Bool
22 from IPython.utils.py3compat import string_types
22 from IPython.utils.py3compat import string_types
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Classes
25 # Classes
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 class Widget(LoggingConfigurable):
27 class Widget(LoggingConfigurable):
28
28
29 # Shared declarations (Class level)
29 # Shared declarations (Class level)
30 widget_construction_callback = None
30 widget_construction_callback = None
31 widgets = {}
31 widgets = {}
32
32
33 def on_widget_constructed(callback):
33 def on_widget_constructed(callback):
34 """Class method, registers a callback to be called when a widget is
34 """Registers a callback to be called when a widget is constructed.
35 constructed. The callback must have the following signature:
35
36 The callback must have the following signature:
36 callback(widget)"""
37 callback(widget)"""
37 Widget.widget_construction_callback = callback
38 Widget.widget_construction_callback = callback
38
39
39 def _call_widget_constructed(widget):
40 def _call_widget_constructed(widget):
40 """Class method, called when a widget is constructed."""
41 """Class method, called when a widget is constructed."""
41 if Widget.widget_construction_callback is not None and callable(Widget.widget_construction_callback):
42 if Widget.widget_construction_callback is not None and callable(Widget.widget_construction_callback):
42 Widget.widget_construction_callback(widget)
43 Widget.widget_construction_callback(widget)
43
44
44
45
46 # Public declarations (Instance level)
45 # Public declarations (Instance level)
47 model_name = Unicode('WidgetModel', help="""Name of the backbone model
46 model_name = Unicode('WidgetModel', help="""Name of the backbone model
48 registered in the front-end to create and sync this widget with.""")
47 registered in the front-end to create and sync this widget with.""")
49 view_name = Unicode(help="""Default view registered in the front-end
48 view_name = Unicode(help="""Default view registered in the front-end
50 to use to represent the widget.""", sync=True)
49 to use to represent the widget.""", sync=True)
51
50
52 @contextmanager
51 @contextmanager
53 def property_lock(self, key, value):
52 def property_lock(self, key, value):
54 """Lock a property-value pair.
53 """Lock a property-value pair.
55
54
56 NOTE: This, in addition to the single lock for all state changes, is
55 NOTE: This, in addition to the single lock for all state changes, is
57 flawed. In the future we may want to look into buffering state changes
56 flawed. In the future we may want to look into buffering state changes
58 back to the front-end."""
57 back to the front-end."""
59 self._property_lock = (key, value)
58 self._property_lock = (key, value)
60 try:
59 try:
61 yield
60 yield
62 finally:
61 finally:
63 self._property_lock = (None, None)
62 self._property_lock = (None, None)
64
63
65 def should_send_property(self, key, value):
64 def should_send_property(self, key, value):
66 """Check the property lock (property_lock)"""
65 """Check the property lock (property_lock)"""
67 return key != self._property_lock[0] or \
66 return key != self._property_lock[0] or \
68 value != self._property_lock[1]
67 value != self._property_lock[1]
69
68
70 @property
69 @property
71 def keys(self):
70 def keys(self):
72 if self._keys is None:
71 if self._keys is None:
73 self._keys = []
72 self._keys = []
74 for trait_name in self.trait_names():
73 for trait_name in self.trait_names():
75 if self.trait_metadata(trait_name, 'sync'):
74 if self.trait_metadata(trait_name, 'sync'):
76 self._keys.append(trait_name)
75 self._keys.append(trait_name)
77 return self._keys
76 return self._keys
78
77
79 # Private/protected declarations
78 # Private/protected declarations
80 _comm = Instance('IPython.kernel.comm.Comm')
79 _comm = Instance('IPython.kernel.comm.Comm')
81
80
82 def __init__(self, **kwargs):
81 def __init__(self, **kwargs):
83 """Public constructor
82 """Public constructor"""
84 """
85 self.closed = False
83 self.closed = False
86 self._property_lock = (None, None)
84 self._property_lock = (None, None)
87 self._display_callbacks = []
85 self._display_callbacks = []
88 self._msg_callbacks = []
86 self._msg_callbacks = []
89 self._keys = None
87 self._keys = None
90 super(Widget, self).__init__(**kwargs)
88 super(Widget, self).__init__(**kwargs)
91
89
92 self.on_trait_change(self._handle_property_changed, self.keys)
90 self.on_trait_change(self._handle_property_changed, self.keys)
93 Widget._call_widget_constructed(self)
91 Widget._call_widget_constructed(self)
94
92
95 def __del__(self):
93 def __del__(self):
96 """Object disposal"""
94 """Object disposal"""
97 self.close()
95 self.close()
98
96
99 def close(self):
97 def close(self):
100 """Close method. Closes the widget which closes the underlying comm.
98 """Close method.
99
100 Closes the widget which closes the underlying comm.
101 When the comm is closed, all of the widget views are automatically
101 When the comm is closed, all of the widget views are automatically
102 removed from the front-end."""
102 removed from the front-end."""
103 if not self.closed:
103 if not self.closed:
104 self._comm.close()
104 self._comm.close()
105 self._close()
105 self._close()
106
106
107
108 def _close(self):
107 def _close(self):
109 """Unsafe close"""
108 """Unsafe close"""
110 del Widget.widgets[self.model_id]
109 del Widget.widgets[self.model_id]
111 self._comm = None
110 self._comm = None
112 self.closed = True
111 self.closed = True
113
112
114
115 @property
113 @property
116 def comm(self):
114 def comm(self):
117 if self._comm is None:
115 if self._comm is None:
118 # Create a comm.
116 # Create a comm.
119 self._comm = Comm(target_name=self.model_name)
117 self._comm = Comm(target_name=self.model_name)
120 self._comm.on_msg(self._handle_msg)
118 self._comm.on_msg(self._handle_msg)
121 self._comm.on_close(self._close)
119 self._comm.on_close(self._close)
122 Widget.widgets[self.model_id] = self
120 Widget.widgets[self.model_id] = self
123
121
124 # first update
122 # first update
125 self.send_state()
123 self.send_state()
126 return self._comm
124 return self._comm
127
125
128 @property
126 @property
129 def model_id(self):
127 def model_id(self):
130 return self.comm.comm_id
128 return self.comm.comm_id
131
129
132 # Event handlers
130 # Event handlers
133 def _handle_msg(self, msg):
131 def _handle_msg(self, msg):
134 """Called when a msg is received from the front-end"""
132 """Called when a msg is received from the front-end"""
135 data = msg['content']['data']
133 data = msg['content']['data']
136 method = data['method']
134 method = data['method']
137 if not method in ['backbone', 'custom']:
135 if not method in ['backbone', 'custom']:
138 self.log.error('Unknown front-end to back-end widget msg with method "%s"' % method)
136 self.log.error('Unknown front-end to back-end widget msg with method "%s"' % method)
139
137
140 # Handle backbone sync methods CREATE, PATCH, and UPDATE all in one.
138 # Handle backbone sync methods CREATE, PATCH, and UPDATE all in one.
141 if method == 'backbone' and 'sync_data' in data:
139 if method == 'backbone' and 'sync_data' in data:
142 sync_data = data['sync_data']
140 sync_data = data['sync_data']
143 self._handle_receive_state(sync_data) # handles all methods
141 self._handle_receive_state(sync_data) # handles all methods
144
142
145 # Handle a custom msg from the front-end
143 # Handle a custom msg from the front-end
146 elif method == 'custom':
144 elif method == 'custom':
147 if 'custom_content' in data:
145 if 'custom_content' in data:
148 self._handle_custom_msg(data['custom_content'])
146 self._handle_custom_msg(data['custom_content'])
149
147
150
151 def _handle_receive_state(self, sync_data):
148 def _handle_receive_state(self, sync_data):
152 """Called when a state is received from the front-end."""
149 """Called when a state is received from the front-end."""
153 for name in self.keys:
150 for name in self.keys:
154 if name in sync_data:
151 if name in sync_data:
155 value = self._unpack_widgets(sync_data[name])
152 value = self._unpack_widgets(sync_data[name])
156 with self.property_lock(name, value):
153 with self.property_lock(name, value):
157 setattr(self, name, value)
154 setattr(self, name, value)
158
155
159
160 def _handle_custom_msg(self, content):
156 def _handle_custom_msg(self, content):
161 """Called when a custom msg is received."""
157 """Called when a custom msg is received."""
162 for handler in self._msg_callbacks:
158 for handler in self._msg_callbacks:
163 handler(self, content)
159 handler(self, content)
164
160
165
166 def _handle_property_changed(self, name, old, new):
161 def _handle_property_changed(self, name, old, new):
167 """Called when a property has been changed."""
162 """Called when a property has been changed."""
168 # Make sure this isn't information that the front-end just sent us.
163 # Make sure this isn't information that the front-end just sent us.
169 if self.should_send_property(name, new):
164 if self.should_send_property(name, new):
170 # Send new state to front-end
165 # Send new state to front-end
171 self.send_state(key=name)
166 self.send_state(key=name)
172
167
173 def _handle_displayed(self, **kwargs):
168 def _handle_displayed(self, **kwargs):
174 """Called when a view has been displayed for this widget instance"""
169 """Called when a view has been displayed for this widget instance"""
175 for handler in self._display_callbacks:
170 for handler in self._display_callbacks:
176 handler(self, **kwargs)
171 handler(self, **kwargs)
177
172
178 # Public methods
173 # Public methods
179 def send_state(self, key=None):
174 def send_state(self, key=None):
180 """Sends the widget state, or a piece of it, to the front-end.
175 """Sends the widget state, or a piece of it, to the front-end.
181
176
182 Parameters
177 Parameters
183 ----------
178 ----------
184 key : unicode (optional)
179 key : unicode (optional)
185 A single property's name to sync with the front-end.
180 A single property's name to sync with the front-end.
186 """
181 """
187 self._send({"method": "update",
182 self._send({"method": "update",
188 "state": self.get_state()})
183 "state": self.get_state()})
189
184
190 def get_state(self, key=None):
185 def get_state(self, key=None):
191 """Gets the widget state, or a piece of it.
186 """Gets the widget state, or a piece of it.
192
187
193 Parameters
188 Parameters
194 ----------
189 ----------
195 key : unicode (optional)
190 key : unicode (optional)
196 A single property's name to get.
191 A single property's name to get.
197 """
192 """
198 keys = self.keys if key is None else [key]
193 keys = self.keys if key is None else [key]
199 return {k: self._pack_widgets(getattr(self, k)) for k in keys}
194 return {k: self._pack_widgets(getattr(self, k)) for k in keys}
200
195
201
202 def _pack_widgets(self, values):
196 def _pack_widgets(self, values):
203 """This function recursively converts all widget instances to model id
197 """Recursively converts all widget instances to model id strings.
204 strings.
205
198
206 Children widgets will be stored and transmitted to the front-end by
199 Children widgets will be stored and transmitted to the front-end by
207 their model ids."""
200 their model ids."""
208 if isinstance(values, dict):
201 if isinstance(values, dict):
209 new_dict = {}
202 new_dict = {}
210 for key, value in values.items():
203 for key, value in values.items():
211 new_dict[key] = self._pack_widgets(value)
204 new_dict[key] = self._pack_widgets(value)
212 return new_dict
205 return new_dict
213 elif isinstance(values, list):
206 elif isinstance(values, list):
214 new_list = []
207 new_list = []
215 for value in values:
208 for value in values:
216 new_list.append(self._pack_widgets(value))
209 new_list.append(self._pack_widgets(value))
217 return new_list
210 return new_list
218 elif isinstance(values, Widget):
211 elif isinstance(values, Widget):
219 return values.model_id
212 return values.model_id
220 else:
213 else:
221 return values
214 return values
222
215
223
224 def _unpack_widgets(self, values):
216 def _unpack_widgets(self, values):
225 """This function recursively converts all model id strings to widget
217 """Recursively converts all model id strings to widget instances.
226 instances.
227
218
228 Children widgets will be stored and transmitted to the front-end by
219 Children widgets will be stored and transmitted to the front-end by
229 their model ids."""
220 their model ids."""
230 if isinstance(values, dict):
221 if isinstance(values, dict):
231 new_dict = {}
222 new_dict = {}
232 for key, values in values.items():
223 for key, values in values.items():
233 new_dict[key] = self._unpack_widgets(values[key])
224 new_dict[key] = self._unpack_widgets(values[key])
234 return new_dict
225 return new_dict
235 elif isinstance(values, list):
226 elif isinstance(values, list):
236 new_list = []
227 new_list = []
237 for value in values:
228 for value in values:
238 new_list.append(self._unpack_widgets(value))
229 new_list.append(self._unpack_widgets(value))
239 return new_list
230 return new_list
240 elif isinstance(values, string_types):
231 elif isinstance(values, string_types):
241 if values in Widget.widgets:
232 if values in Widget.widgets:
242 return Widget.widgets[values]
233 return Widget.widgets[values]
243 else:
234 else:
244 return values
235 return values
245 else:
236 else:
246 return values
237 return values
247
238
248
249 def send(self, content):
239 def send(self, content):
250 """Sends a custom msg to the widget model in the front-end.
240 """Sends a custom msg to the widget model in the front-end.
251
241
252 Parameters
242 Parameters
253 ----------
243 ----------
254 content : dict
244 content : dict
255 Content of the message to send.
245 Content of the message to send.
256 """
246 """
257 self._send({"method": "custom", "custom_content": content})
247 self._send({"method": "custom", "custom_content": content})
258
248
259
260 def on_msg(self, callback, remove=False):
249 def on_msg(self, callback, remove=False):
261 """Register or unregister a callback for when a custom msg is recieved
250 """(Un)Register a custom msg recieve callback.
262 from the front-end.
263
251
264 Parameters
252 Parameters
265 ----------
253 ----------
266 callback: method handler
254 callback: method handler
267 Can have a signature of:
255 Can have a signature of:
268 - callback(content)
256 - callback(content)
269 - callback(sender, content)
257 - callback(sender, content)
270 remove: bool
258 remove: bool
271 True if the callback should be unregistered."""
259 True if the callback should be unregistered."""
272 if remove and callback in self._msg_callbacks:
260 if remove and callback in self._msg_callbacks:
273 self._msg_callbacks.remove(callback)
261 self._msg_callbacks.remove(callback)
274 elif not remove and not callback in self._msg_callbacks:
262 elif not remove and not callback in self._msg_callbacks:
275 if callable(callback):
263 if callable(callback):
276 argspec = inspect.getargspec(callback)
264 argspec = inspect.getargspec(callback)
277 nargs = len(argspec[0])
265 nargs = len(argspec[0])
278
266
279 # Bound methods have an additional 'self' argument
267 # Bound methods have an additional 'self' argument
280 if isinstance(callback, types.MethodType):
268 if isinstance(callback, types.MethodType):
281 nargs -= 1
269 nargs -= 1
282
270
283 # Call the callback
271 # Call the callback
284 if nargs == 1:
272 if nargs == 1:
285 self._msg_callbacks.append(lambda sender, content: callback(content))
273 self._msg_callbacks.append(lambda sender, content: callback(content))
286 elif nargs == 2:
274 elif nargs == 2:
287 self._msg_callbacks.append(callback)
275 self._msg_callbacks.append(callback)
288 else:
276 else:
289 raise TypeError('Widget msg callback must ' \
277 raise TypeError('Widget msg callback must ' \
290 'accept 1 or 2 arguments, not %d.' % nargs)
278 'accept 1 or 2 arguments, not %d.' % nargs)
291 else:
279 else:
292 raise Exception('Callback must be callable.')
280 raise Exception('Callback must be callable.')
293
281
294
295 def on_displayed(self, callback, remove=False):
282 def on_displayed(self, callback, remove=False):
296 """Register or unregister a callback to be called when the widget has
283 """(Un)Register a widget displayed callback.
297 been displayed.
298
284
299 Parameters
285 Parameters
300 ----------
286 ----------
301 callback: method handler
287 callback: method handler
302 Can have a signature of:
288 Can have a signature of:
303 - callback(sender, **kwargs)
289 - callback(sender, **kwargs)
304 kwargs from display call passed through without modification.
290 kwargs from display call passed through without modification.
305 remove: bool
291 remove: bool
306 True if the callback should be unregistered."""
292 True if the callback should be unregistered."""
307 if remove and callback in self._display_callbacks:
293 if remove and callback in self._display_callbacks:
308 self._display_callbacks.remove(callback)
294 self._display_callbacks.remove(callback)
309 elif not remove and not callback in self._display_callbacks:
295 elif not remove and not callback in self._display_callbacks:
310 if callable(handler):
296 if callable(handler):
311 self._display_callbacks.append(callback)
297 self._display_callbacks.append(callback)
312 else:
298 else:
313 raise Exception('Callback must be callable.')
299 raise Exception('Callback must be callable.')
314
300
315
316 # Support methods
301 # Support methods
317 def _ipython_display_(self, **kwargs):
302 def _ipython_display_(self, **kwargs):
318 """Function that is called when `IPython.display.display` is called on
303 """Called when `IPython.display.display` is called on the widget."""
319 the widget."""
320
321 # Show view. By sending a display message, the comm is opened and the
304 # Show view. By sending a display message, the comm is opened and the
322 # initial state is sent.
305 # initial state is sent.
323 self._send({"method": "display"})
306 self._send({"method": "display"})
324 self._handle_displayed(**kwargs)
307 self._handle_displayed(**kwargs)
325
308
326
327 def _send(self, msg):
309 def _send(self, msg):
328 """Sends a message to the model in the front-end"""
310 """Sends a message to the model in the front-end."""
329 self.comm.send(msg)
311 self.comm.send(msg)
330
312
331
313
332 class DOMWidget(Widget):
314 class DOMWidget(Widget):
333 visible = Bool(True, help="Whether or not the widget is visible.", sync=True)
315 visible = Bool(True, help="Whether or not the widget is visible.", sync=True)
334
316
335 # Private/protected declarations
317 # Private/protected declarations
336 _css = Dict(sync=True) # Internal CSS property dict
318 _css = Dict(sync=True) # Internal CSS property dict
337
319
338 def get_css(self, key, selector=""):
320 def get_css(self, key, selector=""):
339 """Get a CSS property of the widget.
321 """Get a CSS property of the widget.
340
322
341 Note: This function does not actually request the CSS from the
323 Note: This function does not actually request the CSS from the
342 front-end; Only properties that have been set with set_css can be read.
324 front-end; Only properties that have been set with set_css can be read.
343
325
344 Parameters
326 Parameters
345 ----------
327 ----------
346 key: unicode
328 key: unicode
347 CSS key
329 CSS key
348 selector: unicode (optional)
330 selector: unicode (optional)
349 JQuery selector used when the CSS key/value was set.
331 JQuery selector used when the CSS key/value was set.
350 """
332 """
351 if selector in self._css and key in self._css[selector]:
333 if selector in self._css and key in self._css[selector]:
352 return self._css[selector][key]
334 return self._css[selector][key]
353 else:
335 else:
354 return None
336 return None
355
337
356 def set_css(self, *args, **kwargs):
338 def set_css(self, *args, **kwargs):
357 """Set one or more CSS properties of the widget.
339 """Set one or more CSS properties of the widget.
358
340
359 This function has two signatures:
341 This function has two signatures:
360 - set_css(css_dict, selector='')
342 - set_css(css_dict, selector='')
361 - set_css(key, value, selector='')
343 - set_css(key, value, selector='')
362
344
363 Parameters
345 Parameters
364 ----------
346 ----------
365 css_dict : dict
347 css_dict : dict
366 CSS key/value pairs to apply
348 CSS key/value pairs to apply
367 key: unicode
349 key: unicode
368 CSS key
350 CSS key
369 value
351 value
370 CSS value
352 CSS value
371 selector: unicode (optional)
353 selector: unicode (optional)
372 JQuery selector to use to apply the CSS key/value. If no selector
354 JQuery selector to use to apply the CSS key/value. If no selector
373 is provided, an empty selector is used. An empty selector makes the
355 is provided, an empty selector is used. An empty selector makes the
374 front-end try to apply the css to a default element. The default
356 front-end try to apply the css to a default element. The default
375 element is an attribute unique to each view, which is a DOM element
357 element is an attribute unique to each view, which is a DOM element
376 of the view that should be styled with common CSS (see
358 of the view that should be styled with common CSS (see
377 `$el_to_style` in the Javascript code).
359 `$el_to_style` in the Javascript code).
378 """
360 """
379 selector = kwargs.get('selector', '')
361 selector = kwargs.get('selector', '')
380 if not selector in self._css:
362 if not selector in self._css:
381 self._css[selector] = {}
363 self._css[selector] = {}
382
364
383 # Signature 1: set_css(css_dict, selector='')
365 # Signature 1: set_css(css_dict, selector='')
384 if len(args) == 1:
366 if len(args) == 1:
385 if isinstance(args[0], dict):
367 if isinstance(args[0], dict):
386 for (key, value) in args[0].items():
368 for (key, value) in args[0].items():
387 if not (key in self._css[selector] and value == self._css[selector][key]):
369 if not (key in self._css[selector] and value == self._css[selector][key]):
388 self._css[selector][key] = value
370 self._css[selector][key] = value
389 self.send_state('_css')
371 self.send_state('_css')
390 else:
372 else:
391 raise Exception('css_dict must be a dict.')
373 raise Exception('css_dict must be a dict.')
392
374
393 # Signature 2: set_css(key, value, selector='')
375 # Signature 2: set_css(key, value, selector='')
394 elif len(args) == 2 or len(args) == 3:
376 elif len(args) == 2 or len(args) == 3:
395
377
396 # Selector can be a positional arg if it's the 3rd value
378 # Selector can be a positional arg if it's the 3rd value
397 if len(args) == 3:
379 if len(args) == 3:
398 selector = args[2]
380 selector = args[2]
399 if selector not in self._css:
381 if selector not in self._css:
400 self._css[selector] = {}
382 self._css[selector] = {}
401
383
402 # Only update the property if it has changed.
384 # Only update the property if it has changed.
403 key = args[0]
385 key = args[0]
404 value = args[1]
386 value = args[1]
405 if not (key in self._css[selector] and value == self._css[selector][key]):
387 if not (key in self._css[selector] and value == self._css[selector][key]):
406 self._css[selector][key] = value
388 self._css[selector][key] = value
407 self.send_state('_css') # Send new state to client.
389 self.send_state('_css') # Send new state to client.
408 else:
390 else:
409 raise Exception('set_css only accepts 1-3 arguments')
391 raise Exception('set_css only accepts 1-3 arguments')
410
392
411
412 def add_class(self, class_names, selector=""):
393 def add_class(self, class_names, selector=""):
413 """Add class[es] to a DOM element
394 """Add class[es] to a DOM element.
414
395
415 Parameters
396 Parameters
416 ----------
397 ----------
417 class_names: unicode or list
398 class_names: unicode or list
418 Class name(s) to add to the DOM element(s).
399 Class name(s) to add to the DOM element(s).
419 selector: unicode (optional)
400 selector: unicode (optional)
420 JQuery selector to select the DOM element(s) that the class(es) will
401 JQuery selector to select the DOM element(s) that the class(es) will
421 be added to.
402 be added to.
422 """
403 """
423 class_list = class_names
404 class_list = class_names
424 if isinstance(class_list, list):
405 if isinstance(class_list, list):
425 class_list = ' '.join(class_list)
406 class_list = ' '.join(class_list)
426
407
427 self.send({"msg_type": "add_class",
408 self.send({"msg_type": "add_class",
428 "class_list": class_list,
409 "class_list": class_list,
429 "selector": selector})
410 "selector": selector})
430
411
431
432 def remove_class(self, class_names, selector=""):
412 def remove_class(self, class_names, selector=""):
433 """Remove class[es] from a DOM element
413 """Remove class[es] from a DOM element.
434
414
435 Parameters
415 Parameters
436 ----------
416 ----------
437 class_names: unicode or list
417 class_names: unicode or list
438 Class name(s) to remove from the DOM element(s).
418 Class name(s) to remove from the DOM element(s).
439 selector: unicode (optional)
419 selector: unicode (optional)
440 JQuery selector to select the DOM element(s) that the class(es) will
420 JQuery selector to select the DOM element(s) that the class(es) will
441 be removed from.
421 be removed from.
442 """
422 """
443 class_list = class_names
423 class_list = class_names
444 if isinstance(class_list, list):
424 if isinstance(class_list, list):
445 class_list = ' '.join(class_list)
425 class_list = ' '.join(class_list)
446
426
447 self.send({"msg_type": "remove_class",
427 self.send({"msg_type": "remove_class",
448 "class_list": class_list,
428 "class_list": class_list,
449 "selector": selector})
429 "selector": selector})
@@ -1,32 +1,33 b''
1 """BoolWidget class.
1 """BoolWidget class.
2
2
3 Represents a boolean using a widget.
3 Represents a boolean using a widget.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from .widget import DOMWidget
16 from .widget import DOMWidget
17 from IPython.utils.traitlets import Unicode, Bool, List
17 from IPython.utils.traitlets import Unicode, Bool, List
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Classes
20 # Classes
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 class CheckBoxWidget(DOMWidget):
22 class CheckBoxWidget(DOMWidget):
23 view_name = Unicode('CheckBoxView', sync=True)
23 view_name = Unicode('CheckBoxView', sync=True)
24
24
25 # Model Keys
25 # Model Keys
26 value = Bool(False, help="Bool value", sync=True)
26 value = Bool(False, help="Bool value", sync=True)
27 description = Unicode('', help="Description of the boolean (label).", sync=True)
27 description = Unicode('', help="Description of the boolean (label).", sync=True)
28 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
28 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
29
29
30
30 class ToggleButtonWidget(CheckBoxWidget):
31 class ToggleButtonWidget(CheckBoxWidget):
31 view_name = Unicode('ToggleButtonView', sync=True)
32 view_name = Unicode('ToggleButtonView', sync=True)
32 No newline at end of file
33
@@ -1,91 +1,88 b''
1 """ButtonWidget class.
1 """ButtonWidget class.
2
2
3 Represents a button in the frontend using a widget. Allows user to listen for
3 Represents a button in the frontend using a widget. Allows user to listen for
4 click events on the button and trigger backend code when the clicks are fired.
4 click events on the button and trigger backend code when the clicks are fired.
5 """
5 """
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (c) 2013, the IPython Development Team.
7 # Copyright (c) 2013, the IPython Development Team.
8 #
8 #
9 # Distributed under the terms of the Modified BSD License.
9 # Distributed under the terms of the Modified BSD License.
10 #
10 #
11 # The full license is in the file COPYING.txt, distributed with this software.
11 # The full license is in the file COPYING.txt, distributed with this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 import inspect
17 import inspect
18 import types
18 import types
19
19
20 from .widget import DOMWidget
20 from .widget import DOMWidget
21 from IPython.utils.traitlets import Unicode, Bool
21 from IPython.utils.traitlets import Unicode, Bool
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Classes
24 # Classes
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 class ButtonWidget(DOMWidget):
26 class ButtonWidget(DOMWidget):
27 view_name = Unicode('ButtonView', sync=True)
27 view_name = Unicode('ButtonView', sync=True)
28
28
29 # Keys
29 # Keys
30 description = Unicode('', help="Description of the button (label).", sync=True)
30 description = Unicode('', help="Description of the button (label).", sync=True)
31 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
31 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
32
32
33
34 def __init__(self, **kwargs):
33 def __init__(self, **kwargs):
34 """Constructor"""
35 super(ButtonWidget, self).__init__(**kwargs)
35 super(ButtonWidget, self).__init__(**kwargs)
36
36
37 self._click_handlers = []
37 self._click_handlers = []
38 self.on_msg(self._handle_button_msg)
38 self.on_msg(self._handle_button_msg)
39
39
40
41 def on_click(self, callback, remove=False):
40 def on_click(self, callback, remove=False):
42 """Register a callback to execute when the button is clicked. The
41 """Register a callback to execute when the button is clicked.
43 callback can either accept no parameters or one sender parameter:
42
43 The callback can either accept no parameters or one sender parameter:
44 - callback()
44 - callback()
45 - callback(sender)
45 - callback(sender)
46 If the callback has a sender parameter, the ButtonWidget instance that
46 If the callback has a sender parameter, the ButtonWidget instance that
47 called the callback will be passed into the method as the sender.
47 called the callback will be passed into the method as the sender.
48
48
49 Parameters
49 Parameters
50 ----------
50 ----------
51 remove : bool (optional)
51 remove : bool (optional)
52 Set to true to remove the callback from the list of callbacks."""
52 Set to true to remove the callback from the list of callbacks."""
53 if remove:
53 if remove:
54 self._click_handlers.remove(callback)
54 self._click_handlers.remove(callback)
55 elif not callback in self._click_handlers:
55 elif not callback in self._click_handlers:
56 self._click_handlers.append(callback)
56 self._click_handlers.append(callback)
57
57
58
59 def _handle_button_msg(self, content):
58 def _handle_button_msg(self, content):
60 """Handle a msg from the front-end
59 """Handle a msg from the front-end.
61
60
62 Parameters
61 Parameters
63 ----------
62 ----------
64 content: dict
63 content: dict
65 Content of the msg."""
64 Content of the msg."""
66 if 'event' in content and content['event'] == 'click':
65 if 'event' in content and content['event'] == 'click':
67 self._handle_click()
66 self._handle_click()
68
67
69
70 def _handle_click(self):
68 def _handle_click(self):
71 """Handles when the button has been clicked. Fires on_click
69 """Handles when the button has been clicked.
72 callbacks when appropriate."""
73
70
71 Fires on_click callbacks when appropriate."""
74 for handler in self._click_handlers:
72 for handler in self._click_handlers:
75 if callable(handler):
73 if callable(handler):
76 argspec = inspect.getargspec(handler)
74 argspec = inspect.getargspec(handler)
77 nargs = len(argspec[0])
75 nargs = len(argspec[0])
78
76
79 # Bound methods have an additional 'self' argument
77 # Bound methods have an additional 'self' argument
80 if isinstance(handler, types.MethodType):
78 if isinstance(handler, types.MethodType):
81 nargs -= 1
79 nargs -= 1
82
80
83 # Call the callback
81 # Call the callback
84 if nargs == 0:
82 if nargs == 0:
85 handler()
83 handler()
86 elif nargs == 1:
84 elif nargs == 1:
87 handler(self)
85 handler(self)
88 else:
86 else:
89 raise TypeError('ButtonWidget click callback must ' \
87 raise TypeError('ButtonWidget click callback must ' \
90 'accept 0 or 1 arguments.')
88 'accept 0 or 1 arguments.')
91
@@ -1,33 +1,34 b''
1 """ContainerWidget class.
1 """ContainerWidget class.
2
2
3 Represents a container that can be used to group other widgets.
3 Represents a container that can be used to group other widgets.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from .widget import DOMWidget
16 from .widget import DOMWidget
17 from IPython.utils.traitlets import Unicode, Bool, List, Instance
17 from IPython.utils.traitlets import Unicode, Bool, List, Instance
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Classes
20 # Classes
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 class ContainerWidget(DOMWidget):
22 class ContainerWidget(DOMWidget):
23 view_name = Unicode('ContainerView', sync=True)
23 view_name = Unicode('ContainerView', sync=True)
24
24
25 # Keys, all private and managed by helper methods. Flexible box model
25 # Keys, all private and managed by helper methods. Flexible box model
26 # classes...
26 # classes...
27 children = List(Instance(DOMWidget), sync=True)
27 children = List(Instance(DOMWidget), sync=True)
28
28
29 description = Unicode(sync=True)
29 description = Unicode(sync=True)
30 button_text = Unicode(sync=True)
30 button_text = Unicode(sync=True)
31
31
32
32 class ModalWidget(ContainerWidget):
33 class ModalWidget(ContainerWidget):
33 view_name = Unicode('ModalView', sync=True)
34 view_name = Unicode('ModalView', sync=True)
@@ -1,49 +1,49 b''
1 """FloatRangeWidget class.
1 """FloatRangeWidget class.
2
2
3 Represents a bounded float using a widget.
3 Represents a bounded float using a widget.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from .widget import DOMWidget
16 from .widget import DOMWidget
17 from IPython.utils.traitlets import Unicode, CFloat, Bool, List, Enum
17 from IPython.utils.traitlets import Unicode, CFloat, Bool, List, Enum
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Classes
20 # Classes
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 class BoundedFloatTextWidget(DOMWidget):
22 class BoundedFloatTextWidget(DOMWidget):
23 view_name = Unicode('FloatTextView', sync=True)
23 view_name = Unicode('FloatTextView', sync=True)
24 value = CFloat(0.0, help="Float value", sync=True)
24 value = CFloat(0.0, help="Float value", sync=True)
25 max = CFloat(100.0, help="Max value", sync=True)
25 max = CFloat(100.0, help="Max value", sync=True)
26 min = CFloat(0.0, help="Min value", sync=True)
26 min = CFloat(0.0, help="Min value", sync=True)
27 disabled = Bool(False, help="Enable or disable user changes", sync=True)
27 disabled = Bool(False, help="Enable or disable user changes", sync=True)
28 step = CFloat(0.1, help="Minimum step that the value can take (ignored by some views)", sync=True)
28 step = CFloat(0.1, help="Minimum step that the value can take (ignored by some views)", sync=True)
29 description = Unicode(help="Description of the value this widget represents", sync=True)
29 description = Unicode(help="Description of the value this widget represents", sync=True)
30
30
31 def __init__(self, *pargs, **kwargs):
31 def __init__(self, *pargs, **kwargs):
32 """Constructor"""
32 """Constructor"""
33 DOMWidget.__init__(self, *pargs, **kwargs)
33 DOMWidget.__init__(self, *pargs, **kwargs)
34 self.on_trait_change(self._validate, ['value', 'min', 'max'])
34 self.on_trait_change(self._validate, ['value', 'min', 'max'])
35
35
36 def _validate(self, name, old, new):
36 def _validate(self, name, old, new):
37 """Validate value, max, min"""
37 """Validate value, max, min."""
38 if self.min > new or new > self.max:
38 if self.min > new or new > self.max:
39 self.value = min(max(new, self.min), self.max)
39 self.value = min(max(new, self.min), self.max)
40
40
41
41
42 class FloatSliderWidget(BoundedFloatTextWidget):
42 class FloatSliderWidget(BoundedFloatTextWidget):
43 view_name = Unicode('FloatSliderView', sync=True)
43 view_name = Unicode('FloatSliderView', sync=True)
44 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
44 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
45 help="Vertical or horizontal.", sync=True)
45 help="Vertical or horizontal.", sync=True)
46
46
47
47
48 class FloatProgressWidget(BoundedFloatTextWidget):
48 class FloatProgressWidget(BoundedFloatTextWidget):
49 view_name = Unicode('ProgressView', sync=True)
49 view_name = Unicode('ProgressView', sync=True)
@@ -1,51 +1,51 b''
1 """IntRangeWidget class.
1 """IntRangeWidget class.
2
2
3 Represents a bounded int using a widget.
3 Represents a bounded int using a widget.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from .widget import DOMWidget
16 from .widget import DOMWidget
17 from IPython.utils.traitlets import Unicode, CInt, Bool, List, Enum
17 from IPython.utils.traitlets import Unicode, CInt, Bool, List, Enum
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Classes
20 # Classes
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 class BoundedIntTextWidget(DOMWidget):
22 class BoundedIntTextWidget(DOMWidget):
23 view_name = Unicode('IntTextView', sync=True)
23 view_name = Unicode('IntTextView', sync=True)
24
24
25 # Keys
25 # Keys
26 value = CInt(0, help="Int value", sync=True)
26 value = CInt(0, help="Int value", sync=True)
27 max = CInt(100, help="Max value", sync=True)
27 max = CInt(100, help="Max value", sync=True)
28 min = CInt(0, help="Min value", sync=True)
28 min = CInt(0, help="Min value", sync=True)
29 disabled = Bool(False, help="Enable or disable user changes", sync=True)
29 disabled = Bool(False, help="Enable or disable user changes", sync=True)
30 step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
30 step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
31 description = Unicode(help="Description of the value this widget represents", sync=True)
31 description = Unicode(help="Description of the value this widget represents", sync=True)
32
32
33 def __init__(self, *pargs, **kwargs):
33 def __init__(self, *pargs, **kwargs):
34 """Constructor"""
34 """Constructor"""
35 DOMWidget.__init__(self, *pargs, **kwargs)
35 DOMWidget.__init__(self, *pargs, **kwargs)
36 self.on_trait_change(self._validate, ['value', 'min', 'max'])
36 self.on_trait_change(self._validate, ['value', 'min', 'max'])
37
37
38 def _validate(self, name, old, new):
38 def _validate(self, name, old, new):
39 """Validate value, max, min"""
39 """Validate value, max, min."""
40 if self.min > new or new > self.max:
40 if self.min > new or new > self.max:
41 self.value = min(max(new, self.min), self.max)
41 self.value = min(max(new, self.min), self.max)
42
42
43
43
44 class IntSliderWidget(BoundedIntTextWidget):
44 class IntSliderWidget(BoundedIntTextWidget):
45 view_name = Unicode('IntSliderView', sync=True)
45 view_name = Unicode('IntSliderView', sync=True)
46 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
46 orientation = Enum([u'horizontal', u'vertical'], u'horizontal',
47 help="Vertical or horizontal.", sync=True)
47 help="Vertical or horizontal.", sync=True)
48
48
49
49
50 class IntProgressWidget(BoundedIntTextWidget):
50 class IntProgressWidget(BoundedIntTextWidget):
51 view_name = Unicode('ProgressView', sync=True)
51 view_name = Unicode('ProgressView', sync=True)
@@ -1,60 +1,59 b''
1 """SelectionContainerWidget class.
1 """SelectionContainerWidget class.
2
2
3 Represents a multipage container that can be used to group other widgets into
3 Represents a multipage container that can be used to group other widgets into
4 pages.
4 pages.
5 """
5 """
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (c) 2013, the IPython Development Team.
7 # Copyright (c) 2013, the IPython Development Team.
8 #
8 #
9 # Distributed under the terms of the Modified BSD License.
9 # Distributed under the terms of the Modified BSD License.
10 #
10 #
11 # The full license is in the file COPYING.txt, distributed with this software.
11 # The full license is in the file COPYING.txt, distributed with this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 from .widget import DOMWidget
17 from .widget import DOMWidget
18 from IPython.utils.traitlets import Unicode, Dict, CInt, List, Instance
18 from IPython.utils.traitlets import Unicode, Dict, CInt, List, Instance
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Classes
21 # Classes
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 class AccordionWidget(DOMWidget):
23 class AccordionWidget(DOMWidget):
24 view_name = Unicode('AccordionView', sync=True)
24 view_name = Unicode('AccordionView', sync=True)
25
25
26 # Keys
26 # Keys
27 _titles = Dict(help="Titles of the pages", sync=True)
27 _titles = Dict(help="Titles of the pages", sync=True)
28 selected_index = CInt(0, sync=True)
28 selected_index = CInt(0, sync=True)
29
29
30 children = List(Instance(DOMWidget), sync=True)
30 children = List(Instance(DOMWidget), sync=True)
31
31
32 # Public methods
32 # Public methods
33 def set_title(self, index, title):
33 def set_title(self, index, title):
34 """Sets the title of a container page
34 """Sets the title of a container page.
35
35
36 Parameters
36 Parameters
37 ----------
37 ----------
38 index : int
38 index : int
39 Index of the container page
39 Index of the container page
40 title : unicode
40 title : unicode
41 New title"""
41 New title"""
42 self._titles[index] = title
42 self._titles[index] = title
43 self.send_state('_titles')
43 self.send_state('_titles')
44
44
45
46 def get_title(self, index):
45 def get_title(self, index):
47 """Gets the title of a container pages
46 """Gets the title of a container pages.
48
47
49 Parameters
48 Parameters
50 ----------
49 ----------
51 index : int
50 index : int
52 Index of the container page"""
51 Index of the container page"""
53 if index in self._titles:
52 if index in self._titles:
54 return self._titles[index]
53 return self._titles[index]
55 else:
54 else:
56 return None
55 return None
57
56
58
57
59 class TabWidget(AccordionWidget):
58 class TabWidget(AccordionWidget):
60 view_name = Unicode('TabView', sync=True)
59 view_name = Unicode('TabView', sync=True)
@@ -1,94 +1,95 b''
1 """StringWidget class.
1 """StringWidget class.
2
2
3 Represents a unicode string using a widget.
3 Represents a unicode string using a widget.
4 """
4 """
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
6 # Copyright (c) 2013, the IPython Development Team.
7 #
7 #
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9 #
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 import inspect
16 import inspect
17 import types
17 import types
18
18
19 from .widget import DOMWidget
19 from .widget import DOMWidget
20 from IPython.utils.traitlets import Unicode, Bool, List
20 from IPython.utils.traitlets import Unicode, Bool, List
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Classes
23 # Classes
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 class HTMLWidget(DOMWidget):
25 class HTMLWidget(DOMWidget):
26 view_name = Unicode('HTMLView', sync=True)
26 view_name = Unicode('HTMLView', sync=True)
27
27
28 # Keys
28 # Keys
29 value = Unicode(help="String value", sync=True)
29 value = Unicode(help="String value", sync=True)
30 disabled = Bool(False, help="Enable or disable user changes", sync=True)
30 disabled = Bool(False, help="Enable or disable user changes", sync=True)
31 description = Unicode(help="Description of the value this widget represents", sync=True)
31 description = Unicode(help="Description of the value this widget represents", sync=True)
32
32
33
33
34 class LatexWidget(HTMLWidget):
34 class LatexWidget(HTMLWidget):
35 view_name = Unicode('LatexView', sync=True)
35 view_name = Unicode('LatexView', sync=True)
36
36
37
37
38 class TextAreaWidget(HTMLWidget):
38 class TextAreaWidget(HTMLWidget):
39 view_name = Unicode('TextAreaView', sync=True)
39 view_name = Unicode('TextAreaView', sync=True)
40
40
41 def scroll_to_bottom(self):
41 def scroll_to_bottom(self):
42 self.send({"method": "scroll_to_bottom"})
42 self.send({"method": "scroll_to_bottom"})
43
43
44
44
45 class TextBoxWidget(HTMLWidget):
45 class TextBoxWidget(HTMLWidget):
46 view_name = Unicode('TextBoxView', sync=True)
46 view_name = Unicode('TextBoxView', sync=True)
47
47
48 def __init__(self, **kwargs):
48 def __init__(self, **kwargs):
49 super(TextBoxWidget, self).__init__(**kwargs)
49 super(TextBoxWidget, self).__init__(**kwargs)
50 self._submission_callbacks = []
50 self._submission_callbacks = []
51 self.on_msg(self._handle_string_msg)
51 self.on_msg(self._handle_string_msg)
52
52
53 def _handle_string_msg(self, content):
53 def _handle_string_msg(self, content):
54 """Handle a msg from the front-end
54 """Handle a msg from the front-end.
55
55
56 Parameters
56 Parameters
57 ----------
57 ----------
58 content: dict
58 content: dict
59 Content of the msg."""
59 Content of the msg."""
60 if 'event' in content and content['event'] == 'submit':
60 if 'event' in content and content['event'] == 'submit':
61 for handler in self._submission_callbacks:
61 for handler in self._submission_callbacks:
62 handler(self)
62 handler(self)
63
63
64 def on_submit(self, callback, remove=False):
64 def on_submit(self, callback, remove=False):
65 """Register a callback to handle text submission (triggered when the
65 """(Un)Register a callback to handle text submission.
66 user clicks enter).
66
67 Triggered when the user clicks enter.
67
68
68 Parameters
69 Parameters
69 callback: Method handle
70 callback: Method handle
70 Function to be called when the text has been submitted. Function
71 Function to be called when the text has been submitted. Function
71 can have two possible signatures:
72 can have two possible signatures:
72 callback()
73 callback()
73 callback(sender)
74 callback(sender)
74 remove: bool (optional)
75 remove: bool (optional)
75 Whether or not to unregister the callback"""
76 Whether or not to unregister the callback"""
76 if remove and callback in self._submission_callbacks:
77 if remove and callback in self._submission_callbacks:
77 self._submission_callbacks.remove(callback)
78 self._submission_callbacks.remove(callback)
78 elif not remove and not callback in self._submission_callbacks:
79 elif not remove and not callback in self._submission_callbacks:
79 if callable(callback):
80 if callable(callback):
80 argspec = inspect.getargspec(callback)
81 argspec = inspect.getargspec(callback)
81 nargs = len(argspec[0])
82 nargs = len(argspec[0])
82
83
83 # Bound methods have an additional 'self' argument
84 # Bound methods have an additional 'self' argument
84 if isinstance(callback, types.MethodType):
85 if isinstance(callback, types.MethodType):
85 nargs -= 1
86 nargs -= 1
86
87
87 # Call the callback
88 # Call the callback
88 if nargs == 0:
89 if nargs == 0:
89 self._submission_callbacks.append(lambda sender: callback())
90 self._submission_callbacks.append(lambda sender: callback())
90 elif nargs == 1:
91 elif nargs == 1:
91 self._submission_callbacks.append(callback)
92 self._submission_callbacks.append(callback)
92 else:
93 else:
93 raise TypeError('TextBoxWidget submit callback must ' \
94 raise TypeError('TextBoxWidget submit callback must ' \
94 'accept 0 or 1 arguments.')
95 'accept 0 or 1 arguments.')
General Comments 0
You need to be logged in to leave comments. Login now