##// END OF EJS Templates
Added support for multiple model views in one widget area.
Added support for multiple model views in one widget area.

File last commit:

r14278:f58ca8b9
r14281:155c18e8
Show More
widget.py
171 lines | 5.0 KiB | text/x-python | PythonLexer
from copy import copy
from glob import glob
import uuid
import sys
import os
import IPython
from IPython.kernel.comm import Comm
from IPython.config import LoggingConfigurable
from IPython.utils.traitlets import Unicode, Dict, List
from IPython.display import Javascript, display
from IPython.utils.py3compat import string_types
def init_widget_js():
path = os.path.split(os.path.abspath( __file__ ))[0]
for filepath in glob(os.path.join(path, "*.py")):
filename = os.path.split(filepath)[1]
name = filename.rsplit('.', 1)[0]
if not (name == 'widget' or name == '__init__') and name.startswith('widget_'):
# Remove 'widget_' from the start of the name before compiling the path.
js_path = '../static/notebook/js/widgets/%s.js' % name[7:]
display(Javascript(data='$.getScript("%s");' % js_path))
class Widget(LoggingConfigurable):
### Public declarations
target_name = Unicode('widget')
default_view_name = Unicode()
### Private/protected declarations
_keys = []
_property_lock = False
_parent = None
_children = []
_css = Dict()
### Public constructor
def __init__(self, parent=None, **kwargs):
super(Widget, self).__init__(**kwargs)
self._children = []
if parent is not None:
parent._children.append(self)
self._parent = parent
self.comm = None
# Register after init to allow default values to be specified
self.on_trait_change(self._handle_property_changed, self.keys)
def __del__(self):
self.close()
def close(self):
self.comm.close()
del self.comm
### Properties
def _get_parent(self):
return self._parent
parent = property(_get_parent)
def _get_children(self):
return copy(self._children)
children = property(_get_children)
def _get_keys(self):
keys = ['_css']
keys.extend(self._keys)
return keys
keys = property(_get_keys)
def _get_css(self, key, selector=""):
if selector in self._css and key in self._css[selector]:
return self._css[selector][key]
else:
return None
def _set_css(self, value, key, selector=""):
if selector not in self._css:
self._css[selector] = {}
# Only update the property if it has changed.
if not (key in self._css[selector] and value in self._css[selector][key]):
self._css[selector][key] = value
self.send_state() # Send new state to client.
css = property(_get_css, _set_css)
### Event handlers
def _handle_msg(self, msg):
# Handle backbone sync methods CREATE, PATCH, and UPDATE
sync_method = msg['content']['data']['sync_method']
sync_data = msg['content']['data']['sync_data']
self._handle_recieve_state(sync_data) # handles all methods
def _handle_recieve_state(self, sync_data):
self._property_lock = True
try:
# Use _keys instead of keys - Don't get retrieve the css from the client side.
for name in self._keys:
if name in sync_data:
setattr(self, name, sync_data[name])
finally:
self._property_lock = False
def _handle_property_changed(self, name, old, new):
if not self._property_lock and self.comm is not None:
# TODO: Validate properties.
# Send new state to frontend
self.send_state(key=name)
def _handle_close(self):
self.comm = None
### Public methods
def _repr_widget_(self, view_name=None):
if not view_name:
view_name = self.default_view_name
# Create a comm.
if self.comm is None:
self.comm = Comm(target_name=self.target_name)
self.comm.on_msg(self._handle_msg)
self.comm.on_close(self._handle_close)
# Make sure model is syncronized
self.send_state()
# Show view.
if self.parent is None:
self.comm.send({"method": "display", "view_name": view_name})
else:
self.comm.send({"method": "display",
"view_name": view_name,
"parent": self.parent.comm.comm_id})
# Now display children if any.
for child in self.children:
child._repr_widget_()
return None
def send_state(self, key=None):
state = {}
# If a key is provided, just send the state of that key.
keys = []
if key is None:
keys.extend(self.keys)
else:
keys.append(key)
for key in self.keys:
try:
state[key] = getattr(self, key)
except Exception as e:
pass # Eat errors, nom nom nom
self.comm.send({"method": "update",
"state": state})