##// 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 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 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 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