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