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

1 Answer 1

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$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.