##// END OF EJS Templates
Merge pull request #6493 from SylvainCorlay/python_widget_registry...
Thomas Kluyver -
r18544:3801ab3d merge
parent child Browse files
Show More
@@ -1,23 +1,23 b''
1 from .widget import Widget, DOMWidget, CallbackDispatcher
1 from .widget import Widget, DOMWidget, CallbackDispatcher, register
2
2
3 from .widget_bool import Checkbox, ToggleButton
3 from .widget_bool import Checkbox, ToggleButton
4 from .widget_button import Button
4 from .widget_button import Button
5 from .widget_box import Box, Popup, FlexBox, HBox, VBox
5 from .widget_box import Box, Popup, FlexBox, HBox, VBox
6 from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider
6 from .widget_float import FloatText, BoundedFloatText, FloatSlider, FloatProgress, FloatRangeSlider
7 from .widget_image import Image
7 from .widget_image import Image
8 from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider
8 from .widget_int import IntText, BoundedIntText, IntSlider, IntProgress, IntRangeSlider
9 from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select
9 from .widget_selection import RadioButtons, ToggleButtons, Dropdown, Select
10 from .widget_selectioncontainer import Tab, Accordion
10 from .widget_selectioncontainer import Tab, Accordion
11 from .widget_string import HTML, Latex, Text, Textarea
11 from .widget_string import HTML, Latex, Text, Textarea
12 from .interaction import interact, interactive, fixed, interact_manual
12 from .interaction import interact, interactive, fixed, interact_manual
13
13
14 # Deprecated classes
14 # Deprecated classes
15 from .widget_bool import CheckboxWidget, ToggleButtonWidget
15 from .widget_bool import CheckboxWidget, ToggleButtonWidget
16 from .widget_button import ButtonWidget
16 from .widget_button import ButtonWidget
17 from .widget_box import ContainerWidget, PopupWidget
17 from .widget_box import ContainerWidget, PopupWidget
18 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
18 from .widget_float import FloatTextWidget, BoundedFloatTextWidget, FloatSliderWidget, FloatProgressWidget
19 from .widget_image import ImageWidget
19 from .widget_image import ImageWidget
20 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
20 from .widget_int import IntTextWidget, BoundedIntTextWidget, IntSliderWidget, IntProgressWidget
21 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
21 from .widget_selection import RadioButtonsWidget, ToggleButtonsWidget, DropdownWidget, SelectWidget
22 from .widget_selectioncontainer import TabWidget, AccordionWidget
22 from .widget_selectioncontainer import TabWidget, AccordionWidget
23 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
23 from .widget_string import HTMLWidget, LatexWidget, TextWidget, TextareaWidget
@@ -1,466 +1,480 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 collections
16 import collections
17
17
18 from IPython.core.getipython import get_ipython
18 from IPython.core.getipython import get_ipython
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.importstring import import_item
21 from IPython.utils.importstring import import_item
22 from IPython.utils.traitlets import Unicode, Dict, Instance, Bool, List, \
22 from IPython.utils.traitlets import Unicode, Dict, Instance, Bool, List, \
23 CaselessStrEnum, Tuple, CUnicode, Int, Set
23 CaselessStrEnum, Tuple, CUnicode, Int, Set
24 from IPython.utils.py3compat import string_types
24 from IPython.utils.py3compat import string_types
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Classes
27 # Classes
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 class CallbackDispatcher(LoggingConfigurable):
29 class CallbackDispatcher(LoggingConfigurable):
30 """A structure for registering and running callbacks"""
30 """A structure for registering and running callbacks"""
31 callbacks = List()
31 callbacks = List()
32
32
33 def __call__(self, *args, **kwargs):
33 def __call__(self, *args, **kwargs):
34 """Call all of the registered callbacks."""
34 """Call all of the registered callbacks."""
35 value = None
35 value = None
36 for callback in self.callbacks:
36 for callback in self.callbacks:
37 try:
37 try:
38 local_value = callback(*args, **kwargs)
38 local_value = callback(*args, **kwargs)
39 except Exception as e:
39 except Exception as e:
40 ip = get_ipython()
40 ip = get_ipython()
41 if ip is None:
41 if ip is None:
42 self.log.warn("Exception in callback %s: %s", callback, e, exc_info=True)
42 self.log.warn("Exception in callback %s: %s", callback, e, exc_info=True)
43 else:
43 else:
44 ip.showtraceback()
44 ip.showtraceback()
45 else:
45 else:
46 value = local_value if local_value is not None else value
46 value = local_value if local_value is not None else value
47 return value
47 return value
48
48
49 def register_callback(self, callback, remove=False):
49 def register_callback(self, callback, remove=False):
50 """(Un)Register a callback
50 """(Un)Register a callback
51
51
52 Parameters
52 Parameters
53 ----------
53 ----------
54 callback: method handle
54 callback: method handle
55 Method to be registered or unregistered.
55 Method to be registered or unregistered.
56 remove=False: bool
56 remove=False: bool
57 Whether to unregister the callback."""
57 Whether to unregister the callback."""
58
58
59 # (Un)Register the callback.
59 # (Un)Register the callback.
60 if remove and callback in self.callbacks:
60 if remove and callback in self.callbacks:
61 self.callbacks.remove(callback)
61 self.callbacks.remove(callback)
62 elif not remove and callback not in self.callbacks:
62 elif not remove and callback not in self.callbacks:
63 self.callbacks.append(callback)
63 self.callbacks.append(callback)
64
64
65 def _show_traceback(method):
65 def _show_traceback(method):
66 """decorator for showing tracebacks in IPython"""
66 """decorator for showing tracebacks in IPython"""
67 def m(self, *args, **kwargs):
67 def m(self, *args, **kwargs):
68 try:
68 try:
69 return(method(self, *args, **kwargs))
69 return(method(self, *args, **kwargs))
70 except Exception as e:
70 except Exception as e:
71 ip = get_ipython()
71 ip = get_ipython()
72 if ip is None:
72 if ip is None:
73 self.log.warn("Exception in widget method %s: %s", method, e, exc_info=True)
73 self.log.warn("Exception in widget method %s: %s", method, e, exc_info=True)
74 else:
74 else:
75 ip.showtraceback()
75 ip.showtraceback()
76 return m
76 return m
77
77
78
79 def register(key=None):
80 """Returns a decorator registering a widget class in the widget registry.
81 If no key is provided, the class name is used as a key. A key is
82 provided for each core IPython widget so that the frontend can use
83 this key regardless of the language of the kernel"""
84 def wrap(widget):
85 l = key if key is not None else widget.__module__ + widget.__name__
86 Widget.widget_types[l] = widget
87 return widget
88 return wrap
89
90
78 class Widget(LoggingConfigurable):
91 class Widget(LoggingConfigurable):
79 #-------------------------------------------------------------------------
92 #-------------------------------------------------------------------------
80 # Class attributes
93 # Class attributes
81 #-------------------------------------------------------------------------
94 #-------------------------------------------------------------------------
82 _widget_construction_callback = None
95 _widget_construction_callback = None
83 widgets = {}
96 widgets = {}
97 widget_types = {}
84
98
85 @staticmethod
99 @staticmethod
86 def on_widget_constructed(callback):
100 def on_widget_constructed(callback):
87 """Registers a callback to be called when a widget is constructed.
101 """Registers a callback to be called when a widget is constructed.
88
102
89 The callback must have the following signature:
103 The callback must have the following signature:
90 callback(widget)"""
104 callback(widget)"""
91 Widget._widget_construction_callback = callback
105 Widget._widget_construction_callback = callback
92
106
93 @staticmethod
107 @staticmethod
94 def _call_widget_constructed(widget):
108 def _call_widget_constructed(widget):
95 """Static method, called when a widget is constructed."""
109 """Static method, called when a widget is constructed."""
96 if Widget._widget_construction_callback is not None and callable(Widget._widget_construction_callback):
110 if Widget._widget_construction_callback is not None and callable(Widget._widget_construction_callback):
97 Widget._widget_construction_callback(widget)
111 Widget._widget_construction_callback(widget)
98
112
99 @staticmethod
113 @staticmethod
100 def handle_comm_opened(comm, msg):
114 def handle_comm_opened(comm, msg):
101 """Static method, called when a widget is constructed."""
115 """Static method, called when a widget is constructed."""
102 widget_class = import_item(msg['content']['data']['widget_class'])
116 widget_class = import_item(msg['content']['data']['widget_class'])
103 widget = widget_class(comm=comm)
117 widget = widget_class(comm=comm)
104
118
105
119
106 #-------------------------------------------------------------------------
120 #-------------------------------------------------------------------------
107 # Traits
121 # Traits
108 #-------------------------------------------------------------------------
122 #-------------------------------------------------------------------------
109 _model_module = Unicode(None, allow_none=True, help="""A requirejs module name
123 _model_module = Unicode(None, allow_none=True, help="""A requirejs module name
110 in which to find _model_name. If empty, look in the global registry.""")
124 in which to find _model_name. If empty, look in the global registry.""")
111 _model_name = Unicode('WidgetModel', help="""Name of the backbone model
125 _model_name = Unicode('WidgetModel', help="""Name of the backbone model
112 registered in the front-end to create and sync this widget with.""")
126 registered in the front-end to create and sync this widget with.""")
113 _view_module = Unicode(help="""A requirejs module in which to find _view_name.
127 _view_module = Unicode(help="""A requirejs module in which to find _view_name.
114 If empty, look in the global registry.""", sync=True)
128 If empty, look in the global registry.""", sync=True)
115 _view_name = Unicode(None, allow_none=True, help="""Default view registered in the front-end
129 _view_name = Unicode(None, allow_none=True, help="""Default view registered in the front-end
116 to use to represent the widget.""", sync=True)
130 to use to represent the widget.""", sync=True)
117 comm = Instance('IPython.kernel.comm.Comm')
131 comm = Instance('IPython.kernel.comm.Comm')
118
132
119 msg_throttle = Int(3, sync=True, help="""Maximum number of msgs the
133 msg_throttle = Int(3, sync=True, help="""Maximum number of msgs the
120 front-end can send before receiving an idle msg from the back-end.""")
134 front-end can send before receiving an idle msg from the back-end.""")
121
135
122 version = Int(0, sync=True, help="""Widget's version""")
136 version = Int(0, sync=True, help="""Widget's version""")
123 keys = List()
137 keys = List()
124 def _keys_default(self):
138 def _keys_default(self):
125 return [name for name in self.traits(sync=True)]
139 return [name for name in self.traits(sync=True)]
126
140
127 _property_lock = Tuple((None, None))
141 _property_lock = Tuple((None, None))
128 _send_state_lock = Int(0)
142 _send_state_lock = Int(0)
129 _states_to_send = Set(allow_none=False)
143 _states_to_send = Set(allow_none=False)
130 _display_callbacks = Instance(CallbackDispatcher, ())
144 _display_callbacks = Instance(CallbackDispatcher, ())
131 _msg_callbacks = Instance(CallbackDispatcher, ())
145 _msg_callbacks = Instance(CallbackDispatcher, ())
132
146
133 #-------------------------------------------------------------------------
147 #-------------------------------------------------------------------------
134 # (Con/de)structor
148 # (Con/de)structor
135 #-------------------------------------------------------------------------
149 #-------------------------------------------------------------------------
136 def __init__(self, **kwargs):
150 def __init__(self, **kwargs):
137 """Public constructor"""
151 """Public constructor"""
138 self._model_id = kwargs.pop('model_id', None)
152 self._model_id = kwargs.pop('model_id', None)
139 super(Widget, self).__init__(**kwargs)
153 super(Widget, self).__init__(**kwargs)
140
154
141 Widget._call_widget_constructed(self)
155 Widget._call_widget_constructed(self)
142 self.open()
156 self.open()
143
157
144 def __del__(self):
158 def __del__(self):
145 """Object disposal"""
159 """Object disposal"""
146 self.close()
160 self.close()
147
161
148 #-------------------------------------------------------------------------
162 #-------------------------------------------------------------------------
149 # Properties
163 # Properties
150 #-------------------------------------------------------------------------
164 #-------------------------------------------------------------------------
151
165
152 def open(self):
166 def open(self):
153 """Open a comm to the frontend if one isn't already open."""
167 """Open a comm to the frontend if one isn't already open."""
154 if self.comm is None:
168 if self.comm is None:
155 args = dict(target_name='ipython.widget',
169 args = dict(target_name='ipython.widget',
156 data={'model_name': self._model_name,
170 data={'model_name': self._model_name,
157 'model_module': self._model_module})
171 'model_module': self._model_module})
158 if self._model_id is not None:
172 if self._model_id is not None:
159 args['comm_id'] = self._model_id
173 args['comm_id'] = self._model_id
160 self.comm = Comm(**args)
174 self.comm = Comm(**args)
161
175
162 def _comm_changed(self, name, new):
176 def _comm_changed(self, name, new):
163 """Called when the comm is changed."""
177 """Called when the comm is changed."""
164 self.comm = new
178 self.comm = new
165 self._model_id = self.model_id
179 self._model_id = self.model_id
166
180
167 self.comm.on_msg(self._handle_msg)
181 self.comm.on_msg(self._handle_msg)
168 Widget.widgets[self.model_id] = self
182 Widget.widgets[self.model_id] = self
169
183
170 # first update
184 # first update
171 self.send_state()
185 self.send_state()
172
186
173 @property
187 @property
174 def model_id(self):
188 def model_id(self):
175 """Gets the model id of this widget.
189 """Gets the model id of this widget.
176
190
177 If a Comm doesn't exist yet, a Comm will be created automagically."""
191 If a Comm doesn't exist yet, a Comm will be created automagically."""
178 return self.comm.comm_id
192 return self.comm.comm_id
179
193
180 #-------------------------------------------------------------------------
194 #-------------------------------------------------------------------------
181 # Methods
195 # Methods
182 #-------------------------------------------------------------------------
196 #-------------------------------------------------------------------------
183
197
184 def close(self):
198 def close(self):
185 """Close method.
199 """Close method.
186
200
187 Closes the underlying comm.
201 Closes the underlying comm.
188 When the comm is closed, all of the widget views are automatically
202 When the comm is closed, all of the widget views are automatically
189 removed from the front-end."""
203 removed from the front-end."""
190 if self.comm is not None:
204 if self.comm is not None:
191 Widget.widgets.pop(self.model_id, None)
205 Widget.widgets.pop(self.model_id, None)
192 self.comm.close()
206 self.comm.close()
193 self.comm = None
207 self.comm = None
194
208
195 def send_state(self, key=None):
209 def send_state(self, key=None):
196 """Sends the widget state, or a piece of it, to the front-end.
210 """Sends the widget state, or a piece of it, to the front-end.
197
211
198 Parameters
212 Parameters
199 ----------
213 ----------
200 key : unicode, or iterable (optional)
214 key : unicode, or iterable (optional)
201 A single property's name or iterable of property names to sync with the front-end.
215 A single property's name or iterable of property names to sync with the front-end.
202 """
216 """
203 self._send({
217 self._send({
204 "method" : "update",
218 "method" : "update",
205 "state" : self.get_state(key=key)
219 "state" : self.get_state(key=key)
206 })
220 })
207
221
208 def get_state(self, key=None):
222 def get_state(self, key=None):
209 """Gets the widget state, or a piece of it.
223 """Gets the widget state, or a piece of it.
210
224
211 Parameters
225 Parameters
212 ----------
226 ----------
213 key : unicode or iterable (optional)
227 key : unicode or iterable (optional)
214 A single property's name or iterable of property names to get.
228 A single property's name or iterable of property names to get.
215 """
229 """
216 if key is None:
230 if key is None:
217 keys = self.keys
231 keys = self.keys
218 elif isinstance(key, string_types):
232 elif isinstance(key, string_types):
219 keys = [key]
233 keys = [key]
220 elif isinstance(key, collections.Iterable):
234 elif isinstance(key, collections.Iterable):
221 keys = key
235 keys = key
222 else:
236 else:
223 raise ValueError("key must be a string, an iterable of keys, or None")
237 raise ValueError("key must be a string, an iterable of keys, or None")
224 state = {}
238 state = {}
225 for k in keys:
239 for k in keys:
226 f = self.trait_metadata(k, 'to_json', self._trait_to_json)
240 f = self.trait_metadata(k, 'to_json', self._trait_to_json)
227 value = getattr(self, k)
241 value = getattr(self, k)
228 state[k] = f(value)
242 state[k] = f(value)
229 return state
243 return state
230
244
231 def set_state(self, sync_data):
245 def set_state(self, sync_data):
232 """Called when a state is received from the front-end."""
246 """Called when a state is received from the front-end."""
233 for name in self.keys:
247 for name in self.keys:
234 if name in sync_data:
248 if name in sync_data:
235 json_value = sync_data[name]
249 json_value = sync_data[name]
236 from_json = self.trait_metadata(name, 'from_json', self._trait_from_json)
250 from_json = self.trait_metadata(name, 'from_json', self._trait_from_json)
237 with self._lock_property(name, json_value):
251 with self._lock_property(name, json_value):
238 setattr(self, name, from_json(json_value))
252 setattr(self, name, from_json(json_value))
239
253
240 def send(self, content):
254 def send(self, content):
241 """Sends a custom msg to the widget model in the front-end.
255 """Sends a custom msg to the widget model in the front-end.
242
256
243 Parameters
257 Parameters
244 ----------
258 ----------
245 content : dict
259 content : dict
246 Content of the message to send.
260 Content of the message to send.
247 """
261 """
248 self._send({"method": "custom", "content": content})
262 self._send({"method": "custom", "content": content})
249
263
250 def on_msg(self, callback, remove=False):
264 def on_msg(self, callback, remove=False):
251 """(Un)Register a custom msg receive callback.
265 """(Un)Register a custom msg receive callback.
252
266
253 Parameters
267 Parameters
254 ----------
268 ----------
255 callback: callable
269 callback: callable
256 callback will be passed two arguments when a message arrives::
270 callback will be passed two arguments when a message arrives::
257
271
258 callback(widget, content)
272 callback(widget, content)
259
273
260 remove: bool
274 remove: bool
261 True if the callback should be unregistered."""
275 True if the callback should be unregistered."""
262 self._msg_callbacks.register_callback(callback, remove=remove)
276 self._msg_callbacks.register_callback(callback, remove=remove)
263
277
264 def on_displayed(self, callback, remove=False):
278 def on_displayed(self, callback, remove=False):
265 """(Un)Register a widget displayed callback.
279 """(Un)Register a widget displayed callback.
266
280
267 Parameters
281 Parameters
268 ----------
282 ----------
269 callback: method handler
283 callback: method handler
270 Must have a signature of::
284 Must have a signature of::
271
285
272 callback(widget, **kwargs)
286 callback(widget, **kwargs)
273
287
274 kwargs from display are passed through without modification.
288 kwargs from display are passed through without modification.
275 remove: bool
289 remove: bool
276 True if the callback should be unregistered."""
290 True if the callback should be unregistered."""
277 self._display_callbacks.register_callback(callback, remove=remove)
291 self._display_callbacks.register_callback(callback, remove=remove)
278
292
279 #-------------------------------------------------------------------------
293 #-------------------------------------------------------------------------
280 # Support methods
294 # Support methods
281 #-------------------------------------------------------------------------
295 #-------------------------------------------------------------------------
282 @contextmanager
296 @contextmanager
283 def _lock_property(self, key, value):
297 def _lock_property(self, key, value):
284 """Lock a property-value pair.
298 """Lock a property-value pair.
285
299
286 The value should be the JSON state of the property.
300 The value should be the JSON state of the property.
287
301
288 NOTE: This, in addition to the single lock for all state changes, is
302 NOTE: This, in addition to the single lock for all state changes, is
289 flawed. In the future we may want to look into buffering state changes
303 flawed. In the future we may want to look into buffering state changes
290 back to the front-end."""
304 back to the front-end."""
291 self._property_lock = (key, value)
305 self._property_lock = (key, value)
292 try:
306 try:
293 yield
307 yield
294 finally:
308 finally:
295 self._property_lock = (None, None)
309 self._property_lock = (None, None)
296
310
297 @contextmanager
311 @contextmanager
298 def hold_sync(self):
312 def hold_sync(self):
299 """Hold syncing any state until the context manager is released"""
313 """Hold syncing any state until the context manager is released"""
300 # We increment a value so that this can be nested. Syncing will happen when
314 # We increment a value so that this can be nested. Syncing will happen when
301 # all levels have been released.
315 # all levels have been released.
302 self._send_state_lock += 1
316 self._send_state_lock += 1
303 try:
317 try:
304 yield
318 yield
305 finally:
319 finally:
306 self._send_state_lock -=1
320 self._send_state_lock -=1
307 if self._send_state_lock == 0:
321 if self._send_state_lock == 0:
308 self.send_state(self._states_to_send)
322 self.send_state(self._states_to_send)
309 self._states_to_send.clear()
323 self._states_to_send.clear()
310
324
311 def _should_send_property(self, key, value):
325 def _should_send_property(self, key, value):
312 """Check the property lock (property_lock)"""
326 """Check the property lock (property_lock)"""
313 to_json = self.trait_metadata(key, 'to_json', self._trait_to_json)
327 to_json = self.trait_metadata(key, 'to_json', self._trait_to_json)
314 if (key == self._property_lock[0]
328 if (key == self._property_lock[0]
315 and to_json(value) == self._property_lock[1]):
329 and to_json(value) == self._property_lock[1]):
316 return False
330 return False
317 elif self._send_state_lock > 0:
331 elif self._send_state_lock > 0:
318 self._states_to_send.add(key)
332 self._states_to_send.add(key)
319 return False
333 return False
320 else:
334 else:
321 return True
335 return True
322
336
323 # Event handlers
337 # Event handlers
324 @_show_traceback
338 @_show_traceback
325 def _handle_msg(self, msg):
339 def _handle_msg(self, msg):
326 """Called when a msg is received from the front-end"""
340 """Called when a msg is received from the front-end"""
327 data = msg['content']['data']
341 data = msg['content']['data']
328 method = data['method']
342 method = data['method']
329 if not method in ['backbone', 'custom']:
343 if not method in ['backbone', 'custom']:
330 self.log.error('Unknown front-end to back-end widget msg with method "%s"' % method)
344 self.log.error('Unknown front-end to back-end widget msg with method "%s"' % method)
331
345
332 # Handle backbone sync methods CREATE, PATCH, and UPDATE all in one.
346 # Handle backbone sync methods CREATE, PATCH, and UPDATE all in one.
333 if method == 'backbone' and 'sync_data' in data:
347 if method == 'backbone' and 'sync_data' in data:
334 sync_data = data['sync_data']
348 sync_data = data['sync_data']
335 self.set_state(sync_data) # handles all methods
349 self.set_state(sync_data) # handles all methods
336
350
337 # Handle a custom msg from the front-end
351 # Handle a custom msg from the front-end
338 elif method == 'custom':
352 elif method == 'custom':
339 if 'content' in data:
353 if 'content' in data:
340 self._handle_custom_msg(data['content'])
354 self._handle_custom_msg(data['content'])
341
355
342 def _handle_custom_msg(self, content):
356 def _handle_custom_msg(self, content):
343 """Called when a custom msg is received."""
357 """Called when a custom msg is received."""
344 self._msg_callbacks(self, content)
358 self._msg_callbacks(self, content)
345
359
346 def _notify_trait(self, name, old_value, new_value):
360 def _notify_trait(self, name, old_value, new_value):
347 """Called when a property has been changed."""
361 """Called when a property has been changed."""
348 # Trigger default traitlet callback machinery. This allows any user
362 # Trigger default traitlet callback machinery. This allows any user
349 # registered validation to be processed prior to allowing the widget
363 # registered validation to be processed prior to allowing the widget
350 # machinery to handle the state.
364 # machinery to handle the state.
351 LoggingConfigurable._notify_trait(self, name, old_value, new_value)
365 LoggingConfigurable._notify_trait(self, name, old_value, new_value)
352
366
353 # Send the state after the user registered callbacks for trait changes
367 # Send the state after the user registered callbacks for trait changes
354 # have all fired (allows for user to validate values).
368 # have all fired (allows for user to validate values).
355 if self.comm is not None and name in self.keys:
369 if self.comm is not None and name in self.keys:
356 # Make sure this isn't information that the front-end just sent us.
370 # Make sure this isn't information that the front-end just sent us.
357 if self._should_send_property(name, new_value):
371 if self._should_send_property(name, new_value):
358 # Send new state to front-end
372 # Send new state to front-end
359 self.send_state(key=name)
373 self.send_state(key=name)
360
374
361 def _handle_displayed(self, **kwargs):
375 def _handle_displayed(self, **kwargs):
362 """Called when a view has been displayed for this widget instance"""
376 """Called when a view has been displayed for this widget instance"""
363 self._display_callbacks(self, **kwargs)
377 self._display_callbacks(self, **kwargs)
364
378
365 def _trait_to_json(self, x):
379 def _trait_to_json(self, x):
366 """Convert a trait value to json
380 """Convert a trait value to json
367
381
368 Traverse lists/tuples and dicts and serialize their values as well.
382 Traverse lists/tuples and dicts and serialize their values as well.
369 Replace any widgets with their model_id
383 Replace any widgets with their model_id
370 """
384 """
371 if isinstance(x, dict):
385 if isinstance(x, dict):
372 return {k: self._trait_to_json(v) for k, v in x.items()}
386 return {k: self._trait_to_json(v) for k, v in x.items()}
373 elif isinstance(x, (list, tuple)):
387 elif isinstance(x, (list, tuple)):
374 return [self._trait_to_json(v) for v in x]
388 return [self._trait_to_json(v) for v in x]
375 elif isinstance(x, Widget):
389 elif isinstance(x, Widget):
376 return "IPY_MODEL_" + x.model_id
390 return "IPY_MODEL_" + x.model_id
377 else:
391 else:
378 return x # Value must be JSON-able
392 return x # Value must be JSON-able
379
393
380 def _trait_from_json(self, x):
394 def _trait_from_json(self, x):
381 """Convert json values to objects
395 """Convert json values to objects
382
396
383 Replace any strings representing valid model id values to Widget references.
397 Replace any strings representing valid model id values to Widget references.
384 """
398 """
385 if isinstance(x, dict):
399 if isinstance(x, dict):
386 return {k: self._trait_from_json(v) for k, v in x.items()}
400 return {k: self._trait_from_json(v) for k, v in x.items()}
387 elif isinstance(x, (list, tuple)):
401 elif isinstance(x, (list, tuple)):
388 return [self._trait_from_json(v) for v in x]
402 return [self._trait_from_json(v) for v in x]
389 elif isinstance(x, string_types) and x.startswith('IPY_MODEL_') and x[10:] in Widget.widgets:
403 elif isinstance(x, string_types) and x.startswith('IPY_MODEL_') and x[10:] in Widget.widgets:
390 # we want to support having child widgets at any level in a hierarchy
404 # we want to support having child widgets at any level in a hierarchy
391 # trusting that a widget UUID will not appear out in the wild
405 # trusting that a widget UUID will not appear out in the wild
392 return Widget.widgets[x[10:]]
406 return Widget.widgets[x[10:]]
393 else:
407 else:
394 return x
408 return x
395
409
396 def _ipython_display_(self, **kwargs):
410 def _ipython_display_(self, **kwargs):
397 """Called when `IPython.display.display` is called on the widget."""
411 """Called when `IPython.display.display` is called on the widget."""
398 # Show view.
412 # Show view.
399 if self._view_name is not None:
413 if self._view_name is not None:
400 self._send({"method": "display"})
414 self._send({"method": "display"})
401 self._handle_displayed(**kwargs)
415 self._handle_displayed(**kwargs)
402
416
403 def _send(self, msg):
417 def _send(self, msg):
404 """Sends a message to the model in the front-end."""
418 """Sends a message to the model in the front-end."""
405 self.comm.send(msg)
419 self.comm.send(msg)
406
420
407
421
408 class DOMWidget(Widget):
422 class DOMWidget(Widget):
409 visible = Bool(True, help="Whether the widget is visible.", sync=True)
423 visible = Bool(True, help="Whether the widget is visible.", sync=True)
410 _css = Tuple(sync=True, help="CSS property list: (selector, key, value)")
424 _css = Tuple(sync=True, help="CSS property list: (selector, key, value)")
411 _dom_classes = Tuple(sync=True, help="DOM classes applied to widget.$el.")
425 _dom_classes = Tuple(sync=True, help="DOM classes applied to widget.$el.")
412
426
413 width = CUnicode(sync=True)
427 width = CUnicode(sync=True)
414 height = CUnicode(sync=True)
428 height = CUnicode(sync=True)
415 padding = CUnicode(sync=True)
429 padding = CUnicode(sync=True)
416 margin = CUnicode(sync=True)
430 margin = CUnicode(sync=True)
417
431
418 color = Unicode(sync=True)
432 color = Unicode(sync=True)
419 background_color = Unicode(sync=True)
433 background_color = Unicode(sync=True)
420 border_color = Unicode(sync=True)
434 border_color = Unicode(sync=True)
421
435
422 border_width = CUnicode(sync=True)
436 border_width = CUnicode(sync=True)
423 border_radius = CUnicode(sync=True)
437 border_radius = CUnicode(sync=True)
424 border_style = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_border-style.asp
438 border_style = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_border-style.asp
425 'none',
439 'none',
426 'hidden',
440 'hidden',
427 'dotted',
441 'dotted',
428 'dashed',
442 'dashed',
429 'solid',
443 'solid',
430 'double',
444 'double',
431 'groove',
445 'groove',
432 'ridge',
446 'ridge',
433 'inset',
447 'inset',
434 'outset',
448 'outset',
435 'initial',
449 'initial',
436 'inherit', ''],
450 'inherit', ''],
437 default_value='', sync=True)
451 default_value='', sync=True)
438
452
439 font_style = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_font_font-style.asp
453 font_style = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_font_font-style.asp
440 'normal',
454 'normal',
441 'italic',
455 'italic',
442 'oblique',
456 'oblique',
443 'initial',
457 'initial',
444 'inherit', ''],
458 'inherit', ''],
445 default_value='', sync=True)
459 default_value='', sync=True)
446 font_weight = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_font_weight.asp
460 font_weight = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_font_weight.asp
447 'normal',
461 'normal',
448 'bold',
462 'bold',
449 'bolder',
463 'bolder',
450 'lighter',
464 'lighter',
451 'initial',
465 'initial',
452 'inherit', ''] + [str(100 * (i+1)) for i in range(9)],
466 'inherit', ''] + [str(100 * (i+1)) for i in range(9)],
453 default_value='', sync=True)
467 default_value='', sync=True)
454 font_size = CUnicode(sync=True)
468 font_size = CUnicode(sync=True)
455 font_family = Unicode(sync=True)
469 font_family = Unicode(sync=True)
456
470
457 def __init__(self, *pargs, **kwargs):
471 def __init__(self, *pargs, **kwargs):
458 super(DOMWidget, self).__init__(*pargs, **kwargs)
472 super(DOMWidget, self).__init__(*pargs, **kwargs)
459
473
460 def _validate_border(name, old, new):
474 def _validate_border(name, old, new):
461 if new is not None and new != '':
475 if new is not None and new != '':
462 if name != 'border_width' and not self.border_width:
476 if name != 'border_width' and not self.border_width:
463 self.border_width = 1
477 self.border_width = 1
464 if name != 'border_style' and self.border_style == '':
478 if name != 'border_style' and self.border_style == '':
465 self.border_style = 'solid'
479 self.border_style = 'solid'
466 self.on_trait_change(_validate_border, ['border_width', 'border_style', 'border_color'])
480 self.on_trait_change(_validate_border, ['border_width', 'border_style', 'border_color'])
@@ -1,48 +1,50 b''
1 """Bool class.
1 """Bool 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, register
17 from IPython.utils.traitlets import Unicode, Bool, CaselessStrEnum
17 from IPython.utils.traitlets import Unicode, Bool, CaselessStrEnum
18 from IPython.utils.warn import DeprecatedClass
18 from IPython.utils.warn import DeprecatedClass
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Classes
21 # Classes
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 class _Bool(DOMWidget):
23 class _Bool(DOMWidget):
24 """A base class for creating widgets that represent booleans."""
24 """A base class for creating widgets that represent booleans."""
25 value = Bool(False, help="Bool value", sync=True)
25 value = Bool(False, help="Bool value", sync=True)
26 description = Unicode('', help="Description of the boolean (label).", sync=True)
26 description = Unicode('', help="Description of the boolean (label).", 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
28
29
29
30 @register('IPython.Checkbox')
30 class Checkbox(_Bool):
31 class Checkbox(_Bool):
31 """Displays a boolean `value`."""
32 """Displays a boolean `value`."""
32 _view_name = Unicode('CheckboxView', sync=True)
33 _view_name = Unicode('CheckboxView', sync=True)
33
34
34
35
36 @register('IPython.ToggleButton')
35 class ToggleButton(_Bool):
37 class ToggleButton(_Bool):
36 """Displays a boolean `value`."""
38 """Displays a boolean `value`."""
37
39
38 _view_name = Unicode('ToggleButtonView', sync=True)
40 _view_name = Unicode('ToggleButtonView', sync=True)
39
41
40 button_style = CaselessStrEnum(
42 button_style = CaselessStrEnum(
41 values=['primary', 'success', 'info', 'warning', 'danger', ''],
43 values=['primary', 'success', 'info', 'warning', 'danger', ''],
42 default_value='', allow_none=True, sync=True, help="""Use a
44 default_value='', allow_none=True, sync=True, help="""Use a
43 predefined styling for the button.""")
45 predefined styling for the button.""")
44
46
45
47
46 # Remove in IPython 4.0
48 # Remove in IPython 4.0
47 CheckboxWidget = DeprecatedClass(Checkbox, 'CheckboxWidget')
49 CheckboxWidget = DeprecatedClass(Checkbox, 'CheckboxWidget')
48 ToggleButtonWidget = DeprecatedClass(ToggleButton, 'ToggleButtonWidget')
50 ToggleButtonWidget = DeprecatedClass(ToggleButton, 'ToggleButtonWidget')
@@ -1,88 +1,91 b''
1 """Box class.
1 """Box 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) IPython Development Team.
6 # Copyright (c) IPython Development Team.
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8
8
9 from .widget import DOMWidget
9 from .widget import DOMWidget, register
10 from IPython.utils.traitlets import Unicode, Tuple, TraitError, Int, CaselessStrEnum
10 from IPython.utils.traitlets import Unicode, Tuple, TraitError, Int, CaselessStrEnum
11 from IPython.utils.warn import DeprecatedClass
11 from IPython.utils.warn import DeprecatedClass
12
12
13 @register('IPython.Box')
13 class Box(DOMWidget):
14 class Box(DOMWidget):
14 """Displays multiple widgets in a group."""
15 """Displays multiple widgets in a group."""
15 _view_name = Unicode('BoxView', sync=True)
16 _view_name = Unicode('BoxView', sync=True)
16
17
17 # Child widgets in the container.
18 # Child widgets in the container.
18 # Using a tuple here to force reassignment to update the list.
19 # Using a tuple here to force reassignment to update the list.
19 # When a proper notifying-list trait exists, that is what should be used here.
20 # When a proper notifying-list trait exists, that is what should be used here.
20 children = Tuple(sync=True, allow_none=False)
21 children = Tuple(sync=True, allow_none=False)
21
22
22 _overflow_values = ['visible', 'hidden', 'scroll', 'auto', 'initial', 'inherit', '']
23 _overflow_values = ['visible', 'hidden', 'scroll', 'auto', 'initial', 'inherit', '']
23 overflow_x = CaselessStrEnum(
24 overflow_x = CaselessStrEnum(
24 values=_overflow_values,
25 values=_overflow_values,
25 default_value='', allow_none=False, sync=True, help="""Specifies what
26 default_value='', allow_none=False, sync=True, help="""Specifies what
26 happens to content that is too large for the rendered region.""")
27 happens to content that is too large for the rendered region.""")
27 overflow_y = CaselessStrEnum(
28 overflow_y = CaselessStrEnum(
28 values=_overflow_values,
29 values=_overflow_values,
29 default_value='', allow_none=False, sync=True, help="""Specifies what
30 default_value='', allow_none=False, sync=True, help="""Specifies what
30 happens to content that is too large for the rendered region.""")
31 happens to content that is too large for the rendered region.""")
31
32
32 box_style = CaselessStrEnum(
33 box_style = CaselessStrEnum(
33 values=['success', 'info', 'warning', 'danger', ''],
34 values=['success', 'info', 'warning', 'danger', ''],
34 default_value='', allow_none=True, sync=True, help="""Use a
35 default_value='', allow_none=True, sync=True, help="""Use a
35 predefined styling for the box.""")
36 predefined styling for the box.""")
36
37
37 def __init__(self, children = (), **kwargs):
38 def __init__(self, children = (), **kwargs):
38 kwargs['children'] = children
39 kwargs['children'] = children
39 super(Box, self).__init__(**kwargs)
40 super(Box, self).__init__(**kwargs)
40 self.on_displayed(Box._fire_children_displayed)
41 self.on_displayed(Box._fire_children_displayed)
41
42
42 def _fire_children_displayed(self):
43 def _fire_children_displayed(self):
43 for child in self.children:
44 for child in self.children:
44 child._handle_displayed()
45 child._handle_displayed()
45
46
46
47
48 @register('IPython.Popup')
47 class Popup(Box):
49 class Popup(Box):
48 """Displays multiple widgets in an in page popup div."""
50 """Displays multiple widgets in an in page popup div."""
49 _view_name = Unicode('PopupView', sync=True)
51 _view_name = Unicode('PopupView', sync=True)
50
52
51 description = Unicode(sync=True)
53 description = Unicode(sync=True)
52 button_text = Unicode(sync=True)
54 button_text = Unicode(sync=True)
53
55
54
56
57 @register('IPython.FlexBox')
55 class FlexBox(Box):
58 class FlexBox(Box):
56 """Displays multiple widgets using the flexible box model."""
59 """Displays multiple widgets using the flexible box model."""
57 _view_name = Unicode('FlexBoxView', sync=True)
60 _view_name = Unicode('FlexBoxView', sync=True)
58 orientation = CaselessStrEnum(values=['vertical', 'horizontal'], default_value='vertical', sync=True)
61 orientation = CaselessStrEnum(values=['vertical', 'horizontal'], default_value='vertical', sync=True)
59 flex = Int(0, sync=True, help="""Specify the flexible-ness of the model.""")
62 flex = Int(0, sync=True, help="""Specify the flexible-ness of the model.""")
60 def _flex_changed(self, name, old, new):
63 def _flex_changed(self, name, old, new):
61 new = min(max(0, new), 2)
64 new = min(max(0, new), 2)
62 if self.flex != new:
65 if self.flex != new:
63 self.flex = new
66 self.flex = new
64
67
65 _locations = ['start', 'center', 'end', 'baseline', 'stretch']
68 _locations = ['start', 'center', 'end', 'baseline', 'stretch']
66 pack = CaselessStrEnum(
69 pack = CaselessStrEnum(
67 values=_locations,
70 values=_locations,
68 default_value='start', allow_none=False, sync=True)
71 default_value='start', allow_none=False, sync=True)
69 align = CaselessStrEnum(
72 align = CaselessStrEnum(
70 values=_locations,
73 values=_locations,
71 default_value='start', allow_none=False, sync=True)
74 default_value='start', allow_none=False, sync=True)
72
75
73
76
74 def VBox(*pargs, **kwargs):
77 def VBox(*pargs, **kwargs):
75 """Displays multiple widgets vertically using the flexible box model."""
78 """Displays multiple widgets vertically using the flexible box model."""
76 kwargs['orientation'] = 'vertical'
79 kwargs['orientation'] = 'vertical'
77 return FlexBox(*pargs, **kwargs)
80 return FlexBox(*pargs, **kwargs)
78
81
79 def HBox(*pargs, **kwargs):
82 def HBox(*pargs, **kwargs):
80 """Displays multiple widgets horizontally using the flexible box model."""
83 """Displays multiple widgets horizontally using the flexible box model."""
81 kwargs['orientation'] = 'horizontal'
84 kwargs['orientation'] = 'horizontal'
82 return FlexBox(*pargs, **kwargs)
85 return FlexBox(*pargs, **kwargs)
83
86
84
87
85 # Remove in IPython 4.0
88 # Remove in IPython 4.0
86 ContainerWidget = DeprecatedClass(Box, 'ContainerWidget')
89 ContainerWidget = DeprecatedClass(Box, 'ContainerWidget')
87 PopupWidget = DeprecatedClass(Popup, 'PopupWidget')
90 PopupWidget = DeprecatedClass(Popup, 'PopupWidget')
88
91
@@ -1,71 +1,72 b''
1 """Button class.
1 """Button 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 from .widget import DOMWidget, CallbackDispatcher
17 from .widget import DOMWidget, CallbackDispatcher, register
18 from IPython.utils.traitlets import Unicode, Bool, CaselessStrEnum
18 from IPython.utils.traitlets import Unicode, Bool, CaselessStrEnum
19 from IPython.utils.warn import DeprecatedClass
19 from IPython.utils.warn import DeprecatedClass
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Classes
22 # Classes
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 @register('IPython.Button')
24 class Button(DOMWidget):
25 class Button(DOMWidget):
25 """Button widget.
26 """Button widget.
26
27
27 This widget has an `on_click` method that allows you to listen for the
28 This widget has an `on_click` method that allows you to listen for the
28 user clicking on the button. The click event itself is stateless."""
29 user clicking on the button. The click event itself is stateless."""
29 _view_name = Unicode('ButtonView', sync=True)
30 _view_name = Unicode('ButtonView', sync=True)
30
31
31 # Keys
32 # Keys
32 description = Unicode('', help="Button label.", sync=True)
33 description = Unicode('', help="Button label.", sync=True)
33 tooltip = Unicode(help="Tooltip caption of the button.", sync=True)
34 tooltip = Unicode(help="Tooltip caption of the button.", sync=True)
34 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
35 disabled = Bool(False, help="Enable or disable user changes.", sync=True)
35
36
36 button_style = CaselessStrEnum(
37 button_style = CaselessStrEnum(
37 values=['primary', 'success', 'info', 'warning', 'danger', ''],
38 values=['primary', 'success', 'info', 'warning', 'danger', ''],
38 default_value='', allow_none=True, sync=True, help="""Use a
39 default_value='', allow_none=True, sync=True, help="""Use a
39 predefined styling for the button.""")
40 predefined styling for the button.""")
40
41
41 def __init__(self, **kwargs):
42 def __init__(self, **kwargs):
42 """Constructor"""
43 """Constructor"""
43 super(Button, self).__init__(**kwargs)
44 super(Button, self).__init__(**kwargs)
44 self._click_handlers = CallbackDispatcher()
45 self._click_handlers = CallbackDispatcher()
45 self.on_msg(self._handle_button_msg)
46 self.on_msg(self._handle_button_msg)
46
47
47 def on_click(self, callback, remove=False):
48 def on_click(self, callback, remove=False):
48 """Register a callback to execute when the button is clicked.
49 """Register a callback to execute when the button is clicked.
49
50
50 The callback will be called with one argument,
51 The callback will be called with one argument,
51 the clicked button widget instance.
52 the clicked button widget instance.
52
53
53 Parameters
54 Parameters
54 ----------
55 ----------
55 remove : bool (optional)
56 remove : bool (optional)
56 Set to true to remove the callback from the list of callbacks."""
57 Set to true to remove the callback from the list of callbacks."""
57 self._click_handlers.register_callback(callback, remove=remove)
58 self._click_handlers.register_callback(callback, remove=remove)
58
59
59 def _handle_button_msg(self, _, content):
60 def _handle_button_msg(self, _, content):
60 """Handle a msg from the front-end.
61 """Handle a msg from the front-end.
61
62
62 Parameters
63 Parameters
63 ----------
64 ----------
64 content: dict
65 content: dict
65 Content of the msg."""
66 Content of the msg."""
66 if content.get('event', '') == 'click':
67 if content.get('event', '') == 'click':
67 self._click_handlers(self)
68 self._click_handlers(self)
68
69
69
70
70 # Remove in IPython 4.0
71 # Remove in IPython 4.0
71 ButtonWidget = DeprecatedClass(Button, 'ButtonWidget')
72 ButtonWidget = DeprecatedClass(Button, 'ButtonWidget')
@@ -1,179 +1,184 b''
1 """Float class.
1 """Float class.
2
2
3 Represents an unbounded float using a widget.
3 Represents an unbounded 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, register
17 from IPython.utils.traitlets import Unicode, CFloat, Bool, CaselessStrEnum, Tuple
17 from IPython.utils.traitlets import Unicode, CFloat, Bool, CaselessStrEnum, Tuple
18 from IPython.utils.warn import DeprecatedClass
18 from IPython.utils.warn import DeprecatedClass
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Classes
21 # Classes
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 class _Float(DOMWidget):
23 class _Float(DOMWidget):
24 value = CFloat(0.0, help="Float value", sync=True)
24 value = CFloat(0.0, help="Float value", sync=True)
25 disabled = Bool(False, help="Enable or disable user changes", sync=True)
25 disabled = Bool(False, help="Enable or disable user changes", sync=True)
26 description = Unicode(help="Description of the value this widget represents", sync=True)
26 description = Unicode(help="Description of the value this widget represents", sync=True)
27
27
28
28
29 class _BoundedFloat(_Float):
29 class _BoundedFloat(_Float):
30 max = CFloat(100.0, help="Max value", sync=True)
30 max = CFloat(100.0, help="Max value", sync=True)
31 min = CFloat(0.0, help="Min value", sync=True)
31 min = CFloat(0.0, help="Min value", sync=True)
32 step = CFloat(0.1, help="Minimum step that the value can take (ignored by some views)", sync=True)
32 step = CFloat(0.1, help="Minimum step that the value can take (ignored by some views)", sync=True)
33
33
34 def __init__(self, *pargs, **kwargs):
34 def __init__(self, *pargs, **kwargs):
35 """Constructor"""
35 """Constructor"""
36 DOMWidget.__init__(self, *pargs, **kwargs)
36 DOMWidget.__init__(self, *pargs, **kwargs)
37 self._validate('value', None, self.value)
37 self._validate('value', None, self.value)
38 self.on_trait_change(self._validate, ['value', 'min', 'max'])
38 self.on_trait_change(self._validate, ['value', 'min', 'max'])
39
39
40 def _validate(self, name, old, new):
40 def _validate(self, name, old, new):
41 """Validate value, max, min."""
41 """Validate value, max, min."""
42 if self.min > new or new > self.max:
42 if self.min > new or new > self.max:
43 self.value = min(max(new, self.min), self.max)
43 self.value = min(max(new, self.min), self.max)
44
44
45
45
46 @register('IPython.FloatText')
46 class FloatText(_Float):
47 class FloatText(_Float):
47 _view_name = Unicode('FloatTextView', sync=True)
48 _view_name = Unicode('FloatTextView', sync=True)
48
49
49
50
51 @register('IPython.BoundedFloatText')
50 class BoundedFloatText(_BoundedFloat):
52 class BoundedFloatText(_BoundedFloat):
51 _view_name = Unicode('FloatTextView', sync=True)
53 _view_name = Unicode('FloatTextView', sync=True)
52
54
53
55
56 @register('IPython.FloatSlider')
54 class FloatSlider(_BoundedFloat):
57 class FloatSlider(_BoundedFloat):
55 _view_name = Unicode('FloatSliderView', sync=True)
58 _view_name = Unicode('FloatSliderView', sync=True)
56 orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
59 orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
57 default_value='horizontal',
60 default_value='horizontal',
58 help="Vertical or horizontal.", allow_none=False, sync=True)
61 help="Vertical or horizontal.", allow_none=False, sync=True)
59 _range = Bool(False, help="Display a range selector", sync=True)
62 _range = Bool(False, help="Display a range selector", sync=True)
60 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
63 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
61 slider_color = Unicode(sync=True)
64 slider_color = Unicode(sync=True)
62
65
63
66
67 @register('IPython.FloatProgress')
64 class FloatProgress(_BoundedFloat):
68 class FloatProgress(_BoundedFloat):
65 _view_name = Unicode('ProgressView', sync=True)
69 _view_name = Unicode('ProgressView', sync=True)
66
70
67 bar_style = CaselessStrEnum(
71 bar_style = CaselessStrEnum(
68 values=['success', 'info', 'warning', 'danger', ''],
72 values=['success', 'info', 'warning', 'danger', ''],
69 default_value='', allow_none=True, sync=True, help="""Use a
73 default_value='', allow_none=True, sync=True, help="""Use a
70 predefined styling for the progess bar.""")
74 predefined styling for the progess bar.""")
71
75
72 class _FloatRange(_Float):
76 class _FloatRange(_Float):
73 value = Tuple(CFloat, CFloat, default_value=(0.0, 1.0), help="Tuple of (lower, upper) bounds", sync=True)
77 value = Tuple(CFloat, CFloat, default_value=(0.0, 1.0), help="Tuple of (lower, upper) bounds", sync=True)
74 lower = CFloat(0.0, help="Lower bound", sync=False)
78 lower = CFloat(0.0, help="Lower bound", sync=False)
75 upper = CFloat(1.0, help="Upper bound", sync=False)
79 upper = CFloat(1.0, help="Upper bound", sync=False)
76
80
77 def __init__(self, *pargs, **kwargs):
81 def __init__(self, *pargs, **kwargs):
78 value_given = 'value' in kwargs
82 value_given = 'value' in kwargs
79 lower_given = 'lower' in kwargs
83 lower_given = 'lower' in kwargs
80 upper_given = 'upper' in kwargs
84 upper_given = 'upper' in kwargs
81 if value_given and (lower_given or upper_given):
85 if value_given and (lower_given or upper_given):
82 raise ValueError("Cannot specify both 'value' and 'lower'/'upper' for range widget")
86 raise ValueError("Cannot specify both 'value' and 'lower'/'upper' for range widget")
83 if lower_given != upper_given:
87 if lower_given != upper_given:
84 raise ValueError("Must specify both 'lower' and 'upper' for range widget")
88 raise ValueError("Must specify both 'lower' and 'upper' for range widget")
85
89
86 DOMWidget.__init__(self, *pargs, **kwargs)
90 DOMWidget.__init__(self, *pargs, **kwargs)
87
91
88 # ensure the traits match, preferring whichever (if any) was given in kwargs
92 # ensure the traits match, preferring whichever (if any) was given in kwargs
89 if value_given:
93 if value_given:
90 self.lower, self.upper = self.value
94 self.lower, self.upper = self.value
91 else:
95 else:
92 self.value = (self.lower, self.upper)
96 self.value = (self.lower, self.upper)
93
97
94 self.on_trait_change(self._validate, ['value', 'upper', 'lower'])
98 self.on_trait_change(self._validate, ['value', 'upper', 'lower'])
95
99
96 def _validate(self, name, old, new):
100 def _validate(self, name, old, new):
97 if name == 'value':
101 if name == 'value':
98 self.lower, self.upper = min(new), max(new)
102 self.lower, self.upper = min(new), max(new)
99 elif name == 'lower':
103 elif name == 'lower':
100 self.value = (new, self.value[1])
104 self.value = (new, self.value[1])
101 elif name == 'upper':
105 elif name == 'upper':
102 self.value = (self.value[0], new)
106 self.value = (self.value[0], new)
103
107
104 class _BoundedFloatRange(_FloatRange):
108 class _BoundedFloatRange(_FloatRange):
105 step = CFloat(1.0, help="Minimum step that the value can take (ignored by some views)", sync=True)
109 step = CFloat(1.0, help="Minimum step that the value can take (ignored by some views)", sync=True)
106 max = CFloat(100.0, help="Max value", sync=True)
110 max = CFloat(100.0, help="Max value", sync=True)
107 min = CFloat(0.0, help="Min value", sync=True)
111 min = CFloat(0.0, help="Min value", sync=True)
108
112
109 def __init__(self, *pargs, **kwargs):
113 def __init__(self, *pargs, **kwargs):
110 any_value_given = 'value' in kwargs or 'upper' in kwargs or 'lower' in kwargs
114 any_value_given = 'value' in kwargs or 'upper' in kwargs or 'lower' in kwargs
111 _FloatRange.__init__(self, *pargs, **kwargs)
115 _FloatRange.__init__(self, *pargs, **kwargs)
112
116
113 # ensure a minimal amount of sanity
117 # ensure a minimal amount of sanity
114 if self.min > self.max:
118 if self.min > self.max:
115 raise ValueError("min must be <= max")
119 raise ValueError("min must be <= max")
116
120
117 if any_value_given:
121 if any_value_given:
118 # if a value was given, clamp it within (min, max)
122 # if a value was given, clamp it within (min, max)
119 self._validate("value", None, self.value)
123 self._validate("value", None, self.value)
120 else:
124 else:
121 # otherwise, set it to 25-75% to avoid the handles overlapping
125 # otherwise, set it to 25-75% to avoid the handles overlapping
122 self.value = (0.75*self.min + 0.25*self.max,
126 self.value = (0.75*self.min + 0.25*self.max,
123 0.25*self.min + 0.75*self.max)
127 0.25*self.min + 0.75*self.max)
124 # callback already set for 'value', 'lower', 'upper'
128 # callback already set for 'value', 'lower', 'upper'
125 self.on_trait_change(self._validate, ['min', 'max'])
129 self.on_trait_change(self._validate, ['min', 'max'])
126
130
127
131
128 def _validate(self, name, old, new):
132 def _validate(self, name, old, new):
129 if name == "min":
133 if name == "min":
130 if new > self.max:
134 if new > self.max:
131 raise ValueError("setting min > max")
135 raise ValueError("setting min > max")
132 self.min = new
136 self.min = new
133 elif name == "max":
137 elif name == "max":
134 if new < self.min:
138 if new < self.min:
135 raise ValueError("setting max < min")
139 raise ValueError("setting max < min")
136 self.max = new
140 self.max = new
137
141
138 low, high = self.value
142 low, high = self.value
139 if name == "value":
143 if name == "value":
140 low, high = min(new), max(new)
144 low, high = min(new), max(new)
141 elif name == "upper":
145 elif name == "upper":
142 if new < self.lower:
146 if new < self.lower:
143 raise ValueError("setting upper < lower")
147 raise ValueError("setting upper < lower")
144 high = new
148 high = new
145 elif name == "lower":
149 elif name == "lower":
146 if new > self.upper:
150 if new > self.upper:
147 raise ValueError("setting lower > upper")
151 raise ValueError("setting lower > upper")
148 low = new
152 low = new
149
153
150 low = max(self.min, min(low, self.max))
154 low = max(self.min, min(low, self.max))
151 high = min(self.max, max(high, self.min))
155 high = min(self.max, max(high, self.min))
152
156
153 # determine the order in which we should update the
157 # determine the order in which we should update the
154 # lower, upper traits to avoid a temporary inverted overlap
158 # lower, upper traits to avoid a temporary inverted overlap
155 lower_first = high < self.lower
159 lower_first = high < self.lower
156
160
157 self.value = (low, high)
161 self.value = (low, high)
158 if lower_first:
162 if lower_first:
159 self.lower = low
163 self.lower = low
160 self.upper = high
164 self.upper = high
161 else:
165 else:
162 self.upper = high
166 self.upper = high
163 self.lower = low
167 self.lower = low
164
168
165
169
170 @register('IPython.FloatRangeSlider')
166 class FloatRangeSlider(_BoundedFloatRange):
171 class FloatRangeSlider(_BoundedFloatRange):
167 _view_name = Unicode('FloatSliderView', sync=True)
172 _view_name = Unicode('FloatSliderView', sync=True)
168 orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
173 orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
169 default_value='horizontal', allow_none=False,
174 default_value='horizontal', allow_none=False,
170 help="Vertical or horizontal.", sync=True)
175 help="Vertical or horizontal.", sync=True)
171 _range = Bool(True, help="Display a range selector", sync=True)
176 _range = Bool(True, help="Display a range selector", sync=True)
172 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
177 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
173 slider_color = Unicode(sync=True)
178 slider_color = Unicode(sync=True)
174
179
175 # Remove in IPython 4.0
180 # Remove in IPython 4.0
176 FloatTextWidget = DeprecatedClass(FloatText, 'FloatTextWidget')
181 FloatTextWidget = DeprecatedClass(FloatText, 'FloatTextWidget')
177 BoundedFloatTextWidget = DeprecatedClass(BoundedFloatText, 'BoundedFloatTextWidget')
182 BoundedFloatTextWidget = DeprecatedClass(BoundedFloatText, 'BoundedFloatTextWidget')
178 FloatSliderWidget = DeprecatedClass(FloatSlider, 'FloatSliderWidget')
183 FloatSliderWidget = DeprecatedClass(FloatSlider, 'FloatSliderWidget')
179 FloatProgressWidget = DeprecatedClass(FloatProgress, 'FloatProgressWidget')
184 FloatProgressWidget = DeprecatedClass(FloatProgress, 'FloatProgressWidget')
@@ -1,46 +1,47 b''
1 """Image class.
1 """Image class.
2
2
3 Represents an image in the frontend using a widget.
3 Represents an image in the frontend 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 base64
16 import base64
17
17
18 from .widget import DOMWidget
18 from .widget import DOMWidget, register
19 from IPython.utils.traitlets import Unicode, CUnicode, Bytes
19 from IPython.utils.traitlets import Unicode, CUnicode, Bytes
20 from IPython.utils.warn import DeprecatedClass
20 from IPython.utils.warn import DeprecatedClass
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Classes
23 # Classes
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 @register('IPython.Image')
25 class Image(DOMWidget):
26 class Image(DOMWidget):
26 """Displays an image as a widget.
27 """Displays an image as a widget.
27
28
28 The `value` of this widget accepts a byte string. The byte string is the raw
29 The `value` of this widget accepts a byte string. The byte string is the raw
29 image data that you want the browser to display. You can explicitly define
30 image data that you want the browser to display. You can explicitly define
30 the format of the byte string using the `format` trait (which defaults to
31 the format of the byte string using the `format` trait (which defaults to
31 "png")."""
32 "png")."""
32 _view_name = Unicode('ImageView', sync=True)
33 _view_name = Unicode('ImageView', sync=True)
33
34
34 # Define the custom state properties to sync with the front-end
35 # Define the custom state properties to sync with the front-end
35 format = Unicode('png', sync=True)
36 format = Unicode('png', sync=True)
36 width = CUnicode(sync=True)
37 width = CUnicode(sync=True)
37 height = CUnicode(sync=True)
38 height = CUnicode(sync=True)
38 _b64value = Unicode(sync=True)
39 _b64value = Unicode(sync=True)
39
40
40 value = Bytes()
41 value = Bytes()
41 def _value_changed(self, name, old, new):
42 def _value_changed(self, name, old, new):
42 self._b64value = base64.b64encode(new)
43 self._b64value = base64.b64encode(new)
43
44
44
45
45 # Remove in IPython 4.0
46 # Remove in IPython 4.0
46 ImageWidget = DeprecatedClass(Image, 'ImageWidget')
47 ImageWidget = DeprecatedClass(Image, 'ImageWidget')
@@ -1,192 +1,197 b''
1 """Int class.
1 """Int class.
2
2
3 Represents an unbounded int using a widget.
3 Represents an unbounded 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, register
17 from IPython.utils.traitlets import Unicode, CInt, Bool, CaselessStrEnum, Tuple
17 from IPython.utils.traitlets import Unicode, CInt, Bool, CaselessStrEnum, Tuple
18 from IPython.utils.warn import DeprecatedClass
18 from IPython.utils.warn import DeprecatedClass
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Classes
21 # Classes
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 class _Int(DOMWidget):
23 class _Int(DOMWidget):
24 """Base class used to create widgets that represent an int."""
24 """Base class used to create widgets that represent an int."""
25 value = CInt(0, help="Int value", sync=True)
25 value = CInt(0, help="Int value", sync=True)
26 disabled = Bool(False, help="Enable or disable user changes", sync=True)
26 disabled = Bool(False, help="Enable or disable user changes", sync=True)
27 description = Unicode(help="Description of the value this widget represents", sync=True)
27 description = Unicode(help="Description of the value this widget represents", sync=True)
28
28
29
29
30 class _BoundedInt(_Int):
30 class _BoundedInt(_Int):
31 """Base class used to create widgets that represent a int that is bounded
31 """Base class used to create widgets that represent a int that is bounded
32 by a minium and maximum."""
32 by a minium and maximum."""
33 step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
33 step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
34 max = CInt(100, help="Max value", sync=True)
34 max = CInt(100, help="Max value", sync=True)
35 min = CInt(0, help="Min value", sync=True)
35 min = CInt(0, help="Min value", sync=True)
36
36
37 def __init__(self, *pargs, **kwargs):
37 def __init__(self, *pargs, **kwargs):
38 """Constructor"""
38 """Constructor"""
39 DOMWidget.__init__(self, *pargs, **kwargs)
39 DOMWidget.__init__(self, *pargs, **kwargs)
40 self.on_trait_change(self._validate_value, ['value'])
40 self.on_trait_change(self._validate_value, ['value'])
41 self.on_trait_change(self._handle_max_changed, ['max'])
41 self.on_trait_change(self._handle_max_changed, ['max'])
42 self.on_trait_change(self._handle_min_changed, ['min'])
42 self.on_trait_change(self._handle_min_changed, ['min'])
43
43
44 def _validate_value(self, name, old, new):
44 def _validate_value(self, name, old, new):
45 """Validate value."""
45 """Validate value."""
46 if self.min > new or new > self.max:
46 if self.min > new or new > self.max:
47 self.value = min(max(new, self.min), self.max)
47 self.value = min(max(new, self.min), self.max)
48
48
49 def _handle_max_changed(self, name, old, new):
49 def _handle_max_changed(self, name, old, new):
50 """Make sure the min is always <= the max."""
50 """Make sure the min is always <= the max."""
51 if new < self.min:
51 if new < self.min:
52 raise ValueError("setting max < min")
52 raise ValueError("setting max < min")
53
53
54 def _handle_min_changed(self, name, old, new):
54 def _handle_min_changed(self, name, old, new):
55 """Make sure the max is always >= the min."""
55 """Make sure the max is always >= the min."""
56 if new > self.max:
56 if new > self.max:
57 raise ValueError("setting min > max")
57 raise ValueError("setting min > max")
58
58
59 @register('IPython.IntText')
59 class IntText(_Int):
60 class IntText(_Int):
60 """Textbox widget that represents a int."""
61 """Textbox widget that represents a int."""
61 _view_name = Unicode('IntTextView', sync=True)
62 _view_name = Unicode('IntTextView', sync=True)
62
63
63
64
65 @register('IPython.BoundedIntText')
64 class BoundedIntText(_BoundedInt):
66 class BoundedIntText(_BoundedInt):
65 """Textbox widget that represents a int bounded by a minimum and maximum value."""
67 """Textbox widget that represents a int bounded by a minimum and maximum value."""
66 _view_name = Unicode('IntTextView', sync=True)
68 _view_name = Unicode('IntTextView', sync=True)
67
69
68
70
71 @register('IPython.IntSlider')
69 class IntSlider(_BoundedInt):
72 class IntSlider(_BoundedInt):
70 """Slider widget that represents a int bounded by a minimum and maximum value."""
73 """Slider widget that represents a int bounded by a minimum and maximum value."""
71 _view_name = Unicode('IntSliderView', sync=True)
74 _view_name = Unicode('IntSliderView', sync=True)
72 orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
75 orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
73 default_value='horizontal', allow_none=False,
76 default_value='horizontal', allow_none=False,
74 help="Vertical or horizontal.", sync=True)
77 help="Vertical or horizontal.", sync=True)
75 _range = Bool(False, help="Display a range selector", sync=True)
78 _range = Bool(False, help="Display a range selector", sync=True)
76 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
79 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
77 slider_color = Unicode(sync=True)
80 slider_color = Unicode(sync=True)
78
81
79
82
83 @register('IPython.IntProgress')
80 class IntProgress(_BoundedInt):
84 class IntProgress(_BoundedInt):
81 """Progress bar that represents a int bounded by a minimum and maximum value."""
85 """Progress bar that represents a int bounded by a minimum and maximum value."""
82 _view_name = Unicode('ProgressView', sync=True)
86 _view_name = Unicode('ProgressView', sync=True)
83
87
84 bar_style = CaselessStrEnum(
88 bar_style = CaselessStrEnum(
85 values=['success', 'info', 'warning', 'danger', ''],
89 values=['success', 'info', 'warning', 'danger', ''],
86 default_value='', allow_none=True, sync=True, help="""Use a
90 default_value='', allow_none=True, sync=True, help="""Use a
87 predefined styling for the progess bar.""")
91 predefined styling for the progess bar.""")
88
92
89 class _IntRange(_Int):
93 class _IntRange(_Int):
90 value = Tuple(CInt, CInt, default_value=(0, 1), help="Tuple of (lower, upper) bounds", sync=True)
94 value = Tuple(CInt, CInt, default_value=(0, 1), help="Tuple of (lower, upper) bounds", sync=True)
91 lower = CInt(0, help="Lower bound", sync=False)
95 lower = CInt(0, help="Lower bound", sync=False)
92 upper = CInt(1, help="Upper bound", sync=False)
96 upper = CInt(1, help="Upper bound", sync=False)
93
97
94 def __init__(self, *pargs, **kwargs):
98 def __init__(self, *pargs, **kwargs):
95 value_given = 'value' in kwargs
99 value_given = 'value' in kwargs
96 lower_given = 'lower' in kwargs
100 lower_given = 'lower' in kwargs
97 upper_given = 'upper' in kwargs
101 upper_given = 'upper' in kwargs
98 if value_given and (lower_given or upper_given):
102 if value_given and (lower_given or upper_given):
99 raise ValueError("Cannot specify both 'value' and 'lower'/'upper' for range widget")
103 raise ValueError("Cannot specify both 'value' and 'lower'/'upper' for range widget")
100 if lower_given != upper_given:
104 if lower_given != upper_given:
101 raise ValueError("Must specify both 'lower' and 'upper' for range widget")
105 raise ValueError("Must specify both 'lower' and 'upper' for range widget")
102
106
103 DOMWidget.__init__(self, *pargs, **kwargs)
107 DOMWidget.__init__(self, *pargs, **kwargs)
104
108
105 # ensure the traits match, preferring whichever (if any) was given in kwargs
109 # ensure the traits match, preferring whichever (if any) was given in kwargs
106 if value_given:
110 if value_given:
107 self.lower, self.upper = self.value
111 self.lower, self.upper = self.value
108 else:
112 else:
109 self.value = (self.lower, self.upper)
113 self.value = (self.lower, self.upper)
110
114
111 self.on_trait_change(self._validate, ['value', 'upper', 'lower'])
115 self.on_trait_change(self._validate, ['value', 'upper', 'lower'])
112
116
113 def _validate(self, name, old, new):
117 def _validate(self, name, old, new):
114 if name == 'value':
118 if name == 'value':
115 self.lower, self.upper = min(new), max(new)
119 self.lower, self.upper = min(new), max(new)
116 elif name == 'lower':
120 elif name == 'lower':
117 self.value = (new, self.value[1])
121 self.value = (new, self.value[1])
118 elif name == 'upper':
122 elif name == 'upper':
119 self.value = (self.value[0], new)
123 self.value = (self.value[0], new)
120
124
121 class _BoundedIntRange(_IntRange):
125 class _BoundedIntRange(_IntRange):
122 step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
126 step = CInt(1, help="Minimum step that the value can take (ignored by some views)", sync=True)
123 max = CInt(100, help="Max value", sync=True)
127 max = CInt(100, help="Max value", sync=True)
124 min = CInt(0, help="Min value", sync=True)
128 min = CInt(0, help="Min value", sync=True)
125
129
126 def __init__(self, *pargs, **kwargs):
130 def __init__(self, *pargs, **kwargs):
127 any_value_given = 'value' in kwargs or 'upper' in kwargs or 'lower' in kwargs
131 any_value_given = 'value' in kwargs or 'upper' in kwargs or 'lower' in kwargs
128 _IntRange.__init__(self, *pargs, **kwargs)
132 _IntRange.__init__(self, *pargs, **kwargs)
129
133
130 # ensure a minimal amount of sanity
134 # ensure a minimal amount of sanity
131 if self.min > self.max:
135 if self.min > self.max:
132 raise ValueError("min must be <= max")
136 raise ValueError("min must be <= max")
133
137
134 if any_value_given:
138 if any_value_given:
135 # if a value was given, clamp it within (min, max)
139 # if a value was given, clamp it within (min, max)
136 self._validate("value", None, self.value)
140 self._validate("value", None, self.value)
137 else:
141 else:
138 # otherwise, set it to 25-75% to avoid the handles overlapping
142 # otherwise, set it to 25-75% to avoid the handles overlapping
139 self.value = (0.75*self.min + 0.25*self.max,
143 self.value = (0.75*self.min + 0.25*self.max,
140 0.25*self.min + 0.75*self.max)
144 0.25*self.min + 0.75*self.max)
141 # callback already set for 'value', 'lower', 'upper'
145 # callback already set for 'value', 'lower', 'upper'
142 self.on_trait_change(self._validate, ['min', 'max'])
146 self.on_trait_change(self._validate, ['min', 'max'])
143
147
144 def _validate(self, name, old, new):
148 def _validate(self, name, old, new):
145 if name == "min":
149 if name == "min":
146 if new > self.max:
150 if new > self.max:
147 raise ValueError("setting min > max")
151 raise ValueError("setting min > max")
148 elif name == "max":
152 elif name == "max":
149 if new < self.min:
153 if new < self.min:
150 raise ValueError("setting max < min")
154 raise ValueError("setting max < min")
151
155
152 low, high = self.value
156 low, high = self.value
153 if name == "value":
157 if name == "value":
154 low, high = min(new), max(new)
158 low, high = min(new), max(new)
155 elif name == "upper":
159 elif name == "upper":
156 if new < self.lower:
160 if new < self.lower:
157 raise ValueError("setting upper < lower")
161 raise ValueError("setting upper < lower")
158 high = new
162 high = new
159 elif name == "lower":
163 elif name == "lower":
160 if new > self.upper:
164 if new > self.upper:
161 raise ValueError("setting lower > upper")
165 raise ValueError("setting lower > upper")
162 low = new
166 low = new
163
167
164 low = max(self.min, min(low, self.max))
168 low = max(self.min, min(low, self.max))
165 high = min(self.max, max(high, self.min))
169 high = min(self.max, max(high, self.min))
166
170
167 # determine the order in which we should update the
171 # determine the order in which we should update the
168 # lower, upper traits to avoid a temporary inverted overlap
172 # lower, upper traits to avoid a temporary inverted overlap
169 lower_first = high < self.lower
173 lower_first = high < self.lower
170
174
171 self.value = (low, high)
175 self.value = (low, high)
172 if lower_first:
176 if lower_first:
173 self.lower = low
177 self.lower = low
174 self.upper = high
178 self.upper = high
175 else:
179 else:
176 self.upper = high
180 self.upper = high
177 self.lower = low
181 self.lower = low
178
182
183 @register('IPython.IntRangeSlider')
179 class IntRangeSlider(_BoundedIntRange):
184 class IntRangeSlider(_BoundedIntRange):
180 _view_name = Unicode('IntSliderView', sync=True)
185 _view_name = Unicode('IntSliderView', sync=True)
181 orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
186 orientation = CaselessStrEnum(values=['horizontal', 'vertical'],
182 default_value='horizontal', allow_none=False,
187 default_value='horizontal', allow_none=False,
183 help="Vertical or horizontal.", sync=True)
188 help="Vertical or horizontal.", sync=True)
184 _range = Bool(True, help="Display a range selector", sync=True)
189 _range = Bool(True, help="Display a range selector", sync=True)
185 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
190 readout = Bool(True, help="Display the current value of the slider next to it.", sync=True)
186 slider_color = Unicode(sync=True)
191 slider_color = Unicode(sync=True)
187
192
188 # Remove in IPython 4.0
193 # Remove in IPython 4.0
189 IntTextWidget = DeprecatedClass(IntText, 'IntTextWidget')
194 IntTextWidget = DeprecatedClass(IntText, 'IntTextWidget')
190 BoundedIntTextWidget = DeprecatedClass(BoundedIntText, 'BoundedIntTextWidget')
195 BoundedIntTextWidget = DeprecatedClass(BoundedIntText, 'BoundedIntTextWidget')
191 IntSliderWidget = DeprecatedClass(IntSlider, 'IntSliderWidget')
196 IntSliderWidget = DeprecatedClass(IntSlider, 'IntSliderWidget')
192 IntProgressWidget = DeprecatedClass(IntProgress, 'IntProgressWidget')
197 IntProgressWidget = DeprecatedClass(IntProgress, 'IntProgressWidget')
@@ -1,153 +1,156 b''
1 """Selection classes.
1 """Selection classes.
2
2
3 Represents an enumeration using a widget.
3 Represents an enumeration 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
16
17 from collections import OrderedDict
17 from collections import OrderedDict
18 from threading import Lock
18 from threading import Lock
19
19
20 from .widget import DOMWidget
20 from .widget import DOMWidget, register
21 from IPython.utils.traitlets import Unicode, List, Bool, Any, Dict, TraitError, CaselessStrEnum
21 from IPython.utils.traitlets import Unicode, List, Bool, Any, Dict, TraitError, CaselessStrEnum
22 from IPython.utils.py3compat import unicode_type
22 from IPython.utils.py3compat import unicode_type
23 from IPython.utils.warn import DeprecatedClass
23 from IPython.utils.warn import DeprecatedClass
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # SelectionWidget
26 # SelectionWidget
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 class _Selection(DOMWidget):
28 class _Selection(DOMWidget):
29 """Base class for Selection widgets
29 """Base class for Selection widgets
30
30
31 ``values`` can be specified as a list or dict. If given as a list,
31 ``values`` can be specified as a list or dict. If given as a list,
32 it will be transformed to a dict of the form ``{str(value):value}``.
32 it will be transformed to a dict of the form ``{str(value):value}``.
33 """
33 """
34
34
35 value = Any(help="Selected value")
35 value = Any(help="Selected value")
36 values = Dict(help="""Dictionary of {name: value} the user can select.
36 values = Dict(help="""Dictionary of {name: value} the user can select.
37
37
38 The keys of this dictionary are the strings that will be displayed in the UI,
38 The keys of this dictionary are the strings that will be displayed in the UI,
39 representing the actual Python choices.
39 representing the actual Python choices.
40
40
41 The keys of this dictionary are also available as value_names.
41 The keys of this dictionary are also available as value_names.
42 """)
42 """)
43 value_name = Unicode(help="The name of the selected value", sync=True)
43 value_name = Unicode(help="The name of the selected value", sync=True)
44 value_names = List(Unicode, help="""Read-only list of names for each value.
44 value_names = List(Unicode, help="""Read-only list of names for each value.
45
45
46 If values is specified as a list, this is the string representation of each element.
46 If values is specified as a list, this is the string representation of each element.
47 Otherwise, it is the keys of the values dictionary.
47 Otherwise, it is the keys of the values dictionary.
48
48
49 These strings are used to display the choices in the front-end.""", sync=True)
49 These strings are used to display the choices in the front-end.""", sync=True)
50 disabled = Bool(False, help="Enable or disable user changes", sync=True)
50 disabled = Bool(False, help="Enable or disable user changes", sync=True)
51 description = Unicode(help="Description of the value this widget represents", sync=True)
51 description = Unicode(help="Description of the value this widget represents", sync=True)
52
52
53
53
54 def __init__(self, *args, **kwargs):
54 def __init__(self, *args, **kwargs):
55 self.value_lock = Lock()
55 self.value_lock = Lock()
56 self._in_values_changed = False
56 self._in_values_changed = False
57 if 'values' in kwargs:
57 if 'values' in kwargs:
58 values = kwargs['values']
58 values = kwargs['values']
59 # convert list values to an dict of {str(v):v}
59 # convert list values to an dict of {str(v):v}
60 if isinstance(values, list):
60 if isinstance(values, list):
61 # preserve list order with an OrderedDict
61 # preserve list order with an OrderedDict
62 kwargs['values'] = OrderedDict((unicode_type(v), v) for v in values)
62 kwargs['values'] = OrderedDict((unicode_type(v), v) for v in values)
63 # python3.3 turned on hash randomization by default - this means that sometimes, randomly
63 # python3.3 turned on hash randomization by default - this means that sometimes, randomly
64 # we try to set value before setting values, due to dictionary ordering. To fix this, force
64 # we try to set value before setting values, due to dictionary ordering. To fix this, force
65 # the setting of self.values right now, before anything else runs
65 # the setting of self.values right now, before anything else runs
66 self.values = kwargs.pop('values')
66 self.values = kwargs.pop('values')
67 DOMWidget.__init__(self, *args, **kwargs)
67 DOMWidget.__init__(self, *args, **kwargs)
68 self._value_in_values()
68 self._value_in_values()
69
69
70 def _values_changed(self, name, old, new):
70 def _values_changed(self, name, old, new):
71 """Handles when the values dict has been changed.
71 """Handles when the values dict has been changed.
72
72
73 Setting values implies setting value names from the keys of the dict.
73 Setting values implies setting value names from the keys of the dict.
74 """
74 """
75 self._in_values_changed = True
75 self._in_values_changed = True
76 try:
76 try:
77 self.value_names = list(new.keys())
77 self.value_names = list(new.keys())
78 finally:
78 finally:
79 self._in_values_changed = False
79 self._in_values_changed = False
80 self._value_in_values()
80 self._value_in_values()
81
81
82 def _value_in_values(self):
82 def _value_in_values(self):
83 # ensure that the chosen value is one of the choices
83 # ensure that the chosen value is one of the choices
84 if self.values:
84 if self.values:
85 if self.value not in self.values.values():
85 if self.value not in self.values.values():
86 self.value = next(iter(self.values.values()))
86 self.value = next(iter(self.values.values()))
87
87
88 def _value_names_changed(self, name, old, new):
88 def _value_names_changed(self, name, old, new):
89 if not self._in_values_changed:
89 if not self._in_values_changed:
90 raise TraitError("value_names is a read-only proxy to values.keys(). Use the values dict instead.")
90 raise TraitError("value_names is a read-only proxy to values.keys(). Use the values dict instead.")
91
91
92 def _value_changed(self, name, old, new):
92 def _value_changed(self, name, old, new):
93 """Called when value has been changed"""
93 """Called when value has been changed"""
94 if self.value_lock.acquire(False):
94 if self.value_lock.acquire(False):
95 try:
95 try:
96 # Reverse dictionary lookup for the value name
96 # Reverse dictionary lookup for the value name
97 for k,v in self.values.items():
97 for k,v in self.values.items():
98 if new == v:
98 if new == v:
99 # set the selected value name
99 # set the selected value name
100 self.value_name = k
100 self.value_name = k
101 return
101 return
102 # undo the change, and raise KeyError
102 # undo the change, and raise KeyError
103 self.value = old
103 self.value = old
104 raise KeyError(new)
104 raise KeyError(new)
105 finally:
105 finally:
106 self.value_lock.release()
106 self.value_lock.release()
107
107
108 def _value_name_changed(self, name, old, new):
108 def _value_name_changed(self, name, old, new):
109 """Called when the value name has been changed (typically by the frontend)."""
109 """Called when the value name has been changed (typically by the frontend)."""
110 if self.value_lock.acquire(False):
110 if self.value_lock.acquire(False):
111 try:
111 try:
112 self.value = self.values[new]
112 self.value = self.values[new]
113 finally:
113 finally:
114 self.value_lock.release()
114 self.value_lock.release()
115
115
116
116
117 @register('IPython.ToggleButtons')
117 class ToggleButtons(_Selection):
118 class ToggleButtons(_Selection):
118 """Group of toggle buttons that represent an enumeration. Only one toggle
119 """Group of toggle buttons that represent an enumeration. Only one toggle
119 button can be toggled at any point in time."""
120 button can be toggled at any point in time."""
120 _view_name = Unicode('ToggleButtonsView', sync=True)
121 _view_name = Unicode('ToggleButtonsView', sync=True)
121
122
122 button_style = CaselessStrEnum(
123 button_style = CaselessStrEnum(
123 values=['primary', 'success', 'info', 'warning', 'danger', ''],
124 values=['primary', 'success', 'info', 'warning', 'danger', ''],
124 default_value='', allow_none=True, sync=True, help="""Use a
125 default_value='', allow_none=True, sync=True, help="""Use a
125 predefined styling for the buttons.""")
126 predefined styling for the buttons.""")
126
127
127
128 @register('IPython.Dropdown')
128 class Dropdown(_Selection):
129 class Dropdown(_Selection):
129 """Allows you to select a single item from a dropdown."""
130 """Allows you to select a single item from a dropdown."""
130 _view_name = Unicode('DropdownView', sync=True)
131 _view_name = Unicode('DropdownView', sync=True)
131
132
132 button_style = CaselessStrEnum(
133 button_style = CaselessStrEnum(
133 values=['primary', 'success', 'info', 'warning', 'danger', ''],
134 values=['primary', 'success', 'info', 'warning', 'danger', ''],
134 default_value='', allow_none=True, sync=True, help="""Use a
135 default_value='', allow_none=True, sync=True, help="""Use a
135 predefined styling for the buttons.""")
136 predefined styling for the buttons.""")
136
137
137
138 @register('IPython.RadioButtons')
138 class RadioButtons(_Selection):
139 class RadioButtons(_Selection):
139 """Group of radio buttons that represent an enumeration. Only one radio
140 """Group of radio buttons that represent an enumeration. Only one radio
140 button can be toggled at any point in time."""
141 button can be toggled at any point in time."""
141 _view_name = Unicode('RadioButtonsView', sync=True)
142 _view_name = Unicode('RadioButtonsView', sync=True)
142
143
143
144
145
146 @register('IPython.Select')
144 class Select(_Selection):
147 class Select(_Selection):
145 """Listbox that only allows one item to be selected at any given time."""
148 """Listbox that only allows one item to be selected at any given time."""
146 _view_name = Unicode('SelectView', sync=True)
149 _view_name = Unicode('SelectView', sync=True)
147
150
148
151
149 # Remove in IPython 4.0
152 # Remove in IPython 4.0
150 ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
153 ToggleButtonsWidget = DeprecatedClass(ToggleButtons, 'ToggleButtonsWidget')
151 DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
154 DropdownWidget = DeprecatedClass(Dropdown, 'DropdownWidget')
152 RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget')
155 RadioButtonsWidget = DeprecatedClass(RadioButtons, 'RadioButtonsWidget')
153 SelectWidget = DeprecatedClass(Select, 'SelectWidget')
156 SelectWidget = DeprecatedClass(Select, 'SelectWidget')
@@ -1,67 +1,68 b''
1 """SelectionContainer class.
1 """SelectionContainer 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_box import Box
17 from .widget_box import Box, register
18 from IPython.utils.traitlets import Unicode, Dict, CInt
18 from IPython.utils.traitlets import Unicode, Dict, CInt
19 from IPython.utils.warn import DeprecatedClass
19 from IPython.utils.warn import DeprecatedClass
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Classes
22 # Classes
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 class _SelectionContainer(Box):
24 class _SelectionContainer(Box):
25 """Base class used to display multiple child widgets."""
25 """Base class used to display multiple child widgets."""
26 _titles = Dict(help="Titles of the pages", sync=True)
26 _titles = Dict(help="Titles of the pages", sync=True)
27 selected_index = CInt(0, sync=True)
27 selected_index = CInt(0, sync=True)
28
28
29 # Public methods
29 # Public methods
30 def set_title(self, index, title):
30 def set_title(self, index, title):
31 """Sets the title of a container page.
31 """Sets the title of a container page.
32
32
33 Parameters
33 Parameters
34 ----------
34 ----------
35 index : int
35 index : int
36 Index of the container page
36 Index of the container page
37 title : unicode
37 title : unicode
38 New title"""
38 New title"""
39 self._titles[index] = title
39 self._titles[index] = title
40 self.send_state('_titles')
40 self.send_state('_titles')
41
41
42 def get_title(self, index):
42 def get_title(self, index):
43 """Gets the title of a container pages.
43 """Gets the title of a container pages.
44
44
45 Parameters
45 Parameters
46 ----------
46 ----------
47 index : int
47 index : int
48 Index of the container page"""
48 Index of the container page"""
49 if index in self._titles:
49 if index in self._titles:
50 return self._titles[index]
50 return self._titles[index]
51 else:
51 else:
52 return None
52 return None
53
53
54
54 @register('IPython.Accordion')
55 class Accordion(_SelectionContainer):
55 class Accordion(_SelectionContainer):
56 """Displays children each on a separate accordion page."""
56 """Displays children each on a separate accordion page."""
57 _view_name = Unicode('AccordionView', sync=True)
57 _view_name = Unicode('AccordionView', sync=True)
58
58
59
59
60 @register('IPython.Tab')
60 class Tab(_SelectionContainer):
61 class Tab(_SelectionContainer):
61 """Displays children each on a separate accordion tab."""
62 """Displays children each on a separate accordion tab."""
62 _view_name = Unicode('TabView', sync=True)
63 _view_name = Unicode('TabView', sync=True)
63
64
64
65
65 # Remove in IPython 4.0
66 # Remove in IPython 4.0
66 AccordionWidget = DeprecatedClass(Accordion, 'AccordionWidget')
67 AccordionWidget = DeprecatedClass(Accordion, 'AccordionWidget')
67 TabWidget = DeprecatedClass(Tab, 'TabWidget')
68 TabWidget = DeprecatedClass(Tab, 'TabWidget')
@@ -1,87 +1,91 b''
1 """String class.
1 """String 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 from .widget import DOMWidget, CallbackDispatcher
16 from .widget import DOMWidget, CallbackDispatcher, register
17 from IPython.utils.traitlets import Unicode, Bool
17 from IPython.utils.traitlets import Unicode, Bool
18 from IPython.utils.warn import DeprecatedClass
18 from IPython.utils.warn import DeprecatedClass
19
19
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21 # Classes
21 # Classes
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 class _String(DOMWidget):
23 class _String(DOMWidget):
24 """Base class used to create widgets that represent a string."""
24 """Base class used to create widgets that represent a string."""
25 value = Unicode(help="String value", sync=True)
25 value = Unicode(help="String value", sync=True)
26 disabled = Bool(False, help="Enable or disable user changes", sync=True)
26 disabled = Bool(False, help="Enable or disable user changes", sync=True)
27 description = Unicode(help="Description of the value this widget represents", sync=True)
27 description = Unicode(help="Description of the value this widget represents", sync=True)
28 placeholder = Unicode("", help="Placeholder text to display when nothing has been typed", sync=True)
28 placeholder = Unicode("", help="Placeholder text to display when nothing has been typed", sync=True)
29
29
30
30
31 @register('IPython.HTML')
31 class HTML(_String):
32 class HTML(_String):
32 """Renders the string `value` as HTML."""
33 """Renders the string `value` as HTML."""
33 _view_name = Unicode('HTMLView', sync=True)
34 _view_name = Unicode('HTMLView', sync=True)
34
35
35
36
37 @register('IPython.Latex')
36 class Latex(_String):
38 class Latex(_String):
37 """Renders math inside the string `value` as Latex (requires $ $ or $$ $$
39 """Renders math inside the string `value` as Latex (requires $ $ or $$ $$
38 and similar latex tags)."""
40 and similar latex tags)."""
39 _view_name = Unicode('LatexView', sync=True)
41 _view_name = Unicode('LatexView', sync=True)
40
42
41
43
44 @register('IPython.Textarea')
42 class Textarea(_String):
45 class Textarea(_String):
43 """Multiline text area widget."""
46 """Multiline text area widget."""
44 _view_name = Unicode('TextareaView', sync=True)
47 _view_name = Unicode('TextareaView', sync=True)
45
48
46 def scroll_to_bottom(self):
49 def scroll_to_bottom(self):
47 self.send({"method": "scroll_to_bottom"})
50 self.send({"method": "scroll_to_bottom"})
48
51
49
52
53 @register('IPython.Text')
50 class Text(_String):
54 class Text(_String):
51 """Single line textbox widget."""
55 """Single line textbox widget."""
52 _view_name = Unicode('TextView', sync=True)
56 _view_name = Unicode('TextView', sync=True)
53
57
54 def __init__(self, **kwargs):
58 def __init__(self, **kwargs):
55 super(Text, self).__init__(**kwargs)
59 super(Text, self).__init__(**kwargs)
56 self._submission_callbacks = CallbackDispatcher()
60 self._submission_callbacks = CallbackDispatcher()
57 self.on_msg(self._handle_string_msg)
61 self.on_msg(self._handle_string_msg)
58
62
59 def _handle_string_msg(self, _, content):
63 def _handle_string_msg(self, _, content):
60 """Handle a msg from the front-end.
64 """Handle a msg from the front-end.
61
65
62 Parameters
66 Parameters
63 ----------
67 ----------
64 content: dict
68 content: dict
65 Content of the msg."""
69 Content of the msg."""
66 if content.get('event', '') == 'submit':
70 if content.get('event', '') == 'submit':
67 self._submission_callbacks(self)
71 self._submission_callbacks(self)
68
72
69 def on_submit(self, callback, remove=False):
73 def on_submit(self, callback, remove=False):
70 """(Un)Register a callback to handle text submission.
74 """(Un)Register a callback to handle text submission.
71
75
72 Triggered when the user clicks enter.
76 Triggered when the user clicks enter.
73
77
74 Parameters
78 Parameters
75 ----------
79 ----------
76 callback: callable
80 callback: callable
77 Will be called with exactly one argument: the Widget instance
81 Will be called with exactly one argument: the Widget instance
78 remove: bool (optional)
82 remove: bool (optional)
79 Whether to unregister the callback"""
83 Whether to unregister the callback"""
80 self._submission_callbacks.register_callback(callback, remove=remove)
84 self._submission_callbacks.register_callback(callback, remove=remove)
81
85
82
86
83 # Remove in IPython 4.0
87 # Remove in IPython 4.0
84 HTMLWidget = DeprecatedClass(HTML, 'HTMLWidget')
88 HTMLWidget = DeprecatedClass(HTML, 'HTMLWidget')
85 LatexWidget = DeprecatedClass(Latex, 'LatexWidget')
89 LatexWidget = DeprecatedClass(Latex, 'LatexWidget')
86 TextareaWidget = DeprecatedClass(Textarea, 'TextareaWidget')
90 TextareaWidget = DeprecatedClass(Textarea, 'TextareaWidget')
87 TextWidget = DeprecatedClass(Text, 'TextWidget')
91 TextWidget = DeprecatedClass(Text, 'TextWidget')
General Comments 0
You need to be logged in to leave comments. Login now