Refreshing the 3D view scene

Howdy
Im very new to Blender and and python and have written a simple script in python
to generate random poses of my rigged mesh.

The script works fine when n=1 and i push the Run Script button multiple times -> I see multiple different poses in the 3D viewport.
I now want to set n = 100 and see the poses displayed one after the other in the vieport. Unfortunately this doesnt happen. Instead I see the first pose, a long delay and then the last pose. There is no update of the viewport.

How can I force the viewport to update at the end of my loop? bpy.data.scenes[0].update() doesnt seem to do anything.


import bpy
import random
import time

hand_pose = bpy.data.objects["Armature"].pose;

for bone in hand_pose.bones:
    bone.rotation_mode = "XYZ";
    bone.rotation_euler = (0,0,0);
    

# bone_name - which bone 
# phi   - left right angle    
# theta - forward backward angle

def set_rot( pose, bone_name, phi_lims, theta_lims ): 
    phi     = phi_lims[0]   + random.random() * (phi_lims[1]    - phi_lims[0]);
    theta   = theta_lims[0] + random.random() * (theta_lims[1]  - theta_lims[0]);
    pose.bones[bone_name].rotation_euler = ( -phi/57.3, 0 , -theta/57.3 );
    return

n = 100
i = 0
while i < n:
    i = i + 1;
    set_rot(hand_pose,  'wrist',         ( -10 , 10 ),  ( -30 , 30 ) );
    set_rot(hand_pose,  'pinkybase',     ( -3 , 3 ),  ( -2 , 2 ) );
    set_rot(hand_pose,  'pinkylower',    ( -5 , 5 ),  ( -3 , 90 ) );
    set_rot(hand_pose,  'pinkymiddle',   ( 0 , 0 ),  ( 0 , 90 ) );
    set_rot(hand_pose,  'pinkytop',      ( 0 , 0 ),  ( 0 , 90 ) );
    bpy.data.scenes[0].update() # <-- PROBLEM?
    # bpy.ops.render.render( write_still=True )


My suggestion is use a frame change handler to run your script.

http://www.blender.org/documentation/blender_python_api_2_61_4/bpy.app.handlers.html?highlight=frame_change_post

Or use a modal timer operator. This is in the templates. Had to change view_3d.back to view_3d.space.back



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

    def modal(self, context, event):
        if event.type == 'ESC':
            return self.cancel(context)

        if event.type == 'TIMER':
            # change theme color, silly!
            color = context.user_preferences.themes[0].view_3d.space.back
            color.s = 1.0
            color.h += 0.01

        return {'PASS_THROUGH'}

    def execute(self, context):
        context.window_manager.modal_handler_add(self)
        self._timer = context.window_manager.event_timer_add(0.1, context.window)
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        context.window_manager.event_timer_remove(self._timer)
        return {'CANCELLED'}


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()

@batFinger, thanks for your help

Unfortunately I dont understand how to implement your solution.
Where do I start i.e. what files do I have to create and where do I have to put them?
Lets assume I call the little script I wrote in my first post RandHandPose.py then where would I put it? Where would I put the timer script you provided and how would I call RandHandPose.py from that script?

Also, is there absolutely no way to refresh the Blender 3D viewport? In 2.49 there was the redraw() method which seemed to work but I cant find the equivalent version in 2.57 (the version im using).

Found the solution. Sort of.

The easiest hack Ive found to do what I need is to swap

bpy.data.scenes[0].update()

with

bpy.ops.wm.redraw_timer(type='DRAW_WIN_SWAP', iterations=1)

I say this is a hack because according to the FAQ its not a supported technique
and wont be accepted in any user supplied addons. The correct way is, as batFinger pointed out, modal timers or frame handlers.

Fistly i suggest you get 2.61. If you want to use the modal timer example posted put this part of your code


    hand_pose = bpy.data.objects["Armature"].pose;
    set_rot(hand_pose,  'wrist',         ( -10 , 10 ),  ( -30 , 30 ) );
    set_rot(hand_pose,  'pinkybase',     ( -3 , 3 ),  ( -2 , 2 ) );
    set_rot(hand_pose,  'pinkylower',    ( -5 , 5 ),  ( -3 , 90 ) );
    set_rot(hand_pose,  'pinkymiddle',   ( 0 , 0 ),  ( 0 , 90 ) );
    set_rot(hand_pose,  'pinkytop',      ( 0 , 0 ),  ( 0 , 90 ) );

in the if event.type = “TIMER”: block instead of the colour change bit.

and this part



# bone_name - which bone 
# phi   - left right angle    
# theta - forward backward angle

def set_rot( pose, bone_name, phi_lims, theta_lims ): 
    phi     = phi_lims[0]   + random.random() * (phi_lims[1]    - phi_lims[0]);
    theta   = theta_lims[0] + random.random() * (theta_lims[1]  - theta_lims[0]);
    pose.bones[bone_name].rotation_euler = ( -phi/57.3, 0 , -theta/57.3 );
    return

After import bpy.