Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

clear_output improvements #1563

Merged
merged 4 commits into from

4 participants

Min RK Brian E. Granger Matthias Bussonnier Fernando Perez
Min RK
Owner

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) {
// We just eval the JS code, element appears in the local scope.
var element = $("<div/>").addClass("box_flex1 output_subarea");
e.append(element);
+ // Div for js shouldn't be drawn, as it will add empty height to the area.
Brian E. Granger Owner

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

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

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

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) {
CodeCell.prototype.clear_output = function (stdout, stderr, other) {
+ var that = this;
+ if (this.clear_out_timeout != null){
+ // fire previous pending clear *immediately*
+ clearTimeout(this.clear_out_timeout);
+ this.clear_out_timeout = null;
+ this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
+ }
+ // store flags for flushing the timeout
+ this._clear_stdout = stdout;
+ this._clear_stderr = stderr;
+ this._clear_other = other;
+ this.clear_out_timeout = setTimeout(function(){
+ // really clear timeout only after a short delay
+ // this reduces flicker in 'clear_output; print' cases
+ console.log("clear_out_timeout");
Brian E. Granger Owner

Let's remove the console.log before merging.

Min RK Owner
minrk added a note

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.

minrk added some commits
Min RK minrk [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 minrk hide output_area for js
prevents growing vertical space from adding empty output_areas.
e169ad6
Min RK minrk document initially hidden javascript container 24972e2
Min RK
Owner

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
Owner

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 9, 2012
  1. Min RK
  2. Min RK

    [notebook] clear_output is handled after a delay

    minrk authored
    This reduces flicker during common loops like:
    
    for step in stuff:
        clear_output()
        print something
    
    the timeout is flushed *immediately* on any subsequent output.
  3. Min RK

    hide output_area for js

    minrk authored
    prevents growing vertical space from adding empty output_areas.
  4. Min RK
This page is out of date. Refresh to see the latest.
23 IPython/core/display.py
View
@@ -17,6 +17,8 @@
# Imports
#-----------------------------------------------------------------------------
+from __future__ import print_function
+
from xml.dom import minidom
from .displaypub import (
@@ -365,6 +367,11 @@ def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
display function, it will result in the data being displayed
in the frontend. If the data is a URL, the data will first be
downloaded and then displayed.
+
+ In the Notebook, the containing element will be available as `element`,
+ and jQuery will be available. The output area starts hidden, so if
+ the js appends content to `element` that should be visible, then
+ it must call `container.show()` to unhide the area.
Parameters
----------
@@ -496,6 +503,16 @@ def clear_output(stdout=True, stderr=True, other=True):
(e.g. figures,images,HTML, any result of display()).
"""
from IPython.core.interactiveshell import InteractiveShell
- InteractiveShell.instance().display_pub.clear_output(
- stdout=stdout, stderr=stderr, other=other,
- )
+ if InteractiveShell.initialized():
+ InteractiveShell.instance().display_pub.clear_output(
+ stdout=stdout, stderr=stderr, other=other,
+ )
+ else:
+ from IPython.utils import io
+ if stdout:
+ print('\033[2K\r', file=io.stdout, end='')
+ io.stdout.flush()
+ if stderr:
+ print('\033[2K\r', file=io.stderr, end='')
+ io.stderr.flush()
+
15 IPython/core/displaypub.py
View
@@ -30,6 +30,7 @@
from __future__ import print_function
from IPython.config.configurable import Configurable
+from IPython.utils import io
#-----------------------------------------------------------------------------
# Main payload class
@@ -99,14 +100,20 @@ def publish(self, source, data, metadata=None):
arbitrary key, value pairs that frontends can use to interpret
the data.
"""
- from IPython.utils import io
+
# The default is to simply write the plain text data using io.stdout.
if data.has_key('text/plain'):
print(data['text/plain'], file=io.stdout)
- def clear_output(self, stdout=True, stderr=True, other=True):
- """Clear the output of the cell receiving output."""
- pass
+ def clear_output(self, stdout=True, stderr=True, other=True):
+ """Clear the output of the cell receiving output."""
+ if stdout:
+ print('\033[2K\r', file=io.stdout, end='')
+ io.stdout.flush()
+ if stderr:
+ print('\033[2K\r', file=io.stderr, end='')
+ io.stderr.flush()
+
def publish_display_data(source, data, metadata=None):
47 IPython/frontend/html/notebook/static/js/codecell.js
View
@@ -21,6 +21,7 @@ var IPython = (function (IPython) {
this.outputs = [];
this.collapsed = false;
this.tooltip_timeout = null;
+ this.clear_out_timeout = null;
IPython.Cell.apply(this, arguments);
};
@@ -566,6 +567,7 @@ var IPython = (function (IPython) {
CodeCell.prototype.append_output = function (json, dynamic) {
// If dynamic is true, javascript output will be eval'd.
this.expand();
+ this.flush_clear_timeout();
if (json.output_type === 'pyout') {
this.append_pyout(json, dynamic);
} else if (json.output_type === 'pyerr') {
@@ -621,6 +623,11 @@ var IPython = (function (IPython) {
if (json.stream == undefined){
json.stream = 'stdout';
}
+ if (!utils.fixConsole(json.text)){
+ // fixConsole gives nothing (empty string, \r, etc.)
+ // so don't append any elements, which might add undesirable space
+ return;
+ }
var subclass = "output_"+json.stream;
if (this.outputs.length > 0){
// have at least one output to consider
@@ -679,10 +686,14 @@ var IPython = (function (IPython) {
};
- CodeCell.prototype.append_javascript = function (js, e) {
+ CodeCell.prototype.append_javascript = function (js, container) {
// We just eval the JS code, element appears in the local scope.
var element = $("<div/>").addClass("box_flex1 output_subarea");
- e.append(element);
+ container.append(element);
+ // Div for js shouldn't be drawn, as it will add empty height to the area.
+ container.hide();
+ // If the Javascript appends content to `element` that should be drawn, then
+ // it must also call `container.show()`.
eval(js);
}
@@ -730,7 +741,30 @@ var IPython = (function (IPython) {
CodeCell.prototype.clear_output = function (stdout, stderr, other) {
+ var that = this;
+ if (this.clear_out_timeout != null){
+ // fire previous pending clear *immediately*
+ clearTimeout(this.clear_out_timeout);
+ this.clear_out_timeout = null;
+ this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
+ }
+ // store flags for flushing the timeout
+ this._clear_stdout = stdout;
+ this._clear_stderr = stderr;
+ this._clear_other = other;
+ this.clear_out_timeout = setTimeout(function(){
+ // really clear timeout only after a short delay
+ // this reduces flicker in 'clear_output; print' cases
+ that.clear_out_timeout = null;
+ that._clear_stdout = that._clear_stderr = that._clear_other = null;
+ that.clear_output_callback(stdout, stderr, other);
+ }, 500
+ );
+ };
+
+ CodeCell.prototype.clear_output_callback = function (stdout, stderr, other) {
var output_div = this.element.find("div.output");
+
if (stdout && stderr && other){
// clear all, no need for logic
output_div.html("");
@@ -770,6 +804,15 @@ var IPython = (function (IPython) {
CodeCell.prototype.clear_input = function () {
this.code_mirror.setValue('');
};
+
+ CodeCell.prototype.flush_clear_timeout = function() {
+ var output_div = this.element.find('div.output');
+ if (this.clear_out_timeout){
+ clearTimeout(this.clear_out_timeout);
+ this.clear_out_timeout = null;
+ this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other);
+ };
+ }
CodeCell.prototype.collapse = function () {
5 IPython/frontend/html/notebook/static/js/utils.js
View
@@ -52,12 +52,13 @@ IPython.utils = (function (IPython) {
// are set in the css file.
function fixConsole(txt) {
txt = xmlencode(txt);
- var re = /\033\[([\d;]*?)m/;
+ var re = /\033\[([\dA-Fa-f;]*?)m/;
var opened = false;
var cmds = [];
var opener = "";
var closer = "";
-
+ // \r does nothing, so shouldn't be included
+ txt = txt.replace('\r', '');
while (re.test(txt)) {
var cmds = txt.match(re)[1].split(";");
closer = opened?"</span>":"";
9 IPython/zmq/zmqshell.py
View
@@ -81,8 +81,15 @@ def publish(self, source, data, metadata=None):
)
def clear_output(self, stdout=True, stderr=True, other=True):
- self._flush_streams()
content = dict(stdout=stdout, stderr=stderr, other=other)
+
+ if stdout:
+ print('\r', file=sys.stdout, end='')
+ if stderr:
+ print('\r', file=sys.stderr, end='')
+
+ self._flush_streams()
+
self.session.send(
self.pub_socket, u'clear_output', content,
parent=self.parent_header
Something went wrong with that request. Please try again.