Skip to content
This repository

clear_output improvements #1563

Merged
merged 4 commits into from about 2 years ago

4 participants

Min RK Brian E. Granger Matthias Bussonnier Fernando Perez
Min RK
Owner
minrk commented April 09, 2012

This was a part of PR #1548, but as I kept finding issues, I decided this should be a separate PR. That PR does depend on this one, and should not be merged before this one.

Changes:

  • clear_output() clears the line, even in terminal IPython, the QtConsole and plain Python as well, by printing \r to streams.
  • clear_output() avoids the flicker in the notebook by adding a delay, and firing immediately upon the next actual display message.
  • display_javascript hides its output_area element, so using display to run a bunch of javascript doesn't result in ever-growing vertical space.
IPython/frontend/html/notebook/static/js/codecell.js
... ...
@@ -683,6 +690,9 @@ var IPython = (function (IPython) {
683 690
         // We just eval the JS code, element appears in the local scope.
684 691
         var element = $("<div/>").addClass("box_flex1 output_subarea");
685 692
         e.append(element);
  693
+        // Div for js shouldn't be drawn, as it will add empty height to the area.
4
Brian E. Granger Owner
ellisonbg added a note April 09, 2012

Are you hiding the entire output_area for that cell? If so, won't the javascript be unable to append things to the output area and have them show up on the screen?

Min RK Owner
minrk added a note April 09, 2012

Yes I am. The js can call e.show() if it wants to draw. But it is certainly true that if there is nothing to draw, the height should be zero (right now it's ~15 px, so running a series of js calls results in infinitely growing vertical space).

Brian E. Granger Owner
ellisonbg added a note April 09, 2012

OK, we should document the fact that the users needs to call the show method. Not sure where to put that documentation. I agree that this is a good idea though.

Min RK Owner
minrk added a note April 09, 2012

Documented in the Javascript object docstring.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
IPython/frontend/html/notebook/static/js/codecell.js
... ...
@@ -730,7 +740,31 @@ var IPython = (function (IPython) {
730 740
 
731 741
 
732 742
     CodeCell.prototype.clear_output = function (stdout, stderr, other) {
  743
+        var that = this;
  744
+        if (this.clear_out_timeout != null){
  745
+            // fire previous pending clear *immediately*
  746
+            clearTimeout(this.clear_out_timeout);
  747
+            this.clear_out_timeout = null;
  748
+            this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
  749
+        }
  750
+        // store flags for flushing the timeout
  751
+        this._clear_stdout = stdout;
  752
+        this._clear_stderr = stderr;
  753
+        this._clear_other = other;
  754
+        this.clear_out_timeout = setTimeout(function(){
  755
+            // really clear timeout only after a short delay
  756
+            // this reduces flicker in 'clear_output; print' cases
  757
+            console.log("clear_out_timeout");
2
Brian E. Granger Owner
ellisonbg added a note April 09, 2012

Let's remove the console.log before merging.

Min RK Owner
minrk added a note April 09, 2012

removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Brian E. Granger
Owner

These are clear improvements to the clear_output logic. Have not tested it, but the code looks good.

added some commits April 03, 2012
Min RK [notebook] clear_output is handled after a delay
This reduces flicker during common loops like:

for step in stuff:
    clear_output()
    print something

the timeout is flushed *immediately* on any subsequent output.
0960f3b
Min RK hide output_area for js
prevents growing vertical space from adding empty output_areas.
e169ad6
Min RK document initially hidden javascript container 24972e2
Min RK
Owner
minrk commented April 09, 2012

Here is a notebook with text progress bar using clear_output improvements and simple javascript progress bar using js changes.

Writing a javascript progress bar is really trivial:

import uuid
from IPython.core.display import HTML, Javascript, display

divid = str(uuid.uuid4())

display(HTML(
"""
<div style="border: 1px solid black; width:500px">
  <div id="%s" style="background-color:blue; width:0%%">&nbsp;</div>
</div> 
""" % divid)
)

for i in range(1,101):
    time.sleep(0.1)
    display(Javascript("$('div#%s').width('%i%%')" % (divid, i)))
Brian E. Granger
Owner

This is extremely cool!

Matthias Bussonnier
Collaborator

I came across this article :
http://www.alsacreations.com/article/lire/1416-html5-meter-progress.html
I just learn that it seems that HTML5 support 'native' progress bar (not all browser implement it yet).

Just for the record of course. A div with a width is perfectly fine with me. :-)

Fernando Perez
Owner

The code looks good to me and the functionality is fantastic, great job! I'll merge now, but I want to make a suggestion: @minrk, with just adding a tiny bit of explanatory text (a title and a short paragraph before each cell should suffice), your progressbar notebook should really go into the doc examples! It's a great little illustration of how to use the system which I'm sure many people will find useful.

I won't delay this PR on that alone, esp. since #1548 depends on this one, so I'll merge now. Great work!!

Fernando Perez fperez merged commit d66ece6 into from April 13, 2012
Fernando Perez fperez closed this April 13, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 4 unique commits by 1 author.

Apr 09, 2012
Min RK clear_output implies '\r' for terminal frontends fa157db
Min RK [notebook] clear_output is handled after a delay
This reduces flicker during common loops like:

for step in stuff:
    clear_output()
    print something

the timeout is flushed *immediately* on any subsequent output.
0960f3b
Min RK hide output_area for js
prevents growing vertical space from adding empty output_areas.
e169ad6
Min RK document initially hidden javascript container 24972e2
This page is out of date. Refresh to see the latest.
23  IPython/core/display.py
@@ -17,6 +17,8 @@
17 17
 # Imports
18 18
 #-----------------------------------------------------------------------------
19 19
 
  20
+from __future__ import print_function
  21
+
20 22
 from xml.dom import minidom
21 23
 
22 24
 from .displaypub import (
@@ -365,6 +367,11 @@ def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
365 367
         display function, it will result in the data being displayed
366 368
         in the frontend. If the data is a URL, the data will first be
367 369
         downloaded and then displayed. 
  370
+        
  371
+        In the Notebook, the containing element will be available as `element`,
  372
+        and jQuery will be available.  The output area starts hidden, so if
  373
+        the js appends content to `element` that should be visible, then
  374
+        it must call `container.show()` to unhide the area.
368 375
 
369 376
         Parameters
370 377
         ----------
@@ -496,6 +503,16 @@ def clear_output(stdout=True, stderr=True, other=True):
496 503
         (e.g. figures,images,HTML, any result of display()).
497 504
     """
498 505
     from IPython.core.interactiveshell import InteractiveShell
499  
-    InteractiveShell.instance().display_pub.clear_output(
500  
-        stdout=stdout, stderr=stderr, other=other,
501  
-    )
  506
+    if InteractiveShell.initialized():
  507
+        InteractiveShell.instance().display_pub.clear_output(
  508
+            stdout=stdout, stderr=stderr, other=other,
  509
+        )
  510
+    else:
  511
+        from IPython.utils import io
  512
+        if stdout:
  513
+            print('\033[2K\r', file=io.stdout, end='')
  514
+            io.stdout.flush()
  515
+        if stderr:
  516
+            print('\033[2K\r', file=io.stderr, end='')
  517
+            io.stderr.flush()
  518
+        
15  IPython/core/displaypub.py
@@ -30,6 +30,7 @@
30 30
 from __future__ import print_function
31 31
 
32 32
 from IPython.config.configurable import Configurable
  33
+from IPython.utils import io
33 34
 
34 35
 #-----------------------------------------------------------------------------
35 36
 # Main payload class
@@ -99,14 +100,20 @@ def publish(self, source, data, metadata=None):
99 100
             arbitrary key, value pairs that frontends can use to interpret
100 101
             the data.
101 102
         """
102  
-        from IPython.utils import io
  103
+
103 104
         # The default is to simply write the plain text data using io.stdout.
104 105
         if data.has_key('text/plain'):
105 106
             print(data['text/plain'], file=io.stdout)
106 107
 
107  
-        def clear_output(self, stdout=True, stderr=True, other=True):
108  
-            """Clear the output of the cell receiving output."""
109  
-            pass
  108
+    def clear_output(self, stdout=True, stderr=True, other=True):
  109
+        """Clear the output of the cell receiving output."""
  110
+        if stdout:
  111
+            print('\033[2K\r', file=io.stdout, end='')
  112
+            io.stdout.flush()
  113
+        if stderr:
  114
+            print('\033[2K\r', file=io.stderr, end='')
  115
+            io.stderr.flush()
  116
+            
110 117
 
111 118
 
112 119
 def publish_display_data(source, data, metadata=None):
47  IPython/frontend/html/notebook/static/js/codecell.js
@@ -21,6 +21,7 @@ var IPython = (function (IPython) {
21 21
         this.outputs = [];
22 22
         this.collapsed = false;
23 23
         this.tooltip_timeout = null;
  24
+        this.clear_out_timeout = null;
24 25
         IPython.Cell.apply(this, arguments);
25 26
     };
26 27
 
@@ -566,6 +567,7 @@ var IPython = (function (IPython) {
566 567
     CodeCell.prototype.append_output = function (json, dynamic) {
567 568
         // If dynamic is true, javascript output will be eval'd.
568 569
         this.expand();
  570
+        this.flush_clear_timeout();
569 571
         if (json.output_type === 'pyout') {
570 572
             this.append_pyout(json, dynamic);
571 573
         } else if (json.output_type === 'pyerr') {
@@ -621,6 +623,11 @@ var IPython = (function (IPython) {
621 623
         if (json.stream == undefined){
622 624
             json.stream = 'stdout';
623 625
         }
  626
+        if (!utils.fixConsole(json.text)){
  627
+            // fixConsole gives nothing (empty string, \r, etc.)
  628
+            // so don't append any elements, which might add undesirable space
  629
+            return;
  630
+        }
624 631
         var subclass = "output_"+json.stream;
625 632
         if (this.outputs.length > 0){
626 633
             // have at least one output to consider
@@ -679,10 +686,14 @@ var IPython = (function (IPython) {
679 686
     };
680 687
 
681 688
 
682  
-    CodeCell.prototype.append_javascript = function (js, e) {
  689
+    CodeCell.prototype.append_javascript = function (js, container) {
683 690
         // We just eval the JS code, element appears in the local scope.
684 691
         var element = $("<div/>").addClass("box_flex1 output_subarea");
685  
-        e.append(element);
  692
+        container.append(element);
  693
+        // Div for js shouldn't be drawn, as it will add empty height to the area.
  694
+        container.hide();
  695
+        // If the Javascript appends content to `element` that should be drawn, then
  696
+        // it must also call `container.show()`.
686 697
         eval(js);
687 698
     }
688 699
 
@@ -730,7 +741,30 @@ var IPython = (function (IPython) {
730 741
 
731 742
 
732 743
     CodeCell.prototype.clear_output = function (stdout, stderr, other) {
  744
+        var that = this;
  745
+        if (this.clear_out_timeout != null){
  746
+            // fire previous pending clear *immediately*
  747
+            clearTimeout(this.clear_out_timeout);
  748
+            this.clear_out_timeout = null;
  749
+            this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
  750
+        }
  751
+        // store flags for flushing the timeout
  752
+        this._clear_stdout = stdout;
  753
+        this._clear_stderr = stderr;
  754
+        this._clear_other = other;
  755
+        this.clear_out_timeout = setTimeout(function(){
  756
+            // really clear timeout only after a short delay
  757
+            // this reduces flicker in 'clear_output; print' cases
  758
+            that.clear_out_timeout = null;
  759
+            that._clear_stdout = that._clear_stderr = that._clear_other = null;
  760
+            that.clear_output_callback(stdout, stderr, other);
  761
+        }, 500
  762
+        );
  763
+    };
  764
+    
  765
+    CodeCell.prototype.clear_output_callback = function (stdout, stderr, other) {
733 766
         var output_div = this.element.find("div.output");
  767
+        
734 768
         if (stdout && stderr && other){
735 769
             // clear all, no need for logic
736 770
             output_div.html("");
@@ -770,6 +804,15 @@ var IPython = (function (IPython) {
770 804
     CodeCell.prototype.clear_input = function () {
771 805
         this.code_mirror.setValue('');
772 806
     };
  807
+    
  808
+    CodeCell.prototype.flush_clear_timeout = function() {
  809
+        var output_div = this.element.find('div.output');
  810
+        if (this.clear_out_timeout){
  811
+            clearTimeout(this.clear_out_timeout);
  812
+            this.clear_out_timeout = null;
  813
+            this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
  814
+        };
  815
+    }
773 816
 
774 817
 
775 818
     CodeCell.prototype.collapse = function () {
5  IPython/frontend/html/notebook/static/js/utils.js
@@ -52,12 +52,13 @@ IPython.utils = (function (IPython) {
52 52
     // are set in the css file.
53 53
     function fixConsole(txt) {
54 54
         txt = xmlencode(txt);
55  
-        var re = /\033\[([\d;]*?)m/;
  55
+        var re = /\033\[([\dA-Fa-f;]*?)m/;
56 56
         var opened = false;
57 57
         var cmds = [];
58 58
         var opener = "";
59 59
         var closer = "";
60  
-        
  60
+        // \r does nothing, so shouldn't be included
  61
+        txt = txt.replace('\r', '');
61 62
         while (re.test(txt)) {
62 63
             var cmds = txt.match(re)[1].split(";");
63 64
             closer = opened?"</span>":"";
9  IPython/zmq/zmqshell.py
@@ -81,8 +81,15 @@ def publish(self, source, data, metadata=None):
81 81
         )
82 82
 
83 83
     def clear_output(self, stdout=True, stderr=True, other=True):
84  
-        self._flush_streams()
85 84
         content = dict(stdout=stdout, stderr=stderr, other=other)
  85
+        
  86
+        if stdout:
  87
+            print('\r', file=sys.stdout, end='')
  88
+        if stderr:
  89
+            print('\r', file=sys.stderr, end='')
  90
+        
  91
+        self._flush_streams()
  92
+        
86 93
         self.session.send(
87 94
             self.pub_socket, u'clear_output', content,
88 95
             parent=self.parent_header
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.