Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clear_output: Animation & widget related changes. #4229

Merged
merged 8 commits into from
Sep 23, 2013
Merged
32 changes: 8 additions & 24 deletions IPython/core/display.py
Original file line number Diff line number Diff line change
Expand Up @@ -657,35 +657,19 @@ def _find_ext(self, s):
return unicode(s.split('.')[-1].lower())


def clear_output(stdout=True, stderr=True, other=True):
def clear_output(wait=False):
"""Clear the output of the current cell receiving output.

Optionally, each of stdout/stderr or other non-stream data (e.g. anything
produced by display()) can be excluded from the clear event.

By default, everything is cleared.

Parameters
----------
stdout : bool [default: True]
Whether to clear stdout.
stderr : bool [default: True]
Whether to clear stderr.
other : bool [default: True]
Whether to clear everything else that is not stdout/stderr
(e.g. figures,images,HTML, any result of display()).
"""
wait : bool [default: false]
Wait to clear the output until new output is available to replace it."""
from IPython.core.interactiveshell import InteractiveShell
if InteractiveShell.initialized():
InteractiveShell.instance().display_pub.clear_output(
stdout=stdout, stderr=stderr, other=other,
)
InteractiveShell.instance().display_pub.clear_output(wait)
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()

print('\033[2K\r', file=io.stdout, end='')
io.stdout.flush()
print('\033[2K\r', file=io.stderr, end='')
io.stderr.flush()
16 changes: 7 additions & 9 deletions IPython/core/displaypub.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,12 @@ def publish(self, source, data, metadata=None):
if 'text/plain' in data:
print(data['text/plain'], file=io.stdout)

def clear_output(self, stdout=True, stderr=True, other=True):
def clear_output(self, wait=False):
"""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()
print('\033[2K\r', file=io.stdout, end='')
io.stdout.flush()
print('\033[2K\r', file=io.stderr, end='')
io.stderr.flush()


class CapturingDisplayPublisher(DisplayPublisher):
Expand All @@ -125,8 +123,8 @@ class CapturingDisplayPublisher(DisplayPublisher):
def publish(self, source, data, metadata=None):
self.outputs.append((source, data, metadata))

def clear_output(self, stdout=True, stderr=True, other=True):
super(CapturingDisplayPublisher, self).clear_output(stdout, stderr, other)
def clear_output(self, wait=False):
super(CapturingDisplayPublisher, self).clear_output(wait)
if other:
# empty the list, *do not* reassign a new list
del self.outputs[:]
Expand Down
6 changes: 3 additions & 3 deletions IPython/html/static/notebook/js/codecell.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ var IPython = (function (IPython) {
* @method execute
*/
CodeCell.prototype.execute = function () {
this.output_area.clear_output(true, true, true);
this.output_area.clear_output();
this.set_input_prompt('*');
this.element.addClass("running");
var callbacks = {
Expand Down Expand Up @@ -386,8 +386,8 @@ var IPython = (function (IPython) {
};


CodeCell.prototype.clear_output = function (stdout, stderr, other) {
this.output_area.clear_output(stdout, stderr, other);
CodeCell.prototype.clear_output = function (wait) {
this.output_area.clear_output(wait);
};


Expand Down
2 changes: 1 addition & 1 deletion IPython/html/static/notebook/js/notebook.js
Original file line number Diff line number Diff line change
Expand Up @@ -1339,7 +1339,7 @@ var IPython = (function (IPython) {
var cells = this.get_cells();
for (var i=0; i<ncells; i++) {
if (cells[i] instanceof IPython.CodeCell) {
cells[i].clear_output(true,true,true);
cells[i].clear_output();
// Make all In[] prompts blank, as well
// TODO: make this configurable (via checkbox?)
cells[i].set_input_prompt();
Expand Down
95 changes: 27 additions & 68 deletions IPython/html/static/notebook/js/outputarea.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var IPython = (function (IPython) {
this.outputs = [];
this.collapsed = false;
this.scrolled = false;
this.clear_out_timeout = null;
this.clear_queued = null;
if (prompt_area === undefined) {
this.prompt_area = true;
} else {
Expand Down Expand Up @@ -289,7 +289,12 @@ var IPython = (function (IPython) {
OutputArea.prototype.append_output = function (json, dynamic) {
// If dynamic is true, javascript output will be eval'd.
this.expand();
this.flush_clear_timeout();

// Clear the output if clear is queued.
if (this.clear_queued) {
this.clear_output(false);
}

if (json.output_type === 'pyout') {
this.append_pyout(json, dynamic);
} else if (json.output_type === 'pyerr') {
Expand All @@ -300,6 +305,7 @@ var IPython = (function (IPython) {
this.append_stream(json);
}
this.outputs.push(json);
this.element.height('auto');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this auto height the right behavior, or was that a remnant from the previous implementation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It still measures/sets the height if wait=True, this line is supposed to reset that. So yes & yes.

If parsing javascript threadlocks the browser's render function, this is pointless since it wont redraw in between clear_output and the append operation. Do you have insight to whether or not this is the case?

var that = this;
setTimeout(function(){that.element.trigger('resize');}, 100);
};
Expand Down Expand Up @@ -553,7 +559,6 @@ var IPython = (function (IPython) {
OutputArea.prototype.append_raw_input = function (content) {
var that = this;
this.expand();
this.flush_clear_timeout();
var area = this.create_output_area();

// disable any other raw_inputs, if they are left around
Expand Down Expand Up @@ -606,81 +611,35 @@ var IPython = (function (IPython) {


OutputArea.prototype.handle_clear_output = function (content) {
this.clear_output(content.stdout, content.stderr, content.other);
this.clear_output(content.wait);
};


OutputArea.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
);
};
OutputArea.prototype.clear_output = function(wait) {
if (wait) {

// If a clear is queued, clear before adding another to the queue.
if (this.clear_queued) {
this.clear_output(false);
};

OutputArea.prototype.clear_output_callback = function (stdout, stderr, other) {
var output_div = this.element;
this.clear_queued = true;
} else {

if (stdout && stderr && other){
// Fix the output div's height if the clear_output is waiting for
// new output (it is being used in an animation).
if (this.clear_queued) {
var height = this.element.height();
this.element.height(height);
this.clear_queued = false;
}

// clear all, no need for logic
output_div.html("");
this.element.html("");
this.outputs = [];
this.unscroll_area();
return;
}
// remove html output
// each output_subarea that has an identifying class is in an output_area
// which is the element to be removed.
if (stdout) {
output_div.find("div.output_stdout").parent().remove();
}
if (stderr) {
output_div.find("div.output_stderr").parent().remove();
}
if (other) {
output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
}
this.unscroll_area();

// remove cleared outputs from JSON list:
for (var i = this.outputs.length - 1; i >= 0; i--) {
var out = this.outputs[i];
var output_type = out.output_type;
if (output_type == "display_data" && other) {
this.outputs.splice(i,1);
} else if (output_type == "stream") {
if (stdout && out.stream == "stdout") {
this.outputs.splice(i,1);
} else if (stderr && out.stream == "stderr") {
this.outputs.splice(i,1);
}
}
}
};


OutputArea.prototype.flush_clear_timeout = function() {
var output_div = this.element;
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);
}
};
};


Expand Down
13 changes: 5 additions & 8 deletions IPython/kernel/zmq/zmqshell.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,11 @@ def publish(self, source, data, metadata=None):
parent=self.parent_header, ident=self.topic,
)

def clear_output(self, stdout=True, stderr=True, other=True):
content = dict(stdout=stdout, stderr=stderr, other=other)

if stdout:
print('\r', file=sys.stdout, end='')
if stderr:
print('\r', file=sys.stderr, end='')

def clear_output(self, wait=False):
content = dict(wait=wait)

print('\r', file=sys.stdout, end='')
print('\r', file=sys.stderr, end='')
self._flush_streams()

self.session.send(
Expand Down
2 changes: 1 addition & 1 deletion IPython/parallel/client/asyncresult.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ def wait_interactive(self, interval=1., timeout=-1):
tic = time.time()
while not self.ready() and (timeout < 0 or time.time() - tic <= timeout):
self.wait(interval)
clear_output()
clear_output(wait=True)
print("%4i/%i tasks finished after %4i s" % (self.progress, N, self.elapsed), end="")
sys.stdout.flush()
print()
Expand Down
14 changes: 14 additions & 0 deletions docs/source/development/messaging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -984,6 +984,20 @@ Message type: ``status``::
execution_state : ('busy', 'idle', 'starting')
}

Clear output
------------

This message type is used to clear the output that is visible on the frontend.

Message type: ``clear_output``::

content = {

# Wait to clear the output until new output is available. Clears the
# existing output immediately before the new output is displayed.
# Useful for creating simple animations with minimal flickering.
'wait' : bool,
}

Messages on the stdin ROUTER/DEALER sockets
===========================================
Expand Down
9 changes: 9 additions & 0 deletions docs/source/whatsnew/pr/clear_output.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
clear_output changes
--------------------

* There is no longer a 500ms delay when calling ``clear_output``.
* The ability to clear stderr and stdout individually was removed.
* A new ``wait`` flag that prevents ``clear_output`` from being executed until new
output is available. This eliminates animation flickering by allowing the
user to double buffer the output.
* The output div height is remembered when the ``wait=True`` flag is used.
6 changes: 3 additions & 3 deletions examples/notebooks/Animations Using clear_output.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"source": [
"Sometimes you want to clear the output area in the middle of a calculation. This can be useful for doing simple animations. In terminals, there is the carriage-return (`'\\r'`) for overwriting a single line, but the notebook frontend can clear the whole output area, not just a single line.\n",
"\n",
"To clear output in the Notebook you can use the `clear_output` function."
"To clear output in the Notebook you can use the `clear_output()` function. If you are clearing the output every frame of an animation, calling `clear_output()` will create noticeable flickering. You can use `clear_output(wait=True)` to add the *clear_output* call to a queue. When data becomes available to replace the existing output, the *clear_output* will be called immediately before the new data is added. This avoids the flickering by not rendering the cleared output to the screen."
]
},
{
Expand Down Expand Up @@ -58,7 +58,7 @@
"from IPython.display import display, clear_output\n",
"for i in range(10):\n",
" time.sleep(0.25)\n",
" clear_output()\n",
" clear_output(wait=True)\n",
" print(i)\n",
" sys.stdout.flush()"
],
Expand Down Expand Up @@ -166,7 +166,7 @@
"for n in range(1,10):\n",
" time.sleep(1)\n",
" ax.plot(x, jn(x,n))\n",
" clear_output()\n",
" clear_output(wait=True)\n",
" display(f)\n",
"\n",
"# close the figure at the end, so we don't get a duplicate\n",
Expand Down
4 changes: 2 additions & 2 deletions examples/parallel/InteractiveMPI-publish-data.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@
" # We clear the notebook output before plotting this if in-place \n",
" # plot updating is requested\n",
" if in_place:\n",
" clear_output()\n",
" clear_output(wait=True)\n",
" display(fig)\n",
" \n",
" return fig"
Expand Down Expand Up @@ -333,7 +333,7 @@
" msg = 'Simulation completed!'\n",
" tmon = dt.datetime.now() - t0\n",
" if plots_in_place and fig is not None:\n",
" clear_output()\n",
" clear_output(wait=True)\n",
" plt.close('all')\n",
" display(fig)\n",
" print msg\n",
Expand Down
4 changes: 2 additions & 2 deletions examples/parallel/InteractiveMPI.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@
" plt.axis('off')\n",
" # We clear the notebook output before plotting this if in-place plot updating is requested\n",
" if in_place:\n",
" clear_output()\n",
" clear_output(wait=True)\n",
" display(fig)\n",
" return fig"
],
Expand Down Expand Up @@ -377,7 +377,7 @@
" msg = 'Simulation completed!'\n",
" tmon = dt.datetime.now() - t0\n",
" if plots_in_place and fig is not None:\n",
" clear_output()\n",
" clear_output(wait=True)\n",
" plt.close('all')\n",
" display(fig)\n",
" print msg\n",
Expand Down
2 changes: 1 addition & 1 deletion examples/widgets/directview/directview.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ DirectViewWidget.prototype.set_kernel = function (kernel) {


DirectViewWidget.prototype.execute = function () {
this.output_area.clear_output(true, true, true);
this.output_area.clear_output();
this.element.addClass("running");
var callbacks = {
'execute_reply': $.proxy(this._handle_execute_reply, this),
Expand Down