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