##// END OF EJS Templates
Merge branch 'flush'
Thomas Kluyver -
r5420:cb0cd721 merge
parent child Browse files
Show More
@@ -1,1069 +1,1074 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 var Notebook = function (selector) {
16 var Notebook = function (selector) {
17 this.read_only = IPython.read_only;
17 this.read_only = IPython.read_only;
18 this.element = $(selector);
18 this.element = $(selector);
19 this.element.scroll();
19 this.element.scroll();
20 this.element.data("notebook", this);
20 this.element.data("notebook", this);
21 this.next_prompt_number = 1;
21 this.next_prompt_number = 1;
22 this.kernel = null;
22 this.kernel = null;
23 this.dirty = false;
23 this.dirty = false;
24 this.msg_cell_map = {};
24 this.msg_cell_map = {};
25 this.metadata = {};
25 this.metadata = {};
26 this.control_key_active = false;
26 this.control_key_active = false;
27 this.style();
27 this.style();
28 this.create_elements();
28 this.create_elements();
29 this.bind_events();
29 this.bind_events();
30 this.set_tooltipontab(true);
30 this.set_tooltipontab(true);
31 this.set_smartcompleter(true);
31 this.set_smartcompleter(true);
32 this.set_timebeforetooltip(1200);
32 this.set_timebeforetooltip(1200);
33 };
33 };
34
34
35
35
36 Notebook.prototype.style = function () {
36 Notebook.prototype.style = function () {
37 $('div#notebook').addClass('border-box-sizing');
37 $('div#notebook').addClass('border-box-sizing');
38 };
38 };
39
39
40
40
41 Notebook.prototype.create_elements = function () {
41 Notebook.prototype.create_elements = function () {
42 // We add this end_space div to the end of the notebook div to:
42 // We add this end_space div to the end of the notebook div to:
43 // i) provide a margin between the last cell and the end of the notebook
43 // i) provide a margin between the last cell and the end of the notebook
44 // ii) to prevent the div from scrolling up when the last cell is being
44 // ii) to prevent the div from scrolling up when the last cell is being
45 // edited, but is too low on the page, which browsers will do automatically.
45 // edited, but is too low on the page, which browsers will do automatically.
46 var that = this;
46 var that = this;
47 var end_space = $('<div class="end_space"></div>').height(150);
47 var end_space = $('<div class="end_space"></div>').height(150);
48 end_space.dblclick(function (e) {
48 end_space.dblclick(function (e) {
49 if (that.read_only) return;
49 if (that.read_only) return;
50 var ncells = that.ncells();
50 var ncells = that.ncells();
51 that.insert_code_cell_below(ncells-1);
51 that.insert_code_cell_below(ncells-1);
52 });
52 });
53 this.element.append(end_space);
53 this.element.append(end_space);
54 $('div#notebook').addClass('border-box-sizing');
54 $('div#notebook').addClass('border-box-sizing');
55 };
55 };
56
56
57
57
58 Notebook.prototype.bind_events = function () {
58 Notebook.prototype.bind_events = function () {
59 var that = this;
59 var that = this;
60 $(document).keydown(function (event) {
60 $(document).keydown(function (event) {
61 // console.log(event);
61 // console.log(event);
62 if (that.read_only) return;
62 if (that.read_only) return;
63 if (event.which === 27) {
63 if (event.which === 27) {
64 // Intercept escape at highest level to avoid closing
64 // Intercept escape at highest level to avoid closing
65 // websocket connection with firefox
65 // websocket connection with firefox
66 event.preventDefault();
66 event.preventDefault();
67 }
67 }
68 if (event.which === 38 && !event.shiftKey) {
68 if (event.which === 38 && !event.shiftKey) {
69 var cell = that.selected_cell();
69 var cell = that.selected_cell();
70 if (cell.at_top()) {
70 if (cell.at_top()) {
71 event.preventDefault();
71 event.preventDefault();
72 that.select_prev();
72 that.select_prev();
73 };
73 };
74 } else if (event.which === 40 && !event.shiftKey) {
74 } else if (event.which === 40 && !event.shiftKey) {
75 var cell = that.selected_cell();
75 var cell = that.selected_cell();
76 if (cell.at_bottom()) {
76 if (cell.at_bottom()) {
77 event.preventDefault();
77 event.preventDefault();
78 that.select_next();
78 that.select_next();
79 };
79 };
80 } else if (event.which === 13 && event.shiftKey) {
80 } else if (event.which === 13 && event.shiftKey) {
81 that.execute_selected_cell();
81 that.execute_selected_cell();
82 return false;
82 return false;
83 } else if (event.which === 13 && event.ctrlKey) {
83 } else if (event.which === 13 && event.ctrlKey) {
84 that.execute_selected_cell({terminal:true});
84 that.execute_selected_cell({terminal:true});
85 return false;
85 return false;
86 } else if (event.which === 77 && event.ctrlKey) {
86 } else if (event.which === 77 && event.ctrlKey) {
87 that.control_key_active = true;
87 that.control_key_active = true;
88 return false;
88 return false;
89 } else if (event.which === 68 && that.control_key_active) {
89 } else if (event.which === 68 && that.control_key_active) {
90 // Delete selected cell = d
90 // Delete selected cell = d
91 that.delete_cell();
91 that.delete_cell();
92 that.control_key_active = false;
92 that.control_key_active = false;
93 return false;
93 return false;
94 } else if (event.which === 65 && that.control_key_active) {
94 } else if (event.which === 65 && that.control_key_active) {
95 // Insert code cell above selected = a
95 // Insert code cell above selected = a
96 that.insert_code_cell_above();
96 that.insert_code_cell_above();
97 that.control_key_active = false;
97 that.control_key_active = false;
98 return false;
98 return false;
99 } else if (event.which === 66 && that.control_key_active) {
99 } else if (event.which === 66 && that.control_key_active) {
100 // Insert code cell below selected = b
100 // Insert code cell below selected = b
101 that.insert_code_cell_below();
101 that.insert_code_cell_below();
102 that.control_key_active = false;
102 that.control_key_active = false;
103 return false;
103 return false;
104 } else if (event.which === 67 && that.control_key_active) {
104 } else if (event.which === 67 && that.control_key_active) {
105 // To code = c
105 // To code = c
106 that.to_code();
106 that.to_code();
107 that.control_key_active = false;
107 that.control_key_active = false;
108 return false;
108 return false;
109 } else if (event.which === 77 && that.control_key_active) {
109 } else if (event.which === 77 && that.control_key_active) {
110 // To markdown = m
110 // To markdown = m
111 that.to_markdown();
111 that.to_markdown();
112 that.control_key_active = false;
112 that.control_key_active = false;
113 return false;
113 return false;
114 } else if (event.which === 84 && that.control_key_active) {
114 } else if (event.which === 84 && that.control_key_active) {
115 // Toggle output = t
115 // Toggle output = t
116 that.toggle_output();
116 that.toggle_output();
117 that.control_key_active = false;
117 that.control_key_active = false;
118 return false;
118 return false;
119 } else if (event.which === 83 && that.control_key_active) {
119 } else if (event.which === 83 && that.control_key_active) {
120 // Save notebook = s
120 // Save notebook = s
121 IPython.save_widget.save_notebook();
121 IPython.save_widget.save_notebook();
122 that.control_key_active = false;
122 that.control_key_active = false;
123 return false;
123 return false;
124 } else if (event.which === 74 && that.control_key_active) {
124 } else if (event.which === 74 && that.control_key_active) {
125 // Move cell down = j
125 // Move cell down = j
126 that.move_cell_down();
126 that.move_cell_down();
127 that.control_key_active = false;
127 that.control_key_active = false;
128 return false;
128 return false;
129 } else if (event.which === 75 && that.control_key_active) {
129 } else if (event.which === 75 && that.control_key_active) {
130 // Move cell up = k
130 // Move cell up = k
131 that.move_cell_up();
131 that.move_cell_up();
132 that.control_key_active = false;
132 that.control_key_active = false;
133 return false;
133 return false;
134 } else if (event.which === 80 && that.control_key_active) {
134 } else if (event.which === 80 && that.control_key_active) {
135 // Select previous = p
135 // Select previous = p
136 that.select_prev();
136 that.select_prev();
137 that.control_key_active = false;
137 that.control_key_active = false;
138 return false;
138 return false;
139 } else if (event.which === 78 && that.control_key_active) {
139 } else if (event.which === 78 && that.control_key_active) {
140 // Select next = n
140 // Select next = n
141 that.select_next();
141 that.select_next();
142 that.control_key_active = false;
142 that.control_key_active = false;
143 return false;
143 return false;
144 } else if (event.which === 76 && that.control_key_active) {
144 } else if (event.which === 76 && that.control_key_active) {
145 // Toggle line numbers = l
145 // Toggle line numbers = l
146 that.cell_toggle_line_numbers();
146 that.cell_toggle_line_numbers();
147 that.control_key_active = false;
147 that.control_key_active = false;
148 return false;
148 return false;
149 } else if (event.which === 73 && that.control_key_active) {
149 } else if (event.which === 73 && that.control_key_active) {
150 // Interrupt kernel = i
150 // Interrupt kernel = i
151 IPython.notebook.kernel.interrupt();
151 IPython.notebook.kernel.interrupt();
152 that.control_key_active = false;
152 that.control_key_active = false;
153 return false;
153 return false;
154 } else if (event.which === 190 && that.control_key_active) {
154 } else if (event.which === 190 && that.control_key_active) {
155 // Restart kernel = . # matches qt console
155 // Restart kernel = . # matches qt console
156 IPython.notebook.restart_kernel();
156 IPython.notebook.restart_kernel();
157 that.control_key_active = false;
157 that.control_key_active = false;
158 return false;
158 return false;
159 } else if (event.which === 72 && that.control_key_active) {
159 } else if (event.which === 72 && that.control_key_active) {
160 // Show keyboard shortcuts = h
160 // Show keyboard shortcuts = h
161 that.toggle_keyboard_shortcuts();
161 that.toggle_keyboard_shortcuts();
162 that.control_key_active = false;
162 that.control_key_active = false;
163 return false;
163 return false;
164 } else if (that.control_key_active) {
164 } else if (that.control_key_active) {
165 that.control_key_active = false;
165 that.control_key_active = false;
166 return true;
166 return true;
167 };
167 };
168 });
168 });
169
169
170 this.element.bind('collapse_pager', function () {
170 this.element.bind('collapse_pager', function () {
171 var app_height = $('div#main_app').height(); // content height
171 var app_height = $('div#main_app').height(); // content height
172 var splitter_height = $('div#pager_splitter').outerHeight(true);
172 var splitter_height = $('div#pager_splitter').outerHeight(true);
173 var new_height = app_height - splitter_height;
173 var new_height = app_height - splitter_height;
174 that.element.animate({height : new_height + 'px'}, 'fast');
174 that.element.animate({height : new_height + 'px'}, 'fast');
175 });
175 });
176
176
177 this.element.bind('expand_pager', function () {
177 this.element.bind('expand_pager', function () {
178 var app_height = $('div#main_app').height(); // content height
178 var app_height = $('div#main_app').height(); // content height
179 var splitter_height = $('div#pager_splitter').outerHeight(true);
179 var splitter_height = $('div#pager_splitter').outerHeight(true);
180 var pager_height = $('div#pager').outerHeight(true);
180 var pager_height = $('div#pager').outerHeight(true);
181 var new_height = app_height - pager_height - splitter_height;
181 var new_height = app_height - pager_height - splitter_height;
182 that.element.animate({height : new_height + 'px'}, 'fast');
182 that.element.animate({height : new_height + 'px'}, 'fast');
183 });
183 });
184
184
185 this.element.bind('collapse_left_panel', function () {
185 this.element.bind('collapse_left_panel', function () {
186 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
186 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
187 var new_margin = splitter_width;
187 var new_margin = splitter_width;
188 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
188 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
189 });
189 });
190
190
191 this.element.bind('expand_left_panel', function () {
191 this.element.bind('expand_left_panel', function () {
192 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
192 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
193 var left_panel_width = IPython.left_panel.width;
193 var left_panel_width = IPython.left_panel.width;
194 var new_margin = splitter_width + left_panel_width;
194 var new_margin = splitter_width + left_panel_width;
195 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
195 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
196 });
196 });
197
197
198 $(window).bind('beforeunload', function () {
198 $(window).bind('beforeunload', function () {
199 var kill_kernel = $('#kill_kernel').prop('checked');
199 var kill_kernel = $('#kill_kernel').prop('checked');
200 if (kill_kernel) {
200 if (kill_kernel) {
201 that.kernel.kill();
201 that.kernel.kill();
202 }
202 }
203 if (that.dirty && ! that.read_only) {
203 if (that.dirty && ! that.read_only) {
204 return "You have unsaved changes that will be lost if you leave this page.";
204 return "You have unsaved changes that will be lost if you leave this page.";
205 };
205 };
206 });
206 });
207 };
207 };
208
208
209
209
210 Notebook.prototype.toggle_keyboard_shortcuts = function () {
210 Notebook.prototype.toggle_keyboard_shortcuts = function () {
211 // toggles display of keyboard shortcut dialog
211 // toggles display of keyboard shortcut dialog
212 var that = this;
212 var that = this;
213 if ( this.shortcut_dialog ){
213 if ( this.shortcut_dialog ){
214 // if dialog is already shown, close it
214 // if dialog is already shown, close it
215 this.shortcut_dialog.dialog("close");
215 this.shortcut_dialog.dialog("close");
216 this.shortcut_dialog = null;
216 this.shortcut_dialog = null;
217 return;
217 return;
218 }
218 }
219 var dialog = $('<div/>');
219 var dialog = $('<div/>');
220 this.shortcut_dialog = dialog;
220 this.shortcut_dialog = dialog;
221 var shortcuts = [
221 var shortcuts = [
222 {key: 'Shift-Enter', help: 'run cell'},
222 {key: 'Shift-Enter', help: 'run cell'},
223 {key: 'Ctrl-Enter', help: 'run cell in-place'},
223 {key: 'Ctrl-Enter', help: 'run cell in-place'},
224 {key: 'Ctrl-m d', help: 'delete cell'},
224 {key: 'Ctrl-m d', help: 'delete cell'},
225 {key: 'Ctrl-m a', help: 'insert cell above'},
225 {key: 'Ctrl-m a', help: 'insert cell above'},
226 {key: 'Ctrl-m b', help: 'insert cell below'},
226 {key: 'Ctrl-m b', help: 'insert cell below'},
227 {key: 'Ctrl-m t', help: 'toggle output'},
227 {key: 'Ctrl-m t', help: 'toggle output'},
228 {key: 'Ctrl-m l', help: 'toggle line numbers'},
228 {key: 'Ctrl-m l', help: 'toggle line numbers'},
229 {key: 'Ctrl-m s', help: 'save notebook'},
229 {key: 'Ctrl-m s', help: 'save notebook'},
230 {key: 'Ctrl-m j', help: 'move cell down'},
230 {key: 'Ctrl-m j', help: 'move cell down'},
231 {key: 'Ctrl-m k', help: 'move cell up'},
231 {key: 'Ctrl-m k', help: 'move cell up'},
232 {key: 'Ctrl-m c', help: 'code cell'},
232 {key: 'Ctrl-m c', help: 'code cell'},
233 {key: 'Ctrl-m m', help: 'markdown cell'},
233 {key: 'Ctrl-m m', help: 'markdown cell'},
234 {key: 'Ctrl-m p', help: 'select previous'},
234 {key: 'Ctrl-m p', help: 'select previous'},
235 {key: 'Ctrl-m n', help: 'select next'},
235 {key: 'Ctrl-m n', help: 'select next'},
236 {key: 'Ctrl-m i', help: 'interrupt kernel'},
236 {key: 'Ctrl-m i', help: 'interrupt kernel'},
237 {key: 'Ctrl-m .', help: 'restart kernel'},
237 {key: 'Ctrl-m .', help: 'restart kernel'},
238 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
238 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
239 ];
239 ];
240 for (var i=0; i<shortcuts.length; i++) {
240 for (var i=0; i<shortcuts.length; i++) {
241 dialog.append($('<div>').
241 dialog.append($('<div>').
242 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
242 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
243 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
243 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
244 );
244 );
245 };
245 };
246 dialog.bind('dialogclose', function(event) {
246 dialog.bind('dialogclose', function(event) {
247 // dialog has been closed, allow it to be drawn again.
247 // dialog has been closed, allow it to be drawn again.
248 that.shortcut_dialog = null;
248 that.shortcut_dialog = null;
249 });
249 });
250 dialog.dialog({title: 'Keyboard shortcuts'});
250 dialog.dialog({title: 'Keyboard shortcuts'});
251 };
251 };
252
252
253
253
254 Notebook.prototype.scroll_to_bottom = function () {
254 Notebook.prototype.scroll_to_bottom = function () {
255 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
255 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
256 };
256 };
257
257
258
258
259 Notebook.prototype.scroll_to_top = function () {
259 Notebook.prototype.scroll_to_top = function () {
260 this.element.animate({scrollTop:0}, 0);
260 this.element.animate({scrollTop:0}, 0);
261 };
261 };
262
262
263
263
264 // Cell indexing, retrieval, etc.
264 // Cell indexing, retrieval, etc.
265
265
266
266
267 Notebook.prototype.cell_elements = function () {
267 Notebook.prototype.cell_elements = function () {
268 return this.element.children("div.cell");
268 return this.element.children("div.cell");
269 }
269 }
270
270
271
271
272 Notebook.prototype.ncells = function (cell) {
272 Notebook.prototype.ncells = function (cell) {
273 return this.cell_elements().length;
273 return this.cell_elements().length;
274 }
274 }
275
275
276
276
277 // TODO: we are often calling cells as cells()[i], which we should optimize
277 // TODO: we are often calling cells as cells()[i], which we should optimize
278 // to cells(i) or a new method.
278 // to cells(i) or a new method.
279 Notebook.prototype.cells = function () {
279 Notebook.prototype.cells = function () {
280 return this.cell_elements().toArray().map(function (e) {
280 return this.cell_elements().toArray().map(function (e) {
281 return $(e).data("cell");
281 return $(e).data("cell");
282 });
282 });
283 }
283 }
284
284
285
285
286 Notebook.prototype.find_cell_index = function (cell) {
286 Notebook.prototype.find_cell_index = function (cell) {
287 var result = null;
287 var result = null;
288 this.cell_elements().filter(function (index) {
288 this.cell_elements().filter(function (index) {
289 if ($(this).data("cell") === cell) {
289 if ($(this).data("cell") === cell) {
290 result = index;
290 result = index;
291 };
291 };
292 });
292 });
293 return result;
293 return result;
294 };
294 };
295
295
296
296
297 Notebook.prototype.index_or_selected = function (index) {
297 Notebook.prototype.index_or_selected = function (index) {
298 return index || this.selected_index() || 0;
298 return index || this.selected_index() || 0;
299 }
299 }
300
300
301
301
302 Notebook.prototype.select = function (index) {
302 Notebook.prototype.select = function (index) {
303 if (index !== undefined && index >= 0 && index < this.ncells()) {
303 if (index !== undefined && index >= 0 && index < this.ncells()) {
304 if (this.selected_index() !== null) {
304 if (this.selected_index() !== null) {
305 this.selected_cell().unselect();
305 this.selected_cell().unselect();
306 };
306 };
307 this.cells()[index].select();
307 this.cells()[index].select();
308 };
308 };
309 return this;
309 return this;
310 };
310 };
311
311
312
312
313 Notebook.prototype.select_next = function () {
313 Notebook.prototype.select_next = function () {
314 var index = this.selected_index();
314 var index = this.selected_index();
315 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
315 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
316 this.select(index+1);
316 this.select(index+1);
317 };
317 };
318 return this;
318 return this;
319 };
319 };
320
320
321
321
322 Notebook.prototype.select_prev = function () {
322 Notebook.prototype.select_prev = function () {
323 var index = this.selected_index();
323 var index = this.selected_index();
324 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
324 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
325 this.select(index-1);
325 this.select(index-1);
326 };
326 };
327 return this;
327 return this;
328 };
328 };
329
329
330
330
331 Notebook.prototype.selected_index = function () {
331 Notebook.prototype.selected_index = function () {
332 var result = null;
332 var result = null;
333 this.cell_elements().filter(function (index) {
333 this.cell_elements().filter(function (index) {
334 if ($(this).data("cell").selected === true) {
334 if ($(this).data("cell").selected === true) {
335 result = index;
335 result = index;
336 };
336 };
337 });
337 });
338 return result;
338 return result;
339 };
339 };
340
340
341
341
342 Notebook.prototype.cell_for_msg = function (msg_id) {
342 Notebook.prototype.cell_for_msg = function (msg_id) {
343 var cell_id = this.msg_cell_map[msg_id];
343 var cell_id = this.msg_cell_map[msg_id];
344 var result = null;
344 var result = null;
345 this.cell_elements().filter(function (index) {
345 this.cell_elements().filter(function (index) {
346 cell = $(this).data("cell");
346 cell = $(this).data("cell");
347 if (cell.cell_id === cell_id) {
347 if (cell.cell_id === cell_id) {
348 result = cell;
348 result = cell;
349 };
349 };
350 });
350 });
351 return result;
351 return result;
352 };
352 };
353
353
354
354
355 Notebook.prototype.selected_cell = function () {
355 Notebook.prototype.selected_cell = function () {
356 return this.cell_elements().eq(this.selected_index()).data("cell");
356 return this.cell_elements().eq(this.selected_index()).data("cell");
357 }
357 }
358
358
359
359
360 // Cell insertion, deletion and moving.
360 // Cell insertion, deletion and moving.
361
361
362
362
363 Notebook.prototype.delete_cell = function (index) {
363 Notebook.prototype.delete_cell = function (index) {
364 var i = index || this.selected_index();
364 var i = index || this.selected_index();
365 if (i !== null && i >= 0 && i < this.ncells()) {
365 if (i !== null && i >= 0 && i < this.ncells()) {
366 this.cell_elements().eq(i).remove();
366 this.cell_elements().eq(i).remove();
367 if (i === (this.ncells())) {
367 if (i === (this.ncells())) {
368 this.select(i-1);
368 this.select(i-1);
369 } else {
369 } else {
370 this.select(i);
370 this.select(i);
371 };
371 };
372 };
372 };
373 this.dirty = true;
373 this.dirty = true;
374 return this;
374 return this;
375 };
375 };
376
376
377
377
378 Notebook.prototype.append_cell = function (cell) {
378 Notebook.prototype.append_cell = function (cell) {
379 this.element.find('div.end_space').before(cell.element);
379 this.element.find('div.end_space').before(cell.element);
380 this.dirty = true;
380 this.dirty = true;
381 return this;
381 return this;
382 };
382 };
383
383
384
384
385 Notebook.prototype.insert_cell_below = function (cell, index) {
385 Notebook.prototype.insert_cell_below = function (cell, index) {
386 var ncells = this.ncells();
386 var ncells = this.ncells();
387 if (ncells === 0) {
387 if (ncells === 0) {
388 this.append_cell(cell);
388 this.append_cell(cell);
389 return this;
389 return this;
390 };
390 };
391 if (index >= 0 && index < ncells) {
391 if (index >= 0 && index < ncells) {
392 this.cell_elements().eq(index).after(cell.element);
392 this.cell_elements().eq(index).after(cell.element);
393 };
393 };
394 this.dirty = true;
394 this.dirty = true;
395 return this
395 return this
396 };
396 };
397
397
398
398
399 Notebook.prototype.insert_cell_above = function (cell, index) {
399 Notebook.prototype.insert_cell_above = function (cell, index) {
400 var ncells = this.ncells();
400 var ncells = this.ncells();
401 if (ncells === 0) {
401 if (ncells === 0) {
402 this.append_cell(cell);
402 this.append_cell(cell);
403 return this;
403 return this;
404 };
404 };
405 if (index >= 0 && index < ncells) {
405 if (index >= 0 && index < ncells) {
406 this.cell_elements().eq(index).before(cell.element);
406 this.cell_elements().eq(index).before(cell.element);
407 };
407 };
408 this.dirty = true;
408 this.dirty = true;
409 return this;
409 return this;
410 };
410 };
411
411
412
412
413 Notebook.prototype.move_cell_up = function (index) {
413 Notebook.prototype.move_cell_up = function (index) {
414 var i = index || this.selected_index();
414 var i = index || this.selected_index();
415 if (i !== null && i < this.ncells() && i > 0) {
415 if (i !== null && i < this.ncells() && i > 0) {
416 var pivot = this.cell_elements().eq(i-1);
416 var pivot = this.cell_elements().eq(i-1);
417 var tomove = this.cell_elements().eq(i);
417 var tomove = this.cell_elements().eq(i);
418 if (pivot !== null && tomove !== null) {
418 if (pivot !== null && tomove !== null) {
419 tomove.detach();
419 tomove.detach();
420 pivot.before(tomove);
420 pivot.before(tomove);
421 this.select(i-1);
421 this.select(i-1);
422 };
422 };
423 };
423 };
424 this.dirty = true;
424 this.dirty = true;
425 return this;
425 return this;
426 }
426 }
427
427
428
428
429 Notebook.prototype.move_cell_down = function (index) {
429 Notebook.prototype.move_cell_down = function (index) {
430 var i = index || this.selected_index();
430 var i = index || this.selected_index();
431 if (i !== null && i < (this.ncells()-1) && i >= 0) {
431 if (i !== null && i < (this.ncells()-1) && i >= 0) {
432 var pivot = this.cell_elements().eq(i+1)
432 var pivot = this.cell_elements().eq(i+1)
433 var tomove = this.cell_elements().eq(i)
433 var tomove = this.cell_elements().eq(i)
434 if (pivot !== null && tomove !== null) {
434 if (pivot !== null && tomove !== null) {
435 tomove.detach();
435 tomove.detach();
436 pivot.after(tomove);
436 pivot.after(tomove);
437 this.select(i+1);
437 this.select(i+1);
438 };
438 };
439 };
439 };
440 this.dirty = true;
440 this.dirty = true;
441 return this;
441 return this;
442 }
442 }
443
443
444
444
445 Notebook.prototype.sort_cells = function () {
445 Notebook.prototype.sort_cells = function () {
446 var ncells = this.ncells();
446 var ncells = this.ncells();
447 var sindex = this.selected_index();
447 var sindex = this.selected_index();
448 var swapped;
448 var swapped;
449 do {
449 do {
450 swapped = false
450 swapped = false
451 for (var i=1; i<ncells; i++) {
451 for (var i=1; i<ncells; i++) {
452 current = this.cell_elements().eq(i).data("cell");
452 current = this.cell_elements().eq(i).data("cell");
453 previous = this.cell_elements().eq(i-1).data("cell");
453 previous = this.cell_elements().eq(i-1).data("cell");
454 if (previous.input_prompt_number > current.input_prompt_number) {
454 if (previous.input_prompt_number > current.input_prompt_number) {
455 this.move_cell_up(i);
455 this.move_cell_up(i);
456 swapped = true;
456 swapped = true;
457 };
457 };
458 };
458 };
459 } while (swapped);
459 } while (swapped);
460 this.select(sindex);
460 this.select(sindex);
461 return this;
461 return this;
462 };
462 };
463
463
464
464
465 Notebook.prototype.insert_code_cell_above = function (index) {
465 Notebook.prototype.insert_code_cell_above = function (index) {
466 // TODO: Bounds check for i
466 // TODO: Bounds check for i
467 var i = this.index_or_selected(index);
467 var i = this.index_or_selected(index);
468 var cell = new IPython.CodeCell(this);
468 var cell = new IPython.CodeCell(this);
469 cell.set_input_prompt();
469 cell.set_input_prompt();
470 this.insert_cell_above(cell, i);
470 this.insert_cell_above(cell, i);
471 this.select(this.find_cell_index(cell));
471 this.select(this.find_cell_index(cell));
472 return cell;
472 return cell;
473 }
473 }
474
474
475
475
476 Notebook.prototype.insert_code_cell_below = function (index) {
476 Notebook.prototype.insert_code_cell_below = function (index) {
477 // TODO: Bounds check for i
477 // TODO: Bounds check for i
478 var i = this.index_or_selected(index);
478 var i = this.index_or_selected(index);
479 var cell = new IPython.CodeCell(this);
479 var cell = new IPython.CodeCell(this);
480 cell.set_input_prompt();
480 cell.set_input_prompt();
481 this.insert_cell_below(cell, i);
481 this.insert_cell_below(cell, i);
482 this.select(this.find_cell_index(cell));
482 this.select(this.find_cell_index(cell));
483 return cell;
483 return cell;
484 }
484 }
485
485
486
486
487 Notebook.prototype.insert_html_cell_above = function (index) {
487 Notebook.prototype.insert_html_cell_above = function (index) {
488 // TODO: Bounds check for i
488 // TODO: Bounds check for i
489 var i = this.index_or_selected(index);
489 var i = this.index_or_selected(index);
490 var cell = new IPython.HTMLCell(this);
490 var cell = new IPython.HTMLCell(this);
491 cell.config_mathjax();
491 cell.config_mathjax();
492 this.insert_cell_above(cell, i);
492 this.insert_cell_above(cell, i);
493 this.select(this.find_cell_index(cell));
493 this.select(this.find_cell_index(cell));
494 return cell;
494 return cell;
495 }
495 }
496
496
497
497
498 Notebook.prototype.insert_html_cell_below = function (index) {
498 Notebook.prototype.insert_html_cell_below = function (index) {
499 // TODO: Bounds check for i
499 // TODO: Bounds check for i
500 var i = this.index_or_selected(index);
500 var i = this.index_or_selected(index);
501 var cell = new IPython.HTMLCell(this);
501 var cell = new IPython.HTMLCell(this);
502 cell.config_mathjax();
502 cell.config_mathjax();
503 this.insert_cell_below(cell, i);
503 this.insert_cell_below(cell, i);
504 this.select(this.find_cell_index(cell));
504 this.select(this.find_cell_index(cell));
505 return cell;
505 return cell;
506 }
506 }
507
507
508
508
509 Notebook.prototype.insert_markdown_cell_above = function (index) {
509 Notebook.prototype.insert_markdown_cell_above = function (index) {
510 // TODO: Bounds check for i
510 // TODO: Bounds check for i
511 var i = this.index_or_selected(index);
511 var i = this.index_or_selected(index);
512 var cell = new IPython.MarkdownCell(this);
512 var cell = new IPython.MarkdownCell(this);
513 cell.config_mathjax();
513 cell.config_mathjax();
514 this.insert_cell_above(cell, i);
514 this.insert_cell_above(cell, i);
515 this.select(this.find_cell_index(cell));
515 this.select(this.find_cell_index(cell));
516 return cell;
516 return cell;
517 }
517 }
518
518
519
519
520 Notebook.prototype.insert_markdown_cell_below = function (index) {
520 Notebook.prototype.insert_markdown_cell_below = function (index) {
521 // TODO: Bounds check for i
521 // TODO: Bounds check for i
522 var i = this.index_or_selected(index);
522 var i = this.index_or_selected(index);
523 var cell = new IPython.MarkdownCell(this);
523 var cell = new IPython.MarkdownCell(this);
524 cell.config_mathjax();
524 cell.config_mathjax();
525 this.insert_cell_below(cell, i);
525 this.insert_cell_below(cell, i);
526 this.select(this.find_cell_index(cell));
526 this.select(this.find_cell_index(cell));
527 return cell;
527 return cell;
528 }
528 }
529
529
530
530
531 Notebook.prototype.to_code = function (index) {
531 Notebook.prototype.to_code = function (index) {
532 // TODO: Bounds check for i
532 // TODO: Bounds check for i
533 var i = this.index_or_selected(index);
533 var i = this.index_or_selected(index);
534 var source_element = this.cell_elements().eq(i);
534 var source_element = this.cell_elements().eq(i);
535 var source_cell = source_element.data("cell");
535 var source_cell = source_element.data("cell");
536 if (source_cell instanceof IPython.HTMLCell ||
536 if (source_cell instanceof IPython.HTMLCell ||
537 source_cell instanceof IPython.MarkdownCell) {
537 source_cell instanceof IPython.MarkdownCell) {
538 this.insert_code_cell_below(i);
538 this.insert_code_cell_below(i);
539 var target_cell = this.cells()[i+1];
539 var target_cell = this.cells()[i+1];
540 target_cell.set_code(source_cell.get_source());
540 target_cell.set_code(source_cell.get_source());
541 source_element.remove();
541 source_element.remove();
542 target_cell.select();
542 target_cell.select();
543 };
543 };
544 this.dirty = true;
544 this.dirty = true;
545 };
545 };
546
546
547
547
548 Notebook.prototype.to_markdown = function (index) {
548 Notebook.prototype.to_markdown = function (index) {
549 // TODO: Bounds check for i
549 // TODO: Bounds check for i
550 var i = this.index_or_selected(index);
550 var i = this.index_or_selected(index);
551 var source_element = this.cell_elements().eq(i);
551 var source_element = this.cell_elements().eq(i);
552 var source_cell = source_element.data("cell");
552 var source_cell = source_element.data("cell");
553 var target_cell = null;
553 var target_cell = null;
554 if (source_cell instanceof IPython.CodeCell) {
554 if (source_cell instanceof IPython.CodeCell) {
555 this.insert_markdown_cell_below(i);
555 this.insert_markdown_cell_below(i);
556 var target_cell = this.cells()[i+1];
556 var target_cell = this.cells()[i+1];
557 var text = source_cell.get_code();
557 var text = source_cell.get_code();
558 } else if (source_cell instanceof IPython.HTMLCell) {
558 } else if (source_cell instanceof IPython.HTMLCell) {
559 this.insert_markdown_cell_below(i);
559 this.insert_markdown_cell_below(i);
560 var target_cell = this.cells()[i+1];
560 var target_cell = this.cells()[i+1];
561 var text = source_cell.get_source();
561 var text = source_cell.get_source();
562 if (text === source_cell.placeholder) {
562 if (text === source_cell.placeholder) {
563 text = target_cell.placeholder;
563 text = target_cell.placeholder;
564 }
564 }
565 }
565 }
566 if (target_cell !== null) {
566 if (target_cell !== null) {
567 if (text === "") {text = target_cell.placeholder;};
567 if (text === "") {text = target_cell.placeholder;};
568 target_cell.set_source(text);
568 target_cell.set_source(text);
569 source_element.remove();
569 source_element.remove();
570 target_cell.edit();
570 target_cell.edit();
571 }
571 }
572 this.dirty = true;
572 this.dirty = true;
573 };
573 };
574
574
575
575
576 Notebook.prototype.to_html = function (index) {
576 Notebook.prototype.to_html = function (index) {
577 // TODO: Bounds check for i
577 // TODO: Bounds check for i
578 var i = this.index_or_selected(index);
578 var i = this.index_or_selected(index);
579 var source_element = this.cell_elements().eq(i);
579 var source_element = this.cell_elements().eq(i);
580 var source_cell = source_element.data("cell");
580 var source_cell = source_element.data("cell");
581 var target_cell = null;
581 var target_cell = null;
582 if (source_cell instanceof IPython.CodeCell) {
582 if (source_cell instanceof IPython.CodeCell) {
583 this.insert_html_cell_below(i);
583 this.insert_html_cell_below(i);
584 var target_cell = this.cells()[i+1];
584 var target_cell = this.cells()[i+1];
585 var text = source_cell.get_code();
585 var text = source_cell.get_code();
586 } else if (source_cell instanceof IPython.MarkdownCell) {
586 } else if (source_cell instanceof IPython.MarkdownCell) {
587 this.insert_html_cell_below(i);
587 this.insert_html_cell_below(i);
588 var target_cell = this.cells()[i+1];
588 var target_cell = this.cells()[i+1];
589 var text = source_cell.get_source();
589 var text = source_cell.get_source();
590 if (text === source_cell.placeholder) {
590 if (text === source_cell.placeholder) {
591 text = target_cell.placeholder;
591 text = target_cell.placeholder;
592 }
592 }
593 }
593 }
594 if (target_cell !== null) {
594 if (target_cell !== null) {
595 if (text === "") {text = target_cell.placeholder;};
595 if (text === "") {text = target_cell.placeholder;};
596 target_cell.set_source(text);
596 target_cell.set_source(text);
597 source_element.remove();
597 source_element.remove();
598 target_cell.edit();
598 target_cell.edit();
599 }
599 }
600 this.dirty = true;
600 this.dirty = true;
601 };
601 };
602
602
603
603
604 // Cell collapsing and output clearing
604 // Cell collapsing and output clearing
605
605
606 Notebook.prototype.collapse = function (index) {
606 Notebook.prototype.collapse = function (index) {
607 var i = this.index_or_selected(index);
607 var i = this.index_or_selected(index);
608 this.cells()[i].collapse();
608 this.cells()[i].collapse();
609 this.dirty = true;
609 this.dirty = true;
610 };
610 };
611
611
612
612
613 Notebook.prototype.expand = function (index) {
613 Notebook.prototype.expand = function (index) {
614 var i = this.index_or_selected(index);
614 var i = this.index_or_selected(index);
615 this.cells()[i].expand();
615 this.cells()[i].expand();
616 this.dirty = true;
616 this.dirty = true;
617 };
617 };
618
618
619
619
620 Notebook.prototype.toggle_output = function (index) {
620 Notebook.prototype.toggle_output = function (index) {
621 var i = this.index_or_selected(index);
621 var i = this.index_or_selected(index);
622 this.cells()[i].toggle_output();
622 this.cells()[i].toggle_output();
623 this.dirty = true;
623 this.dirty = true;
624 };
624 };
625
625
626
626
627 Notebook.prototype.set_timebeforetooltip = function (time) {
627 Notebook.prototype.set_timebeforetooltip = function (time) {
628 console.log("change time before tooltip to : "+time);
628 console.log("change time before tooltip to : "+time);
629 this.time_before_tooltip = time;
629 this.time_before_tooltip = time;
630 };
630 };
631
631
632 Notebook.prototype.set_tooltipontab = function (state) {
632 Notebook.prototype.set_tooltipontab = function (state) {
633 console.log("change tooltip on tab to : "+state);
633 console.log("change tooltip on tab to : "+state);
634 this.tooltip_on_tab = state;
634 this.tooltip_on_tab = state;
635 };
635 };
636
636
637 Notebook.prototype.set_smartcompleter = function (state) {
637 Notebook.prototype.set_smartcompleter = function (state) {
638 console.log("Smart completion (kwargs first) changed to to : "+state);
638 console.log("Smart completion (kwargs first) changed to to : "+state);
639 this.smart_completer = state;
639 this.smart_completer = state;
640 };
640 };
641
641
642 Notebook.prototype.set_autoindent = function (state) {
642 Notebook.prototype.set_autoindent = function (state) {
643 var cells = this.cells();
643 var cells = this.cells();
644 len = cells.length;
644 len = cells.length;
645 for (var i=0; i<len; i++) {
645 for (var i=0; i<len; i++) {
646 cells[i].set_autoindent(state)
646 cells[i].set_autoindent(state)
647 };
647 };
648 };
648 };
649
649
650
650
651 Notebook.prototype.clear_all_output = function () {
651 Notebook.prototype.clear_all_output = function () {
652 var ncells = this.ncells();
652 var ncells = this.ncells();
653 var cells = this.cells();
653 var cells = this.cells();
654 for (var i=0; i<ncells; i++) {
654 for (var i=0; i<ncells; i++) {
655 if (cells[i] instanceof IPython.CodeCell) {
655 if (cells[i] instanceof IPython.CodeCell) {
656 cells[i].clear_output(true,true,true);
656 cells[i].clear_output(true,true,true);
657 }
657 }
658 };
658 };
659 this.dirty = true;
659 this.dirty = true;
660 };
660 };
661
661
662 // Other cell functions: line numbers, ...
662 // Other cell functions: line numbers, ...
663
663
664 Notebook.prototype.cell_toggle_line_numbers = function() {
664 Notebook.prototype.cell_toggle_line_numbers = function() {
665 this.selected_cell().toggle_line_numbers()
665 this.selected_cell().toggle_line_numbers()
666 };
666 };
667
667
668 // Kernel related things
668 // Kernel related things
669
669
670 Notebook.prototype.start_kernel = function () {
670 Notebook.prototype.start_kernel = function () {
671 this.kernel = new IPython.Kernel();
671 this.kernel = new IPython.Kernel();
672 var notebook_id = IPython.save_widget.get_notebook_id();
672 var notebook_id = IPython.save_widget.get_notebook_id();
673 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
673 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
674 };
674 };
675
675
676
676
677 Notebook.prototype.restart_kernel = function () {
677 Notebook.prototype.restart_kernel = function () {
678 var that = this;
678 var that = this;
679 var notebook_id = IPython.save_widget.get_notebook_id();
679 var notebook_id = IPython.save_widget.get_notebook_id();
680
680
681 var dialog = $('<div/>');
681 var dialog = $('<div/>');
682 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
682 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
683 $(document).append(dialog);
683 $(document).append(dialog);
684 dialog.dialog({
684 dialog.dialog({
685 resizable: false,
685 resizable: false,
686 modal: true,
686 modal: true,
687 title: "Restart kernel or continue running?",
687 title: "Restart kernel or continue running?",
688 buttons : {
688 buttons : {
689 "Restart": function () {
689 "Restart": function () {
690 that.kernel.restart($.proxy(that.kernel_started, that));
690 that.kernel.restart($.proxy(that.kernel_started, that));
691 $(this).dialog('close');
691 $(this).dialog('close');
692 },
692 },
693 "Continue running": function () {
693 "Continue running": function () {
694 $(this).dialog('close');
694 $(this).dialog('close');
695 }
695 }
696 }
696 }
697 });
697 });
698 };
698 };
699
699
700
700
701 Notebook.prototype.kernel_started = function () {
701 Notebook.prototype.kernel_started = function () {
702 console.log("Kernel started: ", this.kernel.kernel_id);
702 console.log("Kernel started: ", this.kernel.kernel_id);
703 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
703 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
704 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
704 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
705 };
705 };
706
706
707
707
708 Notebook.prototype.handle_shell_reply = function (e) {
708 Notebook.prototype.handle_shell_reply = function (e) {
709 reply = $.parseJSON(e.data);
709 reply = $.parseJSON(e.data);
710 var header = reply.header;
710 var header = reply.header;
711 var content = reply.content;
711 var content = reply.content;
712 var msg_type = header.msg_type;
712 var msg_type = header.msg_type;
713 // console.log(reply);
713 // console.log(reply);
714 var cell = this.cell_for_msg(reply.parent_header.msg_id);
714 var cell = this.cell_for_msg(reply.parent_header.msg_id);
715 if (msg_type === "execute_reply") {
715 if (msg_type === "execute_reply") {
716 cell.set_input_prompt(content.execution_count);
716 cell.set_input_prompt(content.execution_count);
717 cell.element.removeClass("running");
717 cell.element.removeClass("running");
718 this.dirty = true;
718 this.dirty = true;
719 } else if (msg_type === "complete_reply") {
719 } else if (msg_type === "complete_reply") {
720 cell.finish_completing(content.matched_text, content.matches);
720 cell.finish_completing(content.matched_text, content.matches);
721 } else if (msg_type === "object_info_reply"){
721 } else if (msg_type === "object_info_reply"){
722 //console.log('back from object_info_request : ')
722 //console.log('back from object_info_request : ')
723 rep = reply.content;
723 rep = reply.content;
724 if(rep.found)
724 if(rep.found)
725 {
725 {
726 cell.finish_tooltip(rep);
726 cell.finish_tooltip(rep);
727 }
727 }
728 } else {
728 } else {
729 //console.log("unknown reply:"+msg_type);
729 //console.log("unknown reply:"+msg_type);
730 }
730 }
731 // when having a rely from object_info_reply,
731 // when having a rely from object_info_reply,
732 // no payload so no nned to handle it
732 // no payload so no nned to handle it
733 if(typeof(content.payload)!='undefined') {
733 if(typeof(content.payload)!='undefined') {
734 var payload = content.payload || [];
734 var payload = content.payload || [];
735 this.handle_payload(cell, payload);
735 this.handle_payload(cell, payload);
736 }
736 }
737 };
737 };
738
738
739
739
740 Notebook.prototype.handle_payload = function (cell, payload) {
740 Notebook.prototype.handle_payload = function (cell, payload) {
741 var l = payload.length;
741 var l = payload.length;
742 for (var i=0; i<l; i++) {
742 for (var i=0; i<l; i++) {
743 if (payload[i].source === 'IPython.zmq.page.page') {
743 if (payload[i].source === 'IPython.zmq.page.page') {
744 if (payload[i].text.trim() !== '') {
744 if (payload[i].text.trim() !== '') {
745 IPython.pager.clear();
745 IPython.pager.clear();
746 IPython.pager.expand();
746 IPython.pager.expand();
747 IPython.pager.append_text(payload[i].text);
747 IPython.pager.append_text(payload[i].text);
748 }
748 }
749 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
749 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
750 var index = this.find_cell_index(cell);
750 var index = this.find_cell_index(cell);
751 var new_cell = this.insert_code_cell_below(index);
751 var new_cell = this.insert_code_cell_below(index);
752 new_cell.set_code(payload[i].text);
752 new_cell.set_code(payload[i].text);
753 this.dirty = true;
753 this.dirty = true;
754 }
754 }
755 };
755 };
756 };
756 };
757
757
758
758
759 Notebook.prototype.handle_iopub_reply = function (e) {
759 Notebook.prototype.handle_iopub_reply = function (e) {
760 reply = $.parseJSON(e.data);
760 reply = $.parseJSON(e.data);
761 var content = reply.content;
761 var content = reply.content;
762 // console.log(reply);
762 // console.log(reply);
763 var msg_type = reply.header.msg_type;
763 var msg_type = reply.header.msg_type;
764 var cell = this.cell_for_msg(reply.parent_header.msg_id);
764 var cell = this.cell_for_msg(reply.parent_header.msg_id);
765 if (!cell){
766 // message not from this notebook
767 console.log("Received IOPub message not caused by one of my cells");
768 return;
769 }
765 var output_types = ['stream','display_data','pyout','pyerr'];
770 var output_types = ['stream','display_data','pyout','pyerr'];
766 if (output_types.indexOf(msg_type) >= 0) {
771 if (output_types.indexOf(msg_type) >= 0) {
767 this.handle_output(cell, msg_type, content);
772 this.handle_output(cell, msg_type, content);
768 } else if (msg_type === 'status') {
773 } else if (msg_type === 'status') {
769 if (content.execution_state === 'busy') {
774 if (content.execution_state === 'busy') {
770 IPython.kernel_status_widget.status_busy();
775 IPython.kernel_status_widget.status_busy();
771 } else if (content.execution_state === 'idle') {
776 } else if (content.execution_state === 'idle') {
772 IPython.kernel_status_widget.status_idle();
777 IPython.kernel_status_widget.status_idle();
773 } else if (content.execution_state === 'dead') {
778 } else if (content.execution_state === 'dead') {
774 this.handle_status_dead();
779 this.handle_status_dead();
775 };
780 };
776 } else if (msg_type === 'clear_output') {
781 } else if (msg_type === 'clear_output') {
777 cell.clear_output(content.stdout, content.stderr, content.other);
782 cell.clear_output(content.stdout, content.stderr, content.other);
778 };
783 };
779 };
784 };
780
785
781
786
782 Notebook.prototype.handle_status_dead = function () {
787 Notebook.prototype.handle_status_dead = function () {
783 var that = this;
788 var that = this;
784 this.kernel.stop_channels();
789 this.kernel.stop_channels();
785 var dialog = $('<div/>');
790 var dialog = $('<div/>');
786 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
791 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
787 $(document).append(dialog);
792 $(document).append(dialog);
788 dialog.dialog({
793 dialog.dialog({
789 resizable: false,
794 resizable: false,
790 modal: true,
795 modal: true,
791 title: "Dead kernel",
796 title: "Dead kernel",
792 buttons : {
797 buttons : {
793 "Restart": function () {
798 "Restart": function () {
794 that.start_kernel();
799 that.start_kernel();
795 $(this).dialog('close');
800 $(this).dialog('close');
796 },
801 },
797 "Continue running": function () {
802 "Continue running": function () {
798 $(this).dialog('close');
803 $(this).dialog('close');
799 }
804 }
800 }
805 }
801 });
806 });
802 };
807 };
803
808
804
809
805 Notebook.prototype.handle_output = function (cell, msg_type, content) {
810 Notebook.prototype.handle_output = function (cell, msg_type, content) {
806 var json = {};
811 var json = {};
807 json.output_type = msg_type;
812 json.output_type = msg_type;
808 if (msg_type === "stream") {
813 if (msg_type === "stream") {
809 json.text = utils.fixConsole(content.data);
814 json.text = utils.fixConsole(content.data);
810 json.stream = content.name;
815 json.stream = content.name;
811 } else if (msg_type === "display_data") {
816 } else if (msg_type === "display_data") {
812 json = this.convert_mime_types(json, content.data);
817 json = this.convert_mime_types(json, content.data);
813 } else if (msg_type === "pyout") {
818 } else if (msg_type === "pyout") {
814 json.prompt_number = content.execution_count;
819 json.prompt_number = content.execution_count;
815 json = this.convert_mime_types(json, content.data);
820 json = this.convert_mime_types(json, content.data);
816 } else if (msg_type === "pyerr") {
821 } else if (msg_type === "pyerr") {
817 json.ename = content.ename;
822 json.ename = content.ename;
818 json.evalue = content.evalue;
823 json.evalue = content.evalue;
819 var traceback = [];
824 var traceback = [];
820 for (var i=0; i<content.traceback.length; i++) {
825 for (var i=0; i<content.traceback.length; i++) {
821 traceback.push(utils.fixConsole(content.traceback[i]));
826 traceback.push(utils.fixConsole(content.traceback[i]));
822 }
827 }
823 json.traceback = traceback;
828 json.traceback = traceback;
824 };
829 };
825 cell.append_output(json);
830 cell.append_output(json);
826 this.dirty = true;
831 this.dirty = true;
827 };
832 };
828
833
829
834
830 Notebook.prototype.convert_mime_types = function (json, data) {
835 Notebook.prototype.convert_mime_types = function (json, data) {
831 if (data['text/plain'] !== undefined) {
836 if (data['text/plain'] !== undefined) {
832 json.text = utils.fixConsole(data['text/plain']);
837 json.text = utils.fixConsole(data['text/plain']);
833 };
838 };
834 if (data['text/html'] !== undefined) {
839 if (data['text/html'] !== undefined) {
835 json.html = data['text/html'];
840 json.html = data['text/html'];
836 };
841 };
837 if (data['image/svg+xml'] !== undefined) {
842 if (data['image/svg+xml'] !== undefined) {
838 json.svg = data['image/svg+xml'];
843 json.svg = data['image/svg+xml'];
839 };
844 };
840 if (data['image/png'] !== undefined) {
845 if (data['image/png'] !== undefined) {
841 json.png = data['image/png'];
846 json.png = data['image/png'];
842 };
847 };
843 if (data['image/jpeg'] !== undefined) {
848 if (data['image/jpeg'] !== undefined) {
844 json.jpeg = data['image/jpeg'];
849 json.jpeg = data['image/jpeg'];
845 };
850 };
846 if (data['text/latex'] !== undefined) {
851 if (data['text/latex'] !== undefined) {
847 json.latex = data['text/latex'];
852 json.latex = data['text/latex'];
848 };
853 };
849 if (data['application/json'] !== undefined) {
854 if (data['application/json'] !== undefined) {
850 json.json = data['application/json'];
855 json.json = data['application/json'];
851 };
856 };
852 if (data['application/javascript'] !== undefined) {
857 if (data['application/javascript'] !== undefined) {
853 json.javascript = data['application/javascript'];
858 json.javascript = data['application/javascript'];
854 }
859 }
855 return json;
860 return json;
856 };
861 };
857
862
858
863
859 Notebook.prototype.execute_selected_cell = function (options) {
864 Notebook.prototype.execute_selected_cell = function (options) {
860 // add_new: should a new cell be added if we are at the end of the nb
865 // add_new: should a new cell be added if we are at the end of the nb
861 // terminal: execute in terminal mode, which stays in the current cell
866 // terminal: execute in terminal mode, which stays in the current cell
862 default_options = {terminal: false, add_new: true}
867 default_options = {terminal: false, add_new: true}
863 $.extend(default_options, options)
868 $.extend(default_options, options)
864 var that = this;
869 var that = this;
865 var cell = that.selected_cell();
870 var cell = that.selected_cell();
866 var cell_index = that.find_cell_index(cell);
871 var cell_index = that.find_cell_index(cell);
867 if (cell instanceof IPython.CodeCell) {
872 if (cell instanceof IPython.CodeCell) {
868 cell.clear_output(true, true, true);
873 cell.clear_output(true, true, true);
869 cell.set_input_prompt('*');
874 cell.set_input_prompt('*');
870 cell.element.addClass("running");
875 cell.element.addClass("running");
871 var code = cell.get_code();
876 var code = cell.get_code();
872 var msg_id = that.kernel.execute(cell.get_code());
877 var msg_id = that.kernel.execute(cell.get_code());
873 that.msg_cell_map[msg_id] = cell.cell_id;
878 that.msg_cell_map[msg_id] = cell.cell_id;
874 } else if (cell instanceof IPython.HTMLCell) {
879 } else if (cell instanceof IPython.HTMLCell) {
875 cell.render();
880 cell.render();
876 }
881 }
877 if (default_options.terminal) {
882 if (default_options.terminal) {
878 cell.select_all();
883 cell.select_all();
879 } else {
884 } else {
880 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
885 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
881 that.insert_code_cell_below();
886 that.insert_code_cell_below();
882 // If we are adding a new cell at the end, scroll down to show it.
887 // If we are adding a new cell at the end, scroll down to show it.
883 that.scroll_to_bottom();
888 that.scroll_to_bottom();
884 } else {
889 } else {
885 that.select(cell_index+1);
890 that.select(cell_index+1);
886 };
891 };
887 };
892 };
888 this.dirty = true;
893 this.dirty = true;
889 };
894 };
890
895
891
896
892 Notebook.prototype.execute_all_cells = function () {
897 Notebook.prototype.execute_all_cells = function () {
893 var ncells = this.ncells();
898 var ncells = this.ncells();
894 for (var i=0; i<ncells; i++) {
899 for (var i=0; i<ncells; i++) {
895 this.select(i);
900 this.select(i);
896 this.execute_selected_cell({add_new:false});
901 this.execute_selected_cell({add_new:false});
897 };
902 };
898 this.scroll_to_bottom();
903 this.scroll_to_bottom();
899 };
904 };
900
905
901
906
902 Notebook.prototype.request_tool_tip = function (cell,func) {
907 Notebook.prototype.request_tool_tip = function (cell,func) {
903 // Feel free to shorten this logic if you are better
908 // Feel free to shorten this logic if you are better
904 // than me in regEx
909 // than me in regEx
905 // basicaly you shoul be able to get xxx.xxx.xxx from
910 // basicaly you shoul be able to get xxx.xxx.xxx from
906 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
911 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
907 // remove everything between matchin bracket (need to iterate)
912 // remove everything between matchin bracket (need to iterate)
908 matchBracket = /\([^\(\)]+\)/g;
913 matchBracket = /\([^\(\)]+\)/g;
909 oldfunc = func;
914 oldfunc = func;
910 func = func.replace(matchBracket,"");
915 func = func.replace(matchBracket,"");
911 while( oldfunc != func )
916 while( oldfunc != func )
912 {
917 {
913 oldfunc = func;
918 oldfunc = func;
914 func = func.replace(matchBracket,"");
919 func = func.replace(matchBracket,"");
915 }
920 }
916 // remove everythin after last open bracket
921 // remove everythin after last open bracket
917 endBracket = /\([^\(]*$/g;
922 endBracket = /\([^\(]*$/g;
918 func = func.replace(endBracket,"");
923 func = func.replace(endBracket,"");
919 var re = /[a-zA-Z._]+$/g;
924 var re = /[a-zA-Z._]+$/g;
920 var msg_id = this.kernel.object_info_request(re.exec(func));
925 var msg_id = this.kernel.object_info_request(re.exec(func));
921 if(typeof(msg_id)!='undefined'){
926 if(typeof(msg_id)!='undefined'){
922 this.msg_cell_map[msg_id] = cell.cell_id;
927 this.msg_cell_map[msg_id] = cell.cell_id;
923 }
928 }
924 };
929 };
925
930
926 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
931 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
927 var msg_id = this.kernel.complete(line, cursor_pos);
932 var msg_id = this.kernel.complete(line, cursor_pos);
928 this.msg_cell_map[msg_id] = cell.cell_id;
933 this.msg_cell_map[msg_id] = cell.cell_id;
929 };
934 };
930
935
931 // Persistance and loading
936 // Persistance and loading
932
937
933
938
934 Notebook.prototype.fromJSON = function (data) {
939 Notebook.prototype.fromJSON = function (data) {
935 var ncells = this.ncells();
940 var ncells = this.ncells();
936 for (var i=0; i<ncells; i++) {
941 for (var i=0; i<ncells; i++) {
937 // Always delete cell 0 as they get renumbered as they are deleted.
942 // Always delete cell 0 as they get renumbered as they are deleted.
938 this.delete_cell(0);
943 this.delete_cell(0);
939 };
944 };
940 // Save the metadata
945 // Save the metadata
941 this.metadata = data.metadata;
946 this.metadata = data.metadata;
942 // Only handle 1 worksheet for now.
947 // Only handle 1 worksheet for now.
943 var worksheet = data.worksheets[0];
948 var worksheet = data.worksheets[0];
944 if (worksheet !== undefined) {
949 if (worksheet !== undefined) {
945 var new_cells = worksheet.cells;
950 var new_cells = worksheet.cells;
946 ncells = new_cells.length;
951 ncells = new_cells.length;
947 var cell_data = null;
952 var cell_data = null;
948 var new_cell = null;
953 var new_cell = null;
949 for (var i=0; i<ncells; i++) {
954 for (var i=0; i<ncells; i++) {
950 cell_data = new_cells[i];
955 cell_data = new_cells[i];
951 if (cell_data.cell_type == 'code') {
956 if (cell_data.cell_type == 'code') {
952 new_cell = this.insert_code_cell_below();
957 new_cell = this.insert_code_cell_below();
953 new_cell.fromJSON(cell_data);
958 new_cell.fromJSON(cell_data);
954 } else if (cell_data.cell_type === 'html') {
959 } else if (cell_data.cell_type === 'html') {
955 new_cell = this.insert_html_cell_below();
960 new_cell = this.insert_html_cell_below();
956 new_cell.fromJSON(cell_data);
961 new_cell.fromJSON(cell_data);
957 } else if (cell_data.cell_type === 'markdown') {
962 } else if (cell_data.cell_type === 'markdown') {
958 new_cell = this.insert_markdown_cell_below();
963 new_cell = this.insert_markdown_cell_below();
959 new_cell.fromJSON(cell_data);
964 new_cell.fromJSON(cell_data);
960 };
965 };
961 };
966 };
962 };
967 };
963 };
968 };
964
969
965
970
966 Notebook.prototype.toJSON = function () {
971 Notebook.prototype.toJSON = function () {
967 var cells = this.cells();
972 var cells = this.cells();
968 var ncells = cells.length;
973 var ncells = cells.length;
969 cell_array = new Array(ncells);
974 cell_array = new Array(ncells);
970 for (var i=0; i<ncells; i++) {
975 for (var i=0; i<ncells; i++) {
971 cell_array[i] = cells[i].toJSON();
976 cell_array[i] = cells[i].toJSON();
972 };
977 };
973 data = {
978 data = {
974 // Only handle 1 worksheet for now.
979 // Only handle 1 worksheet for now.
975 worksheets : [{cells:cell_array}],
980 worksheets : [{cells:cell_array}],
976 metadata : this.metadata
981 metadata : this.metadata
977 }
982 }
978 return data
983 return data
979 };
984 };
980
985
981 Notebook.prototype.save_notebook = function () {
986 Notebook.prototype.save_notebook = function () {
982 if (IPython.save_widget.test_notebook_name()) {
987 if (IPython.save_widget.test_notebook_name()) {
983 var notebook_id = IPython.save_widget.get_notebook_id();
988 var notebook_id = IPython.save_widget.get_notebook_id();
984 var nbname = IPython.save_widget.get_notebook_name();
989 var nbname = IPython.save_widget.get_notebook_name();
985 // We may want to move the name/id/nbformat logic inside toJSON?
990 // We may want to move the name/id/nbformat logic inside toJSON?
986 var data = this.toJSON();
991 var data = this.toJSON();
987 data.metadata.name = nbname;
992 data.metadata.name = nbname;
988 data.nbformat = 2;
993 data.nbformat = 2;
989 // We do the call with settings so we can set cache to false.
994 // We do the call with settings so we can set cache to false.
990 var settings = {
995 var settings = {
991 processData : false,
996 processData : false,
992 cache : false,
997 cache : false,
993 type : "PUT",
998 type : "PUT",
994 data : JSON.stringify(data),
999 data : JSON.stringify(data),
995 headers : {'Content-Type': 'application/json'},
1000 headers : {'Content-Type': 'application/json'},
996 success : $.proxy(this.notebook_saved,this),
1001 success : $.proxy(this.notebook_saved,this),
997 error : $.proxy(this.notebook_save_failed,this)
1002 error : $.proxy(this.notebook_save_failed,this)
998 };
1003 };
999 IPython.save_widget.status_saving();
1004 IPython.save_widget.status_saving();
1000 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id
1005 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id
1001 $.ajax(url, settings);
1006 $.ajax(url, settings);
1002 };
1007 };
1003 };
1008 };
1004
1009
1005
1010
1006 Notebook.prototype.notebook_saved = function (data, status, xhr) {
1011 Notebook.prototype.notebook_saved = function (data, status, xhr) {
1007 this.dirty = false;
1012 this.dirty = false;
1008 IPython.save_widget.notebook_saved();
1013 IPython.save_widget.notebook_saved();
1009 IPython.save_widget.status_save();
1014 IPython.save_widget.status_save();
1010 }
1015 }
1011
1016
1012
1017
1013 Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) {
1018 Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) {
1014 // Notify the user and reset the save button
1019 // Notify the user and reset the save button
1015 // TODO: Handle different types of errors (timeout etc.)
1020 // TODO: Handle different types of errors (timeout etc.)
1016 alert('An unexpected error occured while saving the notebook.');
1021 alert('An unexpected error occured while saving the notebook.');
1017 IPython.save_widget.reset_status();
1022 IPython.save_widget.reset_status();
1018 }
1023 }
1019
1024
1020
1025
1021 Notebook.prototype.load_notebook = function (callback) {
1026 Notebook.prototype.load_notebook = function (callback) {
1022 var that = this;
1027 var that = this;
1023 var notebook_id = IPython.save_widget.get_notebook_id();
1028 var notebook_id = IPython.save_widget.get_notebook_id();
1024 // We do the call with settings so we can set cache to false.
1029 // We do the call with settings so we can set cache to false.
1025 var settings = {
1030 var settings = {
1026 processData : false,
1031 processData : false,
1027 cache : false,
1032 cache : false,
1028 type : "GET",
1033 type : "GET",
1029 dataType : "json",
1034 dataType : "json",
1030 success : function (data, status, xhr) {
1035 success : function (data, status, xhr) {
1031 that.notebook_loaded(data, status, xhr);
1036 that.notebook_loaded(data, status, xhr);
1032 if (callback !== undefined) {
1037 if (callback !== undefined) {
1033 callback();
1038 callback();
1034 };
1039 };
1035 }
1040 }
1036 };
1041 };
1037 IPython.save_widget.status_loading();
1042 IPython.save_widget.status_loading();
1038 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id
1043 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id
1039 $.ajax(url, settings);
1044 $.ajax(url, settings);
1040 }
1045 }
1041
1046
1042
1047
1043 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
1048 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
1044 var allowed = xhr.getResponseHeader('Allow');
1049 var allowed = xhr.getResponseHeader('Allow');
1045 this.fromJSON(data);
1050 this.fromJSON(data);
1046 if (this.ncells() === 0) {
1051 if (this.ncells() === 0) {
1047 this.insert_code_cell_below();
1052 this.insert_code_cell_below();
1048 };
1053 };
1049 IPython.save_widget.status_save();
1054 IPython.save_widget.status_save();
1050 IPython.save_widget.set_notebook_name(data.metadata.name);
1055 IPython.save_widget.set_notebook_name(data.metadata.name);
1051 this.dirty = false;
1056 this.dirty = false;
1052 if (! this.read_only) {
1057 if (! this.read_only) {
1053 this.start_kernel();
1058 this.start_kernel();
1054 }
1059 }
1055 // fromJSON always selects the last cell inserted. We need to wait
1060 // fromJSON always selects the last cell inserted. We need to wait
1056 // until that is done before scrolling to the top.
1061 // until that is done before scrolling to the top.
1057 setTimeout(function () {
1062 setTimeout(function () {
1058 IPython.notebook.select(0);
1063 IPython.notebook.select(0);
1059 IPython.notebook.scroll_to_top();
1064 IPython.notebook.scroll_to_top();
1060 }, 50);
1065 }, 50);
1061 };
1066 };
1062
1067
1063 IPython.Notebook = Notebook;
1068 IPython.Notebook = Notebook;
1064
1069
1065
1070
1066 return IPython;
1071 return IPython;
1067
1072
1068 }(IPython));
1073 }(IPython));
1069
1074
@@ -1,109 +1,114 b''
1 """ Defines a convenient mix-in class for implementing Qt frontends.
1 """ Defines a convenient mix-in class for implementing Qt frontends.
2 """
2 """
3
3
4 class BaseFrontendMixin(object):
4 class BaseFrontendMixin(object):
5 """ A mix-in class for implementing Qt frontends.
5 """ A mix-in class for implementing Qt frontends.
6
6
7 To handle messages of a particular type, frontends need only define an
7 To handle messages of a particular type, frontends need only define an
8 appropriate handler method. For example, to handle 'stream' messaged, define
8 appropriate handler method. For example, to handle 'stream' messaged, define
9 a '_handle_stream(msg)' method.
9 a '_handle_stream(msg)' method.
10 """
10 """
11
11
12 #---------------------------------------------------------------------------
12 #---------------------------------------------------------------------------
13 # 'BaseFrontendMixin' concrete interface
13 # 'BaseFrontendMixin' concrete interface
14 #---------------------------------------------------------------------------
14 #---------------------------------------------------------------------------
15
15
16 def _get_kernel_manager(self):
16 def _get_kernel_manager(self):
17 """ Returns the current kernel manager.
17 """ Returns the current kernel manager.
18 """
18 """
19 return self._kernel_manager
19 return self._kernel_manager
20
20
21 def _set_kernel_manager(self, kernel_manager):
21 def _set_kernel_manager(self, kernel_manager):
22 """ Disconnect from the current kernel manager (if any) and set a new
22 """ Disconnect from the current kernel manager (if any) and set a new
23 kernel manager.
23 kernel manager.
24 """
24 """
25 # Disconnect the old kernel manager, if necessary.
25 # Disconnect the old kernel manager, if necessary.
26 old_manager = self._kernel_manager
26 old_manager = self._kernel_manager
27 if old_manager is not None:
27 if old_manager is not None:
28 old_manager.started_channels.disconnect(self._started_channels)
28 old_manager.started_channels.disconnect(self._started_channels)
29 old_manager.stopped_channels.disconnect(self._stopped_channels)
29 old_manager.stopped_channels.disconnect(self._stopped_channels)
30
30
31 # Disconnect the old kernel manager's channels.
31 # Disconnect the old kernel manager's channels.
32 old_manager.sub_channel.message_received.disconnect(self._dispatch)
32 old_manager.sub_channel.message_received.disconnect(self._dispatch)
33 old_manager.shell_channel.message_received.disconnect(self._dispatch)
33 old_manager.shell_channel.message_received.disconnect(self._dispatch)
34 old_manager.stdin_channel.message_received.disconnect(self._dispatch)
34 old_manager.stdin_channel.message_received.disconnect(self._dispatch)
35 old_manager.hb_channel.kernel_died.disconnect(
35 old_manager.hb_channel.kernel_died.disconnect(
36 self._handle_kernel_died)
36 self._handle_kernel_died)
37
37
38 # Handle the case where the old kernel manager is still listening.
38 # Handle the case where the old kernel manager is still listening.
39 if old_manager.channels_running:
39 if old_manager.channels_running:
40 self._stopped_channels()
40 self._stopped_channels()
41
41
42 # Set the new kernel manager.
42 # Set the new kernel manager.
43 self._kernel_manager = kernel_manager
43 self._kernel_manager = kernel_manager
44 if kernel_manager is None:
44 if kernel_manager is None:
45 return
45 return
46
46
47 # Connect the new kernel manager.
47 # Connect the new kernel manager.
48 kernel_manager.started_channels.connect(self._started_channels)
48 kernel_manager.started_channels.connect(self._started_channels)
49 kernel_manager.stopped_channels.connect(self._stopped_channels)
49 kernel_manager.stopped_channels.connect(self._stopped_channels)
50
50
51 # Connect the new kernel manager's channels.
51 # Connect the new kernel manager's channels.
52 kernel_manager.sub_channel.message_received.connect(self._dispatch)
52 kernel_manager.sub_channel.message_received.connect(self._dispatch)
53 kernel_manager.shell_channel.message_received.connect(self._dispatch)
53 kernel_manager.shell_channel.message_received.connect(self._dispatch)
54 kernel_manager.stdin_channel.message_received.connect(self._dispatch)
54 kernel_manager.stdin_channel.message_received.connect(self._dispatch)
55 kernel_manager.hb_channel.kernel_died.connect(self._handle_kernel_died)
55 kernel_manager.hb_channel.kernel_died.connect(self._handle_kernel_died)
56
56
57 # Handle the case where the kernel manager started channels before
57 # Handle the case where the kernel manager started channels before
58 # we connected.
58 # we connected.
59 if kernel_manager.channels_running:
59 if kernel_manager.channels_running:
60 self._started_channels()
60 self._started_channels()
61
61
62 kernel_manager = property(_get_kernel_manager, _set_kernel_manager)
62 kernel_manager = property(_get_kernel_manager, _set_kernel_manager)
63
63
64 #---------------------------------------------------------------------------
64 #---------------------------------------------------------------------------
65 # 'BaseFrontendMixin' abstract interface
65 # 'BaseFrontendMixin' abstract interface
66 #---------------------------------------------------------------------------
66 #---------------------------------------------------------------------------
67
67
68 def _handle_kernel_died(self, since_last_heartbeat):
68 def _handle_kernel_died(self, since_last_heartbeat):
69 """ This is called when the ``kernel_died`` signal is emitted.
69 """ This is called when the ``kernel_died`` signal is emitted.
70
70
71 This method is called when the kernel heartbeat has not been
71 This method is called when the kernel heartbeat has not been
72 active for a certain amount of time. The typical action will be to
72 active for a certain amount of time. The typical action will be to
73 give the user the option of restarting the kernel.
73 give the user the option of restarting the kernel.
74
74
75 Parameters
75 Parameters
76 ----------
76 ----------
77 since_last_heartbeat : float
77 since_last_heartbeat : float
78 The time since the heartbeat was last received.
78 The time since the heartbeat was last received.
79 """
79 """
80
80
81 def _started_channels(self):
81 def _started_channels(self):
82 """ Called when the KernelManager channels have started listening or
82 """ Called when the KernelManager channels have started listening or
83 when the frontend is assigned an already listening KernelManager.
83 when the frontend is assigned an already listening KernelManager.
84 """
84 """
85
85
86 def _stopped_channels(self):
86 def _stopped_channels(self):
87 """ Called when the KernelManager channels have stopped listening or
87 """ Called when the KernelManager channels have stopped listening or
88 when a listening KernelManager is removed from the frontend.
88 when a listening KernelManager is removed from the frontend.
89 """
89 """
90
90
91 #---------------------------------------------------------------------------
91 #---------------------------------------------------------------------------
92 # 'BaseFrontendMixin' protected interface
92 # 'BaseFrontendMixin' protected interface
93 #---------------------------------------------------------------------------
93 #---------------------------------------------------------------------------
94
94
95 def _dispatch(self, msg):
95 def _dispatch(self, msg):
96 """ Calls the frontend handler associated with the message type of the
96 """ Calls the frontend handler associated with the message type of the
97 given message.
97 given message.
98 """
98 """
99 msg_type = msg['header']['msg_type']
99 msg_type = msg['header']['msg_type']
100 handler = getattr(self, '_handle_' + msg_type, None)
100 handler = getattr(self, '_handle_' + msg_type, None)
101 if handler:
101 if handler:
102 handler(msg)
102 handler(msg)
103
103
104 def _is_from_this_session(self, msg):
104 def _is_from_this_session(self, msg):
105 """ Returns whether a reply from the kernel originated from a request
105 """ Returns whether a reply from the kernel originated from a request
106 from this frontend.
106 from this frontend.
107 """
107 """
108 session = self._kernel_manager.session.session
108 session = self._kernel_manager.session.session
109 return msg['parent_header']['session'] == session
109 parent = msg['parent_header']
110 if not parent:
111 # if the message has no parent, assume it is meant for all frontends
112 return True
113 else:
114 return parent.get('session') == session
@@ -1,322 +1,325 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Pylab (matplotlib) support utilities.
2 """Pylab (matplotlib) support utilities.
3
3
4 Authors
4 Authors
5 -------
5 -------
6
6
7 * Fernando Perez.
7 * Fernando Perez.
8 * Brian Granger
8 * Brian Granger
9 """
9 """
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2009-2011 The IPython Development Team
12 # Copyright (C) 2009-2011 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import sys
22 from io import BytesIO
23 from io import BytesIO
23
24
24 from IPython.utils.decorators import flag_calls
25 from IPython.utils.decorators import flag_calls
25
26
26 # If user specifies a GUI, that dictates the backend, otherwise we read the
27 # If user specifies a GUI, that dictates the backend, otherwise we read the
27 # user's mpl default from the mpl rc structure
28 # user's mpl default from the mpl rc structure
28 backends = {'tk': 'TkAgg',
29 backends = {'tk': 'TkAgg',
29 'gtk': 'GTKAgg',
30 'gtk': 'GTKAgg',
30 'wx': 'WXAgg',
31 'wx': 'WXAgg',
31 'qt': 'Qt4Agg', # qt3 not supported
32 'qt': 'Qt4Agg', # qt3 not supported
32 'qt4': 'Qt4Agg',
33 'qt4': 'Qt4Agg',
33 'osx': 'MacOSX',
34 'osx': 'MacOSX',
34 'inline' : 'module://IPython.zmq.pylab.backend_inline'}
35 'inline' : 'module://IPython.zmq.pylab.backend_inline'}
35
36
36 # We also need a reverse backends2guis mapping that will properly choose which
37 # We also need a reverse backends2guis mapping that will properly choose which
37 # GUI support to activate based on the desired matplotlib backend. For the
38 # GUI support to activate based on the desired matplotlib backend. For the
38 # most part it's just a reverse of the above dict, but we also need to add a
39 # most part it's just a reverse of the above dict, but we also need to add a
39 # few others that map to the same GUI manually:
40 # few others that map to the same GUI manually:
40 backend2gui = dict(zip(backends.values(), backends.keys()))
41 backend2gui = dict(zip(backends.values(), backends.keys()))
41 # In the reverse mapping, there are a few extra valid matplotlib backends that
42 # In the reverse mapping, there are a few extra valid matplotlib backends that
42 # map to the same GUI support
43 # map to the same GUI support
43 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
44 backend2gui['GTK'] = backend2gui['GTKCairo'] = 'gtk'
44 backend2gui['WX'] = 'wx'
45 backend2gui['WX'] = 'wx'
45 backend2gui['CocoaAgg'] = 'osx'
46 backend2gui['CocoaAgg'] = 'osx'
46
47
47 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
48 # Matplotlib utilities
49 # Matplotlib utilities
49 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
50
51
51
52
52 def getfigs(*fig_nums):
53 def getfigs(*fig_nums):
53 """Get a list of matplotlib figures by figure numbers.
54 """Get a list of matplotlib figures by figure numbers.
54
55
55 If no arguments are given, all available figures are returned. If the
56 If no arguments are given, all available figures are returned. If the
56 argument list contains references to invalid figures, a warning is printed
57 argument list contains references to invalid figures, a warning is printed
57 but the function continues pasting further figures.
58 but the function continues pasting further figures.
58
59
59 Parameters
60 Parameters
60 ----------
61 ----------
61 figs : tuple
62 figs : tuple
62 A tuple of ints giving the figure numbers of the figures to return.
63 A tuple of ints giving the figure numbers of the figures to return.
63 """
64 """
64 from matplotlib._pylab_helpers import Gcf
65 from matplotlib._pylab_helpers import Gcf
65 if not fig_nums:
66 if not fig_nums:
66 fig_managers = Gcf.get_all_fig_managers()
67 fig_managers = Gcf.get_all_fig_managers()
67 return [fm.canvas.figure for fm in fig_managers]
68 return [fm.canvas.figure for fm in fig_managers]
68 else:
69 else:
69 figs = []
70 figs = []
70 for num in fig_nums:
71 for num in fig_nums:
71 f = Gcf.figs.get(num)
72 f = Gcf.figs.get(num)
72 if f is None:
73 if f is None:
73 print('Warning: figure %s not available.' % num)
74 print('Warning: figure %s not available.' % num)
74 else:
75 else:
75 figs.append(f.canvas.figure)
76 figs.append(f.canvas.figure)
76 return figs
77 return figs
77
78
78
79
79 def figsize(sizex, sizey):
80 def figsize(sizex, sizey):
80 """Set the default figure size to be [sizex, sizey].
81 """Set the default figure size to be [sizex, sizey].
81
82
82 This is just an easy to remember, convenience wrapper that sets::
83 This is just an easy to remember, convenience wrapper that sets::
83
84
84 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
85 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
85 """
86 """
86 import matplotlib
87 import matplotlib
87 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
88 matplotlib.rcParams['figure.figsize'] = [sizex, sizey]
88
89
89
90
90 def print_figure(fig, fmt='png'):
91 def print_figure(fig, fmt='png'):
91 """Convert a figure to svg or png for inline display."""
92 """Convert a figure to svg or png for inline display."""
92 # When there's an empty figure, we shouldn't return anything, otherwise we
93 # When there's an empty figure, we shouldn't return anything, otherwise we
93 # get big blank areas in the qt console.
94 # get big blank areas in the qt console.
94 if not fig.axes:
95 if not fig.axes:
95 return
96 return
96
97
97 fc = fig.get_facecolor()
98 fc = fig.get_facecolor()
98 ec = fig.get_edgecolor()
99 ec = fig.get_edgecolor()
99 fig.set_facecolor('white')
100 fig.set_facecolor('white')
100 fig.set_edgecolor('white')
101 fig.set_edgecolor('white')
101 try:
102 try:
102 bytes_io = BytesIO()
103 bytes_io = BytesIO()
103 fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight')
104 fig.canvas.print_figure(bytes_io, format=fmt, bbox_inches='tight')
104 data = bytes_io.getvalue()
105 data = bytes_io.getvalue()
105 finally:
106 finally:
106 fig.set_facecolor(fc)
107 fig.set_facecolor(fc)
107 fig.set_edgecolor(ec)
108 fig.set_edgecolor(ec)
108 return data
109 return data
109
110
110
111
111 # We need a little factory function here to create the closure where
112 # We need a little factory function here to create the closure where
112 # safe_execfile can live.
113 # safe_execfile can live.
113 def mpl_runner(safe_execfile):
114 def mpl_runner(safe_execfile):
114 """Factory to return a matplotlib-enabled runner for %run.
115 """Factory to return a matplotlib-enabled runner for %run.
115
116
116 Parameters
117 Parameters
117 ----------
118 ----------
118 safe_execfile : function
119 safe_execfile : function
119 This must be a function with the same interface as the
120 This must be a function with the same interface as the
120 :meth:`safe_execfile` method of IPython.
121 :meth:`safe_execfile` method of IPython.
121
122
122 Returns
123 Returns
123 -------
124 -------
124 A function suitable for use as the ``runner`` argument of the %run magic
125 A function suitable for use as the ``runner`` argument of the %run magic
125 function.
126 function.
126 """
127 """
127
128
128 def mpl_execfile(fname,*where,**kw):
129 def mpl_execfile(fname,*where,**kw):
129 """matplotlib-aware wrapper around safe_execfile.
130 """matplotlib-aware wrapper around safe_execfile.
130
131
131 Its interface is identical to that of the :func:`execfile` builtin.
132 Its interface is identical to that of the :func:`execfile` builtin.
132
133
133 This is ultimately a call to execfile(), but wrapped in safeties to
134 This is ultimately a call to execfile(), but wrapped in safeties to
134 properly handle interactive rendering."""
135 properly handle interactive rendering."""
135
136
136 import matplotlib
137 import matplotlib
137 import matplotlib.pylab as pylab
138 import matplotlib.pylab as pylab
138
139
139 #print '*** Matplotlib runner ***' # dbg
140 #print '*** Matplotlib runner ***' # dbg
140 # turn off rendering until end of script
141 # turn off rendering until end of script
141 is_interactive = matplotlib.rcParams['interactive']
142 is_interactive = matplotlib.rcParams['interactive']
142 matplotlib.interactive(False)
143 matplotlib.interactive(False)
143 safe_execfile(fname,*where,**kw)
144 safe_execfile(fname,*where,**kw)
144 matplotlib.interactive(is_interactive)
145 matplotlib.interactive(is_interactive)
145 # make rendering call now, if the user tried to do it
146 # make rendering call now, if the user tried to do it
146 if pylab.draw_if_interactive.called:
147 if pylab.draw_if_interactive.called:
147 pylab.draw()
148 pylab.draw()
148 pylab.draw_if_interactive.called = False
149 pylab.draw_if_interactive.called = False
149
150
150 return mpl_execfile
151 return mpl_execfile
151
152
152
153
153 def select_figure_format(shell, fmt):
154 def select_figure_format(shell, fmt):
154 """Select figure format for inline backend, either 'png' or 'svg'.
155 """Select figure format for inline backend, either 'png' or 'svg'.
155
156
156 Using this method ensures only one figure format is active at a time.
157 Using this method ensures only one figure format is active at a time.
157 """
158 """
158 from matplotlib.figure import Figure
159 from matplotlib.figure import Figure
159 from IPython.zmq.pylab import backend_inline
160 from IPython.zmq.pylab import backend_inline
160
161
161 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
162 svg_formatter = shell.display_formatter.formatters['image/svg+xml']
162 png_formatter = shell.display_formatter.formatters['image/png']
163 png_formatter = shell.display_formatter.formatters['image/png']
163
164
164 if fmt=='png':
165 if fmt=='png':
165 svg_formatter.type_printers.pop(Figure, None)
166 svg_formatter.type_printers.pop(Figure, None)
166 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
167 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png'))
167 elif fmt=='svg':
168 elif fmt=='svg':
168 png_formatter.type_printers.pop(Figure, None)
169 png_formatter.type_printers.pop(Figure, None)
169 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
170 svg_formatter.for_type(Figure, lambda fig: print_figure(fig, 'svg'))
170 else:
171 else:
171 raise ValueError("supported formats are: 'png', 'svg', not %r"%fmt)
172 raise ValueError("supported formats are: 'png', 'svg', not %r"%fmt)
172
173
173 # set the format to be used in the backend()
174 # set the format to be used in the backend()
174 backend_inline._figure_format = fmt
175 backend_inline._figure_format = fmt
175
176
176 #-----------------------------------------------------------------------------
177 #-----------------------------------------------------------------------------
177 # Code for initializing matplotlib and importing pylab
178 # Code for initializing matplotlib and importing pylab
178 #-----------------------------------------------------------------------------
179 #-----------------------------------------------------------------------------
179
180
180
181
181 def find_gui_and_backend(gui=None):
182 def find_gui_and_backend(gui=None):
182 """Given a gui string return the gui and mpl backend.
183 """Given a gui string return the gui and mpl backend.
183
184
184 Parameters
185 Parameters
185 ----------
186 ----------
186 gui : str
187 gui : str
187 Can be one of ('tk','gtk','wx','qt','qt4','inline').
188 Can be one of ('tk','gtk','wx','qt','qt4','inline').
188
189
189 Returns
190 Returns
190 -------
191 -------
191 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
192 A tuple of (gui, backend) where backend is one of ('TkAgg','GTKAgg',
192 'WXAgg','Qt4Agg','module://IPython.zmq.pylab.backend_inline').
193 'WXAgg','Qt4Agg','module://IPython.zmq.pylab.backend_inline').
193 """
194 """
194
195
195 import matplotlib
196 import matplotlib
196
197
197 if gui and gui != 'auto':
198 if gui and gui != 'auto':
198 # select backend based on requested gui
199 # select backend based on requested gui
199 backend = backends[gui]
200 backend = backends[gui]
200 else:
201 else:
201 backend = matplotlib.rcParams['backend']
202 backend = matplotlib.rcParams['backend']
202 # In this case, we need to find what the appropriate gui selection call
203 # In this case, we need to find what the appropriate gui selection call
203 # should be for IPython, so we can activate inputhook accordingly
204 # should be for IPython, so we can activate inputhook accordingly
204 gui = backend2gui.get(backend, None)
205 gui = backend2gui.get(backend, None)
205 return gui, backend
206 return gui, backend
206
207
207
208
208 def activate_matplotlib(backend):
209 def activate_matplotlib(backend):
209 """Activate the given backend and set interactive to True."""
210 """Activate the given backend and set interactive to True."""
210
211
211 import matplotlib
212 import matplotlib
212 if backend.startswith('module://'):
213 if backend.startswith('module://'):
213 # Work around bug in matplotlib: matplotlib.use converts the
214 # Work around bug in matplotlib: matplotlib.use converts the
214 # backend_id to lowercase even if a module name is specified!
215 # backend_id to lowercase even if a module name is specified!
215 matplotlib.rcParams['backend'] = backend
216 matplotlib.rcParams['backend'] = backend
216 else:
217 else:
217 matplotlib.use(backend)
218 matplotlib.use(backend)
218 matplotlib.interactive(True)
219 matplotlib.interactive(True)
219
220
220 # This must be imported last in the matplotlib series, after
221 # This must be imported last in the matplotlib series, after
221 # backend/interactivity choices have been made
222 # backend/interactivity choices have been made
222 import matplotlib.pylab as pylab
223 import matplotlib.pylab as pylab
223
224
224 # XXX For now leave this commented out, but depending on discussions with
225 # XXX For now leave this commented out, but depending on discussions with
225 # mpl-dev, we may be able to allow interactive switching...
226 # mpl-dev, we may be able to allow interactive switching...
226 #import matplotlib.pyplot
227 #import matplotlib.pyplot
227 #matplotlib.pyplot.switch_backend(backend)
228 #matplotlib.pyplot.switch_backend(backend)
228
229
229 pylab.show._needmain = False
230 pylab.show._needmain = False
230 # We need to detect at runtime whether show() is called by the user.
231 # We need to detect at runtime whether show() is called by the user.
231 # For this, we wrap it into a decorator which adds a 'called' flag.
232 # For this, we wrap it into a decorator which adds a 'called' flag.
232 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
233 pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)
233
234
234 def import_pylab(user_ns, backend, import_all=True, shell=None):
235 def import_pylab(user_ns, backend, import_all=True, shell=None):
235 """Import the standard pylab symbols into user_ns."""
236 """Import the standard pylab symbols into user_ns."""
236
237
237 # Import numpy as np/pyplot as plt are conventions we're trying to
238 # Import numpy as np/pyplot as plt are conventions we're trying to
238 # somewhat standardize on. Making them available to users by default
239 # somewhat standardize on. Making them available to users by default
239 # will greatly help this.
240 # will greatly help this.
240 s = ("import numpy\n"
241 s = ("import numpy\n"
241 "import matplotlib\n"
242 "import matplotlib\n"
242 "from matplotlib import pylab, mlab, pyplot\n"
243 "from matplotlib import pylab, mlab, pyplot\n"
243 "np = numpy\n"
244 "np = numpy\n"
244 "plt = pyplot\n"
245 "plt = pyplot\n"
245 )
246 )
246 exec s in user_ns
247 exec s in user_ns
247
248
248 if shell is not None:
249 if shell is not None:
249 exec s in shell.user_ns_hidden
250 exec s in shell.user_ns_hidden
250 # If using our svg payload backend, register the post-execution
251 # If using our svg payload backend, register the post-execution
251 # function that will pick up the results for display. This can only be
252 # function that will pick up the results for display. This can only be
252 # done with access to the real shell object.
253 # done with access to the real shell object.
253 #
254 #
254 from IPython.zmq.pylab.backend_inline import InlineBackend
255 from IPython.zmq.pylab.backend_inline import InlineBackend
255
256
256 cfg = InlineBackend.instance(config=shell.config)
257 cfg = InlineBackend.instance(config=shell.config)
257 cfg.shell = shell
258 cfg.shell = shell
258 if cfg not in shell.configurables:
259 if cfg not in shell.configurables:
259 shell.configurables.append(cfg)
260 shell.configurables.append(cfg)
260
261
261 if backend == backends['inline']:
262 if backend == backends['inline']:
262 from IPython.zmq.pylab.backend_inline import flush_figures
263 from IPython.zmq.pylab.backend_inline import flush_figures
263 from matplotlib import pyplot
264 from matplotlib import pyplot
264 shell.register_post_execute(flush_figures)
265 shell.register_post_execute(flush_figures)
265 # load inline_rc
266 # load inline_rc
266 pyplot.rcParams.update(cfg.rc)
267 pyplot.rcParams.update(cfg.rc)
267
268
268 # Add 'figsize' to pyplot and to the user's namespace
269 # Add 'figsize' to pyplot and to the user's namespace
269 user_ns['figsize'] = pyplot.figsize = figsize
270 user_ns['figsize'] = pyplot.figsize = figsize
270 shell.user_ns_hidden['figsize'] = figsize
271 shell.user_ns_hidden['figsize'] = figsize
271
272
272 # Setup the default figure format
273 # Setup the default figure format
273 fmt = cfg.figure_format
274 fmt = cfg.figure_format
274 select_figure_format(shell, fmt)
275 select_figure_format(shell, fmt)
275
276
276 # The old pastefig function has been replaced by display
277 # The old pastefig function has been replaced by display
277 from IPython.core.display import display
278 from IPython.core.display import display
278 # Add display and display_png to the user's namespace
279 # Add display and display_png to the user's namespace
279 user_ns['display'] = display
280 user_ns['display'] = display
280 shell.user_ns_hidden['display'] = display
281 shell.user_ns_hidden['display'] = display
281 user_ns['getfigs'] = getfigs
282 user_ns['getfigs'] = getfigs
282 shell.user_ns_hidden['getfigs'] = getfigs
283 shell.user_ns_hidden['getfigs'] = getfigs
283
284
284 if import_all:
285 if import_all:
285 s = ("from matplotlib.pylab import *\n"
286 s = ("from matplotlib.pylab import *\n"
286 "from numpy import *\n")
287 "from numpy import *\n")
287 exec s in user_ns
288 exec s in user_ns
288 if shell is not None:
289 if shell is not None:
289 exec s in shell.user_ns_hidden
290 exec s in shell.user_ns_hidden
290
291
291
292
292 def pylab_activate(user_ns, gui=None, import_all=True, shell=None):
293 def pylab_activate(user_ns, gui=None, import_all=True, shell=None):
293 """Activate pylab mode in the user's namespace.
294 """Activate pylab mode in the user's namespace.
294
295
295 Loads and initializes numpy, matplotlib and friends for interactive use.
296 Loads and initializes numpy, matplotlib and friends for interactive use.
296
297
297 Parameters
298 Parameters
298 ----------
299 ----------
299 user_ns : dict
300 user_ns : dict
300 Namespace where the imports will occur.
301 Namespace where the imports will occur.
301
302
302 gui : optional, string
303 gui : optional, string
303 A valid gui name following the conventions of the %gui magic.
304 A valid gui name following the conventions of the %gui magic.
304
305
305 import_all : optional, boolean
306 import_all : optional, boolean
306 If true, an 'import *' is done from numpy and pylab.
307 If true, an 'import *' is done from numpy and pylab.
307
308
308 Returns
309 Returns
309 -------
310 -------
310 The actual gui used (if not given as input, it was obtained from matplotlib
311 The actual gui used (if not given as input, it was obtained from matplotlib
311 itself, and will be needed next to configure IPython's gui integration.
312 itself, and will be needed next to configure IPython's gui integration.
312 """
313 """
313 gui, backend = find_gui_and_backend(gui)
314 gui, backend = find_gui_and_backend(gui)
314 activate_matplotlib(backend)
315 activate_matplotlib(backend)
315 import_pylab(user_ns, backend, import_all, shell)
316 import_pylab(user_ns, backend, import_all, shell)
316
317
317 print """
318 print """
318 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
319 Welcome to pylab, a matplotlib-based Python environment [backend: %s].
319 For more information, type 'help(pylab)'.""" % backend
320 For more information, type 'help(pylab)'.""" % backend
321 # flush stdout, just to be safe
322 sys.stdout.flush()
320
323
321 return gui
324 return gui
322
325
@@ -1,299 +1,303 b''
1 """An Application for launching a kernel
1 """An Application for launching a kernel
2
2
3 Authors
3 Authors
4 -------
4 -------
5 * MinRK
5 * MinRK
6 """
6 """
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2011 The IPython Development Team
8 # Copyright (C) 2011 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING.txt, distributed as part of this software.
11 # the file COPYING.txt, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 # Standard library imports.
18 # Standard library imports.
19 import json
19 import json
20 import os
20 import os
21 import sys
21 import sys
22
22
23 # System library imports.
23 # System library imports.
24 import zmq
24 import zmq
25
25
26 # IPython imports.
26 # IPython imports.
27 from IPython.core.ultratb import FormattedTB
27 from IPython.core.ultratb import FormattedTB
28 from IPython.core.application import (
28 from IPython.core.application import (
29 BaseIPythonApplication, base_flags, base_aliases, catch_config_error
29 BaseIPythonApplication, base_flags, base_aliases, catch_config_error
30 )
30 )
31 from IPython.utils import io
31 from IPython.utils import io
32 from IPython.utils.localinterfaces import LOCALHOST
32 from IPython.utils.localinterfaces import LOCALHOST
33 from IPython.utils.path import filefind
33 from IPython.utils.path import filefind
34 from IPython.utils.py3compat import str_to_bytes
34 from IPython.utils.py3compat import str_to_bytes
35 from IPython.utils.traitlets import (Any, Instance, Dict, Unicode, Integer, Bool,
35 from IPython.utils.traitlets import (Any, Instance, Dict, Unicode, Integer, Bool,
36 DottedObjectName)
36 DottedObjectName)
37 from IPython.utils.importstring import import_item
37 from IPython.utils.importstring import import_item
38 # local imports
38 # local imports
39 from IPython.zmq.entry_point import write_connection_file
39 from IPython.zmq.entry_point import write_connection_file
40 from IPython.zmq.heartbeat import Heartbeat
40 from IPython.zmq.heartbeat import Heartbeat
41 from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows
41 from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows
42 from IPython.zmq.session import (
42 from IPython.zmq.session import (
43 Session, session_flags, session_aliases, default_secure,
43 Session, session_flags, session_aliases, default_secure,
44 )
44 )
45
45
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Flags and Aliases
48 # Flags and Aliases
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51 kernel_aliases = dict(base_aliases)
51 kernel_aliases = dict(base_aliases)
52 kernel_aliases.update({
52 kernel_aliases.update({
53 'ip' : 'KernelApp.ip',
53 'ip' : 'KernelApp.ip',
54 'hb' : 'KernelApp.hb_port',
54 'hb' : 'KernelApp.hb_port',
55 'shell' : 'KernelApp.shell_port',
55 'shell' : 'KernelApp.shell_port',
56 'iopub' : 'KernelApp.iopub_port',
56 'iopub' : 'KernelApp.iopub_port',
57 'stdin' : 'KernelApp.stdin_port',
57 'stdin' : 'KernelApp.stdin_port',
58 'f' : 'KernelApp.connection_file',
58 'f' : 'KernelApp.connection_file',
59 'parent': 'KernelApp.parent',
59 'parent': 'KernelApp.parent',
60 })
60 })
61 if sys.platform.startswith('win'):
61 if sys.platform.startswith('win'):
62 kernel_aliases['interrupt'] = 'KernelApp.interrupt'
62 kernel_aliases['interrupt'] = 'KernelApp.interrupt'
63
63
64 kernel_flags = dict(base_flags)
64 kernel_flags = dict(base_flags)
65 kernel_flags.update({
65 kernel_flags.update({
66 'no-stdout' : (
66 'no-stdout' : (
67 {'KernelApp' : {'no_stdout' : True}},
67 {'KernelApp' : {'no_stdout' : True}},
68 "redirect stdout to the null device"),
68 "redirect stdout to the null device"),
69 'no-stderr' : (
69 'no-stderr' : (
70 {'KernelApp' : {'no_stderr' : True}},
70 {'KernelApp' : {'no_stderr' : True}},
71 "redirect stderr to the null device"),
71 "redirect stderr to the null device"),
72 })
72 })
73
73
74 # inherit flags&aliases for Sessions
74 # inherit flags&aliases for Sessions
75 kernel_aliases.update(session_aliases)
75 kernel_aliases.update(session_aliases)
76 kernel_flags.update(session_flags)
76 kernel_flags.update(session_flags)
77
77
78
78
79
79
80 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
81 # Application class for starting a Kernel
81 # Application class for starting a Kernel
82 #-----------------------------------------------------------------------------
82 #-----------------------------------------------------------------------------
83
83
84 class KernelApp(BaseIPythonApplication):
84 class KernelApp(BaseIPythonApplication):
85 name='pykernel'
85 name='pykernel'
86 aliases = Dict(kernel_aliases)
86 aliases = Dict(kernel_aliases)
87 flags = Dict(kernel_flags)
87 flags = Dict(kernel_flags)
88 classes = [Session]
88 classes = [Session]
89 # the kernel class, as an importstring
89 # the kernel class, as an importstring
90 kernel_class = DottedObjectName('IPython.zmq.pykernel.Kernel')
90 kernel_class = DottedObjectName('IPython.zmq.pykernel.Kernel')
91 kernel = Any()
91 kernel = Any()
92 poller = Any() # don't restrict this even though current pollers are all Threads
92 poller = Any() # don't restrict this even though current pollers are all Threads
93 heartbeat = Instance(Heartbeat)
93 heartbeat = Instance(Heartbeat)
94 session = Instance('IPython.zmq.session.Session')
94 session = Instance('IPython.zmq.session.Session')
95 ports = Dict()
95 ports = Dict()
96
96
97 # inherit config file name from parent:
97 # inherit config file name from parent:
98 parent_appname = Unicode(config=True)
98 parent_appname = Unicode(config=True)
99 def _parent_appname_changed(self, name, old, new):
99 def _parent_appname_changed(self, name, old, new):
100 if self.config_file_specified:
100 if self.config_file_specified:
101 # it was manually specified, ignore
101 # it was manually specified, ignore
102 return
102 return
103 self.config_file_name = new.replace('-','_') + u'_config.py'
103 self.config_file_name = new.replace('-','_') + u'_config.py'
104 # don't let this count as specifying the config file
104 # don't let this count as specifying the config file
105 self.config_file_specified = False
105 self.config_file_specified = False
106
106
107 # connection info:
107 # connection info:
108 ip = Unicode(LOCALHOST, config=True,
108 ip = Unicode(LOCALHOST, config=True,
109 help="Set the IP or interface on which the kernel will listen.")
109 help="Set the IP or interface on which the kernel will listen.")
110 hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]")
110 hb_port = Integer(0, config=True, help="set the heartbeat port [default: random]")
111 shell_port = Integer(0, config=True, help="set the shell (XREP) port [default: random]")
111 shell_port = Integer(0, config=True, help="set the shell (XREP) port [default: random]")
112 iopub_port = Integer(0, config=True, help="set the iopub (PUB) port [default: random]")
112 iopub_port = Integer(0, config=True, help="set the iopub (PUB) port [default: random]")
113 stdin_port = Integer(0, config=True, help="set the stdin (XREQ) port [default: random]")
113 stdin_port = Integer(0, config=True, help="set the stdin (XREQ) port [default: random]")
114 connection_file = Unicode('', config=True,
114 connection_file = Unicode('', config=True,
115 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
115 help="""JSON file in which to store connection info [default: kernel-<pid>.json]
116
116
117 This file will contain the IP, ports, and authentication key needed to connect
117 This file will contain the IP, ports, and authentication key needed to connect
118 clients to this kernel. By default, this file will be created in the security-dir
118 clients to this kernel. By default, this file will be created in the security-dir
119 of the current profile, but can be specified by absolute path.
119 of the current profile, but can be specified by absolute path.
120 """)
120 """)
121
121
122 # streams, etc.
122 # streams, etc.
123 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
123 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
124 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
124 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
125 outstream_class = DottedObjectName('IPython.zmq.iostream.OutStream',
125 outstream_class = DottedObjectName('IPython.zmq.iostream.OutStream',
126 config=True, help="The importstring for the OutStream factory")
126 config=True, help="The importstring for the OutStream factory")
127 displayhook_class = DottedObjectName('IPython.zmq.displayhook.ZMQDisplayHook',
127 displayhook_class = DottedObjectName('IPython.zmq.displayhook.ZMQDisplayHook',
128 config=True, help="The importstring for the DisplayHook factory")
128 config=True, help="The importstring for the DisplayHook factory")
129
129
130 # polling
130 # polling
131 parent = Integer(0, config=True,
131 parent = Integer(0, config=True,
132 help="""kill this process if its parent dies. On Windows, the argument
132 help="""kill this process if its parent dies. On Windows, the argument
133 specifies the HANDLE of the parent process, otherwise it is simply boolean.
133 specifies the HANDLE of the parent process, otherwise it is simply boolean.
134 """)
134 """)
135 interrupt = Integer(0, config=True,
135 interrupt = Integer(0, config=True,
136 help="""ONLY USED ON WINDOWS
136 help="""ONLY USED ON WINDOWS
137 Interrupt this process when the parent is signalled.
137 Interrupt this process when the parent is signalled.
138 """)
138 """)
139
139
140 def init_crash_handler(self):
140 def init_crash_handler(self):
141 # Install minimal exception handling
141 # Install minimal exception handling
142 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
142 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
143 ostream=sys.__stdout__)
143 ostream=sys.__stdout__)
144
144
145 def init_poller(self):
145 def init_poller(self):
146 if sys.platform == 'win32':
146 if sys.platform == 'win32':
147 if self.interrupt or self.parent:
147 if self.interrupt or self.parent:
148 self.poller = ParentPollerWindows(self.interrupt, self.parent)
148 self.poller = ParentPollerWindows(self.interrupt, self.parent)
149 elif self.parent:
149 elif self.parent:
150 self.poller = ParentPollerUnix()
150 self.poller = ParentPollerUnix()
151
151
152 def _bind_socket(self, s, port):
152 def _bind_socket(self, s, port):
153 iface = 'tcp://%s' % self.ip
153 iface = 'tcp://%s' % self.ip
154 if port <= 0:
154 if port <= 0:
155 port = s.bind_to_random_port(iface)
155 port = s.bind_to_random_port(iface)
156 else:
156 else:
157 s.bind(iface + ':%i'%port)
157 s.bind(iface + ':%i'%port)
158 return port
158 return port
159
159
160 def load_connection_file(self):
160 def load_connection_file(self):
161 """load ip/port/hmac config from JSON connection file"""
161 """load ip/port/hmac config from JSON connection file"""
162 try:
162 try:
163 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
163 fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir])
164 except IOError:
164 except IOError:
165 self.log.debug("Connection file not found: %s", self.connection_file)
165 self.log.debug("Connection file not found: %s", self.connection_file)
166 return
166 return
167 self.log.debug(u"Loading connection file %s", fname)
167 self.log.debug(u"Loading connection file %s", fname)
168 with open(fname) as f:
168 with open(fname) as f:
169 s = f.read()
169 s = f.read()
170 cfg = json.loads(s)
170 cfg = json.loads(s)
171 if self.ip == LOCALHOST and 'ip' in cfg:
171 if self.ip == LOCALHOST and 'ip' in cfg:
172 # not overridden by config or cl_args
172 # not overridden by config or cl_args
173 self.ip = cfg['ip']
173 self.ip = cfg['ip']
174 for channel in ('hb', 'shell', 'iopub', 'stdin'):
174 for channel in ('hb', 'shell', 'iopub', 'stdin'):
175 name = channel + '_port'
175 name = channel + '_port'
176 if getattr(self, name) == 0 and name in cfg:
176 if getattr(self, name) == 0 and name in cfg:
177 # not overridden by config or cl_args
177 # not overridden by config or cl_args
178 setattr(self, name, cfg[name])
178 setattr(self, name, cfg[name])
179 if 'key' in cfg:
179 if 'key' in cfg:
180 self.config.Session.key = str_to_bytes(cfg['key'])
180 self.config.Session.key = str_to_bytes(cfg['key'])
181
181
182 def write_connection_file(self):
182 def write_connection_file(self):
183 """write connection info to JSON file"""
183 """write connection info to JSON file"""
184 if os.path.basename(self.connection_file) == self.connection_file:
184 if os.path.basename(self.connection_file) == self.connection_file:
185 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
185 cf = os.path.join(self.profile_dir.security_dir, self.connection_file)
186 else:
186 else:
187 cf = self.connection_file
187 cf = self.connection_file
188 write_connection_file(cf, ip=self.ip, key=self.session.key,
188 write_connection_file(cf, ip=self.ip, key=self.session.key,
189 shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port,
189 shell_port=self.shell_port, stdin_port=self.stdin_port, hb_port=self.hb_port,
190 iopub_port=self.iopub_port)
190 iopub_port=self.iopub_port)
191
191
192 def init_connection_file(self):
192 def init_connection_file(self):
193 if not self.connection_file:
193 if not self.connection_file:
194 self.connection_file = "kernel-%s.json"%os.getpid()
194 self.connection_file = "kernel-%s.json"%os.getpid()
195 try:
195 try:
196 self.load_connection_file()
196 self.load_connection_file()
197 except Exception:
197 except Exception:
198 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
198 self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True)
199 self.exit(1)
199 self.exit(1)
200
200
201 def init_sockets(self):
201 def init_sockets(self):
202 # Create a context, a session, and the kernel sockets.
202 # Create a context, a session, and the kernel sockets.
203 self.log.info("Starting the kernel at pid: %i", os.getpid())
203 self.log.info("Starting the kernel at pid: %i", os.getpid())
204 context = zmq.Context.instance()
204 context = zmq.Context.instance()
205 # Uncomment this to try closing the context.
205 # Uncomment this to try closing the context.
206 # atexit.register(context.term)
206 # atexit.register(context.term)
207
207
208 self.shell_socket = context.socket(zmq.ROUTER)
208 self.shell_socket = context.socket(zmq.ROUTER)
209 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
209 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
210 self.log.debug("shell ROUTER Channel on port: %i"%self.shell_port)
210 self.log.debug("shell ROUTER Channel on port: %i"%self.shell_port)
211
211
212 self.iopub_socket = context.socket(zmq.PUB)
212 self.iopub_socket = context.socket(zmq.PUB)
213 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
213 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
214 self.log.debug("iopub PUB Channel on port: %i"%self.iopub_port)
214 self.log.debug("iopub PUB Channel on port: %i"%self.iopub_port)
215
215
216 self.stdin_socket = context.socket(zmq.ROUTER)
216 self.stdin_socket = context.socket(zmq.ROUTER)
217 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
217 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
218 self.log.debug("stdin ROUTER Channel on port: %i"%self.stdin_port)
218 self.log.debug("stdin ROUTER Channel on port: %i"%self.stdin_port)
219
219
220 self.heartbeat = Heartbeat(context, (self.ip, self.hb_port))
220 self.heartbeat = Heartbeat(context, (self.ip, self.hb_port))
221 self.hb_port = self.heartbeat.port
221 self.hb_port = self.heartbeat.port
222 self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port)
222 self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port)
223
223
224 # Helper to make it easier to connect to an existing kernel.
224 # Helper to make it easier to connect to an existing kernel.
225 # set log-level to critical, to make sure it is output
225 # set log-level to critical, to make sure it is output
226 self.log.critical("To connect another client to this kernel, use:")
226 self.log.critical("To connect another client to this kernel, use:")
227
227
228 basename = os.path.basename(self.connection_file)
228 basename = os.path.basename(self.connection_file)
229 if basename == self.connection_file or \
229 if basename == self.connection_file or \
230 os.path.dirname(self.connection_file) == self.profile_dir.security_dir:
230 os.path.dirname(self.connection_file) == self.profile_dir.security_dir:
231 # use shortname
231 # use shortname
232 tail = basename
232 tail = basename
233 if self.profile != 'default':
233 if self.profile != 'default':
234 tail += " --profile %s" % self.profile
234 tail += " --profile %s" % self.profile
235 else:
235 else:
236 tail = self.connection_file
236 tail = self.connection_file
237 self.log.critical("--existing %s", tail)
237 self.log.critical("--existing %s", tail)
238
238
239
239
240 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
240 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
241 stdin=self.stdin_port, hb=self.hb_port)
241 stdin=self.stdin_port, hb=self.hb_port)
242
242
243 def init_session(self):
243 def init_session(self):
244 """create our session object"""
244 """create our session object"""
245 default_secure(self.config)
245 default_secure(self.config)
246 self.session = Session(config=self.config, username=u'kernel')
246 self.session = Session(config=self.config, username=u'kernel')
247
247
248 def init_blackhole(self):
248 def init_blackhole(self):
249 """redirects stdout/stderr to devnull if necessary"""
249 """redirects stdout/stderr to devnull if necessary"""
250 if self.no_stdout or self.no_stderr:
250 if self.no_stdout or self.no_stderr:
251 blackhole = file(os.devnull, 'w')
251 blackhole = file(os.devnull, 'w')
252 if self.no_stdout:
252 if self.no_stdout:
253 sys.stdout = sys.__stdout__ = blackhole
253 sys.stdout = sys.__stdout__ = blackhole
254 if self.no_stderr:
254 if self.no_stderr:
255 sys.stderr = sys.__stderr__ = blackhole
255 sys.stderr = sys.__stderr__ = blackhole
256
256
257 def init_io(self):
257 def init_io(self):
258 """Redirect input streams and set a display hook."""
258 """Redirect input streams and set a display hook."""
259 if self.outstream_class:
259 if self.outstream_class:
260 outstream_factory = import_item(str(self.outstream_class))
260 outstream_factory = import_item(str(self.outstream_class))
261 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
261 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
262 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
262 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
263 if self.displayhook_class:
263 if self.displayhook_class:
264 displayhook_factory = import_item(str(self.displayhook_class))
264 displayhook_factory = import_item(str(self.displayhook_class))
265 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
265 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
266
266
267 def init_kernel(self):
267 def init_kernel(self):
268 """Create the Kernel object itself"""
268 """Create the Kernel object itself"""
269 kernel_factory = import_item(str(self.kernel_class))
269 kernel_factory = import_item(str(self.kernel_class))
270 self.kernel = kernel_factory(config=self.config, session=self.session,
270 self.kernel = kernel_factory(config=self.config, session=self.session,
271 shell_socket=self.shell_socket,
271 shell_socket=self.shell_socket,
272 iopub_socket=self.iopub_socket,
272 iopub_socket=self.iopub_socket,
273 stdin_socket=self.stdin_socket,
273 stdin_socket=self.stdin_socket,
274 log=self.log
274 log=self.log
275 )
275 )
276 self.kernel.record_ports(self.ports)
276 self.kernel.record_ports(self.ports)
277
277
278 @catch_config_error
278 @catch_config_error
279 def initialize(self, argv=None):
279 def initialize(self, argv=None):
280 super(KernelApp, self).initialize(argv)
280 super(KernelApp, self).initialize(argv)
281 self.init_blackhole()
281 self.init_blackhole()
282 self.init_connection_file()
282 self.init_connection_file()
283 self.init_session()
283 self.init_session()
284 self.init_poller()
284 self.init_poller()
285 self.init_sockets()
285 self.init_sockets()
286 # writing connection file must be *after* init_sockets
286 # writing connection file must be *after* init_sockets
287 self.write_connection_file()
287 self.write_connection_file()
288 self.init_io()
288 self.init_io()
289 self.init_kernel()
289 self.init_kernel()
290 # flush stdout/stderr, so that anything written to these streams during
291 # initialization do not get associated with the first execution request
292 sys.stdout.flush()
293 sys.stderr.flush()
290
294
291 def start(self):
295 def start(self):
292 self.heartbeat.start()
296 self.heartbeat.start()
293 if self.poller is not None:
297 if self.poller is not None:
294 self.poller.start()
298 self.poller.start()
295 try:
299 try:
296 self.kernel.start()
300 self.kernel.start()
297 except KeyboardInterrupt:
301 except KeyboardInterrupt:
298 pass
302 pass
299
303
General Comments 0
You need to be logged in to leave comments. Login now