11
$\begingroup$

I can't find a way to append/prepend an operator to blenders generic "right-click" menu. When looking into the source of interface_handlers.c it seems the operators of the menu itself are hard coded, furthermore I can not find any draw method in /scripts/startup/bl_ui/ folder so I guess the menu is not exposed to the API, correct?

enter image description here

Q: Is there any way to add an entry to the menu and ideally assign a custom value to the active property under the mouse cursor?

$\endgroup$
3
  • $\begingroup$ I was trying to do this a while ago, I was told by a developer in #blendercoders that this was not possible. as you have stated, the menu is hard coded in C, and not accessible by python. $\endgroup$
    – wardrums
    Apr 1, 2017 at 5:34
  • 2
    $\begingroup$ We could try to move this menu to Python. Another alternative would be to append a Python menu to the C menu (that needs to be done from C though). $\endgroup$
    – ideasman42
    Apr 1, 2017 at 9:14
  • $\begingroup$ Talked with Campbell about it, patched Blender locally already and it works great...am preparing a patch for 2.79 and will submit it soon. $\endgroup$ Apr 4, 2017 at 19:37

1 Answer 1

14
+25
$\begingroup$

As of this commit, thanks to @p2or and @brecht, the bpy.ops.ui.copy_data_path_button() operator now allows to copy the full data-path of the active property under the mouse cursor to the clipboard which can be used to assign custom values to the active property (only available in latest versions).

enter image description here

Simplified version of the provided example in the patch (integers only):

import bpy

class MouseCursorPropertyOperator(bpy.types.Operator):
    bl_idname = "screen.active_int_property_add"
    bl_label = "Adjust active mouse cursor integer property"
    bl_options = {'REGISTER', 'UNDO'}
    
    my_int = bpy.props.IntProperty(default=100)
    
    @classmethod
    def poll(cls, context):
        return bpy.ops.ui.copy_data_path_button.poll()

    def execute(self, context):      
        
        # get the data path
        bpy.ops.ui.copy_data_path_button()
        path = context.window_manager.clipboard
        
        # get full data path
        bpy.ops.ui.copy_data_path_button(full_path=True)
        full_path = context.window_manager.clipboard
        
        # split path in class and property
        rna, prop = context.window_manager.clipboard.rsplit('.', 1)
            
        # set attribute if type is integer
        if type(eval(full_path)) is int:
            rna_eval = (eval(rna))
            value = getattr(rna_eval, prop)
            setattr(rna_eval, prop, value + self.my_int)
            self.report({"INFO"}, "{} set to {}".format(prop, value + self.my_int))
        else:
            print (type(eval(full_path)))
            self.report({"INFO"}, "{} is not an integer property".format(prop))
            
        return {'FINISHED'}
    

addon_keymaps = []

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

    # handle the keymap
    wm = bpy.context.window_manager
    kc = wm.keyconfigs.addon
    if kc:
        km = wm.keyconfigs.addon.keymaps.new(name='Screen')
        kmi = km.keymap_items.new(MouseCursorPropertyOperator.bl_idname, type='Q', value='PRESS', shift=True)
        addon_keymaps.append((km, kmi))

def unregister():

    for km, kmi in addon_keymaps:
        km.keymap_items.remove(kmi)
    addon_keymaps.clear()

    bpy.utils.unregister_class(MouseCursorPropertyOperator)


if __name__ == "__main__":
    register()

And as of this commit, thanks to @plasmasolutions you can now add an operator to the menu:

enter image description here

Provided example of the patch:

import bpy
from bpy.types import Header, Menu, Panel

def dump(obj, text):
    print('-'*40, text, '-'*40)
    for attr in dir(obj):
        if hasattr( obj, attr ):
            print( "obj.%s = %s" % (attr, getattr(obj, attr)))

class TestOp(bpy.types.Operator):
    """Tooltip"""
    bl_idname = "object.test_op"
    bl_label = "Execute a custom action"

    @classmethod
    def poll(cls, context):
        return context.active_object is not None

    def execute(self, context):
        if hasattr(context, 'button_pointer'):
            btn = context.button_pointer 
            dump(btn, 'button_pointer')

        if hasattr(context, 'button_prop'):
            prop = context.button_prop
            dump(prop, 'button_prop')

        if hasattr(context, 'button_operator'):
            op = context.button_operator
            dump(op, 'button_operator')     
            
        return {'FINISHED'}

# This class has to be exactly named like that to insert an entry in the right click menu
class WM_MT_button_context(Menu):
    bl_label = "Add Viddyoze Tag"

    def draw(self, context):
        pass

def menu_func(self, context):
    layout = self.layout
    layout.separator()
    layout.operator(TestOp.bl_idname)

def register():
    bpy.types.WM_MT_button_context.append(menu_func)

def unregister():
    bpy.types.WM_MT_button_context.remove(menu_func)

classes = (
    TestOp,
    WM_MT_button_context,
)

if __name__ == "__main__":
    from bpy.utils import register_class
    for cls in classes:
        register_class(cls)
    register()

(Note: In Blender versions 3.3 and above, WM_MT_button_context must be changed to UI_MT_button_context_menu).

$\endgroup$
2
  • 1
    $\begingroup$ I'm really glad that you posted this example here as I didn't have a clue that that's already working ... but it's not the answer to the question. The question is not only targeted at changing a value under the cursor but also at having one / several own items in the menu... and this is currently not possible. I'll patch Blender soon to allow that though. $\endgroup$ Apr 4, 2017 at 19:47
  • $\begingroup$ @plasmasolutions Now that Blender 2.79 is released and has this functionality, could you add an example of how to do it? $\endgroup$ Oct 16, 2017 at 6:57

You must log in to answer this question.

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