jupyter-widgets-contrib / ipycanvas

Interactive Canvas in Jupyter
https://ipycanvas.readthedocs.io/en/latest/
BSD 3-Clause "New" or "Revised" License
690 stars 64 forks source link

Observe callback requires Output widget #103

Closed DominikKoller closed 3 years ago

DominikKoller commented 4 years ago

I ran into a problem when following this documentation on how to sync image data to python, from https://ipycanvas.readthedocs.io/en/latest/retrieve_images.html

from ipycanvas import Canvas

canvas = Canvas(width=200, height=200, sync_image_data=True)

# Perform some drawings...

def save_to_file(*args, **kwargs):
    canvas.to_file('my_file.png')

# Listen to changes on the ``image_data`` trait and call ``save_to_file`` when it changes.
canvas.observe(save_to_file, 'image_data')

Specifically, the callback save_to_file was never called. I found this issue on the ipywidgets repo: https://github.com/jupyter-widgets/ipywidgets/issues/2148

They suggest you need to use an Output widget in some situations. I changed the code to the following, as by their suggestion:

from ipywidgets import Output
from IPython.display import display
from ipycanvas import Canvas

canvas = Canvas(width=200, height=200, sync_image_data=True)

out = Output()
display(out)

# draw something
canvas.fill_rect(0,0, 10, 10)

@out.capture()
def callback(*args, **kwargs):
    canvas.to_file('my_file.png')

canvas.observe(callback, 'image_data')

I suggest you add that to the documentation. That's all :)

I'm using Jupyter Notebook 6.0.3, not using Jupyter Lab

btw this is such an awesome library, I've been building interactive simulations, so smooth with this lib! Will share some stuff soon hopefully

martinRenou commented 4 years ago

Hi @DominikKoller

btw this is such an awesome library, I've been building interactive simulations, so smooth with this lib! Will share some stuff soon hopefully

Thanks for the kind words! I can't wait to see what you come up with :)

I might be wrong, but I feel like your issue has nothing to do with the Output widget, but it has to do with the ipycanvas documentation which is badly written :D

Actually your code should be:

from ipycanvas import Canvas

canvas = Canvas(width=200, height=200, sync_image_data=True)

def save_to_file(*args, **kwargs):
    canvas.to_file('my_file.png')

# Listen to changes on the ``image_data`` trait and call ``save_to_file`` when it changes.
canvas.observe(save_to_file, 'image_data')

# Perform some drawings AFTER SETTING UP THE OBSERVE

So you perform the drawings only after setting up the observer (otherwise the observer might not be triggered because the drawings already happened). I am a bit confused why the Output widget fixed your case, it might be just that the Output widget slowed down the execution of the canvas and you had time to setup the observer before the canvas finished drawing.

Side Note: Just to give you some more information on what the Output widget is: The Output widget is simply a widget in which you display all your print and IPython.display calls by capturing them, instead of displaying them under the cell. Sometimes prints are not displayed in Jupyter Notebooks (typically when they are executed in callbacks), so the Output widget is very convenient in this case because it catches all the prints calls and ensures that you can display them somewhere.