1
$\begingroup$

How can I use Python to find out if all the selected objects have an action and do something then? If one or more of the selected objects don't have an action, I'd like to print an info/error message saying that "All the selected objects must have an action.".

It's easy to check if an object has shape keys or materials but Terminal says object of type 'Action' has no len()if I try to do this:

import bpy

o = bpy.context.object

if o.animation_data.action:
    num_of_actions = len( o.animation_data.action )
    print( "Number of actions: ", num_of_actions )

This helps a little bit but isn't the answer to my problem.

This is the best I can do (had some help from Jeremy for the logic bricks):

# This adds the logic bricks to objects that allow them to be playable in BGE, but only to the selected objects that have an action. Actionless objects are ignored.
# Credit goes mostly to Jeremy Behreandt

import bpy

frames_to_be_played = 9999

ops=bpy.ops
context=bpy.context

# Cache shortcuts.
ops_mesh = ops.mesh
ops_object = ops.object
ops_logic = ops.logic
scene = context.scene

selected = bpy.context.selected_objects

for obj in selected:
    ad = obj.animation_data
    if ad:
        if ad.action:
            bpy.context.scene.objects.active = obj

            current_object = context.active_object

            # Cache shortcut to cube's game engine.
            game_engine = current_object.game

            # Add sensor.
            ops_logic.sensor_add(type='ALWAYS')
                                 #name='Always')
                                 #object=cube_name)
            sensor = game_engine.sensors[-1]

            # Add controller.
            ops_logic.controller_add(type='LOGIC_AND')
                                     #name='And')
                                     #object=cube_name)
            controller = game_engine.controllers[-1]

            # Add actuator.
            ops_logic.actuator_add(type='ACTION')
                                   #name='Action',
                                   #object=cube_name)
            actuator = game_engine.actuators[-1]

            # Set actuator action to cube's animation data action.
            actuator.action = current_object.animation_data.action

            # Play mode options are:
            # ['PLAY', 'PINGPONG', 'FLIPPER', 'LOOPSTOP', 'LOOPEND', 'PROPERTY']
            # 'PLAY' is the default.
            #actuator.play_mode = play_mode

            # Set frame end to scene end.
            #actuator.frame_end = scene.frame_end
            actuator.frame_end = frames_to_be_played

            # Link logic together.
            sensor.link(controller)
            actuator.link(controller)

The above script puts the logic bricks to the objects that have an action and ignores the ones that don't have an action, which is good, but not what I'm after.

The ideal case would be to cancel the whole process if even one of the selected objects is missing an action. And notify about that with a message by using this method, which is this snippet code:

import bpy

class RENDER_OT_test(bpy.types.Operator):
    bl_idname = 'render.actionerrormessage'
    bl_label = 'actionErrorMessage'
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        self.report({'INFO'}, 'Cancelling... All the selected objects must have an action.')
        return {'FINISHED'}

bpy.utils.register_class(RENDER_OT_test) # test call to the newly defined operator bpy.ops.render.actionerrormessage() - try out by running the script and pressing spacebar and typing actionErrorMessage
$\endgroup$
  • 2
    $\begingroup$ There is only ever one (or no) action associated with an object via ad.action Typically multiple actions for an object are added via the NLA. NLA has a list of tracks ad.nla_tracks each track has strips, a strip has an associated action. $\endgroup$ – batFINGER Sep 5 '18 at 9:57
  • $\begingroup$ Thanks, batFINGER. I still can't get it to work :/ len( bpy.context.object.animation_data.nla_tracks.data.action ) is just as useless. What would be the correct way to do this? $\endgroup$ – Manu Järvinen Sep 5 '18 at 11:56
  • 2
    $\begingroup$ An action has no length, it is not an iterable. It has an fcurves collection (what you see in the graph editor) Typically a collection name is the give away as it is a plural like objects, materials, nla_tracks. eg len(ad.nla_tracks) From ad.action: it either has one action if ad.action is not None or none. There is no way of telling if other actions not in the animation data "slot" are assoc to the object.. Typically to associate an action with an object the NLA is used. Does your file use NLA to store actions.. if not will be of little use? $\endgroup$ – batFINGER Sep 5 '18 at 12:20
  • $\begingroup$ Thanks for the comment, batFINGER. But how can I write an if statement that fills the requirements I listed in the original post? This won't work either: goo.gl/vXb21d $\endgroup$ – Manu Järvinen Sep 5 '18 at 14:10
  • 2
    $\begingroup$ Answers here and here might help? $\endgroup$ – Robin Betts Sep 11 '18 at 17:56
3
$\begingroup$

My friend managed to achieve this with a bit nicer code than my artistic approach :)

GitHub link

Code:

import bpy

#########################################################
# INSERT ACTION LOGIC BRICKS TO SELECTED OBJECTS SCRIPT #
#########################################################

#########################################################
# Note:
#
# Each of the selected objects must have a transform 
# (Location/Rotation/Scale) action (animation/keyframes) 
# before running this script. The action can be empty.
# Otherwise nothing will be inserted to the objects.
#########################################################

############
# Settings #
############

action_brick_start_frame = 0

action_brick_end_frame = 9999 

# action_brick_end_frame = bpy.context.scene.frame_end
# Uncomment to set the Action Logic Brick end frame to be 
# the end frame of the Timeline.

#############################################################
# Make sure that each of the selected objects has an action #
# Credit goes mostly to Sakari Ekroth                       #
#############################################################

selection = bpy.context.selected_objects
noActionObjs = []
for obj in selection:
  if not obj.animation_data or not obj.animation_data.action:
    noActionObjs.append(obj.name)

if len(noActionObjs) > 0:
  print ("Objects missing action: %s" % ", ".join(noActionObjs))
  def oops(self, context):
      self.layout.label("All of the selected objects must have an action. Cancelling... ")

  bpy.context.window_manager.popup_menu(oops, title="Error", icon='ERROR')
  print("All of the selected objects must have an action. Cancelling... ")

####################################################################
# The following adds the Logic Bricks to objects that allow them to 
# be playable in Blender Game Engine, but only to the selected 
# objects that have an action. Actionless objects are ignored.
# Credit goes mostly to Jeremy Behreandt
####################################################################

else:
  print("Inserting some Logic Bricks to the selected objects")
  ops = bpy.ops
  context = bpy.context

  # Cache shortcuts.
  ops_mesh = ops.mesh
  ops_object = ops.object
  ops_logic = ops.logic
  scene = context.scene

  for obj in selection:
    if obj.animation_data and obj.animation_data.action:

      bpy.context.scene.objects.active = obj
      current_object = context.active_object
      # Cache shortcut to cube's game engine.
      game_engine = current_object.game

      # Add sensor.
      ops_logic.sensor_add(type = "ALWAYS")
                            #name='Always')
                            #object=cube_name)
      sensor = game_engine.sensors[-1]

      # Add controller.
      ops_logic.controller_add(type = "LOGIC_AND")
                                #name='And')
                                #object=cube_name)
      controller = game_engine.controllers[-1]

      # Add actuator.
      ops_logic.actuator_add(type = "ACTION")
                              #name='Action',
                              #object=cube_name)
      actuator = game_engine.actuators[-1]

      # Set actuator action to cube's animation data action.
      actuator.action = current_object.animation_data.action

      # Play mode options are:
      # ['PLAY', 'PINGPONG', 'FLIPPER', 'LOOPSTOP', 'LOOPEND', 'PROPERTY']
      # 'PLAY' is the default.
      #actuator.play_mode = play_mode

      # Set frame start to scene end.
      actuator.frame_start = action_brick_start_frame

      # Set frame end to scene end.
      actuator.frame_end = action_brick_end_frame

      # Link logic together.
      sensor.link(controller)
      actuator.link(controller)
$\endgroup$
0
$\begingroup$

Yay, after some fiddling I managed to do it! :)

Here's a GitHub link.

Here's the code (the quality is what my artist brain can manage to yield, but at least it seemed to work):

import bpy

#########################################################
# INSERT ACTION LOGIC BRICKS TO SELECTED OBJECTS SCRIPT #
#########################################################

#########################################################
# Note:
#
# Each of the selected objects must have a transform 
# (Location/Rotation/Scale) action (animation/keyframes) 
# before running this script. The action can be empty.
# Otherwise nothing will be inserted to the objects.
#########################################################

############
# Settings #
############

action_brick_start_frame = 0

action_brick_end_frame = 9999 

# action_brick_end_frame = bpy.context.scene.frame_end
# Uncomment to set the Action Logic Brick end frame to be 
# the end frame of the Timeline.

#############################################################
# Make sure that each of the selected objects has an action #
#############################################################

all_objs_have_action = True

for obj in bpy.context.selected_objects:
    ad = obj.animation_data
    if ad == None:
        obj.animation_data_create()
        all_objs_have_action = False
        #print(obj.name,"has no object.animation_data, creating...")
    if ad:
        if not ad.action:
            all_objs_have_action = False
            #print(obj.name,"does NOT have an action")
        #else:
            #print(obj.name,"has an action")

if all_objs_have_action == True:
    print("Inserting some Logic Bricks to the selected objects")

if all_objs_have_action == False:
    def oops(self, context):
        self.layout.label("All of the selected objects must have an action. Cancelling... ")

    bpy.context.window_manager.popup_menu(oops, title="Error", icon='ERROR')
    print("All of the selected objects must have an action. Cancelling... ")

####################################################################
# The following adds the Logic Bricks to objects that allow them to 
# be playable in Blender Game Engine, but only to the selected 
# objects that have an action. Actionless objects are ignored.
# Credit goes mostly to Jeremy Behreandt
####################################################################

if all_objs_have_action == True:
    ops=bpy.ops
    context=bpy.context

    # Cache shortcuts.
    ops_mesh = ops.mesh
    ops_object = ops.object
    ops_logic = ops.logic
    scene = context.scene

    selected = bpy.context.selected_objects

    for obj in selected:
        ad = obj.animation_data
        if ad:
            if ad.action:
                bpy.context.scene.objects.active = obj

                current_object = context.active_object

                # Cache shortcut to cube's game engine.
                game_engine = current_object.game

                # Add sensor.
                ops_logic.sensor_add(type='ALWAYS')
                                     #name='Always')
                                     #object=cube_name)
                sensor = game_engine.sensors[-1]

                # Add controller.
                ops_logic.controller_add(type='LOGIC_AND')
                                         #name='And')
                                         #object=cube_name)
                controller = game_engine.controllers[-1]

                # Add actuator.
                ops_logic.actuator_add(type='ACTION')
                                       #name='Action',
                                       #object=cube_name)
                actuator = game_engine.actuators[-1]

                # Set actuator action to cube's animation data action.
                actuator.action = current_object.animation_data.action

                # Play mode options are:
                # ['PLAY', 'PINGPONG', 'FLIPPER', 'LOOPSTOP', 'LOOPEND', 'PROPERTY']
                # 'PLAY' is the default.
                #actuator.play_mode = play_mode

                # Set frame start to scene end.
                actuator.frame_start = action_brick_start_frame

                # Set frame end to scene end.
                actuator.frame_end = action_brick_end_frame

                # Link logic together.
                sensor.link(controller)
                actuator.link(controller)
$\endgroup$
  • $\begingroup$ Seems a mash of two parts. Part A checks for all objects having an action, posts a message if not and quits, else goes to part B. If it gets to part B, checks again if the object has an action... If this was written as an operator, would use part A as the poll method, and not bother to check for action in part B (execute for operator) as it has to poll to run. $\endgroup$ – batFINGER Sep 13 '18 at 11:07
  • $\begingroup$ Hehe, of course :) This was my artist-code tryout. The accepted answer should have cleaner code. $\endgroup$ – Manu Järvinen Sep 14 '18 at 6:57

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.