7
$\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$
3
  • $\begingroup$ Defining dynamically an operator class for each item ? (just an idea... dont know if it can work...) $\endgroup$
    – lemon
    Jun 27, 2016 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, 2016 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, 2016 at 7:57

3 Answers 3

6
$\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

It gives the following code example:

class WM_OT_tooltip_test(bpy.types.Operator):
    arg: bpy.props.StringProperty()

    @classmethod
    def description(cls, context, properties):
        return "Arg is: " + properties.arg
$\endgroup$
1
  • $\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, 2019 at 19:37
4
$\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$
5
  • $\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, 2016 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, 2016 at 18:40
  • $\begingroup$ still lost (sorry)... Where is that (bpy.types.INFO_MT_add.append) documented, please ? $\endgroup$
    – lemon
    Jun 28, 2016 at 18:59
  • 1
    $\begingroup$ blender.org/api/blender_python_api_2_77_0/… $\endgroup$
    – batFINGER
    Jun 28, 2016 at 19:08
  • $\begingroup$ thanks again ! (ok I know I should avoid thanks... but I need to here...) $\endgroup$
    – lemon
    Jun 28, 2016 at 19:10
3
$\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$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .