0

This is related to another question I posted, but is more specific (and hopefully gets more specific answers).

I am trying to display png images on an IPython notebook, and update the display as the png files are updated.

One possible solution is below. Unfortunately, my implementation does not update the file, and creates a new HTML block at the end of the old one. What I want instead, is to replace the old HTML block with the new one -i.e. replace the content only.

As example code, I have two notebooks. One notebook generates pngs and saves them in a figures directory.

import os
import glob
import time
import matplotlib.pyplot as plt
import numpy as np

for ix in range(20):
    N = 50
    x = np.random.rand(N)
    y = np.random.rand(N)
    colors = np.random.rand(N)
    area = np.pi * (15 * np.random.rand(N))**2 # 0 to 15 point radiuses

    fig = plt.figure(figsize=(8,8))
    plt.scatter(x, y, s=area, c=colors, alpha=0.5)
    fig.savefig(os.path.join("figures", "fig1.png"), bbox_inches="tight")
    plt.close(fig)
    time.sleep(3)

A second notebook shows the pngs It is supposed top show a single png which updates. Instead it stacks the same png on itself, every time the png is updated.

import os
import time
from IPython.html.widgets import interact, interactive, fixed
from IPython.html import widgets
from IPython.display import clear_output, display, HTML

def get_latest_file_ts(directory="figures", file_name="fig1.png", strip_directory=True):
    """
    Continuously check for modifications to the file file_name in directory. If file has been
    modified after touched_on, return the Unix timestamp of the modification time.
    :param directory: string / the directory where the file is
    :param file_name: string / the file name
    :param strip_directory: boolean /  if True, strip the directory part of the file name
    :return:
    """
    if strip_directory:
        fname = os.path.join(directory, file_name)
    else:
        fname = file_name
    try:
        return os.stat(fname).st_mtime
    except:
        print "FileNotFoundException: Could not find file %s" % fname
        return None


def check_if_modified_file(directory="figures", file_name="fig1.png",
                       touched_on=1420070400, sleep_time=1, strip_directory=True):
    """
    Continuously check for modifications to the file file_name in directory. If file has been
    modified after touched_on, return the Unix timestamp of the modification time.
    :param directory: string / the directory where the file is
    :param file_name: string / the file name
    :param touched_on: float / the Unix timestamp on which the file was last modified
    :param sleep_time: float / wait time between interactions
    :param strip_directory: boolean /  if True, strip the directory part of the file name
    :return:
    """
    if strip_directory:
        fname = os.path.join(directory, file_name)
    else:
        fname = file_name
    while True:
        try:
            latest_touch = os.stat(fname).st_mtime
            if latest_touch == touched_on:
                time.sleep(sleep_time)
            else:
                return latest_touch
        except:
            print "FileNotFoundException: Could not find %s" % fname
            return None

def show_figs(directory="figures", file_name="fig1.png"):
    s = """<figure>\n\t<img src="%s" alt="The figure" width="304" height="228">\n</figure>""" % os.path.join(directory, file_name)
    display(HTML(s))


timestamp = get_latest_file_ts(directory="figures", file_name="fig1.png", strip_directory=True)
show_figs(directory="figures", file_name="fig1.png")
cnt = 1
while True and cnt < 4:
    timestamp = check_if_modified_file(directory="figures", file_name="fig1.png", touched_on=timestamp, sleep_time=1, strip_directory=True)
    display(HTML(""))
    show_figs(directory="figures", file_name="fig1.png")
    time.sleep(1)
    cnt += 1

(as you can see I have added upper limits on the executions of both the generator and consumer loops)

Any help on how to make widgets update HTML content would be awesome.

Community
  • 1
  • 1
nikosd
  • 919
  • 3
  • 16
  • 26
  • Can you add code that executes? – User Jun 23 '15 at 06:36
  • @User: I added full working code to the question. – nikosd Jun 23 '15 at 17:47
  • Actually, you are not using widgets, but just the IPython display machinery to render your images. Nevertheless, you have already imported the ``clear_output`` function. Just give it a try, calling it right before you call ``show_figs``. On my machine it works fine. – Jakob Jun 24 '15 at 06:45
  • @Jakob. Thanks! I'm removing `widgets` from the title and the tags. You are right about `clear_output` part, now the images are not stacked on top of each other. However the image content still does not update, I'm always seeing the first image that was shown :-( – nikosd Jun 24 '15 at 16:23

1 Answers1

0

The problem that you only see the first image is very likely related to caching of the browser. To overcome this issue a simple solution is to add a varying query string to the image src as shown e.g. here. Thus your show_figs method could look like:

import time
def show_figs(directory="figures", file_name="fig1.png"):
    s = """<figure>\n\t<img src="{0}?{1}" alt="The figure" width="304" height="228">\n</figure>""".format(os.path.join(directory, file_name),time.time())
    display(HTML(s))

In combination with the clear_output function you should be able to get your updated image.

Community
  • 1
  • 1
Jakob
  • 19,815
  • 6
  • 75
  • 94