Results 1 to 12 of 12

Thread: UI list for actions?

  1. #1

    UI list for actions?

    Hello, I'm interested in creating a UI list similiar to the template in the text editor "ui_list_simple", but instead of showing and selecting materials. I want it to show and select the list of actions (animation clips).
    I have a bit of experience with blender's python, but when I look into that template example, I'm really puzzled.
    maybe someone can give me some pointers on how to turn this template into an action list instead of material?



  2. #2
    Member
    Join Date
    Dec 2011
    Location
    Germany
    Posts
    3,991
    It depends on what you wanna do... It's pretty simple to show all actions in bpy.data.actions in a panel and allow the user to select select one (in the sense, that an integer property stores the index of the currently highlighted action in the template_list):

    Code:
    import bpy
    
    
    class ACTION_UL_list(bpy.types.UIList):
        def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
            ob = data
            if self.layout_type in {'DEFAULT', 'COMPACT'}:
                layout.prop(item, "name", text="", emboss=False, icon_value=icon)
            elif self.layout_type in {'GRID'}:
                pass
    
    
    class UIListPanelExample(bpy.types.Panel):
        """Creates a Panel in the Object properties window"""
        bl_label = "UIList Panel"
        bl_idname = "OBJECT_PT_ui_list_example"
        bl_space_type = 'PROPERTIES'
        bl_region_type = 'WINDOW'
        bl_context = "object"
    
        def draw(self, context):
            layout = self.layout
            ob = context.object
            layout.template_list("ACTION_UL_list", "", bpy.data, "actions", ob, "action_list_index")
    
    
    def register():
        bpy.types.Object.action_list_index = bpy.props.IntProperty()
        bpy.utils.register_module(__name__)
    
    
    def unregister():
        bpy.utils.unregister_module(__name__)
        del bpy.types.Object.action_list_index
    
    if __name__ == "__main__":
        register()
    If you want the user to associate multiple actions with an object for instance, a custom data structure will be required. Namely, CollectionProperty of type bpy.types.PropertyGroup, of which you actually need the built-in "name" StringProperty only to store action names. Note that if you change action names, the connection will break (your collection will contain invalid action names from there on). A few operators are required to handle user interaction with the template_list (add, remove, move up, move down, ...)



  3. #3
    Interesting, I didn't know that was possible. What I did was to do an IntProperty collection property instead, with an int pointing to each action.



  4. #4
    Thanks A lot CoDemanX, it works great!
    I also added the line

    ob.animation_data.action = bpy.data.actions[ob.action_list_index]

    before registration so that the current active action always correspond to the selected one in the list.
    but its probably not the best way, since its now not possible to change the action in the action editor (it works only in 1 direction) :/
    maybe there is a better way to apply it, so that the list and action editor are always co-responding to each other?

    The registration is also different then how I'm used to, so maybe it has something to do with that as well?

    I also didn't understand what is the ob = data in the first class necessary for?
    Last edited by snot_nose; 26-Jul-15 at 07:34.



  5. #5
    Member
    Join Date
    Dec 2011
    Location
    Germany
    Posts
    3,991
    ob = data is a left-over, forgot to remove it.

    Maybe this is what you rather wanna do?
    Code:
    import bpy
    
    class UIListPanelExample(bpy.types.Panel):
        """Creates a Panel in the Object properties window"""
        bl_label = "UIList Panel"
        bl_idname = "OBJECT_PT_ui_list_example"
        bl_space_type = 'PROPERTIES'
        bl_region_type = 'WINDOW'
        bl_context = "object"
    
        def draw(self, context):
            layout = self.layout
            ob = context.object
            
            if ob.animation_data is not None:
                layout.prop(ob.animation_data, "action")
            else:
                layout.label("No animation_data.")
    
    
    def register():
        bpy.utils.register_module(__name__)
    
    
    def unregister():
        bpy.utils.unregister_module(__name__)
    
    if __name__ == "__main__":
        register()
    Be careful with bpy.data.actions[ob.action_list_index], as the index can change as soon as you add another action. Maybe a callback for the IntProperty could solve this.



  6. #6
    Actually the first widget template was exactly what i was looking for, I wanted to use it when I prepare a character for exporting that i could run quickly and intuitivly through all the animation list to see if all the animations work fine.

    what do u mean by a callback to intproperty? currently, the index actually seems to be still similiar when i add an animation clip.
    Is there some special property or python function that shows the index of each action (in bpy.data.actions) or do i have to loop through the whole actions with a counter for that?

    is there a good way to check if an action was changed on the action editor itself or in the ui widget (the value of action_list_index in this case)? should i just register a new property with the old_action and compare it everytime with the current action selected?

    Thanks again
    Tal



  7. #7
    Member
    Join Date
    Dec 2011
    Location
    Germany
    Posts
    3,991
    Properties can call user-defined function on changes (update argument on property creation). Whenever you change the property in UI, the callback will be called and can do something else. Since the template_list index property changes as you change selection, the callback will be called for that exact event every time.

    There's no index property, so either use enumerate(...) or possibly bpy.data.actions.find(ActionName) to return the index or raise an AttributeError if not found.



  8. #8
    awesome, it works great with the update function. now its possible to change the action both in the list and the action editor, and they influence each other in both directions, using the even handler (for influencing the list from the action editor)

    Code:
    import bpy
    
    def action_editor_update(context):
        ob = bpy.context.object
        action_index = bpy.data.actions.find(ob.animation_data.action.name)
        if action_index != ob.action_list_index:
            ob.action_list_index = action_index
                    
    #select the new action when there is a new selection in the ui list and go to the first frame
    def update_action_list(self, context):
        ob = bpy.context.object
        ob.animation_data.action = bpy.data.actions[ob.action_list_index]
        bpy.context.scene.frame_current = 1
    
    class ACTION_UL_list(bpy.types.UIList):
        def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
            if self.layout_type in {'DEFAULT', 'COMPACT'}:
                layout.prop(item, "name", text="", emboss=False, icon_value=icon)
            elif self.layout_type in {'GRID'}:
                pass
    
    class UIListPanelExample(bpy.types.Panel):
        """Creates a Panel in the Object properties window"""
        bl_label = "Action List"
        bl_idname = "OBJECT_PT_ui_list"
        bl_space_type = 'VIEW_3D'
        bl_region_type = "UI"
        bl_context = "object"
    
        def draw(self, context):
            layout = self.layout
            ob = context.object    
            layout.template_list("ACTION_UL_list", "", bpy.data, "actions", ob, "action_list_index")
    
    bpy.app.handlers.scene_update_post.append(action_editor_update)
    
    def register():
        bpy.types.Object.action_list_index = bpy.props.IntProperty(update=update_action_list)
        bpy.utils.register_module(__name__)
    
    
    def unregister():
        bpy.utils.unregister_module(__name__)
        del bpy.types.Object.action_list_index
    
    if __name__ == "__main__":
        register()
    I was trying in the beginning to see if bpy.context.object.animation_data.action.is_update d (instead of the condition that i eventually wrote)
    but it was always getting False, also when the action changed. any idea what is this property actually used for?

    another last question is - does the event handler have a big influence on blender`s performance? since it's calling for the function on each even.
    Last edited by snot_nose; 30-Jul-15 at 15:08.



  9. #9
    Member
    Join Date
    Dec 2011
    Location
    Germany
    Posts
    3,991
    is_updated is part of the rendering API afaik and doesn't necessarily behave as expected. Note that there's also is_updated_data.

    Update-callbacks are usually fine performance-wise, scene update handlers however are more costly I'd say. You should make sure that the function executes fast, because it is called very often, not only when changes occur.



  10. #10
    Member
    Join Date
    Feb 2014
    Location
    Portugal
    Posts
    45
    I know, long time bump but just found this and is really helpful for my project too! thanks alot for sharing the script, I made it work on my side too!



  11. #11
    Is there way to select multible actions from list and have delete button to delete them?
    You can find me from DeviantArt too, link below
    http://cryast.deviantart.com/
    and my portfolio http://nikomanty.carbonmade.com/



  12. #12
    Member
    Join Date
    Feb 2014
    Location
    Portugal
    Posts
    45
    @Crystus I couldnt find a way to implement that in my addon, nor I find a way in blender, Currently I have a delete button, that is easy to use if there are a bunch of animations, just click it a series of time, it is easier than through the outliner, but still not optimal.
    Action Loader! My Blender addon for managing many actions/animations.



Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •