##// END OF EJS Templates
Added ability to title multicontainer pages before multicontainer display()
Jonathan Frederic -
Show More
@@ -1,56 +1,61
1 require(["notebook/js/widget"], function(){
1 require(["notebook/js/widget"], function(){
2 var MulticontainerModel = IPython.WidgetModel.extend({});
2 var MulticontainerModel = IPython.WidgetModel.extend({});
3 IPython.notebook.widget_manager.register_widget_model('MulticontainerWidgetModel', MulticontainerModel);
3 IPython.notebook.widget_manager.register_widget_model('MulticontainerWidgetModel', MulticontainerModel);
4
4
5 var AccordionView = IPython.WidgetView.extend({
5 var AccordionView = IPython.WidgetView.extend({
6
6
7 render: function(){
7 render: function(){
8 this.$el = $('<div />', {id: IPython.utils.uuid()})
8 this.$el = $('<div />', {id: IPython.utils.uuid()})
9 .addClass('accordion');
9 .addClass('accordion');
10 this.containers = [];
10 this.containers = [];
11 },
11 },
12
12
13 update: function() {
13 update: function() {
14 // Set tab titles
14 // Set tab titles
15 var titles = this.model.get('_titles');
15 var titles = this.model.get('_titles');
16 for (var page_index in titles) {
16 for (var page_index in titles) {
17 var accordian_toggle = this.containers[page_index]
17
18 .find('.accordion-heading')
18 var accordian = this.containers[page_index]
19 .find('.accordion-toggle');
19 if (accordian != undefined) {
20 accordian_toggle.html(titles[page_index]);
20 accordian
21 .find('.accordion-heading')
22 .find('.accordion-toggle')
23 .html(titles[page_index]);
24 }
21 }
25 }
22
26
23 return IPython.WidgetView.prototype.update.call(this);
27 return IPython.WidgetView.prototype.update.call(this);
24 },
28 },
25
29
26 display_child: function(view) {
30 display_child: function(view) {
27
31
28 var index = this.containers.length;
32 var index = this.containers.length;
29 var uuid = IPython.utils.uuid();
33 var uuid = IPython.utils.uuid();
30 var accordion_group = $('<div />')
34 var accordion_group = $('<div />')
31 .addClass('accordion-group')
35 .addClass('accordion-group')
32 .appendTo(this.$el);
36 .appendTo(this.$el);
33 var accordion_heading = $('<div />')
37 var accordion_heading = $('<div />')
34 .addClass('accordion-heading')
38 .addClass('accordion-heading')
35 .appendTo(accordion_group);
39 .appendTo(accordion_group);
36 var accordion_toggle = $('<a />')
40 var accordion_toggle = $('<a />')
37 .addClass('accordion-toggle')
41 .addClass('accordion-toggle')
38 .attr('data-toggle', 'collapse')
42 .attr('data-toggle', 'collapse')
39 .attr('data-parent', '#' + this.$el.attr('id'))
43 .attr('data-parent', '#' + this.$el.attr('id'))
40 .attr('href', '#' + uuid)
44 .attr('href', '#' + uuid)
41 .html('Page ' + index)
45 .html('Page ' + index)
42 .appendTo(accordion_heading);
46 .appendTo(accordion_heading);
43 var accordion_body = $('<div />', {id: uuid})
47 var accordion_body = $('<div />', {id: uuid})
44 .addClass('accordion-body collapse')
48 .addClass('accordion-body collapse')
45 .appendTo(accordion_group);
49 .appendTo(accordion_group);
46 var accordion_inner = $('<div />')
50 var accordion_inner = $('<div />')
47 .addClass('accordion-inner')
51 .addClass('accordion-inner')
48 .appendTo(accordion_body);
52 .appendTo(accordion_body);
49 this.containers.push(accordion_group);
53 this.containers.push(accordion_group);
50
54
51 accordion_inner.append(view.$el);
55 accordion_inner.append(view.$el);
56 this.update();
52 },
57 },
53 });
58 });
54
59
55 IPython.notebook.widget_manager.register_widget_view('AccordionView', AccordionView);
60 IPython.notebook.widget_manager.register_widget_view('AccordionView', AccordionView);
56 }); No newline at end of file
61 });
@@ -1,251 +1,252
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
24 from IPython.utils.traitlets import Unicode, Dict, List
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))
39 display(Javascript(data='$.getScript("%s");' % js_path))
40
40
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Classes
43 # Classes
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45 class Widget(LoggingConfigurable):
45 class Widget(LoggingConfigurable):
46
46
47 # Public declarations
47 # Public declarations
48 target_name = Unicode('widget', help="""Name of the backbone model
48 target_name = Unicode('widget', help="""Name of the backbone model
49 registered in the frontend to create and sync this widget with.""")
49 registered in the frontend to create and sync this widget with.""")
50 default_view_name = Unicode(help="""Default view registered in the frontend
50 default_view_name = Unicode(help="""Default view registered in the frontend
51 to use to represent the widget.""")
51 to use to represent the widget.""")
52
52
53
53
54 # Private/protected declarations
54 # Private/protected declarations
55 _keys = []
55 _keys = []
56 _property_lock = False
56 _property_lock = False
57 _parent = None
57 _parent = None
58 _children = []
58 _children = []
59 _css = Dict()
59 _css = Dict()
60
60
61
61
62 def __init__(self, parent=None, **kwargs):
62 def __init__(self, parent=None, **kwargs):
63 """Public constructor
63 """Public constructor
64
64
65 Parameters
65 Parameters
66 ----------
66 ----------
67 parent : Widget instance (optional)
67 parent : Widget instance (optional)
68 Widget that this widget instance is child of. When the widget is
68 Widget that this widget instance is child of. When the widget is
69 displayed in the frontend, it's corresponding view will be made
69 displayed in the frontend, it's corresponding view will be made
70 child of the parent's view if the parent's view exists already. If
70 child of the parent's view if the parent's view exists already. If
71 the parent's view is displayed, it will automatically display this
71 the parent's view is displayed, it will automatically display this
72 widget's default view as it's child. The default view can be set
72 widget's default view as it's child. The default view can be set
73 via the default_view_name property.
73 via the default_view_name property.
74 """
74 """
75 super(Widget, self).__init__(**kwargs)
75 super(Widget, self).__init__(**kwargs)
76
76
77 # Parent/child association
77 # Parent/child association
78 self._children = []
78 self._children = []
79 if parent is not None:
79 if parent is not None:
80 parent._children.append(self)
80 parent._children.append(self)
81 self._parent = parent
81 self._parent = parent
82 self.comm = None
82 self.comm = None
83
83
84 # Register after init to allow default values to be specified
84 # Register after init to allow default values to be specified
85 self.on_trait_change(self._handle_property_changed, self.keys)
85 self.on_trait_change(self._handle_property_changed, self.keys)
86
86
87
87
88 def __del__(self):
88 def __del__(self):
89 """Object disposal"""
89 """Object disposal"""
90 self.close()
90 self.close()
91
91
92
92
93 def close(self):
93 def close(self):
94 """Close method. Closes the widget which closes the underlying comm.
94 """Close method. Closes the widget which closes the underlying comm.
95 When the comm is closed, all of the widget views are automatically
95 When the comm is closed, all of the widget views are automatically
96 removed from the frontend."""
96 removed from the frontend."""
97 self.comm.close()
97 self.comm.close()
98 del self.comm
98 del self.comm
99
99
100
100
101 # Properties
101 # Properties
102 def _get_parent(self):
102 def _get_parent(self):
103 return self._parent
103 return self._parent
104 parent = property(_get_parent)
104 parent = property(_get_parent)
105
105
106
106
107 def _get_children(self):
107 def _get_children(self):
108 return copy(self._children)
108 return copy(self._children)
109 children = property(_get_children)
109 children = property(_get_children)
110
110
111
111
112 def _get_keys(self):
112 def _get_keys(self):
113 keys = ['_css']
113 keys = ['_css']
114 keys.extend(self._keys)
114 keys.extend(self._keys)
115 return keys
115 return keys
116 keys = property(_get_keys)
116 keys = property(_get_keys)
117
117
118
118
119 # Event handlers
119 # Event handlers
120 def _handle_msg(self, msg):
120 def _handle_msg(self, msg):
121 """Called when a msg is recieved from the frontend"""
121 """Called when a msg is recieved from the frontend"""
122 # Handle backbone sync methods CREATE, PATCH, and UPDATE
122 # Handle backbone sync methods CREATE, PATCH, and UPDATE
123 sync_method = msg['content']['data']['sync_method']
123 sync_method = msg['content']['data']['sync_method']
124 sync_data = msg['content']['data']['sync_data']
124 sync_data = msg['content']['data']['sync_data']
125 self._handle_recieve_state(sync_data) # handles all methods
125 self._handle_recieve_state(sync_data) # handles all methods
126
126
127
127
128 def _handle_recieve_state(self, sync_data):
128 def _handle_recieve_state(self, sync_data):
129 """Called when a state is recieved from the frontend."""
129 """Called when a state is recieved from the frontend."""
130 self._property_lock = True
130 self._property_lock = True
131 try:
131 try:
132
132
133 # Use _keys instead of keys - Don't get retrieve the css from the client side.
133 # Use _keys instead of keys - Don't get retrieve the css from the client side.
134 for name in self._keys:
134 for name in self._keys:
135 if name in sync_data:
135 if name in sync_data:
136 setattr(self, name, sync_data[name])
136 setattr(self, name, sync_data[name])
137 finally:
137 finally:
138 self._property_lock = False
138 self._property_lock = False
139
139
140
140
141 def _handle_property_changed(self, name, old, new):
141 def _handle_property_changed(self, name, old, new):
142 """Called when a proeprty has been changed."""
142 """Called when a proeprty has been changed."""
143 if not self._property_lock and self.comm is not None:
143 if not self._property_lock and self.comm is not None:
144 # TODO: Validate properties.
144 # TODO: Validate properties.
145 # Send new state to frontend
145 # Send new state to frontend
146 self.send_state(key=name)
146 self.send_state(key=name)
147
147
148
148
149 def _handle_close(self):
149 def _handle_close(self):
150 """Called when the comm is closed by the frontend."""
150 """Called when the comm is closed by the frontend."""
151 self.comm = None
151 self.comm = None
152
152
153
153
154 # Public methods
154 # Public methods
155 def send_state(self, key=None):
155 def send_state(self, key=None):
156 """Sends the widget state, or a piece of it, to the frontend.
156 """Sends the widget state, or a piece of it, to the frontend.
157
157
158 Parameters
158 Parameters
159 ----------
159 ----------
160 key : unicode (optional)
160 key : unicode (optional)
161 A single property's name to sync with the frontend.
161 A single property's name to sync with the frontend.
162 """
162 """
163 state = {}
163 if self.comm is not None:
164
164 state = {}
165 # If a key is provided, just send the state of that key.
165
166 keys = []
166 # If a key is provided, just send the state of that key.
167 if key is None:
167 keys = []
168 keys.extend(self.keys)
168 if key is None:
169 else:
169 keys.extend(self.keys)
170 keys.append(key)
170 else:
171 for key in self.keys:
171 keys.append(key)
172 try:
172 for key in self.keys:
173 state[key] = getattr(self, key)
173 try:
174 except Exception as e:
174 state[key] = getattr(self, key)
175 pass # Eat errors, nom nom nom
175 except Exception as e:
176 self.comm.send({"method": "update",
176 pass # Eat errors, nom nom nom
177 "state": state})
177 self.comm.send({"method": "update",
178 "state": state})
178
179
179
180
180 def get_css(self, key, selector=""):
181 def get_css(self, key, selector=""):
181 """Get a CSS property of the widget views (shared among all of the
182 """Get a CSS property of the widget views (shared among all of the
182 views)
183 views)
183
184
184 Parameters
185 Parameters
185 ----------
186 ----------
186 key: unicode
187 key: unicode
187 CSS key
188 CSS key
188 selector: unicode (optional)
189 selector: unicode (optional)
189 JQuery selector used when the CSS key/value was set.
190 JQuery selector used when the CSS key/value was set.
190 """
191 """
191 if selector in self._css and key in self._css[selector]:
192 if selector in self._css and key in self._css[selector]:
192 return self._css[selector][key]
193 return self._css[selector][key]
193 else:
194 else:
194 return None
195 return None
195
196
196
197
197 def set_css(self, key, value, selector=""):
198 def set_css(self, key, value, selector=""):
198 """Set a CSS property of the widget views (shared among all of the
199 """Set a CSS property of the widget views (shared among all of the
199 views)
200 views)
200
201
201 Parameters
202 Parameters
202 ----------
203 ----------
203 key: unicode
204 key: unicode
204 CSS key
205 CSS key
205 value
206 value
206 CSS value
207 CSS value
207 selector: unicode (optional)
208 selector: unicode (optional)
208 JQuery selector to use to apply the CSS key/value.
209 JQuery selector to use to apply the CSS key/value.
209 """
210 """
210 if selector not in self._css:
211 if selector not in self._css:
211 self._css[selector] = {}
212 self._css[selector] = {}
212
213
213 # Only update the property if it has changed.
214 # Only update the property if it has changed.
214 if not (key in self._css[selector] and value in self._css[selector][key]):
215 if not (key in self._css[selector] and value in self._css[selector][key]):
215 self._css[selector][key] = value
216 self._css[selector][key] = value
216 self.send_state() # Send new state to client.
217 self.send_state() # Send new state to client.
217
218
218
219
219 # Support methods
220 # Support methods
220 def _repr_widget_(self, view_name=None):
221 def _repr_widget_(self, view_name=None):
221 """Function that is called when `IPython.display.display` is called on
222 """Function that is called when `IPython.display.display` is called on
222 the widget.
223 the widget.
223
224
224 Parameters
225 Parameters
225 ----------
226 ----------
226 view_name: unicode (optional)
227 view_name: unicode (optional)
227 View to display in the frontend. Overrides default_view_name."""
228 View to display in the frontend. Overrides default_view_name."""
228 if not view_name:
229 if not view_name:
229 view_name = self.default_view_name
230 view_name = self.default_view_name
230
231
231 # Create a comm.
232 # Create a comm.
232 if self.comm is None:
233 if self.comm is None:
233 self.comm = Comm(target_name=self.target_name)
234 self.comm = Comm(target_name=self.target_name)
234 self.comm.on_msg(self._handle_msg)
235 self.comm.on_msg(self._handle_msg)
235 self.comm.on_close(self._handle_close)
236 self.comm.on_close(self._handle_close)
236
237
237 # Make sure model is syncronized
238 # Make sure model is syncronized
238 self.send_state()
239 self.send_state()
239
240
240 # Show view.
241 # Show view.
241 if self.parent is None:
242 if self.parent is None:
242 self.comm.send({"method": "display", "view_name": view_name})
243 self.comm.send({"method": "display", "view_name": view_name})
243 else:
244 else:
244 self.comm.send({"method": "display",
245 self.comm.send({"method": "display",
245 "view_name": view_name,
246 "view_name": view_name,
246 "parent": self.parent.comm.comm_id})
247 "parent": self.parent.comm.comm_id})
247
248
248 # Now display children if any.
249 # Now display children if any.
249 for child in self.children:
250 for child in self.children:
250 child._repr_widget_()
251 child._repr_widget_()
251 return None
252 return None
General Comments 0
You need to be logged in to leave comments. Login now