##// END OF EJS Templates
Remove unused code and debugging statements
Jason Grout -
Show More
@@ -1,326 +1,322 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Base Widget Model and View classes
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgetmanager",
18 18 "underscore",
19 19 "backbone"],
20 20 function(widget_manager, underscore, backbone){
21 21
22 22 //--------------------------------------------------------------------
23 23 // WidgetModel class
24 24 //--------------------------------------------------------------------
25 25 var WidgetModel = Backbone.Model.extend({
26 26 constructor: function (widget_manager, widget_id, comm) {
27 27 this.widget_manager = widget_manager;
28 28 this.pending_msgs = 0;
29 29 this.msg_throttle = 3;
30 30 this.msg_buffer = null;
31 31 this.id = widget_id;
32 32
33 33 if (comm !== undefined) {
34 34 // Remember comm associated with the model.
35 35 this.comm = comm;
36 36 comm.model = this;
37 37
38 38 // Hook comm messages up to model.
39 39 var that = this;
40 40 comm.on_close($.proxy(this._handle_comm_closed, this));
41 41 comm.on_msg($.proxy(this._handle_comm_msg, this));
42 42 }
43 43
44 44 return Backbone.Model.apply(this);
45 45 },
46 46
47 47
48 48 send: function (content, callbacks) {
49 console.log('send',content, callbacks);
50 49 if (this.comm !== undefined) {
51 50 var data = {method: 'custom', custom_content: content};
52 51 this.comm.send(data, callbacks);
53 52 }
54 53 },
55 54
56 55 // Handle when a widget is closed.
57 56 _handle_comm_closed: function (msg) {
58 57 // jng: widget manager should observe the comm_close event and delete views when triggered
59 58 this.trigger('comm:close');
60 59 if (this._has_comm()) {
61 60 delete this.comm.model; // Delete ref so GC will collect widget model.
62 61 delete this.comm;
63 62 }
64 63 delete this.widget_id; // Delete id from model so widget manager cleans up.
65 64 },
66 65
67 66
68 67 // Handle incoming comm msg.
69 68 _handle_comm_msg: function (msg) {
70 69 var method = msg.content.data.method;
71 70 switch (method) {
72 71 case 'update':
73 72 this.apply_update(msg.content.data.state);
74 73 break;
75 74 case 'custom':
76 75 this.trigger('msg:custom', msg.content.data.custom_content);
77 76 break;
78 77 default:
79 78 // pass on to widget manager
80 79 this.widget_manager.handle_msg(msg, this);
81 80 }
82 81 },
83 82
84 83
85 84 // Handle when a widget is updated via the python side.
86 85 apply_update: function (state) {
87 86 this.updating = true;
88 87 try {
89 88 for (var key in state) {
90 89 if (state.hasOwnProperty(key)) {
91 90 this.set(key, state[key]);
92 91 }
93 92 }
94 93 this.save();
95 94 } finally {
96 95 this.updating = false;
97 96 }
98 97 },
99 98
100 99
101 100 _handle_status: function (msg, callbacks) {
102 101 //execution_state : ('busy', 'idle', 'starting')
103 102 if (this.comm !== undefined) {
104 103 if (msg.content.execution_state ==='idle') {
105 104
106 105 // Send buffer if this message caused another message to be
107 106 // throttled.
108 107 if (this.msg_buffer !== null &&
109 108 this.msg_throttle === this.pending_msgs) {
110 109 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
111 110 this.comm.send(data, callbacks);
112 111 this.msg_buffer = null;
113 112 } else {
114 113
115 114 // Only decrease the pending message count if the buffer
116 115 // doesn't get flushed (sent).
117 116 --this.pending_msgs;
118 117 }
119 118 }
120 119 }
121 120 },
122 121
123 122
124 123 // Custom syncronization logic.
125 124 _handle_sync: function (method, options) {
126 125 var model_json = this.toJSON();
127 126 var attr;
128 127
129 128 // Only send updated state if the state hasn't been changed
130 129 // during an update.
131 130 if (this.comm !== undefined) {
132 131 if (!this.updating) {
133 132 if (this.pending_msgs >= this.msg_throttle) {
134 133 // The throttle has been exceeded, buffer the current msg so
135 134 // it can be sent once the kernel has finished processing
136 135 // some of the existing messages.
137 136 if (method=='patch') {
138 137 if (this.msg_buffer === null) {
139 138 this.msg_buffer = $.extend({}, model_json); // Copy
140 139 }
141 140 for (attr in options.attrs) {
142 141 this.msg_buffer[attr] = options.attrs[attr];
143 142 }
144 143 } else {
145 144 this.msg_buffer = $.extend({}, model_json); // Copy
146 145 }
147 146
148 147 } else {
149 148 // We haven't exceeded the throttle, send the message like
150 149 // normal. If this is a patch operation, just send the
151 150 // changes.
152 151 var send_json = model_json;
153 152 if (method =='patch') {
154 153 send_json = {};
155 154 for (attr in options.attrs) {
156 155 send_json[attr] = options.attrs[attr];
157 156 }
158 157 }
159 158
160 159 var data = {method: 'backbone', sync_method: method, sync_data: send_json};
161 160 this.comm.send(data, this.cell_callbacks());
162 161 this.pending_msgs++;
163 162 }
164 163 }
165 164 }
166 165
167 166 // Since the comm is a one-way communication, assume the message
168 167 // arrived.
169 168 return model_json;
170 169 },
171 170
172 171 // Build a callback dict.
173 172 cell_callbacks: function (cell) {
174 173 var callbacks = {};
175 console.log('cell_callbacks A', cell);
176 174 if (cell === undefined) {
177 175 // Used the last modified view as the sender of the message. This
178 176 // will insure that any python code triggered by the sent message
179 177 // can create and display widgets and output.
180 178 if (this.last_modified_view !== undefined &&
181 179 this.last_modified_view.cell !== undefined) {
182 180 cell = this.last_modified_view.cell;
183 181 } else {
184 182 cell = null;
185 183 }
186 184 }
187 console.log('cell_callbacks B', cell);
188 185 if (cell !== null) {
189 186
190 187 // Try to get output handlers
191 188 var handle_output = null;
192 189 var handle_clear_output = null;
193 190 if (cell.output_area !== undefined && cell.output_area !== null) {
194 191 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
195 192 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
196 193 }
197 194
198 195 // Create callback dict using what is known
199 196 var that = this;
200 197 callbacks = {
201 198 iopub : {
202 199 output : handle_output,
203 200 clear_output : handle_clear_output,
204 201
205 202 status : function (msg) {
206 203 that._handle_status(msg, that.cell_callbacks(cell));
207 204 },
208 205
209 206 // Special function only registered by widget messages.
210 207 // Allows us to get the cell for a message so we know
211 208 // where to add widgets if the code requires it.
212 209 get_cell : function () {
213 210 return cell;
214 211 },
215 212 },
216 213 };
217 214 }
218 console.log('constructed callbacks for',cell);
219 215 return callbacks;
220 216 },
221 217 });
222 218
223 219
224 220 //--------------------------------------------------------------------
225 221 // WidgetView class
226 222 //--------------------------------------------------------------------
227 223 var BaseWidgetView = Backbone.View.extend({
228 224 initialize: function(options) {
229 225 this.model.on('change',this.update,this);
230 226 this.widget_manager = options.widget_manager;
231 227 this.comm_manager = options.widget_manager.comm_manager;
232 228 this.cell = options.cell
233 229 },
234 230
235 231 update: function(){
236 232 // update thyself to be consistent with this.model
237 233 },
238 234
239 235 child_view: function(comm_id, view_name) {
240 236 var child_model = this.comm_manager.comms[comm_id].model;
241 237 var child_view = this.widget_manager.create_view(child_model, view_name, this.cell);
242 238 return child_view;
243 239 },
244 240
245 241 render: function(){
246 242 // render thyself
247 243 },
248 244 send: function (content) {
249 245 this.model.send(content, this.model.cell_callbacks(this.cell));
250 246 },
251 247
252 248 touch: function () {
253 249 this.model.last_modified_view = this;
254 250 this.model.save(this.model.changedAttributes(), {patch: true});
255 251 },
256 252 });
257 253
258 254 var WidgetView = BaseWidgetView.extend({
259 255 initialize: function (options) {
260 256 this.visible = true;
261 257 BaseWidgetView.prototype.initialize.apply(this, arguments);
262 258 },
263 259
264 260 add_class: function (selector, class_list) {
265 261 var elements = this._get_selector_element(selector);
266 262 if (elements.length > 0) {
267 263 elements.addClass(class_list);
268 264 }
269 265 },
270 266
271 267 remove_class: function (selector, class_list) {
272 268 var elements = this._get_selector_element(selector);
273 269 if (elements.length > 0) {
274 270 elements.removeClass(class_list);
275 271 }
276 272 },
277 273
278 274 update: function () {
279 275 // jng: hook into change:visible trigger
280 276 var visible = this.model.get('visible');
281 277 if (visible !== undefined && this.visible !== visible) {
282 278 this.visible = visible;
283 279 this.$el.toggle(visible)
284 280 }
285 281
286 282 if (this.model.css !== undefined) {
287 283 for (var selector in this.model.css) {
288 284 if (this.model.css.hasOwnProperty(selector)) {
289 285
290 286 // Apply the css traits to all elements that match the selector.
291 287 var elements = this._get_selector_element(selector);
292 288 if (elements.length > 0) {
293 289 var css_traits = this.model.css[selector];
294 290 for (var css_key in css_traits) {
295 291 if (css_traits.hasOwnProperty(css_key)) {
296 292 elements.css(css_key, css_traits[css_key]);
297 293 }
298 294 }
299 295 }
300 296 }
301 297 }
302 298 }
303 299 },
304 300
305 301 _get_selector_element: function (selector) {
306 302 // Get the elements via the css selector. If the selector is
307 303 // blank, apply the style to the $el_to_style element. If
308 304 // the $el_to_style element is not defined, use apply the
309 305 // style to the view's element.
310 306 var elements = this.$el.find(selector);
311 307 if (selector === undefined || selector === null || selector === '') {
312 308 if (this.$el_to_style === undefined) {
313 309 elements = this.$el;
314 310 } else {
315 311 elements = this.$el_to_style;
316 312 }
317 313 }
318 314 return elements;
319 315 },
320 316 });
321 317
322 318 IPython.WidgetModel = WidgetModel;
323 319 IPython.WidgetView = WidgetView;
324 320
325 321 return widget_manager;
326 322 });
@@ -1,263 +1,257 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // ContainerWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/base"], function(widget_manager) {
18 18
19 19 var set_flex_property = function(element, property_name, enabled) {
20 20 if (enabled) {
21 21 element.addClass(property_name);
22 22 } else {
23 23 element.removeClass(property_name);
24 24 }
25 25 };
26 26
27 27 var set_flex_properties = function(context, element) {
28 28
29 29 // Apply flexible box model properties by adding and removing
30 30 // corrosponding CSS classes.
31 31 // Defined in IPython/html/static/base/less/flexbox.less
32 32 set_flex_property(element, 'vbox', context.model.get('_vbox'));
33 33 set_flex_property(element, 'hbox', context.model.get('_hbox'));
34 34 set_flex_property(element, 'start', context.model.get('_pack_start'));
35 35 set_flex_property(element, 'center', context.model.get('_pack_center'));
36 36 set_flex_property(element, 'end', context.model.get('_pack_end'));
37 37 set_flex_property(element, 'align-start', context.model.get('_align_start'));
38 38 set_flex_property(element, 'align-center', context.model.get('_align_center'));
39 39 set_flex_property(element, 'align-end', context.model.get('_align_end'));
40 40 set_flex_property(element, 'box-flex0', context.model.get('_flex0'));
41 41 set_flex_property(element, 'box-flex1', context.model.get('_flex1'));
42 42 set_flex_property(element, 'box-flex2', context.model.get('_flex2'));
43 43 };
44 44
45 45
46 46
47 47 var ContainerModel = IPython.WidgetModel.extend({});
48 48 widget_manager.register_widget_model('ContainerWidgetModel', ContainerModel);
49 49
50 50 var ContainerView = IPython.WidgetView.extend({
51 51
52 52 render: function(){
53 53 this.$el
54 54 .addClass('widget-container');
55 55 var children = this.model.get('children');
56 56 for(var i in children) {
57 57 var view = this.child_view(children[i]);
58 58 this.$el.append(view.$el);
59 59 }
60 60 this.update()
61 61 },
62 62
63 63 update: function(){
64 64 set_flex_properties(this, this.$el);
65 65 return IPython.WidgetView.prototype.update.call(this);
66 66 },
67 67 });
68 68
69 69 widget_manager.register_widget_view('ContainerView', ContainerView);
70 70
71 71
72 72 var ModalView = IPython.WidgetView.extend({
73 73
74 74 render: function(){
75 75 var that = this;
76 76 this.$el
77 77 .html('')
78 78 .on("remove", function(){
79 79 that.$window.remove();
80 80 });
81 81 this.$window = $('<div />')
82 82 .addClass('modal widget-modal')
83 83 .appendTo($('#notebook-container'))
84 84 .mousedown(function(){
85 85 that.bring_to_front();
86 86 });
87 87 this.$title_bar = $('<div />')
88 88 .addClass('popover-title')
89 89 .appendTo(this.$window)
90 90 .mousedown(function(){
91 91 that.bring_to_front();
92 92 });
93 93 this.$close = $('<button />')
94 94 .addClass('close icon-remove')
95 95 .css('margin-left', '5px')
96 96 .appendTo(this.$title_bar)
97 97 .click(function(){
98 98 that.hide();
99 99 event.stopPropagation();
100 100 });
101 101 this.$minimize = $('<button />')
102 102 .addClass('close icon-arrow-down')
103 103 .appendTo(this.$title_bar)
104 104 .click(function(){
105 105 that.popped_out = !that.popped_out;
106 106 if (!that.popped_out) {
107 107 that.$minimize
108 108 .removeClass('icon-arrow-down')
109 109 .addClass('icon-arrow-up');
110 110
111 111 that.$window
112 112 .draggable('destroy')
113 113 .resizable('destroy')
114 114 .removeClass('widget-modal modal')
115 115 .addClass('docked-widget-modal')
116 116 .detach()
117 117 .insertBefore(that.$show_button);
118 118 that.$show_button.hide();
119 119 that.$close.hide();
120 120 } else {
121 121 that.$minimize
122 122 .addClass('icon-arrow-down')
123 123 .removeClass('icon-arrow-up');
124 124
125 125 that.$window
126 126 .removeClass('docked-widget-modal')
127 127 .addClass('widget-modal modal')
128 128 .detach()
129 129 .appendTo($('#notebook-container'))
130 130 .draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'})
131 131 .resizable()
132 132 .children('.ui-resizable-handle').show();
133 133 that.show();
134 134 that.$show_button.show();
135 135 that.$close.show();
136 136 }
137 137 event.stopPropagation();
138 138 });
139 139 this.$title = $('<div />')
140 140 .addClass('widget-modal-title')
141 141 .html('&nbsp;')
142 142 .appendTo(this.$title_bar);
143 143 this.$body = $('<div />')
144 144 .addClass('modal-body')
145 145 .addClass('widget-modal-body')
146 146 .addClass('widget-container')
147 147 .appendTo(this.$window);
148 148
149 149 this.$show_button = $('<button />')
150 150 .html('&nbsp;')
151 151 .addClass('btn btn-info widget-modal-show')
152 152 .appendTo(this.$el)
153 153 .click(function(){
154 154 that.show();
155 155 });
156 156
157 157 this.$window.draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'});
158 158 this.$window.resizable();
159 159 this.$window.on('resize', function(){
160 160 that.$body.outerHeight(that.$window.innerHeight() - that.$title_bar.outerHeight());
161 161 });
162 162
163 163 this.$el_to_style = this.$body;
164 164 this._shown_once = false;
165 165 this.popped_out = true;
166 166 },
167 167
168 168 hide: function() {
169 169 this.$window.hide();
170 170 this.$show_button.removeClass('btn-info');
171 171 },
172 172
173 173 show: function() {
174 174 this.$show_button.addClass('btn-info');
175 175
176 176 this.$window.show();
177 177 if (this.popped_out) {
178 178 this.$window.css("positon", "absolute");
179 179 this.$window.css("top", "0px");
180 180 this.$window.css("left", Math.max(0, (($('body').outerWidth() - this.$window.outerWidth()) / 2) +
181 181 $(window).scrollLeft()) + "px");
182 182 this.bring_to_front();
183 183 }
184 184 },
185 185
186 186 bring_to_front: function() {
187 187 var $widget_modals = $(".widget-modal");
188 188 var max_zindex = 0;
189 189 $widget_modals.each(function (index, el){
190 190 max_zindex = Math.max(max_zindex, parseInt($(el).css('z-index')));
191 191 });
192 192
193 193 // Start z-index of widget modals at 2000
194 194 max_zindex = Math.max(max_zindex, 2000);
195 195
196 196 $widget_modals.each(function (index, el){
197 197 $el = $(el);
198 198 if (max_zindex == parseInt($el.css('z-index'))) {
199 199 $el.css('z-index', max_zindex - 1);
200 200 }
201 201 });
202 202 this.$window.css('z-index', max_zindex);
203 203 },
204 204
205 205 update: function(){
206 206 set_flex_properties(this, this.$body);
207 207
208 208 var description = this.model.get('description');
209 209 description = description.replace(/ /g, '&nbsp;', 'm');
210 210 description = description.replace(/\n/g, '<br>\n', 'm');
211 211 if (description.length === 0) {
212 212 this.$title.html('&nbsp;'); // Preserve title height
213 213 } else {
214 214 this.$title.html(description);
215 215 }
216 216
217 217 var button_text = this.model.get('button_text');
218 218 button_text = button_text.replace(/ /g, '&nbsp;', 'm');
219 219 button_text = button_text.replace(/\n/g, '<br>\n', 'm');
220 220 if (button_text.length === 0) {
221 221 this.$show_button.html('&nbsp;'); // Preserve button height
222 222 } else {
223 223 this.$show_button.html(button_text);
224 224 }
225 225
226 226 if (!this._shown_once) {
227 227 this._shown_once = true;
228 228 this.show();
229 229 }
230 230
231 231 return IPython.WidgetView.prototype.update.call(this);
232 232 },
233 233
234 add_child_view: function(attr, view) {
235 if (attr==='children') {
236 this.$body.append(view.$el);
237 }
238 },
239
240 234 _get_selector_element: function(selector) {
241 235
242 236 // Since the modal actually isn't within the $el in the DOM, we need to extend
243 237 // the selector logic to allow the user to set css on the modal if need be.
244 238 // The convention used is:
245 239 // "modal" - select the modal div
246 240 // "modal [selector]" - select element(s) within the modal div.
247 241 // "[selector]" - select elements within $el
248 242 // "" - select the $el_to_style
249 243 if (selector.substring(0, 5) == 'modal') {
250 244 if (selector == 'modal') {
251 245 return this.$window;
252 246 } else {
253 247 return this.$window.find(selector.substring(6));
254 248 }
255 249 } else {
256 250 return IPython.WidgetView.prototype._get_selector_element.call(this, selector);
257 251 }
258 252 },
259 253
260 254 });
261 255
262 256 widget_manager.register_widget_view('ModalView', ModalView);
263 257 });
@@ -1,257 +1,255 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // FloatRangeWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/base"], function(widget_manager){
18 18 var FloatRangeWidgetModel = IPython.WidgetModel.extend({});
19 19 widget_manager.register_widget_model('FloatRangeWidgetModel', FloatRangeWidgetModel);
20 20
21 21 var FloatSliderView = IPython.WidgetView.extend({
22 22
23 23 // Called when view is rendered.
24 24 render : function(){
25 25 this.$el
26 26 .addClass('widget-hbox-single')
27 27 .html('');
28 28 this.$label = $('<div />')
29 29 .appendTo(this.$el)
30 30 .addClass('widget-hlabel')
31 31 .hide();
32 32 this.$slider = $('<div />')
33 33 .slider({})
34 34 .addClass('slider');
35 35
36 36 // Put the slider in a container
37 37 this.$slider_container = $('<div />')
38 38 .addClass('widget-hslider')
39 39 .append(this.$slider);
40 40 this.$el_to_style = this.$slider_container; // Set default element to style
41 41 this.$el.append(this.$slider_container);
42 42
43 43 // Set defaults.
44 44 this.update();
45 45 },
46 46
47 47 // Handles: Backend -> Frontend Sync
48 48 // Frontent -> Frontend Sync
49 49 update : function(){
50 50 // Slider related keys.
51 51 var _keys = ['step', 'max', 'min', 'disabled'];
52 52 for (var index in _keys) {
53 53 var key = _keys[index];
54 54 if (this.model.get(key) !== undefined) {
55 55 this.$slider.slider("option", key, this.model.get(key));
56 56 }
57 57 }
58 58
59 59 // WORKAROUND FOR JQUERY SLIDER BUG.
60 60 // The horizontal position of the slider handle
61 61 // depends on the value of the slider at the time
62 62 // of orientation change. Before applying the new
63 63 // workaround, we set the value to the minimum to
64 64 // make sure that the horizontal placement of the
65 65 // handle in the vertical slider is always
66 66 // consistent.
67 67 var orientation = this.model.get('orientation');
68 68 var value = this.model.get('min');
69 69 this.$slider.slider('option', 'value', value);
70 70 this.$slider.slider('option', 'orientation', orientation);
71 71 value = this.model.get('value');
72 72 this.$slider.slider('option', 'value', value);
73 console.log('updating value',value)
74 73
75 74 // Use the right CSS classes for vertical & horizontal sliders
76 75 if (orientation=='vertical') {
77 76 this.$slider_container
78 77 .removeClass('widget-hslider')
79 78 .addClass('widget-vslider');
80 79 this.$el
81 80 .removeClass('widget-hbox-single')
82 81 .addClass('widget-vbox-single');
83 82 this.$label
84 83 .removeClass('widget-hlabel')
85 84 .addClass('widget-vlabel');
86 85
87 86 } else {
88 87 this.$slider_container
89 88 .removeClass('widget-vslider')
90 89 .addClass('widget-hslider');
91 90 this.$el
92 91 .removeClass('widget-vbox-single')
93 92 .addClass('widget-hbox-single');
94 93 this.$label
95 94 .removeClass('widget-vlabel')
96 95 .addClass('widget-hlabel');
97 96 }
98 97
99 98 var description = this.model.get('description');
100 99 if (description.length === 0) {
101 100 this.$label.hide();
102 101 } else {
103 102 this.$label.html(description);
104 103 this.$label.show();
105 104 }
106 105 return IPython.WidgetView.prototype.update.call(this);
107 106 },
108 107
109 108 // Handles: User input
110 109 events: { "slide" : "handleSliderChange" },
111 110 handleSliderChange: function(e, ui) {
112 111 this.model.set('value', ui.value);
113 console.log('triggered value change', ui.value, this.model);
114 112 this.touch();
115 113 },
116 114 });
117 115
118 116 widget_manager.register_widget_view('FloatSliderView', FloatSliderView);
119 117
120 118
121 119 var FloatTextView = IPython.WidgetView.extend({
122 120
123 121 // Called when view is rendered.
124 122 render : function(){
125 123 this.$el
126 124 .addClass('widget-hbox-single')
127 125 .html('');
128 126 this.$label = $('<div />')
129 127 .appendTo(this.$el)
130 128 .addClass('widget-hlabel')
131 129 .hide();
132 130 this.$textbox = $('<input type="text" />')
133 131 .addClass('input')
134 132 .addClass('widget-numeric-text')
135 133 .appendTo(this.$el);
136 134 this.$el_to_style = this.$textbox; // Set default element to style
137 135 this.update(); // Set defaults.
138 136 },
139 137
140 138 // Handles: Backend -> Frontend Sync
141 139 // Frontent -> Frontend Sync
142 140 update : function(){
143 141 var value = this.model.get('value');
144 142 if (!this.changing && parseFloat(this.$textbox.val()) != value) {
145 143 this.$textbox.val(value);
146 144 }
147 145
148 146 if (this.model.get('disabled')) {
149 147 this.$textbox.attr('disabled','disabled');
150 148 } else {
151 149 this.$textbox.removeAttr('disabled');
152 150 }
153 151
154 152 var description = this.model.get('description');
155 153 if (description.length === 0) {
156 154 this.$label.hide();
157 155 } else {
158 156 this.$label.html(description);
159 157 this.$label.show();
160 158 }
161 159 return IPython.WidgetView.prototype.update.call(this);
162 160 },
163 161
164 162
165 163 events: {"keyup input" : "handleChanging",
166 164 "paste input" : "handleChanging",
167 165 "cut input" : "handleChanging",
168 166 "change input" : "handleChanged"}, // Fires only when control is validated or looses focus.
169 167
170 168 // Handles and validates user input.
171 169 handleChanging: function(e) {
172 170
173 171 // Try to parse value as a float.
174 172 var numericalValue = 0.0;
175 173 if (e.target.value !== '') {
176 174 numericalValue = parseFloat(e.target.value);
177 175 }
178 176
179 177 // If parse failed, reset value to value stored in model.
180 178 if (isNaN(numericalValue)) {
181 179 e.target.value = this.model.get('value');
182 180 } else if (!isNaN(numericalValue)) {
183 181 if (this.model.get('max') !== undefined) {
184 182 numericalValue = Math.min(this.model.get('max'), numericalValue);
185 183 }
186 184 if (this.model.get('min') !== undefined) {
187 185 numericalValue = Math.max(this.model.get('min'), numericalValue);
188 186 }
189 187
190 188 // Apply the value if it has changed.
191 189 if (numericalValue != this.model.get('value')) {
192 190 this.changing = true;
193 191 this.model.set('value', numericalValue);
194 192 this.touch();
195 193 this.changing = false;
196 194 }
197 195 }
198 196 },
199 197
200 198 // Applies validated input.
201 199 handleChanged: function(e) {
202 200 // Update the textbox
203 201 if (this.model.get('value') != e.target.value) {
204 202 e.target.value = this.model.get('value');
205 203 }
206 204 }
207 205 });
208 206
209 207 widget_manager.register_widget_view('FloatTextView', FloatTextView);
210 208
211 209
212 210 var ProgressView = IPython.WidgetView.extend({
213 211
214 212 // Called when view is rendered.
215 213 render : function(){
216 214 this.$el
217 215 .addClass('widget-hbox-single')
218 216 .html('');
219 217 this.$label = $('<div />')
220 218 .appendTo(this.$el)
221 219 .addClass('widget-hlabel')
222 220 .hide();
223 221 this.$progress = $('<div />')
224 222 .addClass('progress')
225 223 .addClass('widget-progress')
226 224 .appendTo(this.$el);
227 225 this.$el_to_style = this.$progress; // Set default element to style
228 226 this.$bar = $('<div />')
229 227 .addClass('bar')
230 228 .css('width', '50%')
231 229 .appendTo(this.$progress);
232 230 this.update(); // Set defaults.
233 231 },
234 232
235 233 // Handles: Backend -> Frontend Sync
236 234 // Frontent -> Frontend Sync
237 235 update : function(){
238 236 var value = this.model.get('value');
239 237 var max = this.model.get('max');
240 238 var min = this.model.get('min');
241 239 var percent = 100.0 * (value - min) / (max - min);
242 240 this.$bar.css('width', percent + '%');
243 241
244 242 var description = this.model.get('description');
245 243 if (description.length === 0) {
246 244 this.$label.hide();
247 245 } else {
248 246 this.$label.html(description);
249 247 this.$label.show();
250 248 }
251 249 return IPython.WidgetView.prototype.update.call(this);
252 250 },
253 251
254 252 });
255 253
256 254 widget_manager.register_widget_view('ProgressView', ProgressView);
257 255 });
@@ -1,181 +1,180 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // MultiContainerWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/base"], function(widget_manager){
18 18 var MulticontainerModel = IPython.WidgetModel.extend({});
19 19 widget_manager.register_widget_model('MulticontainerWidgetModel', MulticontainerModel);
20 20
21 21 var AccordionView = IPython.WidgetView.extend({
22 22
23 23 render: function(){
24 24 var guid = 'accordion' + IPython.utils.uuid();
25 25 this.$el
26 26 .attr('id', guid)
27 27 .addClass('accordion');
28 28 this.containers = [];
29 29 },
30 30 update: function() {
31 31 // Set tab titles
32 32 var titles = this.model.get('_titles');
33 33 for (var page_index in titles) {
34 34
35 35 var accordian = this.containers[page_index];
36 36 if (accordian !== undefined) {
37 37 accordian
38 38 .find('.accordion-heading')
39 39 .find('.accordion-toggle')
40 40 .html(titles[page_index]);
41 41 }
42 42 }
43 43
44 44 // Set selected page
45 45 var selected_index = this.model.get("selected_index");
46 46 if (0 <= selected_index && selected_index < this.containers.length) {
47 47 for (var index in this.containers) {
48 48 if (index==selected_index) {
49 49 this.containers[index].find('.accordion-body').collapse('show');
50 50 } else {
51 51 this.containers[index].find('.accordion-body').collapse('hide');
52 52 }
53 53
54 54 }
55 55 }
56 56
57 57 return IPython.WidgetView.prototype.update.call(this);
58 58 },
59 59
60 60 add_child_view: function(attr, view) {
61 61
62 62 var index = this.containers.length;
63 63 var uuid = IPython.utils.uuid();
64 64 var accordion_group = $('<div />')
65 65 .addClass('accordion-group')
66 66 .appendTo(this.$el);
67 67 var accordion_heading = $('<div />')
68 68 .addClass('accordion-heading')
69 69 .appendTo(accordion_group);
70 70 var that = this;
71 71 var accordion_toggle = $('<a />')
72 72 .addClass('accordion-toggle')
73 73 .attr('data-toggle', 'collapse')
74 74 .attr('data-parent', '#' + this.$el.attr('id'))
75 75 .attr('href', '#' + uuid)
76 76 .click(function(evt){
77 77 that.model.set("selected_index", index);
78 78 that.touch();
79 79 })
80 80 .html('Page ' + index)
81 81 .appendTo(accordion_heading);
82 82 var accordion_body = $('<div />', {id: uuid})
83 83 .addClass('accordion-body collapse')
84 84 .appendTo(accordion_group);
85 85 var accordion_inner = $('<div />')
86 86 .addClass('accordion-inner')
87 87 .appendTo(accordion_body);
88 88 this.containers.push(accordion_group);
89 89 accordion_inner.append(view.$el);
90 90
91 91 this.update();
92 92
93 93 // Stupid workaround to close the bootstrap accordion tabs which
94 94 // open by default even though they don't have the `in` class
95 95 // attached to them. For some reason a delay is required.
96 96 // TODO: Better fix.
97 97 setTimeout(function(){ that.update(); }, 500);
98 98 },
99 99 });
100 100
101 101 widget_manager.register_widget_view('AccordionView', AccordionView);
102 102
103 103 var TabView = IPython.WidgetView.extend({
104 104
105 105 initialize: function() {
106 106 this.containers = [];
107 107 IPython.WidgetView.prototype.initialize.apply(this, arguments);
108 108 },
109 109
110 110 render: function(){
111 console.log('rendering tabs', this);
112 111 var uuid = 'tabs'+IPython.utils.uuid();
113 112 var that = this;
114 113 this.$tabs = $('<div />', {id: uuid})
115 114 .addClass('nav')
116 115 .addClass('nav-tabs')
117 116 .appendTo(this.$el);
118 117 this.$tab_contents = $('<div />', {id: uuid + 'Content'})
119 118 .addClass('tab-content')
120 119 .appendTo(this.$el);
121 120 var children = this.model.get('children');
122 121 for (var i in children) {
123 122 this.add_child_view(this.child_view(children[i]))
124 123 }
125 124 this.update();
126 125 },
127 126
128 127 update: function() {
129 128 // Set tab titles
130 129 var titles = this.model.get('_titles');
131 130 for (var page_index in titles) {
132 131 var tab_text = this.containers[page_index];
133 132 if (tab_text !== undefined) {
134 133 tab_text.html(titles[page_index]);
135 134 }
136 135 }
137 136
138 137 var selected_index = this.model.get('selected_index');
139 138 if (0 <= selected_index && selected_index < this.containers.length) {
140 139 this.select_page(selected_index);
141 140 }
142 141
143 142 return IPython.WidgetView.prototype.update.call(this);
144 143 },
145 144
146 145 add_child_view: function(view) {
147 146 var index = this.containers.length;
148 147 var uuid = IPython.utils.uuid();
149 148
150 149 var that = this;
151 150 var tab = $('<li />')
152 151 .css('list-style-type', 'none')
153 152 .appendTo(this.$tabs);
154 153 var tab_text = $('<a />')
155 154 .attr('href', '#' + uuid)
156 155 .attr('data-toggle', 'tab')
157 156 .html('Page ' + index)
158 157 .appendTo(tab)
159 158 .click(function (e) {
160 159 that.model.set("selected_index", index);
161 160 that.touch();
162 161 that.select_page(index);
163 162 });
164 163 this.containers.push(tab_text);
165 164
166 165 var contents_div = $('<div />', {id: uuid})
167 166 .addClass('tab-pane')
168 167 .addClass('fade')
169 168 .append(view.$el)
170 169 .appendTo(this.$tab_contents);
171 170 },
172 171
173 172 select_page: function(index) {
174 173 this.$tabs.find('li')
175 174 .removeClass('active');
176 175 this.containers[index].tab('show');
177 176 },
178 177 });
179 178
180 179 widget_manager.register_widget_view('TabView', TabView);
181 180 });
@@ -1,441 +1,438 b''
1 1 """Base Widget class. Allows user to create widgets in the backend that render
2 2 in the IPython notebook frontend.
3 3 """
4 4 #-----------------------------------------------------------------------------
5 5 # Copyright (c) 2013, the IPython Development Team.
6 6 #
7 7 # Distributed under the terms of the Modified BSD License.
8 8 #
9 9 # The full license is in the file COPYING.txt, distributed with this software.
10 10 #-----------------------------------------------------------------------------
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Imports
14 14 #-----------------------------------------------------------------------------
15 15 from copy import copy
16 16 from glob import glob
17 17 import uuid
18 18 import sys
19 19 import os
20 20 import inspect
21 21 import types
22 22
23 23 import IPython
24 24 from IPython.kernel.comm import Comm
25 25 from IPython.config import LoggingConfigurable
26 26 from IPython.utils.traitlets import Unicode, Dict, List, Instance, Bool
27 27 from IPython.display import Javascript, display
28 28 from IPython.utils.py3compat import string_types
29 29
30 30 #-----------------------------------------------------------------------------
31 31 # Classes
32 32 #-----------------------------------------------------------------------------
33 33
34 34 class BaseWidget(LoggingConfigurable):
35 35
36 36 # Shared declarations (Class level)
37 37 _keys = List(Unicode, default_value = [],
38 38 help="List of keys comprising the state of the model.", allow_none=False)
39 39 widget_construction_callback = None
40 40
41 41 def on_widget_constructed(callback):
42 42 """Class method, registers a callback to be called when a widget is
43 43 constructed. The callback must have the following signature:
44 44 callback(widget)"""
45 45 Widget.widget_construction_callback = callback
46 46
47 47 def _handle_widget_constructed(widget):
48 48 """Class method, called when a widget is constructed."""
49 49 if Widget.widget_construction_callback is not None and callable(Widget.widget_construction_callback):
50 50 Widget.widget_construction_callback(widget)
51 51
52 52
53 53
54 54 # Public declarations (Instance level)
55 55 target_name = Unicode('widget', help="""Name of the backbone model
56 56 registered in the frontend to create and sync this widget with.""")
57 57 default_view_name = Unicode(help="""Default view registered in the frontend
58 58 to use to represent the widget.""")
59 59
60 60 # Private/protected declarations
61 61 # todo: change this to a context manager
62 62 _property_lock = (None, None) # Last updated (key, value) from the front-end. Prevents echo.
63 63 _displayed = False
64 64 _comm = Instance('IPython.kernel.comm.Comm')
65 65
66 66 def __init__(self, **kwargs):
67 67 """Public constructor
68 68 """
69 69 self._display_callbacks = []
70 70 self._msg_callbacks = []
71 71 super(BaseWidget, self).__init__(**kwargs)
72 72
73 # Register after init to allow default values to be specified
74 # TODO: register three different handlers, one for each list, and abstract out the common parts
75 #print self.keys, self._children_attr, self._children_lists_attr
76 73 self.on_trait_change(self._handle_property_changed, self.keys)
77 74 Widget._handle_widget_constructed(self)
78 75
79 76 def __del__(self):
80 77 """Object disposal"""
81 78 self.close()
82 79
83 80
84 81 def close(self):
85 82 """Close method. Closes the widget which closes the underlying comm.
86 83 When the comm is closed, all of the widget views are automatically
87 84 removed from the frontend."""
88 85 self._close_communication()
89 86
90 87
91 88 # Properties
92 89 @property
93 90 def keys(self):
94 91 keys = ['default_view_name']
95 92 keys.extend(self._keys)
96 93 return keys
97 94
98 95 @property
99 96 def comm(self):
100 97 if self._comm is None:
101 98 self._open_communication()
102 99 return self._comm
103 100
104 101 # Event handlers
105 102 def _handle_msg(self, msg):
106 103 """Called when a msg is recieved from the frontend"""
107 104 data = msg['content']['data']
108 105 method = data['method']
109 106
110 107 # Handle backbone sync methods CREATE, PATCH, and UPDATE
111 108 if method == 'backbone':
112 109 if 'sync_method' in data and 'sync_data' in data:
113 110 sync_method = data['sync_method']
114 111 sync_data = data['sync_data']
115 112 self._handle_recieve_state(sync_data) # handles all methods
116 113
117 114 # Handle a custom msg from the front-end
118 115 elif method == 'custom':
119 116 if 'custom_content' in data:
120 117 self._handle_custom_msg(data['custom_content'])
121 118
122 119 def _handle_custom_msg(self, content):
123 120 """Called when a custom msg is recieved."""
124 121 for handler in self._msg_callbacks:
125 122 if callable(handler):
126 123 argspec = inspect.getargspec(handler)
127 124 nargs = len(argspec[0])
128 125
129 126 # Bound methods have an additional 'self' argument
130 127 if isinstance(handler, types.MethodType):
131 128 nargs -= 1
132 129
133 130 # Call the callback
134 131 if nargs == 1:
135 132 handler(content)
136 133 elif nargs == 2:
137 134 handler(self, content)
138 135 else:
139 136 raise TypeError('Widget msg callback must ' \
140 137 'accept 1 or 2 arguments, not %d.' % nargs)
141 138
142 139
143 140 def _handle_recieve_state(self, sync_data):
144 141 """Called when a state is recieved from the frontend."""
145 142 # Use _keys instead of keys - Don't get retrieve the css from the client side.
146 143 for name in self._keys:
147 144 if name in sync_data:
148 145 try:
149 146 self._property_lock = (name, sync_data[name])
150 147 setattr(self, name, sync_data[name])
151 148 finally:
152 149 self._property_lock = (None, None)
153 150
154 151
155 152 def _handle_property_changed(self, name, old, new):
156 153 """Called when a property has been changed."""
157 154 # Make sure this isn't information that the front-end just sent us.
158 155 if self._property_lock[0] != name and self._property_lock[1] != new:
159 156 # Send new state to frontend
160 157 self.send_state(key=name)
161 158
162 159 def _handle_displayed(self, **kwargs):
163 160 """Called when a view has been displayed for this widget instance
164 161
165 162 Parameters
166 163 ----------
167 164 [view_name]: unicode (optional kwarg)
168 165 Name of the view that was displayed."""
169 166 for handler in self._display_callbacks:
170 167 if callable(handler):
171 168 argspec = inspect.getargspec(handler)
172 169 nargs = len(argspec[0])
173 170
174 171 # Bound methods have an additional 'self' argument
175 172 if isinstance(handler, types.MethodType):
176 173 nargs -= 1
177 174
178 175 # Call the callback
179 176 if nargs == 0:
180 177 handler()
181 178 elif nargs == 1:
182 179 handler(self)
183 180 elif nargs == 2:
184 181 handler(self, kwargs.get('view_name', None))
185 182 else:
186 183 handler(self, **kwargs)
187 184
188 185 # Public methods
189 186 def send_state(self, key=None):
190 187 """Sends the widget state, or a piece of it, to the frontend.
191 188
192 189 Parameters
193 190 ----------
194 191 key : unicode (optional)
195 192 A single property's name to sync with the frontend.
196 193 """
197 194 self._send({"method": "update",
198 195 "state": self.get_state()})
199 196
200 197 def get_state(self, key=None):
201 198 """Gets the widget state, or a piece of it.
202 199
203 200 Parameters
204 201 ----------
205 202 key : unicode (optional)
206 203 A single property's name to get.
207 204 """
208 205 state = {}
209 206
210 207 # If a key is provided, just send the state of that key.
211 208 if key is None:
212 209 keys = self.keys[:]
213 210 else:
214 211 keys = [key]
215 212 for k in keys:
216 213 value = getattr(self, k)
217 214
218 215 # a more elegant solution to encoding BaseWidgets would be
219 216 # to tap into the JSON encoder and teach it how to deal
220 217 # with BaseWidget objects, or maybe just teach the JSON
221 218 # encoder to look for a _repr_json property before giving
222 219 # up encoding
223 220 if isinstance(value, BaseWidget):
224 221 value = value.comm.comm_id
225 222 elif isinstance(value, list) and isinstance(value[0], BaseWidget):
226 223 # assume all elements of the list are widgets
227 224 value = [i.comm.comm_id for i in value]
228 225 state[k] = value
229 226 return state
230 227
231 228
232 229 def send(self, content):
233 230 """Sends a custom msg to the widget model in the front-end.
234 231
235 232 Parameters
236 233 ----------
237 234 content : dict
238 235 Content of the message to send.
239 236 """
240 237 self._send({"method": "custom",
241 238 "custom_content": content})
242 239
243 240
244 241 def on_msg(self, callback, remove=False):
245 242 """Register a callback for when a custom msg is recieved from the front-end
246 243
247 244 Parameters
248 245 ----------
249 246 callback: method handler
250 247 Can have a signature of:
251 248 - callback(content)
252 249 - callback(sender, content)
253 250 remove: bool
254 251 True if the callback should be unregistered."""
255 252 if remove and callback in self._msg_callbacks:
256 253 self._msg_callbacks.remove(callback)
257 254 elif not remove and not callback in self._msg_callbacks:
258 255 self._msg_callbacks.append(callback)
259 256
260 257
261 258 def on_displayed(self, callback, remove=False):
262 259 """Register a callback to be called when the widget has been displayed
263 260
264 261 Parameters
265 262 ----------
266 263 callback: method handler
267 264 Can have a signature of:
268 265 - callback()
269 266 - callback(sender)
270 267 - callback(sender, view_name)
271 268 - callback(sender, **kwargs)
272 269 kwargs from display call passed through without modification.
273 270 remove: bool
274 271 True if the callback should be unregistered."""
275 272 if remove and callback in self._display_callbacks:
276 273 self._display_callbacks.remove(callback)
277 274 elif not remove and not callback in self._display_callbacks:
278 275 self._display_callbacks.append(callback)
279 276
280 277
281 278 # Support methods
282 279 def _repr_widget_(self, **kwargs):
283 280 """Function that is called when `IPython.display.display` is called on
284 281 the widget.
285 282
286 283 Parameters
287 284 ----------
288 285 view_name: unicode (optional)
289 286 View to display in the frontend. Overrides default_view_name."""
290 287 view_name = kwargs.get('view_name', self.default_view_name)
291 288
292 289 # Create a communication.
293 290 self._open_communication()
294 291
295 292 # Make sure model is syncronized
296 293 self.send_state()
297 294
298 295 # Show view.
299 296 self._send({"method": "display", "view_name": view_name})
300 297 self._displayed = True
301 298 self._handle_displayed(**kwargs)
302 299
303 300
304 301 def _open_communication(self):
305 302 """Opens a communication with the front-end."""
306 303 # Create a comm.
307 304 if self._comm is None:
308 305 self._comm = Comm(target_name=self.target_name)
309 306 self._comm.on_msg(self._handle_msg)
310 307 self._comm.on_close(self._close_communication)
311 308
312 309 # first update
313 310 self.send_state()
314 311
315 312
316 313 def _close_communication(self):
317 314 """Closes a communication with the front-end."""
318 315 if self._comm is not None:
319 316 try:
320 317 self._comm.close()
321 318 finally:
322 319 self._comm = None
323 320
324 321
325 322 def _send(self, msg):
326 323 """Sends a message to the model in the front-end"""
327 324 if self._comm is not None:
328 325 self._comm.send(msg)
329 326 return True
330 327 else:
331 328 return False
332 329
333 330 class Widget(BaseWidget):
334 331 visible = Bool(True, help="Whether or not the widget is visible.")
335 332
336 333 # Private/protected declarations
337 334 _css = Dict() # Internal CSS property dict
338 335
339 336 # Properties
340 337 @property
341 338 def keys(self):
342 339 keys = ['visible', '_css']
343 340 keys.extend(super(Widget, self).keys)
344 341 return keys
345 342
346 343 def get_css(self, key, selector=""):
347 344 """Get a CSS property of the widget. Note, this function does not
348 345 actually request the CSS from the front-end; Only properties that have
349 346 been set with set_css can be read.
350 347
351 348 Parameters
352 349 ----------
353 350 key: unicode
354 351 CSS key
355 352 selector: unicode (optional)
356 353 JQuery selector used when the CSS key/value was set.
357 354 """
358 355 if selector in self._css and key in self._css[selector]:
359 356 return self._css[selector][key]
360 357 else:
361 358 return None
362 359
363 360
364 361 def set_css(self, *args, **kwargs):
365 362 """Set one or more CSS properties of the widget (shared among all of the
366 363 views). This function has two signatures:
367 364 - set_css(css_dict, [selector=''])
368 365 - set_css(key, value, [selector=''])
369 366
370 367 Parameters
371 368 ----------
372 369 css_dict : dict
373 370 CSS key/value pairs to apply
374 371 key: unicode
375 372 CSS key
376 373 value
377 374 CSS value
378 375 selector: unicode (optional)
379 376 JQuery selector to use to apply the CSS key/value.
380 377 """
381 378 selector = kwargs.get('selector', '')
382 379
383 380 # Signature 1: set_css(css_dict, [selector=''])
384 381 if len(args) == 1:
385 382 if isinstance(args[0], dict):
386 383 for (key, value) in args[0].items():
387 384 self.set_css(key, value, selector=selector)
388 385 else:
389 386 raise Exception('css_dict must be a dict.')
390 387
391 388 # Signature 2: set_css(key, value, [selector=''])
392 389 elif len(args) == 2 or len(args) == 3:
393 390
394 391 # Selector can be a positional arg if it's the 3rd value
395 392 if len(args) == 3:
396 393 selector = args[2]
397 394 if selector not in self._css:
398 395 self._css[selector] = {}
399 396
400 397 # Only update the property if it has changed.
401 398 key = args[0]
402 399 value = args[1]
403 400 if not (key in self._css[selector] and value in self._css[selector][key]):
404 401 self._css[selector][key] = value
405 402 self.send_state('_css') # Send new state to client.
406 403 else:
407 404 raise Exception('set_css only accepts 1-3 arguments')
408 405
409 406
410 407 def add_class(self, class_name, selector=""):
411 408 """Add class[es] to a DOM element
412 409
413 410 Parameters
414 411 ----------
415 412 class_name: unicode
416 413 Class name(s) to add to the DOM element(s). Multiple class names
417 414 must be space separated.
418 415 selector: unicode (optional)
419 416 JQuery selector to select the DOM element(s) that the class(es) will
420 417 be added to.
421 418 """
422 419 self._send({"method": "add_class",
423 420 "class_list": class_name,
424 421 "selector": selector})
425 422
426 423
427 424 def remove_class(self, class_name, selector=""):
428 425 """Remove class[es] from a DOM element
429 426
430 427 Parameters
431 428 ----------
432 429 class_name: unicode
433 430 Class name(s) to remove from the DOM element(s). Multiple class
434 431 names must be space separated.
435 432 selector: unicode (optional)
436 433 JQuery selector to select the DOM element(s) that the class(es) will
437 434 be removed from.
438 435 """
439 436 self._send({"method": "remove_class",
440 437 "class_list": class_name,
441 438 "selector": selector})
@@ -1,203 +1,202 b''
1 1 """ContainerWidget class.
2 2
3 3 Represents a container that can be used to group other widgets.
4 4 """
5 5 #-----------------------------------------------------------------------------
6 6 # Copyright (c) 2013, the IPython Development Team.
7 7 #
8 8 # Distributed under the terms of the Modified BSD License.
9 9 #
10 10 # The full license is in the file COPYING.txt, distributed with this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16 from .widget import Widget
17 17 from IPython.utils.traitlets import Unicode, Bool, List, Instance
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Classes
21 21 #-----------------------------------------------------------------------------
22 22 class ContainerWidget(Widget):
23 23 target_name = Unicode('ContainerWidgetModel')
24 24 default_view_name = Unicode('ContainerView')
25 25
26 26 children = []#List(Instance('IPython.html.widgets.widget.Widget'))
27 _children_lists_attr = List(Unicode, ['children'])
28 27
29 28 # Keys, all private and managed by helper methods. Flexible box model
30 29 # classes...
31 30 _keys = ['_vbox', '_hbox', '_align_start', '_align_end', '_align_center',
32 31 '_pack_start', '_pack_end', '_pack_center', '_flex0', '_flex1',
33 32 '_flex2', 'description', 'button_text']
34 33 description = Unicode()
35 34 button_text = Unicode()
36 35 _hbox = Bool(False)
37 36 _vbox = Bool(False)
38 37 _align_start = Bool(False)
39 38 _align_end = Bool(False)
40 39 _align_center = Bool(False)
41 40 _pack_start = Bool(False)
42 41 _pack_end = Bool(False)
43 42 _pack_center = Bool(False)
44 43 _flex0 = Bool(False)
45 44 _flex1 = Bool(False)
46 45 _flex2 = Bool(False)
47 46
48 47 def hbox(self, enabled=True):
49 48 """Make this container an hbox. Automatically disables conflicting
50 49 features.
51 50
52 51 Parameters
53 52 ----------
54 53 enabled: bool (optional)
55 54 Enabled or disable the hbox feature of the container, defaults to
56 55 True."""
57 56 self._hbox = enabled
58 57 if enabled:
59 58 self._vbox = False
60 59
61 60 def vbox(self, enabled=True):
62 61 """Make this container an vbox. Automatically disables conflicting
63 62 features.
64 63
65 64 Parameters
66 65 ----------
67 66 enabled: bool (optional)
68 67 Enabled or disable the vbox feature of the container, defaults to
69 68 True."""
70 69 self._vbox = enabled
71 70 if enabled:
72 71 self._hbox = False
73 72
74 73 def align_start(self, enabled=True):
75 74 """Make the contents of this container align to the start of the axis.
76 75 Automatically disables conflicting alignments.
77 76
78 77 Parameters
79 78 ----------
80 79 enabled: bool (optional)
81 80 Enabled or disable the start alignment of the container, defaults to
82 81 True."""
83 82 self._align_start = enabled
84 83 if enabled:
85 84 self._align_end = False
86 85 self._align_center = False
87 86
88 87 def align_end(self, enabled=True):
89 88 """Make the contents of this container align to the end of the axis.
90 89 Automatically disables conflicting alignments.
91 90
92 91 Parameters
93 92 ----------
94 93 enabled: bool (optional)
95 94 Enabled or disable the end alignment of the container, defaults to
96 95 True."""
97 96 self._align_end = enabled
98 97 if enabled:
99 98 self._align_start = False
100 99 self._align_center = False
101 100
102 101 def align_center(self, enabled=True):
103 102 """Make the contents of this container align to the center of the axis.
104 103 Automatically disables conflicting alignments.
105 104
106 105 Parameters
107 106 ----------
108 107 enabled: bool (optional)
109 108 Enabled or disable the center alignment of the container, defaults to
110 109 True."""
111 110 self._align_center = enabled
112 111 if enabled:
113 112 self._align_start = False
114 113 self._align_end = False
115 114
116 115
117 116 def pack_start(self, enabled=True):
118 117 """Make the contents of this container pack to the start of the axis.
119 118 Automatically disables conflicting packings.
120 119
121 120 Parameters
122 121 ----------
123 122 enabled: bool (optional)
124 123 Enabled or disable the start packing of the container, defaults to
125 124 True."""
126 125 self._pack_start = enabled
127 126 if enabled:
128 127 self._pack_end = False
129 128 self._pack_center = False
130 129
131 130 def pack_end(self, enabled=True):
132 131 """Make the contents of this container pack to the end of the axis.
133 132 Automatically disables conflicting packings.
134 133
135 134 Parameters
136 135 ----------
137 136 enabled: bool (optional)
138 137 Enabled or disable the end packing of the container, defaults to
139 138 True."""
140 139 self._pack_end = enabled
141 140 if enabled:
142 141 self._pack_start = False
143 142 self._pack_center = False
144 143
145 144 def pack_center(self, enabled=True):
146 145 """Make the contents of this container pack to the center of the axis.
147 146 Automatically disables conflicting packings.
148 147
149 148 Parameters
150 149 ----------
151 150 enabled: bool (optional)
152 151 Enabled or disable the center packing of the container, defaults to
153 152 True."""
154 153 self._pack_center = enabled
155 154 if enabled:
156 155 self._pack_start = False
157 156 self._pack_end = False
158 157
159 158
160 159 def flex0(self, enabled=True):
161 160 """Put this container in flex0 mode. Automatically disables conflicting
162 161 flex modes. See the widget tutorial part 5 example notebook for more
163 162 information.
164 163
165 164 Parameters
166 165 ----------
167 166 enabled: bool (optional)
168 167 Enabled or disable the flex0 attribute of the container, defaults to
169 168 True."""
170 169 self._flex0 = enabled
171 170 if enabled:
172 171 self._flex1 = False
173 172 self._flex2 = False
174 173
175 174 def flex1(self, enabled=True):
176 175 """Put this container in flex1 mode. Automatically disables conflicting
177 176 flex modes. See the widget tutorial part 5 example notebook for more
178 177 information.
179 178
180 179 Parameters
181 180 ----------
182 181 enabled: bool (optional)
183 182 Enabled or disable the flex1 attribute of the container, defaults to
184 183 True."""
185 184 self._flex1 = enabled
186 185 if enabled:
187 186 self._flex0 = False
188 187 self._flex2 = False
189 188
190 189 def flex2(self, enabled=True):
191 190 """Put this container in flex2 mode. Automatically disables conflicting
192 191 flex modes. See the widget tutorial part 5 example notebook for more
193 192 information.
194 193
195 194 Parameters
196 195 ----------
197 196 enabled: bool (optional)
198 197 Enabled or disable the flex2 attribute of the container, defaults to
199 198 True."""
200 199 self._flex2 = enabled
201 200 if enabled:
202 201 self._flex0 = False
203 202 self._flex1 = False
@@ -1,59 +1,58 b''
1 1 """MulticontainerWidget class.
2 2
3 3 Represents a multipage container that can be used to group other widgets into
4 4 pages.
5 5 """
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (c) 2013, the IPython Development Team.
8 8 #
9 9 # Distributed under the terms of the Modified BSD License.
10 10 #
11 11 # The full license is in the file COPYING.txt, distributed with this software.
12 12 #-----------------------------------------------------------------------------
13 13
14 14 #-----------------------------------------------------------------------------
15 15 # Imports
16 16 #-----------------------------------------------------------------------------
17 17 from .widget import Widget
18 18 from IPython.utils.traitlets import Unicode, Dict, Int, List, Instance
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Classes
22 22 #-----------------------------------------------------------------------------
23 23 class MulticontainerWidget(Widget):
24 24 target_name = Unicode('MulticontainerWidgetModel')
25 25 default_view_name = Unicode('TabView')
26 26
27 27 # Keys
28 28 _keys = ['_titles', 'selected_index']
29 29 _titles = Dict(help="Titles of the pages")
30 30 selected_index = Int(0)
31 31
32 32 children = []#List(Instance('IPython.html.widgets.widget.Widget'))
33 _children_lists_attr = List(Unicode, ['children'])
34 33
35 34 # Public methods
36 35 def set_title(self, index, title):
37 36 """Sets the title of a container page
38 37
39 38 Parameters
40 39 ----------
41 40 index : int
42 41 Index of the container page
43 42 title : unicode
44 43 New title"""
45 44 self._titles[index] = title
46 45 self.send_state('_titles')
47 46
48 47
49 48 def get_title(self, index):
50 49 """Gets the title of a container pages
51 50
52 51 Parameters
53 52 ----------
54 53 index : int
55 54 Index of the container page"""
56 55 if index in self._titles:
57 56 return self._titles[index]
58 57 else:
59 58 return None
General Comments 0
You need to be logged in to leave comments. Login now