Closed Weihnachtshase closed 3 years ago
Seems to work when I try locally. It does not print anything due to the callback (you could redirect the print statement to an output widget for example, see this).
The only thing I see is that it saves a whatever.txt.npy
file. If you want to save to an image file you could still use canvas.to_file('my-image.png')
.
Closing as answered, but feel free to continue the discussion/reopen if you think otherwise.
Thank you for your quick response!
Yes, I restarted the kernel and now it also creates the file for me (even though I also restarted the kernel earlier). The issue is therefore also closed for me.
The only problem that I'm currently facing is, that the same behaviour does not work for me, when the canvas is a property of another object. Do you maybe have a guess why that could be?
Here is a stripped down version of my problem:
from ipycanvas import Canvas
import numpy as np
def get_array(*args, **kwargs):
print("here")
arr = canvas.get_image_data()
print(arr)
np.save("whatever.txt", arr)
# Do something with arr
class Test:
def __init__(self):
self.canvas = None
test = Test()
test.canvas = Canvas(width=200, height=200, sync_image_data=True)
display(test.canvas)
test.canvas.observe(get_array, 'image_data')
test.canvas.stroke_line(50, 10, 150, 10)
I cannot reproduce. It seems the callback is properly called for me.
Using this code:
from ipycanvas import Canvas
from ipywidgets import Output
import numpy as np
out = Output()
@out.capture()
def get_array(*args, **kwargs):
print("saving")
arr = canvas.get_image_data()
class Test:
def __init__(self):
self.canvas = None
display(out)
test = Test()
test.canvas = Canvas(width=200, height=200, sync_image_data=True)
display(test.canvas)
test.canvas.observe(get_array, 'image_data')
test.canvas.stroke_line(50, 10, 150, 10)
It always shows saving
, so the callback is properly called
Ok. Thank you for your help, I appreciate this very much!
Sure! How do you check if it works or not? The Jupyter Notebook file viewer does not seem to refresh at a necessary rate to see the whatever.txt.npy
file appear
So far I refresh manually every time. But what I'm trying to achieve is still not fully working, therefore manually refreshing every now and then is so far not a problem for me.
Ok, maybe one last question then I'll stop bothering you.
from ipycanvas import Canvas, hold_canvas
import numpy as np
import time
class Test:
def __init__(self):
self.first = True
self.images = []
self.canvas = None
def get_array(self, *args, **kwargs):
arr = self.canvas.get_image_data()
self.images.append(arr)
def render(self, x):
if self.first:
self.canvas.observe(self.get_array, 'image_data')
self.first = False
with hold_canvas(self.canvas):
self.canvas.stroke_line(50, 10+(x*5), 150, 10+(x*5))
test = Test()
canvas = Canvas(width=200, height=200, sync_image_data=True)
display(canvas)
test.canvas = canvas
for i in range(10):
test.render(i)
time.sleep(1)
This code shows a little animation of 10 lines that are displayed one after another. I would like to take an image (as a numpy array) after each individual image is rendered. However the callback function (get_array) is only called twice and not 10 times. Do you might know what the reason for that is?
Indeed... That's an annoying thing that happened to me when trying to make a prototype for the GIF animation creation.
I think it's because ipywidgets optimizes the communication and doesn't crowd the server with too many updates. It sees that the image_data
property changes but it doesn't nudge the server as often. But I might be wrong about this.
It might also be due to your for-loop which stuck the Python kernel. What happens if you run this for-loop in a Python thread? (see the game of life example for a threading example).
This was actually the problem :)
This code works now for me:
from ipycanvas import Canvas, hold_canvas
from threading import Thread
import numpy as np
import time
class Test:
def __init__(self):
self.first = True
self.images = []
self.canvas = None
def get_array(self, *args, **kwargs):
arr = self.canvas.get_image_data()
self.images.append(arr)
def render(self, x):
if self.first:
self.canvas.observe(self.get_array, 'image_data')
self.first = False
self.canvas.stroke_line(50, 10+(x*5), 150, 10+(x*5))
class MyThread(Thread):
def __init__(self, test):
super(MyThread, self).__init__()
self.test = test
def run(self):
for i in range(10):
self.test.render(i)
time.sleep(1)
test = Test()
canvas = Canvas(width=200, height=200, sync_image_data=True)
display(canvas)
test.canvas = canvas
MyThread(test).start()
Hope that somehow helps you in the future with your GIF animation creation.
This is huge ahah. I haven't thought of threading when trying the first time.
Thanks a lot for trying! This will definitely help. We would need to think of a way to expose this as a nice and friendly user-API.
I have tried to get an image of the canvas from the same cell as described in the documentation. I tried the following code:
Unfortunately nothing happens (except the canvas being drawn). Can you confirm that this works for you?
PS: What I am trying to achieve is creating a gif/video from an animation, which is still an open issue as far as I have seen. But I try it "by hand".