##// END OF EJS Templates
Use relative paths for widget js jquery imports
Jonathan Frederic -
Show More
@@ -1,291 +1,291 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
20
21 import IPython
21 import IPython
22 from IPython.kernel.comm import Comm
22 from IPython.kernel.comm import Comm
23 from IPython.config import LoggingConfigurable
23 from IPython.config import LoggingConfigurable
24 from IPython.utils.traitlets import Unicode, Dict, List, Instance, Bool
24 from IPython.utils.traitlets import Unicode, Dict, List, Instance, Bool
25 from IPython.display import Javascript, display
25 from IPython.display import Javascript, display
26 from IPython.utils.py3compat import string_types
26 from IPython.utils.py3compat import string_types
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # Shared
29 # Shared
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 def init_widget_js():
31 def init_widget_js():
32 path = os.path.split(os.path.abspath( __file__ ))[0]
32 path = os.path.split(os.path.abspath( __file__ ))[0]
33 for filepath in glob(os.path.join(path, "*.py")):
33 for filepath in glob(os.path.join(path, "*.py")):
34 filename = os.path.split(filepath)[1]
34 filename = os.path.split(filepath)[1]
35 name = filename.rsplit('.', 1)[0]
35 name = filename.rsplit('.', 1)[0]
36 if not (name == 'widget' or name == '__init__') and name.startswith('widget_'):
36 if not (name == 'widget' or name == '__init__') and name.startswith('widget_'):
37 # Remove 'widget_' from the start of the name before compiling the path.
37 # Remove 'widget_' from the start of the name before compiling the path.
38 js_path = '/static/notebook/js/widgets/%s.js' % name[7:]
38 js_path = '../static/notebook/js/widgets/%s.js' % name[7:]
39 display(Javascript(data='$.getScript("%s");' % js_path), exclude="text/plain")
39 display(Javascript(data='$.getScript("%s");' % js_path), exclude="text/plain")
40
40
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Classes
43 # Classes
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45 class Widget(LoggingConfigurable):
45 class Widget(LoggingConfigurable):
46
46
47 # Shared declarations
47 # Shared declarations
48 _keys = []
48 _keys = []
49
49
50 # Public declarations
50 # Public declarations
51 target_name = Unicode('widget', help="""Name of the backbone model
51 target_name = Unicode('widget', help="""Name of the backbone model
52 registered in the frontend to create and sync this widget with.""")
52 registered in the frontend to create and sync this widget with.""")
53 default_view_name = Unicode(help="""Default view registered in the frontend
53 default_view_name = Unicode(help="""Default view registered in the frontend
54 to use to represent the widget.""")
54 to use to represent the widget.""")
55 parent = Instance('IPython.html.widgets.widget.Widget')
55 parent = Instance('IPython.html.widgets.widget.Widget')
56 visible = Bool(True, help="Whether or not the widget is visible.")
56 visible = Bool(True, help="Whether or not the widget is visible.")
57
57
58 def _parent_changed(self, name, old, new):
58 def _parent_changed(self, name, old, new):
59 if self._displayed:
59 if self._displayed:
60 raise Exception('Parent cannot be set because widget has been displayed.')
60 raise Exception('Parent cannot be set because widget has been displayed.')
61 elif new == self:
61 elif new == self:
62 raise Exception('Parent cannot be set to self.')
62 raise Exception('Parent cannot be set to self.')
63 else:
63 else:
64
64
65 # Parent/child association
65 # Parent/child association
66 if new is not None and not self in new._children:
66 if new is not None and not self in new._children:
67 new._children.append(self)
67 new._children.append(self)
68 if old is not None and self in old._children:
68 if old is not None and self in old._children:
69 old._children.remove(self)
69 old._children.remove(self)
70
70
71 # Private/protected declarations
71 # Private/protected declarations
72 _property_lock = False
72 _property_lock = False
73 _css = Dict() # Internal CSS property dict
73 _css = Dict() # Internal CSS property dict
74 _add_class = List() # Used to add a js class to a DOM element (call#, selector, class_name)
74 _add_class = List() # Used to add a js class to a DOM element (call#, selector, class_name)
75 _remove_class = List() # Used to remove a js class from a DOM element (call#, selector, class_name)
75 _remove_class = List() # Used to remove a js class from a DOM element (call#, selector, class_name)
76 _displayed = False
76 _displayed = False
77 _comm = None
77 _comm = None
78
78
79
79
80 def __init__(self, **kwargs):
80 def __init__(self, **kwargs):
81 """Public constructor
81 """Public constructor
82
82
83 Parameters
83 Parameters
84 ----------
84 ----------
85 parent : Widget instance (optional)
85 parent : Widget instance (optional)
86 Widget that this widget instance is child of. When the widget is
86 Widget that this widget instance is child of. When the widget is
87 displayed in the frontend, it's corresponding view will be made
87 displayed in the frontend, it's corresponding view will be made
88 child of the parent's view if the parent's view exists already. If
88 child of the parent's view if the parent's view exists already. If
89 the parent's view is displayed, it will automatically display this
89 the parent's view is displayed, it will automatically display this
90 widget's default view as it's child. The default view can be set
90 widget's default view as it's child. The default view can be set
91 via the default_view_name property.
91 via the default_view_name property.
92 """
92 """
93 self._children = []
93 self._children = []
94 self._add_class = [0]
94 self._add_class = [0]
95 self._remove_class = [0]
95 self._remove_class = [0]
96 super(Widget, self).__init__(**kwargs)
96 super(Widget, self).__init__(**kwargs)
97
97
98 # Register after init to allow default values to be specified
98 # Register after init to allow default values to be specified
99 self.on_trait_change(self._handle_property_changed, self.keys)
99 self.on_trait_change(self._handle_property_changed, self.keys)
100
100
101
101
102 def __del__(self):
102 def __del__(self):
103 """Object disposal"""
103 """Object disposal"""
104 self.close()
104 self.close()
105
105
106
106
107 def close(self):
107 def close(self):
108 """Close method. Closes the widget which closes the underlying comm.
108 """Close method. Closes the widget which closes the underlying comm.
109 When the comm is closed, all of the widget views are automatically
109 When the comm is closed, all of the widget views are automatically
110 removed from the frontend."""
110 removed from the frontend."""
111 self._comm.close()
111 self._comm.close()
112 del self._comm
112 del self._comm
113
113
114
114
115 # Properties
115 # Properties
116 def _get_keys(self):
116 def _get_keys(self):
117 keys = ['visible', '_css', '_add_class', '_remove_class']
117 keys = ['visible', '_css', '_add_class', '_remove_class']
118 keys.extend(self._keys)
118 keys.extend(self._keys)
119 return keys
119 return keys
120 keys = property(_get_keys)
120 keys = property(_get_keys)
121
121
122
122
123 # Event handlers
123 # Event handlers
124 def _handle_msg(self, msg):
124 def _handle_msg(self, msg):
125 """Called when a msg is recieved from the frontend"""
125 """Called when a msg is recieved from the frontend"""
126 # Handle backbone sync methods CREATE, PATCH, and UPDATE
126 # Handle backbone sync methods CREATE, PATCH, and UPDATE
127 sync_method = msg['content']['data']['sync_method']
127 sync_method = msg['content']['data']['sync_method']
128 sync_data = msg['content']['data']['sync_data']
128 sync_data = msg['content']['data']['sync_data']
129 self._handle_recieve_state(sync_data) # handles all methods
129 self._handle_recieve_state(sync_data) # handles all methods
130
130
131
131
132 def _handle_recieve_state(self, sync_data):
132 def _handle_recieve_state(self, sync_data):
133 """Called when a state is recieved from the frontend."""
133 """Called when a state is recieved from the frontend."""
134 self._property_lock = True
134 self._property_lock = True
135 try:
135 try:
136
136
137 # Use _keys instead of keys - Don't get retrieve the css from the client side.
137 # Use _keys instead of keys - Don't get retrieve the css from the client side.
138 for name in self._keys:
138 for name in self._keys:
139 if name in sync_data:
139 if name in sync_data:
140 setattr(self, name, sync_data[name])
140 setattr(self, name, sync_data[name])
141 finally:
141 finally:
142 self._property_lock = False
142 self._property_lock = False
143
143
144
144
145 def _handle_property_changed(self, name, old, new):
145 def _handle_property_changed(self, name, old, new):
146 """Called when a proeprty has been changed."""
146 """Called when a proeprty has been changed."""
147 if not self._property_lock and self._comm is not None:
147 if not self._property_lock and self._comm is not None:
148 # TODO: Validate properties.
148 # TODO: Validate properties.
149 # Send new state to frontend
149 # Send new state to frontend
150 self.send_state(key=name)
150 self.send_state(key=name)
151
151
152
152
153 def _handle_close(self):
153 def _handle_close(self):
154 """Called when the comm is closed by the frontend."""
154 """Called when the comm is closed by the frontend."""
155 self._comm = None
155 self._comm = None
156
156
157
157
158 # Public methods
158 # Public methods
159 def send_state(self, key=None):
159 def send_state(self, key=None):
160 """Sends the widget state, or a piece of it, to the frontend.
160 """Sends the widget state, or a piece of it, to the frontend.
161
161
162 Parameters
162 Parameters
163 ----------
163 ----------
164 key : unicode (optional)
164 key : unicode (optional)
165 A single property's name to sync with the frontend.
165 A single property's name to sync with the frontend.
166 """
166 """
167 if self._comm is not None:
167 if self._comm is not None:
168 state = {}
168 state = {}
169
169
170 # If a key is provided, just send the state of that key.
170 # If a key is provided, just send the state of that key.
171 keys = []
171 keys = []
172 if key is None:
172 if key is None:
173 keys.extend(self.keys)
173 keys.extend(self.keys)
174 else:
174 else:
175 keys.append(key)
175 keys.append(key)
176 for key in self.keys:
176 for key in self.keys:
177 try:
177 try:
178 state[key] = getattr(self, key)
178 state[key] = getattr(self, key)
179 except Exception as e:
179 except Exception as e:
180 pass # Eat errors, nom nom nom
180 pass # Eat errors, nom nom nom
181 self._comm.send({"method": "update",
181 self._comm.send({"method": "update",
182 "state": state})
182 "state": state})
183
183
184
184
185 def get_css(self, key, selector=""):
185 def get_css(self, key, selector=""):
186 """Get a CSS property of the widget views (shared among all of the
186 """Get a CSS property of the widget views (shared among all of the
187 views)
187 views)
188
188
189 Parameters
189 Parameters
190 ----------
190 ----------
191 key: unicode
191 key: unicode
192 CSS key
192 CSS key
193 selector: unicode (optional)
193 selector: unicode (optional)
194 JQuery selector used when the CSS key/value was set.
194 JQuery selector used when the CSS key/value was set.
195 """
195 """
196 if selector in self._css and key in self._css[selector]:
196 if selector in self._css and key in self._css[selector]:
197 return self._css[selector][key]
197 return self._css[selector][key]
198 else:
198 else:
199 return None
199 return None
200
200
201
201
202 def set_css(self, key, value, selector=""):
202 def set_css(self, key, value, selector=""):
203 """Set a CSS property of the widget views (shared among all of the
203 """Set a CSS property of the widget views (shared among all of the
204 views)
204 views)
205
205
206 Parameters
206 Parameters
207 ----------
207 ----------
208 key: unicode
208 key: unicode
209 CSS key
209 CSS key
210 value
210 value
211 CSS value
211 CSS value
212 selector: unicode (optional)
212 selector: unicode (optional)
213 JQuery selector to use to apply the CSS key/value.
213 JQuery selector to use to apply the CSS key/value.
214 """
214 """
215 if selector not in self._css:
215 if selector not in self._css:
216 self._css[selector] = {}
216 self._css[selector] = {}
217
217
218 # Only update the property if it has changed.
218 # Only update the property if it has changed.
219 if not (key in self._css[selector] and value in self._css[selector][key]):
219 if not (key in self._css[selector] and value in self._css[selector][key]):
220 self._css[selector][key] = value
220 self._css[selector][key] = value
221 self.send_state('_css') # Send new state to client.
221 self.send_state('_css') # Send new state to client.
222
222
223
223
224 def add_class(self, class_name, selector=""):
224 def add_class(self, class_name, selector=""):
225 """Add class[es] to a DOM element
225 """Add class[es] to a DOM element
226
226
227 Parameters
227 Parameters
228 ----------
228 ----------
229 class_name: unicode
229 class_name: unicode
230 Class name(s) to add to the DOM element(s). Multiple class names
230 Class name(s) to add to the DOM element(s). Multiple class names
231 must be space separated.
231 must be space separated.
232 selector: unicode (optional)
232 selector: unicode (optional)
233 JQuery selector to select the DOM element(s) that the class(es) will
233 JQuery selector to select the DOM element(s) that the class(es) will
234 be added to.
234 be added to.
235 """
235 """
236 self._add_class = [self._add_class[0] + 1, selector, class_name]
236 self._add_class = [self._add_class[0] + 1, selector, class_name]
237 self.send_state(key='_add_class')
237 self.send_state(key='_add_class')
238
238
239
239
240 def remove_class(self, class_name, selector=""):
240 def remove_class(self, class_name, selector=""):
241 """Remove class[es] from a DOM element
241 """Remove class[es] from a DOM element
242
242
243 Parameters
243 Parameters
244 ----------
244 ----------
245 class_name: unicode
245 class_name: unicode
246 Class name(s) to remove from the DOM element(s). Multiple class
246 Class name(s) to remove from the DOM element(s). Multiple class
247 names must be space separated.
247 names must be space separated.
248 selector: unicode (optional)
248 selector: unicode (optional)
249 JQuery selector to select the DOM element(s) that the class(es) will
249 JQuery selector to select the DOM element(s) that the class(es) will
250 be removed from.
250 be removed from.
251 """
251 """
252 self._remove_class = [self._remove_class[0] + 1, selector, class_name]
252 self._remove_class = [self._remove_class[0] + 1, selector, class_name]
253 self.send_state(key='_remove_class')
253 self.send_state(key='_remove_class')
254
254
255
255
256 # Support methods
256 # Support methods
257 def _repr_widget_(self, view_name=None):
257 def _repr_widget_(self, view_name=None):
258 """Function that is called when `IPython.display.display` is called on
258 """Function that is called when `IPython.display.display` is called on
259 the widget.
259 the widget.
260
260
261 Parameters
261 Parameters
262 ----------
262 ----------
263 view_name: unicode (optional)
263 view_name: unicode (optional)
264 View to display in the frontend. Overrides default_view_name."""
264 View to display in the frontend. Overrides default_view_name."""
265
265
266 if not view_name:
266 if not view_name:
267 view_name = self.default_view_name
267 view_name = self.default_view_name
268
268
269 # Create a comm.
269 # Create a comm.
270 if self._comm is None:
270 if self._comm is None:
271 self._comm = Comm(target_name=self.target_name)
271 self._comm = Comm(target_name=self.target_name)
272 self._comm.on_msg(self._handle_msg)
272 self._comm.on_msg(self._handle_msg)
273 self._comm.on_close(self._handle_close)
273 self._comm.on_close(self._handle_close)
274
274
275 # Make sure model is syncronized
275 # Make sure model is syncronized
276 self.send_state()
276 self.send_state()
277
277
278 # Show view.
278 # Show view.
279 if self.parent is None or self.parent._comm is None:
279 if self.parent is None or self.parent._comm is None:
280 self._comm.send({"method": "display", "view_name": view_name})
280 self._comm.send({"method": "display", "view_name": view_name})
281 else:
281 else:
282 self._comm.send({"method": "display",
282 self._comm.send({"method": "display",
283 "view_name": view_name,
283 "view_name": view_name,
284 "parent": self.parent._comm.comm_id})
284 "parent": self.parent._comm.comm_id})
285 self._displayed = True
285 self._displayed = True
286
286
287 # Now display children if any.
287 # Now display children if any.
288 for child in self._children:
288 for child in self._children:
289 if child != self:
289 if child != self:
290 child._repr_widget_()
290 child._repr_widget_()
291 return None
291 return None
General Comments 0
You need to be logged in to leave comments. Login now