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