1
$\begingroup$

I am using this script to render animations because of this issue. It was doing it's job in older blender but now it works well only until I switch viewport shade to rendered. Then after few frames saved I get:

AttributeError: Writing to ID classes in this context is not allowed: Scene, Scene datablock, error setting Scene.frame_current

Btw I would like also to ask is there a way to capture viewport content or rendered border only?

render_time = 3
import bpy, os, time, threading
bpy.context.scene.frame_current=bpy.context.scene.frame_start
class ScriptThread (threading.Thread):
    def init(self, threadID, name):
        threading.Thread.init(self)
        self.threadID = threadID
        self.name = name
    def run(self):
        is_running = True
        tick = time.time()
        while is_running is True:
            if time.time() >= tick+render_time:
                tick = time.time()
                time.sleep(render_time)
                if bpy.context.scene.frame_current >= bpy.context.scene.frame_end:
                    is_running = False
                else:
                    os.system('mkdir render')
                    path='render/'+str(bpy.context.scene.frame_current)+'.jpg'
                    os.system('import -window root '+path)                      # linux
                    os.system('nircmd.exe savescreenshot '+path)                # win
                    bpy.context.scene.frame_current+=1
            else:
                continue

thread = ScriptThread(1, "thread") thread.start()

$\endgroup$
  • $\begingroup$ Does using bpy.ops.screen.screenshot(filepath=somefilepath) give you the result you are after? Recommend using scene.frame_set(frame) to set frame. $\endgroup$ – batFINGER Oct 1 '17 at 9:33
  • $\begingroup$ Blender screenshot indeed works better than system ones (captures only blender window) but inside my thread function gives RuntimeError: Operator bpy.ops.screen.screenshot.poll() failed, context is incorrect. scene.frame_set(frame) fixed the AttributeError but does not restarts viewport render, it keeps sampling the first frame. $\endgroup$ – combine multiple Oct 1 '17 at 10:11
  • 1
    $\begingroup$ yeah wouldn't use threading at all... context and threading don't in general mix too well. A modal timer operator that "sleeps" the render time could be one way to go. $\endgroup$ – batFINGER Oct 1 '17 at 10:34
0
$\begingroup$

Here is an approach using the modal timer operator template. The modal timer will wait 30 seconds, take screenshot bpy.ops.screen.screenshot() (increment frame). Stop it with Esc or with a right click. Change to suit.

import bpy

class ModalTimerOperator(bpy.types.Operator):
    """Operator which runs its self from a timer"""
    bl_idname = "wm.modal_timer_operator"
    bl_label = "Modal Timer Operator"

    _timer = None
    # make delay a scene property can be changed in UI
    delay = 30 # wait 30 between screenshots
    count = 0
    frame = 1

    def modal(self, context, event):
        scene = context.scene
        if event.type in {'RIGHTMOUSE', 'ESC'}:
            self.cancel(context)
            return {'CANCELLED'}       

        if event.type == 'TIMER':
            if self.count == 0:
                scene.frame_set(self.frame)
                self.frame += 1
                self.count = 1
            elif self.count < self.delay:
                self.count += 1
            else:
                self.count = 0
                print("rendering /tmp/%04d.png" % scene.frame_current)
                bpy.ops.screen.screenshot(filepath="/tmp/%04d.png" % scene.frame_current)
        return {'PASS_THROUGH'}

    def execute(self, context):
        scene = context.scene
        self.frame = scene.frame_current
        # for all in range
        #scene.frame_set(scene.frame_start)
        wm = context.window_manager
        # make the timer tick every second
        self._timer = wm.event_timer_add(1, context.window)
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        wm = context.window_manager
        wm.event_timer_remove(self._timer)

def register():
    bpy.utils.register_class(ModalTimerOperator)

def unregister():
    bpy.utils.unregister_class(ModalTimerOperator)

if __name__ == "__main__":
    register()

    # test call
    bpy.ops.wm.modal_timer_operator()

Pretty much emulating bpy.ops.screen.screencast(...).

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.