My preferred method would be to use a frame_change handler and an update method on a bool to turn it on off.
import bpy
import os
from bpy.props import BoolProperty
def remove_handler_function(handler, name):
''' Removes all functions with name starting with name '''
fns = [f for f in handler
if f.__name__.startswith(name)]
for f in fns:
handler.remove(f)
def screenshot(scene):
frame = scene.frame_current
in_frame = scene.frame_start <= frame <= scene.frame_end
if not in_frame:
return None
folder = scene.render.filepath
format = scene.render.image_settings.file_format.lower()
filepath = os.path.join(folder,"%04d.%s" % (frame, format))
print("render %d" % frame)
bpy.ops.screen.screenshot(
filepath=filepath,
check_existing=False)
def capture_screen(self, context):
# turn on the handler
handler = bpy.app.handlers.frame_change_post
if self.render_screencast:
handler.append(screenshot)
else:
remove_handler_function(handler, "screenshot")
bpy.types.Scene.render_screencast = BoolProperty(default=False,
name="Screen Shot Cast",
update=capture_screen)
#testing
bpy.context.scene.render_screencast = False
My testing shows that the operator will do as desired with
bpy.ops.screen.screenshot(
filepath=filepath,
check_existing=False)
for area in context.screen.areas:
area.tag_redraw()
but it will lock up blender until finished. Pretty sure there is no need to call scene.update() after scene.frame_set(f), and not sure how much this effects performance, if at all.
Another is a modal timer operator, which enables the chance to cancel. (The handler is much better). The link of @ideasman42 I mentioned is here How to handle modal operator events in a single loop? Which may make handling canceling easier.
class ModalTimerOperator(bpy.types.Operator):
"""Operator which runs its self from a timer"""
bl_idname = "wm.modal_timer_operator"
bl_label = "Modal Timer Operator"
wait = 10
_timer = None
renderframe = -1
def modal(self, context, event):
scene = context.scene
rendering = self.renderframe == scene.frame_current
folder = scene.render.filepath
format = scene.render.image_settings.file_format.lower()
if event.type in {'RIGHTMOUSE', 'ESC'} or scene.frame_current > scene.frame_end:
self.cancel(context)
return {'CANCELLED'}
if event.type == 'TIMER' and self.wait:
self.wait -= 1
return {'PASS_THROUGH'}
if rendering:
return {'PASS_THROUGH'}
else:
self.renderframe = scene.frame_current
print("render %d" % scene.frame_current)
self.report({'INFO'}, "Render: %4d" % scene.frame_current)
filepath = os.path.join(folder,"%04d.%s" % (scene.frame_current, format))
bpy.ops.screen.screenshot(
filepath=filepath,
check_existing=False)
scene.frame_set(scene.frame_current + 1)
self.wait = 10
for area in context.screen.areas:
area.tag_redraw()
return {'PASS_THROUGH'}
def execute(self, context):
scene = context.scene
wm = context.window_manager
self._timer = wm.event_timer_add(0.1, context.window)
wm.modal_handler_add(self)
scene.frame_set(scene.frame_start)
return {'RUNNING_MODAL'}