0
$\begingroup$

I am currently using blender 2.8 and I am trying to make a scripted animation where I move a set of cubes from one end to the other on the x-axis with delays in between animations. This isn't really a real animation because I am not using the timeline. I am trying to create a real-time demo of a personal project. If you look below I want to make the collection of cubes to travel to the right in delayed steps. My current script is like this:

import time

collection = bpy.data.collections["layer_1_filters"].objects # I grouped the 5x5 cube grid to the collection called "layer_1_filters"

for steps in range(23): #steps to the right 23 times

    for cube in collection: # loop each cube

        cube.location[0] = cube.location[0] + 3 # move all the cubes to the right by 3 on the x-axis

    time.sleep(0.3) # pause 0.3 seconds before start moving right again.
                    #THIS HANGS and BREAKS BLENDER!

If I use time.sleep to delay the actions blender hangs then crashes, if I don't use some sort of delay the cubes would just immediately teleport to the end. How can I slove this?

enter image description here

$\endgroup$
1
$\begingroup$

Modal Timer operator

For the most part don't use time.sleep() in blender, as you have seen it doesn't work as expected.

Modify the template text editor > templates > python > operator modal timer

This line

self._timer = wm.event_timer_add(0.3, window=context.window)

creates a timer that fires a 'TIMER' event every 0.3 seconds.

Additionally I've added a count class property to only move the object that many times.

For example sake, only moves the context.object edit 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
    count = 0

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

        if event.type == 'TIMER' and self.count < 24:
            context.object.location.x += 3
            self.count += 1


        return {'PASS_THROUGH'}

    def execute(self, context):
        self.count = 0
        context.object.location.x = 0
        wm = context.window_manager
        self._timer = wm.event_timer_add(0.3, window=context.window)
        wm.modal_handler_add(self)
        return {'RUNNING_MODAL'}

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

Related:

Time delay when selecting objects in a python script!

How to run a python script at regular intervals?

Application Timers in 2.8

2.8 brings in a new timer class.

See my answer to Blender Python: Running multiple timers

Move an object every second

import bpy
import functools

def move_obj(ob):
    ob.location.x += 0.1
    return 1

bpy.app.timers.register(functools.partial(move_obj, bpy.context.object), first_interval=1)
$\endgroup$
  • $\begingroup$ Thanks for the reply, I managed to do this in a different way following this docs.blender.org/api/blender2.8/bpy.app.timers.html $\endgroup$ – Inkplay_ Feb 16 '19 at 2:59
  • $\begingroup$ Yes tossed up whether to go that way, added link to prior answer regarding 2.8 application timers. $\endgroup$ – batFINGER Feb 16 '19 at 3:33

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.