Show More
@@ -268,39 +268,47 b' var IPython = (function (IPython) {' | |||||
268 | }); |
|
268 | }); | |
269 | return json; |
|
269 | return json; | |
270 | }; |
|
270 | }; | |
271 |
|
271 | |||
272 | OutputArea.prototype.append_output = function (json) { |
|
272 | OutputArea.prototype.append_output = function (json) { | |
273 | this.expand(); |
|
273 | this.expand(); | |
|
274 | ||||
|
275 | // validate output data types | |||
|
276 | json = this.validate_output(json); | |||
|
277 | ||||
274 | // Clear the output if clear is queued. |
|
278 | // Clear the output if clear is queued. | |
275 | var needs_height_reset = false; |
|
279 | var needs_height_reset = false; | |
276 | if (this.clear_queued) { |
|
280 | if (this.clear_queued) { | |
277 | this.clear_output(false); |
|
281 | this.clear_output(false); | |
278 | needs_height_reset = true; |
|
282 | needs_height_reset = true; | |
279 | } |
|
283 | } | |
280 |
|
||||
281 | // validate output data types |
|
|||
282 | json = this.validate_output(json); |
|
|||
283 |
|
284 | |||
284 | if (json.output_type === 'pyout') { |
|
285 | if (json.output_type === 'pyout') { | |
285 | this.append_pyout(json); |
|
286 | this.append_pyout(json); | |
286 | } else if (json.output_type === 'pyerr') { |
|
287 | } else if (json.output_type === 'pyerr') { | |
287 | this.append_pyerr(json); |
|
288 | this.append_pyerr(json); | |
288 | } else if (json.output_type === 'display_data') { |
|
|||
289 | this.append_display_data(json); |
|
|||
290 | } else if (json.output_type === 'stream') { |
|
289 | } else if (json.output_type === 'stream') { | |
291 | this.append_stream(json); |
|
290 | this.append_stream(json); | |
292 | } |
|
291 | } | |
293 |
|
||||
294 | this.outputs.push(json); |
|
|||
295 |
|
||||
296 | // Only reset the height to automatic if the height is currently |
|
|||
297 | // fixed (done by wait=True flag on clear_output). |
|
|||
298 | if (needs_height_reset) { |
|
|||
299 | this.element.height(''); |
|
|||
300 | } |
|
|||
301 |
|
292 | |||
|
293 | // We must release the animation fixed height in a callback since Gecko | |||
|
294 | // (FireFox) doesn't render the image immediately as the data is | |||
|
295 | // available. | |||
302 | var that = this; |
|
296 | var that = this; | |
303 | setTimeout(function(){that.element.trigger('resize');}, 100); |
|
297 | var handle_appended = function ($el) { | |
|
298 | // Only reset the height to automatic if the height is currently | |||
|
299 | // fixed (done by wait=True flag on clear_output). | |||
|
300 | if (needs_height_reset) { | |||
|
301 | that.element.height(''); | |||
|
302 | } | |||
|
303 | that.element.trigger('resize'); | |||
|
304 | }; | |||
|
305 | if (json.output_type === 'display_data') { | |||
|
306 | this.append_display_data(json, handle_appended); | |||
|
307 | } else { | |||
|
308 | handle_appended(); | |||
|
309 | } | |||
|
310 | ||||
|
311 | this.outputs.push(json); | |||
304 | }; |
|
312 | }; | |
305 |
|
313 | |||
306 |
|
314 | |||
@@ -475,9 +483,9 b' var IPython = (function (IPython) {' | |||||
475 | }; |
|
483 | }; | |
476 |
|
484 | |||
477 |
|
485 | |||
478 | OutputArea.prototype.append_display_data = function (json) { |
|
486 | OutputArea.prototype.append_display_data = function (json, handle_inserted) { | |
479 | var toinsert = this.create_output_area(); |
|
487 | var toinsert = this.create_output_area(); | |
480 | if (this.append_mime_type(json, toinsert)) { |
|
488 | if (this.append_mime_type(json, toinsert, handle_inserted)) { | |
481 | this._safe_append(toinsert); |
|
489 | this._safe_append(toinsert); | |
482 | // If we just output latex, typeset it. |
|
490 | // If we just output latex, typeset it. | |
483 | if ((json['text/latex'] !== undefined) || (json['text/html'] !== undefined)) { |
|
491 | if ((json['text/latex'] !== undefined) || (json['text/html'] !== undefined)) { | |
@@ -494,7 +502,7 b' var IPython = (function (IPython) {' | |||||
494 | 'image/jpeg' : true |
|
502 | 'image/jpeg' : true | |
495 | }; |
|
503 | }; | |
496 |
|
504 | |||
497 | OutputArea.prototype.append_mime_type = function (json, element) { |
|
505 | OutputArea.prototype.append_mime_type = function (json, element, handle_inserted) { | |
498 | for (var type_i in OutputArea.display_order) { |
|
506 | for (var type_i in OutputArea.display_order) { | |
499 | var type = OutputArea.display_order[type_i]; |
|
507 | var type = OutputArea.display_order[type_i]; | |
500 | var append = OutputArea.append_map[type]; |
|
508 | var append = OutputArea.append_map[type]; | |
@@ -511,7 +519,14 b' var IPython = (function (IPython) {' | |||||
511 | } |
|
519 | } | |
512 | } |
|
520 | } | |
513 | var md = json.metadata || {}; |
|
521 | var md = json.metadata || {}; | |
514 | var toinsert = append.apply(this, [value, md, element]); |
|
522 | var toinsert = append.apply(this, [value, md, element, handle_inserted]); | |
|
523 | // Since only the png and jpeg mime types call the inserted | |||
|
524 | // callback, if the mime type is something other we must call the | |||
|
525 | // inserted callback only when the element is actually inserted | |||
|
526 | // into the DOM. Use a timeout of 0 to do this. | |||
|
527 | if (['image/png', 'image/jpeg'].indexOf(type) < 0 && handle_inserted !== undefined) { | |||
|
528 | setTimeout(handle_inserted, 0); | |||
|
529 | } | |||
515 | $([IPython.events]).trigger('output_appended.OutputArea', [type, value, md, toinsert]); |
|
530 | $([IPython.events]).trigger('output_appended.OutputArea', [type, value, md, toinsert]); | |
516 | return toinsert; |
|
531 | return toinsert; | |
517 | } |
|
532 | } | |
@@ -611,10 +626,16 b' var IPython = (function (IPython) {' | |||||
611 | if (width !== undefined) img.attr('width', width); |
|
626 | if (width !== undefined) img.attr('width', width); | |
612 | }; |
|
627 | }; | |
613 |
|
628 | |||
614 | var append_png = function (png, md, element) { |
|
629 | var append_png = function (png, md, element, handle_inserted) { | |
615 | var type = 'image/png'; |
|
630 | var type = 'image/png'; | |
616 | var toinsert = this.create_output_subarea(md, "output_png", type); |
|
631 | var toinsert = this.create_output_subarea(md, "output_png", type); | |
617 | var img = $("<img/>").attr('src','data:image/png;base64,'+png); |
|
632 | var img = $("<img/>"); | |
|
633 | if (handle_inserted !== undefined) { | |||
|
634 | img.on('load', function(){ | |||
|
635 | handle_inserted(img); | |||
|
636 | }); | |||
|
637 | } | |||
|
638 | img[0].src = 'data:image/png;base64,'+ png; | |||
618 | set_width_height(img, md, 'image/png'); |
|
639 | set_width_height(img, md, 'image/png'); | |
619 | this._dblclick_to_reset_size(img); |
|
640 | this._dblclick_to_reset_size(img); | |
620 | toinsert.append(img); |
|
641 | toinsert.append(img); | |
@@ -623,10 +644,16 b' var IPython = (function (IPython) {' | |||||
623 | }; |
|
644 | }; | |
624 |
|
645 | |||
625 |
|
646 | |||
626 | var append_jpeg = function (jpeg, md, element) { |
|
647 | var append_jpeg = function (jpeg, md, element, handle_inserted) { | |
627 | var type = 'image/jpeg'; |
|
648 | var type = 'image/jpeg'; | |
628 | var toinsert = this.create_output_subarea(md, "output_jpeg", type); |
|
649 | var toinsert = this.create_output_subarea(md, "output_jpeg", type); | |
629 | var img = $("<img/>").attr('src','data:image/jpeg;base64,'+jpeg); |
|
650 | var img = $("<img/>"); | |
|
651 | if (handle_inserted !== undefined) { | |||
|
652 | img.on('load', function(){ | |||
|
653 | handle_inserted(img); | |||
|
654 | }); | |||
|
655 | } | |||
|
656 | img[0].src = 'data:image/jpeg;base64,'+ jpeg; | |||
630 | set_width_height(img, md, 'image/jpeg'); |
|
657 | set_width_height(img, md, 'image/jpeg'); | |
631 | this._dblclick_to_reset_size(img); |
|
658 | this._dblclick_to_reset_size(img); | |
632 | toinsert.append(img); |
|
659 | toinsert.append(img); | |
@@ -748,7 +775,10 b' var IPython = (function (IPython) {' | |||||
748 | this.clear_queued = false; |
|
775 | this.clear_queued = false; | |
749 | } |
|
776 | } | |
750 |
|
777 | |||
751 |
// |
|
778 | // Clear all | |
|
779 | // Remove load event handlers from img tags because we don't want | |||
|
780 | // them to fire if the image is never added to the page. | |||
|
781 | this.element.find('img').off('load'); | |||
752 | this.element.html(""); |
|
782 | this.element.html(""); | |
753 | this.outputs = []; |
|
783 | this.outputs = []; | |
754 | this.trusted = true; |
|
784 | this.trusted = true; |
@@ -90,11 +90,7 b' class ZMQDisplayPublisher(DisplayPublisher):' | |||||
90 |
|
90 | |||
91 | def clear_output(self, wait=False): |
|
91 | def clear_output(self, wait=False): | |
92 | content = dict(wait=wait) |
|
92 | content = dict(wait=wait) | |
93 |
|
||||
94 | print('\r', file=sys.stdout, end='') |
|
|||
95 | print('\r', file=sys.stderr, end='') |
|
|||
96 | self._flush_streams() |
|
93 | self._flush_streams() | |
97 |
|
||||
98 | self.session.send( |
|
94 | self.session.send( | |
99 | self.pub_socket, u'clear_output', content, |
|
95 | self.pub_socket, u'clear_output', content, | |
100 | parent=self.parent_header, ident=self.topic, |
|
96 | parent=self.parent_header, ident=self.topic, |
@@ -197,6 +197,9 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
197 | self._local_kernel = kw.get('local_kernel', |
|
197 | self._local_kernel = kw.get('local_kernel', | |
198 | FrontendWidget._local_kernel) |
|
198 | FrontendWidget._local_kernel) | |
199 |
|
199 | |||
|
200 | # Whether or not a clear_output call is pending new output. | |||
|
201 | self._pending_clearoutput = False | |||
|
202 | ||||
200 | #--------------------------------------------------------------------------- |
|
203 | #--------------------------------------------------------------------------- | |
201 | # 'ConsoleWidget' public interface |
|
204 | # 'ConsoleWidget' public interface | |
202 | #--------------------------------------------------------------------------- |
|
205 | #--------------------------------------------------------------------------- | |
@@ -339,6 +342,14 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
339 | #--------------------------------------------------------------------------- |
|
342 | #--------------------------------------------------------------------------- | |
340 | # 'BaseFrontendMixin' abstract interface |
|
343 | # 'BaseFrontendMixin' abstract interface | |
341 | #--------------------------------------------------------------------------- |
|
344 | #--------------------------------------------------------------------------- | |
|
345 | def _handle_clear_output(self, msg): | |||
|
346 | """Handle clear output messages.""" | |||
|
347 | if not self._hidden and self._is_from_this_session(msg): | |||
|
348 | wait = msg['content'].get('wait', True) | |||
|
349 | if wait: | |||
|
350 | self._pending_clearoutput = True | |||
|
351 | else: | |||
|
352 | self.clear_output() | |||
342 |
|
353 | |||
343 | def _handle_complete_reply(self, rep): |
|
354 | def _handle_complete_reply(self, rep): | |
344 | """ Handle replies for tab completion. |
|
355 | """ Handle replies for tab completion. | |
@@ -520,6 +531,7 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
520 | """ |
|
531 | """ | |
521 | self.log.debug("pyout: %s", msg.get('content', '')) |
|
532 | self.log.debug("pyout: %s", msg.get('content', '')) | |
522 | if not self._hidden and self._is_from_this_session(msg): |
|
533 | if not self._hidden and self._is_from_this_session(msg): | |
|
534 | self.flush_clearoutput() | |||
523 | text = msg['content']['data'] |
|
535 | text = msg['content']['data'] | |
524 | self._append_plain_text(text + '\n', before_prompt=True) |
|
536 | self._append_plain_text(text + '\n', before_prompt=True) | |
525 |
|
537 | |||
@@ -528,13 +540,8 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
528 | """ |
|
540 | """ | |
529 | self.log.debug("stream: %s", msg.get('content', '')) |
|
541 | self.log.debug("stream: %s", msg.get('content', '')) | |
530 | if not self._hidden and self._is_from_this_session(msg): |
|
542 | if not self._hidden and self._is_from_this_session(msg): | |
531 | # Most consoles treat tabs as being 8 space characters. Convert tabs |
|
543 | self.flush_clearoutput() | |
532 | # to spaces so that output looks as expected regardless of this |
|
544 | self.append_stream(msg['content']['data']) | |
533 | # widget's tab width. |
|
|||
534 | text = msg['content']['data'].expandtabs(8) |
|
|||
535 |
|
||||
536 | self._append_plain_text(text, before_prompt=True) |
|
|||
537 | self._control.moveCursor(QtGui.QTextCursor.End) |
|
|||
538 |
|
545 | |||
539 | def _handle_shutdown_reply(self, msg): |
|
546 | def _handle_shutdown_reply(self, msg): | |
540 | """ Handle shutdown signal, only if from other console. |
|
547 | """ Handle shutdown signal, only if from other console. | |
@@ -685,6 +692,29 b' class FrontendWidget(HistoryConsoleWidget, BaseFrontendMixin):' | |||||
685 | before_prompt=True |
|
692 | before_prompt=True | |
686 | ) |
|
693 | ) | |
687 |
|
694 | |||
|
695 | def append_stream(self, text): | |||
|
696 | """Appends text to the output stream.""" | |||
|
697 | # Most consoles treat tabs as being 8 space characters. Convert tabs | |||
|
698 | # to spaces so that output looks as expected regardless of this | |||
|
699 | # widget's tab width. | |||
|
700 | text = text.expandtabs(8) | |||
|
701 | self._append_plain_text(text, before_prompt=True) | |||
|
702 | self._control.moveCursor(QtGui.QTextCursor.End) | |||
|
703 | ||||
|
704 | def flush_clearoutput(self): | |||
|
705 | """If a clearoutput is pending, execute it.""" | |||
|
706 | if self._pending_clearoutput: | |||
|
707 | self._pending_clearoutput = False | |||
|
708 | self.clear_output() | |||
|
709 | ||||
|
710 | def clear_output(self): | |||
|
711 | """Clears the current line of output.""" | |||
|
712 | cursor = self._control.textCursor() | |||
|
713 | cursor.beginEditBlock() | |||
|
714 | cursor.movePosition(cursor.StartOfLine, cursor.KeepAnchor) | |||
|
715 | cursor.insertText('') | |||
|
716 | cursor.endEditBlock() | |||
|
717 | ||||
688 | #--------------------------------------------------------------------------- |
|
718 | #--------------------------------------------------------------------------- | |
689 | # 'FrontendWidget' protected interface |
|
719 | # 'FrontendWidget' protected interface | |
690 | #--------------------------------------------------------------------------- |
|
720 | #--------------------------------------------------------------------------- |
@@ -140,7 +140,6 b' class IPythonWidget(FrontendWidget):' | |||||
140 | #--------------------------------------------------------------------------- |
|
140 | #--------------------------------------------------------------------------- | |
141 | # 'BaseFrontendMixin' abstract interface |
|
141 | # 'BaseFrontendMixin' abstract interface | |
142 | #--------------------------------------------------------------------------- |
|
142 | #--------------------------------------------------------------------------- | |
143 |
|
||||
144 | def _handle_complete_reply(self, rep): |
|
143 | def _handle_complete_reply(self, rep): | |
145 | """ Reimplemented to support IPython's improved completion machinery. |
|
144 | """ Reimplemented to support IPython's improved completion machinery. | |
146 | """ |
|
145 | """ | |
@@ -223,6 +222,7 b' class IPythonWidget(FrontendWidget):' | |||||
223 | """ |
|
222 | """ | |
224 | self.log.debug("pyout: %s", msg.get('content', '')) |
|
223 | self.log.debug("pyout: %s", msg.get('content', '')) | |
225 | if not self._hidden and self._is_from_this_session(msg): |
|
224 | if not self._hidden and self._is_from_this_session(msg): | |
|
225 | self.flush_clearoutput() | |||
226 | content = msg['content'] |
|
226 | content = msg['content'] | |
227 | prompt_number = content.get('execution_count', 0) |
|
227 | prompt_number = content.get('execution_count', 0) | |
228 | data = content['data'] |
|
228 | data = content['data'] | |
@@ -250,6 +250,7 b' class IPythonWidget(FrontendWidget):' | |||||
250 | # eventually will as this allows all frontends to monitor the display |
|
250 | # eventually will as this allows all frontends to monitor the display | |
251 | # data. But we need to figure out how to handle this in the GUI. |
|
251 | # data. But we need to figure out how to handle this in the GUI. | |
252 | if not self._hidden and self._is_from_this_session(msg): |
|
252 | if not self._hidden and self._is_from_this_session(msg): | |
|
253 | self.flush_clearoutput() | |||
253 | source = msg['content']['source'] |
|
254 | source = msg['content']['source'] | |
254 | data = msg['content']['data'] |
|
255 | data = msg['content']['data'] | |
255 | metadata = msg['content']['metadata'] |
|
256 | metadata = msg['content']['metadata'] |
@@ -114,6 +114,7 b' class RichIPythonWidget(IPythonWidget):' | |||||
114 | """ Overridden to handle rich data types, like SVG. |
|
114 | """ Overridden to handle rich data types, like SVG. | |
115 | """ |
|
115 | """ | |
116 | if not self._hidden and self._is_from_this_session(msg): |
|
116 | if not self._hidden and self._is_from_this_session(msg): | |
|
117 | self.flush_clearoutput() | |||
117 | content = msg['content'] |
|
118 | content = msg['content'] | |
118 | prompt_number = content.get('execution_count', 0) |
|
119 | prompt_number = content.get('execution_count', 0) | |
119 | data = content['data'] |
|
120 | data = content['data'] | |
@@ -140,6 +141,7 b' class RichIPythonWidget(IPythonWidget):' | |||||
140 | """ Overridden to handle rich data types, like SVG. |
|
141 | """ Overridden to handle rich data types, like SVG. | |
141 | """ |
|
142 | """ | |
142 | if not self._hidden and self._is_from_this_session(msg): |
|
143 | if not self._hidden and self._is_from_this_session(msg): | |
|
144 | self.flush_clearoutput() | |||
143 | source = msg['content']['source'] |
|
145 | source = msg['content']['source'] | |
144 | data = msg['content']['data'] |
|
146 | data = msg['content']['data'] | |
145 | metadata = msg['content']['metadata'] |
|
147 | metadata = msg['content']['metadata'] |
@@ -43,6 +43,7 b' class ZMQTerminalInteractiveShell(TerminalInteractiveShell):' | |||||
43 | """A subclass of TerminalInteractiveShell that uses the 0MQ kernel""" |
|
43 | """A subclass of TerminalInteractiveShell that uses the 0MQ kernel""" | |
44 | _executing = False |
|
44 | _executing = False | |
45 | _execution_state = Unicode('') |
|
45 | _execution_state = Unicode('') | |
|
46 | _pending_clearoutput = False | |||
46 | kernel_timeout = Float(60, config=True, |
|
47 | kernel_timeout = Float(60, config=True, | |
47 | help="""Timeout for giving up on a kernel (in seconds). |
|
48 | help="""Timeout for giving up on a kernel (in seconds). | |
48 |
|
49 | |||
@@ -241,13 +242,22 b' class ZMQTerminalInteractiveShell(TerminalInteractiveShell):' | |||||
241 | self._execution_state = sub_msg["content"]["execution_state"] |
|
242 | self._execution_state = sub_msg["content"]["execution_state"] | |
242 | elif msg_type == 'stream': |
|
243 | elif msg_type == 'stream': | |
243 | if sub_msg["content"]["name"] == "stdout": |
|
244 | if sub_msg["content"]["name"] == "stdout": | |
|
245 | if self._pending_clearoutput: | |||
|
246 | print("\r", file=io.stdout, end="") | |||
|
247 | self._pending_clearoutput = False | |||
244 | print(sub_msg["content"]["data"], file=io.stdout, end="") |
|
248 | print(sub_msg["content"]["data"], file=io.stdout, end="") | |
245 | io.stdout.flush() |
|
249 | io.stdout.flush() | |
246 | elif sub_msg["content"]["name"] == "stderr" : |
|
250 | elif sub_msg["content"]["name"] == "stderr" : | |
|
251 | if self._pending_clearoutput: | |||
|
252 | print("\r", file=io.stderr, end="") | |||
|
253 | self._pending_clearoutput = False | |||
247 | print(sub_msg["content"]["data"], file=io.stderr, end="") |
|
254 | print(sub_msg["content"]["data"], file=io.stderr, end="") | |
248 | io.stderr.flush() |
|
255 | io.stderr.flush() | |
249 |
|
256 | |||
250 | elif msg_type == 'pyout': |
|
257 | elif msg_type == 'pyout': | |
|
258 | if self._pending_clearoutput: | |||
|
259 | print("\r", file=io.stdout, end="") | |||
|
260 | self._pending_clearoutput = False | |||
251 | self.execution_count = int(sub_msg["content"]["execution_count"]) |
|
261 | self.execution_count = int(sub_msg["content"]["execution_count"]) | |
252 | format_dict = sub_msg["content"]["data"] |
|
262 | format_dict = sub_msg["content"]["data"] | |
253 | self.handle_rich_data(format_dict) |
|
263 | self.handle_rich_data(format_dict) | |
@@ -267,6 +277,12 b' class ZMQTerminalInteractiveShell(TerminalInteractiveShell):' | |||||
267 | if 'text/plain' in data: |
|
277 | if 'text/plain' in data: | |
268 | print(data['text/plain']) |
|
278 | print(data['text/plain']) | |
269 |
|
279 | |||
|
280 | elif msg_type == 'clear_output': | |||
|
281 | if sub_msg["content"]["wait"]: | |||
|
282 | self._pending_clearoutput = True | |||
|
283 | else: | |||
|
284 | print("\r", file=io.stdout, end="") | |||
|
285 | ||||
270 | _imagemime = { |
|
286 | _imagemime = { | |
271 | 'image/png': 'png', |
|
287 | 'image/png': 'png', | |
272 | 'image/jpeg': 'jpeg', |
|
288 | 'image/jpeg': 'jpeg', |
General Comments 0
You need to be logged in to leave comments.
Login now