##// END OF EJS Templates
Get rid of keys magic; make the keys very explicit
Jason Grout -
Show More
@@ -1,460 +1,443 b''
1 """Base Widget class. Allows user to create widgets in the backend that render
1 """Base Widget class. Allows user to create widgets in the backend that render
2 in the IPython notebook frontend.
2 in the IPython notebook frontend.
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 copy import copy
15 from copy import copy
16 from glob import glob
16 from glob import glob
17 import uuid
17 import uuid
18 import sys
18 import sys
19 import os
19 import os
20 import inspect
20 import inspect
21 import types
21 import types
22
22
23 import IPython
23 import IPython
24 from IPython.kernel.comm import Comm
24 from IPython.kernel.comm import Comm
25 from IPython.config import LoggingConfigurable
25 from IPython.config import LoggingConfigurable
26 from IPython.utils.traitlets import Unicode, Dict, List, Instance, Bool
26 from IPython.utils.traitlets import Unicode, Dict, List, Instance, Bool
27 from IPython.display import Javascript, display
27 from IPython.display import Javascript, display
28 from IPython.utils.py3compat import string_types
28 from IPython.utils.py3compat import string_types
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Classes
31 # Classes
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33
33
34 class BaseWidget(LoggingConfigurable):
34 class BaseWidget(LoggingConfigurable):
35
35
36 # Shared declarations (Class level)
36 # Shared declarations (Class level)
37 _keys = List(Unicode, default_value = [],
38 help="List of keys comprising the state of the model.", allow_none=False)
39 widget_construction_callback = None
37 widget_construction_callback = None
40
38
39 keys = ['default_view_name']
40
41 def on_widget_constructed(callback):
41 def on_widget_constructed(callback):
42 """Class method, registers a callback to be called when a widget is
42 """Class method, registers a callback to be called when a widget is
43 constructed. The callback must have the following signature:
43 constructed. The callback must have the following signature:
44 callback(widget)"""
44 callback(widget)"""
45 BaseWidget.widget_construction_callback = callback
45 BaseWidget.widget_construction_callback = callback
46
46
47 def _handle_widget_constructed(widget):
47 def _handle_widget_constructed(widget):
48 """Class method, called when a widget is constructed."""
48 """Class method, called when a widget is constructed."""
49 if BaseWidget.widget_construction_callback is not None and callable(BaseWidget.widget_construction_callback):
49 if BaseWidget.widget_construction_callback is not None and callable(BaseWidget.widget_construction_callback):
50 BaseWidget.widget_construction_callback(widget)
50 BaseWidget.widget_construction_callback(widget)
51
51
52
52
53
53
54 # Public declarations (Instance level)
54 # Public declarations (Instance level)
55 target_name = Unicode('widget', help="""Name of the backbone model
55 target_name = Unicode('widget', help="""Name of the backbone model
56 registered in the frontend to create and sync this widget with.""")
56 registered in the frontend to create and sync this widget with.""")
57 default_view_name = Unicode(help="""Default view registered in the frontend
57 default_view_name = Unicode(help="""Default view registered in the frontend
58 to use to represent the widget.""")
58 to use to represent the widget.""")
59
59
60 # Private/protected declarations
60 # Private/protected declarations
61 # todo: change this to a context manager
61 # todo: change this to a context manager
62 _property_lock = (None, None) # Last updated (key, value) from the front-end. Prevents echo.
62 _property_lock = (None, None) # Last updated (key, value) from the front-end. Prevents echo.
63 _displayed = False
63 _displayed = False
64 _comm = Instance('IPython.kernel.comm.Comm')
64 _comm = Instance('IPython.kernel.comm.Comm')
65
65
66 def __init__(self, **kwargs):
66 def __init__(self, **kwargs):
67 """Public constructor
67 """Public constructor
68 """
68 """
69 self._display_callbacks = []
69 self._display_callbacks = []
70 self._msg_callbacks = []
70 self._msg_callbacks = []
71 super(BaseWidget, self).__init__(**kwargs)
71 super(BaseWidget, self).__init__(**kwargs)
72
72
73 self.on_trait_change(self._handle_property_changed, self.keys)
73 self.on_trait_change(self._handle_property_changed, self.keys)
74 BaseWidget._handle_widget_constructed(self)
74 BaseWidget._handle_widget_constructed(self)
75
75
76 def __del__(self):
76 def __del__(self):
77 """Object disposal"""
77 """Object disposal"""
78 self.close()
78 self.close()
79
79
80
80
81 def close(self):
81 def close(self):
82 """Close method. Closes the widget which closes the underlying comm.
82 """Close method. Closes the widget which closes the underlying comm.
83 When the comm is closed, all of the widget views are automatically
83 When the comm is closed, all of the widget views are automatically
84 removed from the frontend."""
84 removed from the frontend."""
85 self._close_communication()
85 self._close_communication()
86
86
87 _keys = ['default_view_name']
88
89 # Properties
90 @property
91 def keys(self):
92 """Lazily accumulate _keys from all superclasses and cache them in this class"""
93 keys=[]
94 for c in self.__class__.mro():
95 if hasattr(c, '_keys'):
96 keys.extend(getattr(c,'_keys'))
97 else:
98 break
99 # cache so future lookups are fast
100 self.__class__.x = keys
101 return keys
102
103 @property
87 @property
104 def comm(self):
88 def comm(self):
105 if self._comm is None:
89 if self._comm is None:
106 self._open_communication()
90 self._open_communication()
107 return self._comm
91 return self._comm
108
92
109 @property
93 @property
110 def model_id(self):
94 def model_id(self):
111 return self.comm.comm_id
95 return self.comm.comm_id
112
96
113 # Event handlers
97 # Event handlers
114 def _handle_msg(self, msg):
98 def _handle_msg(self, msg):
115 """Called when a msg is recieved from the frontend"""
99 """Called when a msg is recieved from the frontend"""
116 data = msg['content']['data']
100 data = msg['content']['data']
117 method = data['method']
101 method = data['method']
118
102
119 # Handle backbone sync methods CREATE, PATCH, and UPDATE
103 # Handle backbone sync methods CREATE, PATCH, and UPDATE
120 if method == 'backbone':
104 if method == 'backbone':
121 if 'sync_method' in data and 'sync_data' in data:
105 if 'sync_method' in data and 'sync_data' in data:
122 sync_method = data['sync_method']
106 sync_method = data['sync_method']
123 sync_data = data['sync_data']
107 sync_data = data['sync_data']
124 self._handle_recieve_state(sync_data) # handles all methods
108 self._handle_recieve_state(sync_data) # handles all methods
125
109
126 # Handle a custom msg from the front-end
110 # Handle a custom msg from the front-end
127 elif method == 'custom':
111 elif method == 'custom':
128 if 'custom_content' in data:
112 if 'custom_content' in data:
129 self._handle_custom_msg(data['custom_content'])
113 self._handle_custom_msg(data['custom_content'])
130
114
131 def _handle_custom_msg(self, content):
115 def _handle_custom_msg(self, content):
132 """Called when a custom msg is recieved."""
116 """Called when a custom msg is recieved."""
133 for handler in self._msg_callbacks:
117 for handler in self._msg_callbacks:
134 if callable(handler):
118 if callable(handler):
135 argspec = inspect.getargspec(handler)
119 argspec = inspect.getargspec(handler)
136 nargs = len(argspec[0])
120 nargs = len(argspec[0])
137
121
138 # Bound methods have an additional 'self' argument
122 # Bound methods have an additional 'self' argument
139 if isinstance(handler, types.MethodType):
123 if isinstance(handler, types.MethodType):
140 nargs -= 1
124 nargs -= 1
141
125
142 # Call the callback
126 # Call the callback
143 if nargs == 1:
127 if nargs == 1:
144 handler(content)
128 handler(content)
145 elif nargs == 2:
129 elif nargs == 2:
146 handler(self, content)
130 handler(self, content)
147 else:
131 else:
148 raise TypeError('Widget msg callback must ' \
132 raise TypeError('Widget msg callback must ' \
149 'accept 1 or 2 arguments, not %d.' % nargs)
133 'accept 1 or 2 arguments, not %d.' % nargs)
150
134
151
135
152 def _handle_recieve_state(self, sync_data):
136 def _handle_recieve_state(self, sync_data):
153 """Called when a state is recieved from the frontend."""
137 """Called when a state is recieved from the frontend."""
154 # Use _keys instead of keys - Don't get retrieve the css from the client side.
138 for name in self.keys:
155 for name in self._keys:
156 if name in sync_data:
139 if name in sync_data:
157 try:
140 try:
158 self._property_lock = (name, sync_data[name])
141 self._property_lock = (name, sync_data[name])
159 setattr(self, name, sync_data[name])
142 setattr(self, name, sync_data[name])
160 finally:
143 finally:
161 self._property_lock = (None, None)
144 self._property_lock = (None, None)
162
145
163
146
164 def _handle_property_changed(self, name, old, new):
147 def _handle_property_changed(self, name, old, new):
165 """Called when a property has been changed."""
148 """Called when a property has been changed."""
166 # Make sure this isn't information that the front-end just sent us.
149 # Make sure this isn't information that the front-end just sent us.
167 if self._property_lock[0] != name and self._property_lock[1] != new:
150 if self._property_lock[0] != name and self._property_lock[1] != new:
168 # Send new state to frontend
151 # Send new state to frontend
169 self.send_state(key=name)
152 self.send_state(key=name)
170
153
171 def _handle_displayed(self, **kwargs):
154 def _handle_displayed(self, **kwargs):
172 """Called when a view has been displayed for this widget instance
155 """Called when a view has been displayed for this widget instance
173
156
174 Parameters
157 Parameters
175 ----------
158 ----------
176 [view_name]: unicode (optional kwarg)
159 [view_name]: unicode (optional kwarg)
177 Name of the view that was displayed."""
160 Name of the view that was displayed."""
178 for handler in self._display_callbacks:
161 for handler in self._display_callbacks:
179 if callable(handler):
162 if callable(handler):
180 argspec = inspect.getargspec(handler)
163 argspec = inspect.getargspec(handler)
181 nargs = len(argspec[0])
164 nargs = len(argspec[0])
182
165
183 # Bound methods have an additional 'self' argument
166 # Bound methods have an additional 'self' argument
184 if isinstance(handler, types.MethodType):
167 if isinstance(handler, types.MethodType):
185 nargs -= 1
168 nargs -= 1
186
169
187 # Call the callback
170 # Call the callback
188 if nargs == 0:
171 if nargs == 0:
189 handler()
172 handler()
190 elif nargs == 1:
173 elif nargs == 1:
191 handler(self)
174 handler(self)
192 elif nargs == 2:
175 elif nargs == 2:
193 handler(self, kwargs.get('view_name', None))
176 handler(self, kwargs.get('view_name', None))
194 else:
177 else:
195 handler(self, **kwargs)
178 handler(self, **kwargs)
196
179
197 # Public methods
180 # Public methods
198 def send_state(self, key=None):
181 def send_state(self, key=None):
199 """Sends the widget state, or a piece of it, to the frontend.
182 """Sends the widget state, or a piece of it, to the frontend.
200
183
201 Parameters
184 Parameters
202 ----------
185 ----------
203 key : unicode (optional)
186 key : unicode (optional)
204 A single property's name to sync with the frontend.
187 A single property's name to sync with the frontend.
205 """
188 """
206 self._send({"method": "update",
189 self._send({"method": "update",
207 "state": self.get_state()})
190 "state": self.get_state()})
208
191
209 def get_state(self, key=None):
192 def get_state(self, key=None):
210 """Gets the widget state, or a piece of it.
193 """Gets the widget state, or a piece of it.
211
194
212 Parameters
195 Parameters
213 ----------
196 ----------
214 key : unicode (optional)
197 key : unicode (optional)
215 A single property's name to get.
198 A single property's name to get.
216 """
199 """
217 state = {}
200 state = {}
218
201
219 # If a key is provided, just send the state of that key.
202 # If a key is provided, just send the state of that key.
220 if key is None:
203 if key is None:
221 keys = self.keys[:]
204 keys = self.keys[:]
222 else:
205 else:
223 keys = [key]
206 keys = [key]
224 for k in keys:
207 for k in keys:
225 value = getattr(self, k)
208 value = getattr(self, k)
226
209
227 # a more elegant solution to encoding BaseWidgets would be
210 # a more elegant solution to encoding BaseWidgets would be
228 # to tap into the JSON encoder and teach it how to deal
211 # to tap into the JSON encoder and teach it how to deal
229 # with BaseWidget objects, or maybe just teach the JSON
212 # with BaseWidget objects, or maybe just teach the JSON
230 # encoder to look for a _repr_json property before giving
213 # encoder to look for a _repr_json property before giving
231 # up encoding
214 # up encoding
232 if isinstance(value, BaseWidget):
215 if isinstance(value, BaseWidget):
233 value = value.model_id
216 value = value.model_id
234 elif isinstance(value, list) and len(value)>0 and isinstance(value[0], BaseWidget):
217 elif isinstance(value, list) and len(value)>0 and isinstance(value[0], BaseWidget):
235 # assume all elements of the list are widgets
218 # assume all elements of the list are widgets
236 value = [i.model_id for i in value]
219 value = [i.model_id for i in value]
237 state[k] = value
220 state[k] = value
238 return state
221 return state
239
222
240
223
241 def send(self, content):
224 def send(self, content):
242 """Sends a custom msg to the widget model in the front-end.
225 """Sends a custom msg to the widget model in the front-end.
243
226
244 Parameters
227 Parameters
245 ----------
228 ----------
246 content : dict
229 content : dict
247 Content of the message to send.
230 Content of the message to send.
248 """
231 """
249 self._send({"method": "custom",
232 self._send({"method": "custom",
250 "custom_content": content})
233 "custom_content": content})
251
234
252
235
253 def on_msg(self, callback, remove=False):
236 def on_msg(self, callback, remove=False):
254 """Register a callback for when a custom msg is recieved from the front-end
237 """Register a callback for when a custom msg is recieved from the front-end
255
238
256 Parameters
239 Parameters
257 ----------
240 ----------
258 callback: method handler
241 callback: method handler
259 Can have a signature of:
242 Can have a signature of:
260 - callback(content)
243 - callback(content)
261 - callback(sender, content)
244 - callback(sender, content)
262 remove: bool
245 remove: bool
263 True if the callback should be unregistered."""
246 True if the callback should be unregistered."""
264 if remove and callback in self._msg_callbacks:
247 if remove and callback in self._msg_callbacks:
265 self._msg_callbacks.remove(callback)
248 self._msg_callbacks.remove(callback)
266 elif not remove and not callback in self._msg_callbacks:
249 elif not remove and not callback in self._msg_callbacks:
267 self._msg_callbacks.append(callback)
250 self._msg_callbacks.append(callback)
268
251
269
252
270 def on_displayed(self, callback, remove=False):
253 def on_displayed(self, callback, remove=False):
271 """Register a callback to be called when the widget has been displayed
254 """Register a callback to be called when the widget has been displayed
272
255
273 Parameters
256 Parameters
274 ----------
257 ----------
275 callback: method handler
258 callback: method handler
276 Can have a signature of:
259 Can have a signature of:
277 - callback()
260 - callback()
278 - callback(sender)
261 - callback(sender)
279 - callback(sender, view_name)
262 - callback(sender, view_name)
280 - callback(sender, **kwargs)
263 - callback(sender, **kwargs)
281 kwargs from display call passed through without modification.
264 kwargs from display call passed through without modification.
282 remove: bool
265 remove: bool
283 True if the callback should be unregistered."""
266 True if the callback should be unregistered."""
284 if remove and callback in self._display_callbacks:
267 if remove and callback in self._display_callbacks:
285 self._display_callbacks.remove(callback)
268 self._display_callbacks.remove(callback)
286 elif not remove and not callback in self._display_callbacks:
269 elif not remove and not callback in self._display_callbacks:
287 self._display_callbacks.append(callback)
270 self._display_callbacks.append(callback)
288
271
289
272
290 # Support methods
273 # Support methods
291 def _repr_widget_(self, **kwargs):
274 def _repr_widget_(self, **kwargs):
292 """Function that is called when `IPython.display.display` is called on
275 """Function that is called when `IPython.display.display` is called on
293 the widget.
276 the widget.
294
277
295 Parameters
278 Parameters
296 ----------
279 ----------
297 view_name: unicode (optional)
280 view_name: unicode (optional)
298 View to display in the frontend. Overrides default_view_name."""
281 View to display in the frontend. Overrides default_view_name."""
299 view_name = kwargs.get('view_name', self.default_view_name)
282 view_name = kwargs.get('view_name', self.default_view_name)
300
283
301 # Create a communication.
284 # Create a communication.
302 self._open_communication()
285 self._open_communication()
303
286
304 # Make sure model is syncronized
287 # Make sure model is syncronized
305 self.send_state()
288 self.send_state()
306
289
307 # Show view.
290 # Show view.
308 self._send({"method": "display", "view_name": view_name})
291 self._send({"method": "display", "view_name": view_name})
309 self._displayed = True
292 self._displayed = True
310 self._handle_displayed(**kwargs)
293 self._handle_displayed(**kwargs)
311
294
312
295
313 def _open_communication(self):
296 def _open_communication(self):
314 """Opens a communication with the front-end."""
297 """Opens a communication with the front-end."""
315 # Create a comm.
298 # Create a comm.
316 if self._comm is None:
299 if self._comm is None:
317 self._comm = Comm(target_name=self.target_name)
300 self._comm = Comm(target_name=self.target_name)
318 self._comm.on_msg(self._handle_msg)
301 self._comm.on_msg(self._handle_msg)
319 self._comm.on_close(self._close_communication)
302 self._comm.on_close(self._close_communication)
320
303
321 # first update
304 # first update
322 self.send_state()
305 self.send_state()
323
306
324
307
325 def _close_communication(self):
308 def _close_communication(self):
326 """Closes a communication with the front-end."""
309 """Closes a communication with the front-end."""
327 if self._comm is not None:
310 if self._comm is not None:
328 try:
311 try:
329 self._comm.close()
312 self._comm.close()
330 finally:
313 finally:
331 self._comm = None
314 self._comm = None
332
315
333
316
334 def _send(self, msg):
317 def _send(self, msg):
335 """Sends a message to the model in the front-end"""
318 """Sends a message to the model in the front-end"""
336 if self._comm is not None:
319 if self._comm is not None:
337 self._comm.send(msg)
320 self._comm.send(msg)
338 return True
321 return True
339 else:
322 else:
340 return False
323 return False
341
324
342
325
343 class ViewWidget(BaseWidget):
326 class ViewWidget(BaseWidget):
344 target_name = Unicode('ViewModel')
327 target_name = Unicode('ViewModel')
345
328
346 def __init__(self, widget, view):
329 def __init__(self, widget, view):
347 self.default_view_name = view
330 self.default_view_name = view
348 self.widget = widget
331 self.widget = widget
349
332
350
333
351 class Widget(BaseWidget):
334 class Widget(BaseWidget):
352 visible = Bool(True, help="Whether or not the widget is visible.")
335 visible = Bool(True, help="Whether or not the widget is visible.")
353
336
354 # Private/protected declarations
337 # Private/protected declarations
355 _css = Dict() # Internal CSS property dict
338 _css = Dict() # Internal CSS property dict
356
339
357 _keys = ['visible', '_css']
340 keys = ['visible', '_css'] + BaseWidget.keys
358
341
359 def get_css(self, key, selector=""):
342 def get_css(self, key, selector=""):
360 """Get a CSS property of the widget. Note, this function does not
343 """Get a CSS property of the widget. Note, this function does not
361 actually request the CSS from the front-end; Only properties that have
344 actually request the CSS from the front-end; Only properties that have
362 been set with set_css can be read.
345 been set with set_css can be read.
363
346
364 Parameters
347 Parameters
365 ----------
348 ----------
366 key: unicode
349 key: unicode
367 CSS key
350 CSS key
368 selector: unicode (optional)
351 selector: unicode (optional)
369 JQuery selector used when the CSS key/value was set.
352 JQuery selector used when the CSS key/value was set.
370 """
353 """
371 if selector in self._css and key in self._css[selector]:
354 if selector in self._css and key in self._css[selector]:
372 return self._css[selector][key]
355 return self._css[selector][key]
373 else:
356 else:
374 return None
357 return None
375
358
376
359
377 def set_css(self, *args, **kwargs):
360 def set_css(self, *args, **kwargs):
378 """Set one or more CSS properties of the widget (shared among all of the
361 """Set one or more CSS properties of the widget (shared among all of the
379 views). This function has two signatures:
362 views). This function has two signatures:
380 - set_css(css_dict, [selector=''])
363 - set_css(css_dict, [selector=''])
381 - set_css(key, value, [selector=''])
364 - set_css(key, value, [selector=''])
382
365
383 Parameters
366 Parameters
384 ----------
367 ----------
385 css_dict : dict
368 css_dict : dict
386 CSS key/value pairs to apply
369 CSS key/value pairs to apply
387 key: unicode
370 key: unicode
388 CSS key
371 CSS key
389 value
372 value
390 CSS value
373 CSS value
391 selector: unicode (optional)
374 selector: unicode (optional)
392 JQuery selector to use to apply the CSS key/value.
375 JQuery selector to use to apply the CSS key/value.
393 """
376 """
394 selector = kwargs.get('selector', '')
377 selector = kwargs.get('selector', '')
395
378
396 # Signature 1: set_css(css_dict, [selector=''])
379 # Signature 1: set_css(css_dict, [selector=''])
397 if len(args) == 1:
380 if len(args) == 1:
398 if isinstance(args[0], dict):
381 if isinstance(args[0], dict):
399 for (key, value) in args[0].items():
382 for (key, value) in args[0].items():
400 self.set_css(key, value, selector=selector)
383 self.set_css(key, value, selector=selector)
401 else:
384 else:
402 raise Exception('css_dict must be a dict.')
385 raise Exception('css_dict must be a dict.')
403
386
404 # Signature 2: set_css(key, value, [selector=''])
387 # Signature 2: set_css(key, value, [selector=''])
405 elif len(args) == 2 or len(args) == 3:
388 elif len(args) == 2 or len(args) == 3:
406
389
407 # Selector can be a positional arg if it's the 3rd value
390 # Selector can be a positional arg if it's the 3rd value
408 if len(args) == 3:
391 if len(args) == 3:
409 selector = args[2]
392 selector = args[2]
410 if selector not in self._css:
393 if selector not in self._css:
411 self._css[selector] = {}
394 self._css[selector] = {}
412
395
413 # Only update the property if it has changed.
396 # Only update the property if it has changed.
414 key = args[0]
397 key = args[0]
415 value = args[1]
398 value = args[1]
416 if not (key in self._css[selector] and value in self._css[selector][key]):
399 if not (key in self._css[selector] and value in self._css[selector][key]):
417 self._css[selector][key] = value
400 self._css[selector][key] = value
418 self.send_state('_css') # Send new state to client.
401 self.send_state('_css') # Send new state to client.
419 else:
402 else:
420 raise Exception('set_css only accepts 1-3 arguments')
403 raise Exception('set_css only accepts 1-3 arguments')
421
404
422
405
423 def add_class(self, class_name, selector=""):
406 def add_class(self, class_name, selector=""):
424 """Add class[es] to a DOM element
407 """Add class[es] to a DOM element
425
408
426 Parameters
409 Parameters
427 ----------
410 ----------
428 class_name: unicode
411 class_name: unicode
429 Class name(s) to add to the DOM element(s). Multiple class names
412 Class name(s) to add to the DOM element(s). Multiple class names
430 must be space separated.
413 must be space separated.
431 selector: unicode (optional)
414 selector: unicode (optional)
432 JQuery selector to select the DOM element(s) that the class(es) will
415 JQuery selector to select the DOM element(s) that the class(es) will
433 be added to.
416 be added to.
434 """
417 """
435 self.send({"msg_type": "add_class",
418 self.send({"msg_type": "add_class",
436 "class_list": class_name,
419 "class_list": class_name,
437 "selector": selector})
420 "selector": selector})
438
421
439
422
440 def remove_class(self, class_name, selector=""):
423 def remove_class(self, class_name, selector=""):
441 """Remove class[es] from a DOM element
424 """Remove class[es] from a DOM element
442
425
443 Parameters
426 Parameters
444 ----------
427 ----------
445 class_name: unicode
428 class_name: unicode
446 Class name(s) to remove from the DOM element(s). Multiple class
429 Class name(s) to remove from the DOM element(s). Multiple class
447 names must be space separated.
430 names must be space separated.
448 selector: unicode (optional)
431 selector: unicode (optional)
449 JQuery selector to select the DOM element(s) that the class(es) will
432 JQuery selector to select the DOM element(s) that the class(es) will
450 be removed from.
433 be removed from.
451 """
434 """
452 self.send({"msg_type": "remove_class",
435 self.send({"msg_type": "remove_class",
453 "class_list": class_name,
436 "class_list": class_name,
454 "selector": selector})
437 "selector": selector})
455
438
456
439
457 def view(self, view_name=None):
440 def view(self, view_name=None):
458 """Return a widget that can be displayed to display this widget using
441 """Return a widget that can be displayed to display this widget using
459 a non-default view"""
442 a non-default view"""
460 return ViewWidget(self, view_name)
443 return ViewWidget(self, view_name)
General Comments 0
You need to be logged in to leave comments. Login now