Image from scene
I made a cool animation with scene. Is it possible to get a snapshot of the scene from within the program? Or should I rewrite in PIL. The code is here if that helps. It's loosely based on the "particles" example script.
from scene import * from math import sin, cos def polar2cart(r,theta): x = r*cos(theta) y = r*sin(theta) return x, y class Particle(): def __init__(self, location): self.location = location self.alpha=1 class ParticleSwirl(Scene): def setup(self): self.particles = set() self.theta=0 def draw(self): background(0,0,0) global Particle self.theta += 1 if self.theta % 2 == 0: self.particles.add(Particle((0,self.theta))) dead = set() for particle in self.particles: a=particle.alpha r,g,b = a,255*a,255*a fill(r,g,b,a) r, t = tuple(particle.location) x, y = polar2cart(r, t) drawx, drawy = x+self.size.w/2, y+self.size.h/2 ellipse(drawx, drawy, 15,15) particle.alpha -= 0.006 particle.location = (r+2,t+0.01) if particle.alpha <= 0: dead.add(particle) self.particles -= dead run(ParticleSwirl())
Why doesn't this work:
class SnapshotView(ui.View): def __init__(self): self.snapshots =  def draw(self): self.snapshots.append(self.draw_snapshot())
If I close the view and call view.snapshots, I get [None]
@Webmaster4o, I looked up here in the forum. I found something, but modified it to work in the class. Not sure it's exactly what you need, but will give you the next steps.
# coding: utf-8 import ui import console class SnapshotView(ui.View): def __init__(self): self.frame = (0,0,540,576) self.snapshots =  lb = ui.Label(frame = (0,0, self.width, 44)) lb.text = 'Testing' lb.alignment = ui.ALIGN_CENTER self.add_subview(lb) self.background_color = 'white' #def draw(self): #self.screenshot_action() # copied from @omz post, made some changes to # work in your class def take_screenshot(self): with ui.ImageContext(self.width, self.height) as c: self.draw_snapshot() self.snapshots.append(c.get_image()) if __name__ == '__main__': ssv = SnapshotView() ssv.present('sheet') ssv.take_screenshot() ssv.snapshots.show()
Ok, turns out exact same post @ccc pointed to.
Ok, got it working. Turns out with a scene view, draw() is not automatically called in the main view every time it changes. I can't subclass a scene view, so automatic snapshots every time it updates won't work. Adding
while 1: view.take_snapshot().show()
leads to a crash pretty quickly, I'm assuming from large memory consumption. I was, however, able to capture this snapshot successfully:
If I want to compile a recording of the whole animation, I guess I'll use PIL to draw frames, as I have in previous animations.
@Webmaster4o , I am not sure what you have done in the past. But rather than save them in a list, could you just write a method to save them to disk. If it's not fast enough realtime, maybe write to disk when your list has 60 or whatever items in it. Then flush it.
If you implemented an approach like that, maybe it would be better to create a list of a fixed length rather than calling append n the list everytime. I am not sure, but every cycle is going to count I guess.
show() shows it in the console. IIRC that method returns a ui.Image... check the docs, there is a way to save, or else convert to a PIL image and save.
I know, what save does, and I know how to convert it to PIL. That code was just simple code for me to demonstrate that the snapshots were being captured. If you're wondering, you can convert to PIL like so:
from io import BytesIO from PIL import Image i = view.take_snapshot() pil_image = Image.open(BytesIO(i.to_png()))
I think it's sort of strange that
SceneViewdoesn't inherit from ui.View* and can't be subclassed. Recently we've been able to add custom attributes to all ui.View children like buttons, but not
SceneViews. Would it be easy to change SceneView to fit into UI better? How is it recognized as something that can be added as a subview if it doesn't inherit?
*I'm assuming it doesn't inherit, I think
sceneis missing from the Standard Library folder.
SceneViewdoes inherit from
>>> issubclass(scene.SceneView, ui.View) True
Adding custom attributes should also be possible; this was broken in one of the previous builds though.
Why can't I subclass it, then?
class myclass(scene.SceneView): def myfunc(self): pass
TypeError: Error when calling the metaclass bases type '_scene.SceneView' is not an acceptable base type
You can't subclass
ui.Buttoneither. Why would you want to do that?
Oh. So I add attributes like
SceneView().arbitrary_attribute = 5