2

I am attempting to create some simple asynchronously-executing animations based on ipythonblocks and I am trying to update the cell output area using clear_output() followed by a grid.show().

For text output the basis of the technique is discussed in Per-cell output for threaded IPython Notebooks so my simplistic assumption was to use the same method to isolate HTML output. Since I want to repeatedly replace a grid with its updated HTML version I try to use clear_output() to ensure that only one copy of the grid is displayed.

I verified that this proposed technique works for textual output with the following cells. First the context manager.

import sys
from contextlib import contextmanager
import threading

stdout_lock = threading.Lock()
n = 0

@contextmanager
def set_stdout_parent(parent):
    """a context manager for setting a particular parent for sys.stdout
    (i.e. redirecting output to a specific cell). The parent determines
    the destination cell of output
    """
    global n
    save_parent = sys.stdout.parent_header
    # we need a lock, so that other threads don't snatch control
    # while we have set a temporary parent
    with stdout_lock:
        sys.stdout.parent_header = parent
        try:
            yield
        finally:
            # the flush is important, because that's when the parent_header actually has its effect
            n += 1; print("Flushing", n)
            sys.stdout.flush()
            sys.stdout.parent_header = save_parent

Then the test code

import threading
import time
class timedThread(threading.Thread):
    def run(self):
        # record the parent (uncluding the stdout cell) when the thread starts
        thread_parent = sys.stdout.parent_header
        for i in range(3):
            time.sleep(2)
            # then ensure that the parent is the same as when the thread started
            # every time we print
            with set_stdout_parent(thread_parent):
                print(i)

timedThread().start()

This provided the output

0
Flushing 1
1
Flushing 2
2
Flushing 3

So I modified the code to clear the cell between cycles.

import IPython.core.display

class clearingTimedThread(threading.Thread):
    def run(self):
        # record the parent (uncluding the stdout cell) when the thread starts
        thread_parent = sys.stdout.parent_header
        for i in range(3):
            time.sleep(2)
            # then ensure that the parent is the same as when the thread started
            # every time we print
            with set_stdout_parent(thread_parent):
                IPython.core.display.clear_output()
                print(i)

clearingTimedThread().start()

As expected the output area of the cell was repeatedly cleared, and ended up reading

2
Flushing 6

I therefore thought I was on safe ground in using the same technique to clear a cell's output area when using ipythonblocks. Alas no. This code

from ipythonblocks import BlockGrid

w = 10
h = 10

class clearingBlockThread(threading.Thread):
    def run(self):
        grid = BlockGrid(w, h)
        # record the parent (uncluding the stdout cell) when the thread starts
        thread_parent = sys.stdout.parent_header
        for i in range(10):
            # then ensure that the parent is the same as when the thread started
            # every time we print
            with set_stdout_parent(thread_parent):
                block = grid[i, i]
                block.green = 255
                IPython.core.display.clear_output(other=True)
                grid.show()
            time.sleep(0.2)

clearingBlockThread().start()

does indeed produce the desired end state (a black matrix with a green diagonal) but the intermediate steps don't appear in the cell's output area. To complicate things slightly (?) this example is running on Python 3. In checking before posting here I discover that the expected behavior (a simple animation) does in fact occur under Python 2.7. Hence I though to ask whether this is an issue I need to report.

Community
  • 1
  • 1
holdenweb
  • 33,305
  • 7
  • 57
  • 77

0 Answers0