6
$\begingroup$

I have created a set of menu entries which all use the same operator, but with different operator parameters: enter image description here

As it is now all menu entries show the same tool tip. But i need to add different tool tips here so users can be better informed about what the different entries do. The only idea i have so far is to create a unique operator for each menu entry. But how can i do this dynamically?

Or is there a better solution to get different tool tips for each of the menu entries?

Here are the menu and operator that i use (simplified):

class MyAddMenu(bpy.types.Menu):
bl_label = "MyMenu"
bl_idname = "OBJECT_MT_my_add_menu"

def draw(self, context):
    lo = self.layout
    op = MyAddItem.bl_idname
    ps = get_p_list_from_somewhere()

    for p in ps:
        text = "entry-%s" % p
        lo.operator(op, text=text, icon='OUTLINER_OB_ARMATURE').p=p

class MyAddItem(bpy.types.Operator):
    bl_idname = "OBJECT_MT_my_add_item"
    bl_label = "Add Item"
    bl_description ="The operator Tool Tip"
    bl_options = {'REGISTER', 'UNDO'}

    p = StringProperty()

def execute(self, context):
    # Do something
    return {'FINISHED'}
$\endgroup$
  • $\begingroup$ Defining dynamically an operator class for each item ? (just an idea... dont know if it can work...) $\endgroup$ – lemon Jun 27 '16 at 16:42
  • $\begingroup$ FYI related : blender.stackexchange.com/questions/56741/… . If this can work, you can have op with its own tooltip (waiting for an answer) $\endgroup$ – lemon Jun 27 '16 at 18:06
  • $\begingroup$ What about an enum property? For instance the add lamp menu gives a different icon and tooltip based on the bpy.types.OBJECT_OT_lamp_add.bl_rna.properties['type'] enum. (layout.operator_enum) Or depending on the list, set up your own dynamic enum property, display with layout.prop_menu_enum, and call the op via the enum's update method. $\endgroup$ – batFINGER Jun 28 '16 at 7:57
2
$\begingroup$

It Is seems that this will be soon officially implemented in 2.81

Webpage: https://wiki.blender.org/wiki/Reference/Release_Notes/2.81/Python_API

$\endgroup$
  • $\begingroup$ While i think that all answers above can be helpful, i decided to use the new option for having a dynamic description() function. this matches pretty well to my case. $\endgroup$ – Gaia Clary Nov 5 at 19:37
3
$\begingroup$

Using a dynamic operator enum property

Add a custom property "prop" to the scene to show dynamic behaviour. Once added will add a new item to menu.

import bpy
#populate itemslist
def items_list(self, context):
    items = [
        ("RED", "Red", "This will add RED",  'OUTLINER_OB_ARMATURE', 1),
        ("GREEN", "Green", "This will add GREEN",  'OUTLINER_OB_ARMATURE', 2),
        ("BLUE", "Blue", "This will add BLUE",  'X', 4),
        ("YELLOW", "Yellow", "This will add YELLOW",  'BLENDER', 8),
        ]
    # test dynamic by adding custom prop "prop" to scene
    if "prop" in context.scene.keys():
        items.append(('BLACK', "Black", "This will add BLACK", 'BLANK', 16))
    return items


def draw_enum(self, context):
    self.layout.operator_context = 'INVOKE_REGION_WIN'
    self.layout.operator_menu_enum("object.simple_operator", "type", text="My Menu")

bpy.types.INFO_MT_add.append(draw_enum)

class SimpleOperator(bpy.types.Operator):
    bl_idname = "object.simple_operator"
    bl_label = "Add Avastar..."
    bl_description= "Avastar"
    #bl_options = {'REGISTER', 'PRESET', 'UNDO'}
    #dynamic enum property
    type = bpy.props.EnumProperty(items=items_list)
    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        print("run with ", self.type)
        return {'FINISHED'}

def register():
    bpy.utils.register_class(SimpleOperator)

def unregister():
    bpy.utils.unregister_class(SimpleOperator)

if __name__ == "__main__":
    register()

enter image description here

PS: from your screenshot it looks like you are using presets, check out Python add drop down menu that stores data The preset code is in 2.77/scripts/startup/bl_operators/presets.py

$\endgroup$
  • $\begingroup$ I understand nothing about how all that interact... could you please explain a bit more ? (I am very new to Python). What is "bpy.types.INFO_MT_add.append(draw_enum)" doing ? $\endgroup$ – lemon Jun 28 '16 at 18:31
  • $\begingroup$ @lemon It appends the draw_enum method to the Add menu (bpy.types.INFO_MT_add ... used to be in the INFO space, hence the break from usual naming convention. MT for menu type OT for operator type PT for panel type. Can see the menu id by mousing over and the source by right clicking and selecting view source. $\endgroup$ – batFINGER Jun 28 '16 at 18:40
  • $\begingroup$ still lost (sorry)... Where is that (bpy.types.INFO_MT_add.append) documented, please ? $\endgroup$ – lemon Jun 28 '16 at 18:59
  • 1
    $\begingroup$ blender.org/api/blender_python_api_2_77_0/… $\endgroup$ – batFINGER Jun 28 '16 at 19:08
  • $\begingroup$ thanks again ! (ok I know I should avoid thanks... but I need to here...) $\endgroup$ – lemon Jun 28 '16 at 19:10
2
$\begingroup$

You can dynamically define a class at runtime and give each one some unique properties. I would define a base class that performs the needed actions to create an object from a data file and then create a custom version for each object. We can't access the filesystem when registering an addon so we will need to keep a list of available objects that exist in a data directory.

Start by defining some variables -

obj_dir_name = 'custom_data'
obj_list = [
    'Female_body',
    'RacingCar',
    'iPhone',
    'Male_body',
    'Tank',
    'Toaster'
]
custom_items = []

Then define a base operator that can read the object data and create the objects. In this example I just open the file and create an empty with a matching name. If you can keep all the needed data to make your objects in the one file then you can change obj_list to be a list of classes or functions that make each object.

class CustomObjectBase(bpy.types.Operator):
    """Base for adding a custom object"""
    bl_idname = 'object.add_custom_item'
    bl_label = 'Add Item'
    bl_description ='Base operator for adding custom objects'
    bl_options = {'REGISTER', 'UNDO'}

    obj_file = bpy.props.StringProperty()

    def execute(self, context):
        with open(self.obj_file, 'r') as f:
            # read data and create mesh
            obj_name = os.path.basename(self.obj_file)
            obj = bpy.data.objects.new(obj_name, None)
            context.scene.objects.link(obj)
        return {'FINISHED'}

Then we can create an operator for each object and dynamically fill in properties to make each one unique. The bl_description property is set to define the tooltip for each item, the bl_label is also set and will be used to display the item in the menu.

def addCustomItems():
    for obj_name in obj_list:
        op_name = 'object.add_custom_' + obj_name.lower()
        data_file = os.path.join(os.path.dirname(__file__),obj_dir_name,obj_name)
        nc = type(  'DynOp_' + obj_name,
                    (CustomObjectBase, ),
                    {'bl_idname': op_name,
                    'bl_label': 'Add a ' + obj_name,
                    'bl_description': 'This adds an ' + obj_name,
                    'obj_file': data_file
                })
        custom_items.append(nc)
        bpy.utils.register_class(nc)

You can find a complete example of this as an addon here which also adds a submenu to the add objects menu.

example  object menu

$\endgroup$

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.