Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

clear_output improvements #1563

Merged
merged 4 commits into from

4 participants

@minrk
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.
@ellisonbg 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?

@minrk 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).

@ellisonbg 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.

@minrk 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");
@ellisonbg Owner

Let's remove the console.log before merging.

@minrk Owner
minrk added a note

removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@ellisonbg
Owner

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

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

This is extremely cool!

@Carreau
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. :-)

@fperez
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!!

@fperez fperez merged commit d66ece6 into ipython:master
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Apr 10, 2012
  1. @minrk
  2. @minrk

    [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. @minrk

    hide output_area for js

    minrk authored
    prevents growing vertical space from adding empty output_areas.
  4. @minrk
This page is out of date. Refresh to see the latest.
View
23 IPython/core/display.py
@@ -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()
+
View
15 IPython/core/displaypub.py
@@ -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):
View
47 IPython/frontend/html/notebook/static/js/codecell.js
@@ -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 () {
View
5 IPython/frontend/html/notebook/static/js/utils.js
@@ -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>":"";
View
9 IPython/zmq/zmqshell.py
@@ -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.