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
class ScriptThread (threading.Thread):
    def init(self, threadID, name):
        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()
                if bpy.context.scene.frame_current >= bpy.context.scene.frame_end:
                    is_running = False
                    os.system('mkdir render')
                    os.system('import -window root '+path)                      # linux
                    os.system('nircmd.exe savescreenshot '+path)                # win

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

  • $\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, 2017 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$ Oct 1, 2017 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, 2017 at 10:34

1 Answer 1


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'}:
            return {'CANCELLED'}       

        if event.type == 'TIMER':
            if self.count == 0:
                self.frame += 1
                self.count = 1
            elif self.count < self.delay:
                self.count += 1
                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
        wm = context.window_manager
        # make the timer tick every second
        self._timer = wm.event_timer_add(1, context.window)
        return {'RUNNING_MODAL'}

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

def register():

def unregister():

if __name__ == "__main__":

    # test call

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


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .