1
$\begingroup$

I have a Master Addon which adds its own vertical tabs and panels. Then i have a Child Addon which provides some additional tools which fit well into various panels created by master. But the Child Addon can also run standalone.

When both Addons are enabled the additional tools from the Child Addon shall be placed into the Panels provided by the Master Addon. But when the Child Addon is running "standalone", then it shall create its own panels instead. The master may know about the child. but the child must not depend in any way on the master addon.

So the goal is to avoid to duplicate Operator Buttons and Panels when both Addons are enabled. To achieve this i added a global Property to the child addon and defined a child Toolbox like this:

# In the Child Addon:
# ===================

global CHILD_ENABLED
CHILD_ENABLED = True
class PanelChildTool(bpy.types.Panel):
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    bl_label = _("Child Tools")
    bl_idname = "child.tools"
    bl_category    = "Tools"

    @classmethod
    def poll(self, context):
        global CHILD_ENABLED
        return CHILD_ENABLED

    ....        


def set_ui(is_enabled=True):
    global CHILD_ENABLED
    CHILD_ENABLED = is_enabled

So i can now enable/disable the drawing of Child panels. And in the master Addon's init section i added:

# In the Master Addon
# ===================

loaded_default, loaded_state = addon_utils.check("child")
if loaded_default == False and loaded_state is None:
    print("Child seems to be not installed")
elif loaded_state != True :
    addon_utils.enable("child", default_set=True, persistent=False, handle_error=None)
    print("Enabled module 'child' On The Fly")
    loaded_state = True

if loaded_state == True:
    import child
    child.set_ui(is_enabled=False)        
    print("Disabled Child's native UI")

Now the Child panels are disabled. Finally, wherever i want to load an operator from the Child into one of the master Panels, i add something similar to this to the master's panel draw() function:

if  sys.modules.get("child"):
    import child
    child.draw_generic(context, layout) # Generic draw function provided by the child

So this appraoch is working. But is there a better solution ? thanks.

$\endgroup$
5
$\begingroup$

The common way in Blender is to append or prepend draw functions via the panel "class":

bpy.types.CATEGORY_PT_panel_name.append(draw_func)

Registered bpy classes are globally available, you can check for a certain one with hasattr(bpy.types, "CATEGORY_PT_panel_name"):

import bpy

# Change this to some type that doesn't exist and re-run
panel_type = "VIEW3D_PT_tools_transform"

def draw_func(self, context):
    layout = self.layout

    obj = context.object

    row = layout.row()
    row.label(text="Hello world!", icon='WORLD_DATA')

    row = layout.row()
    row.label(text="Active object is: " + obj.name)
    row = layout.row()
    row.prop(obj, "name")

    row = layout.row()
    row.operator("mesh.primitive_cube_add")


class HelloWorldPanel(bpy.types.Panel):
    """Creates a Panel in the Object properties window"""
    bl_label = "Hello World Panel"
    bl_idname = "OBJECT_PT_hello"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'TOOLS'
    #bl_context = "object"
    bl_category = "Tools"

    # re-use draw functions
    draw = draw_func


def register():
    if hasattr(bpy.types, panel_type):
        getattr(bpy.types, panel_type).append(draw_func)
    else:
        bpy.utils.register_class(HelloWorldPanel)


def unregister():
    if hasattr(bpy.types, panel_type):
        getattr(bpy.types, panel_type).remove(draw_func)
    else:
        bpy.utils.unregister_class(HelloWorldPanel)


if __name__ == "__main__":
    register()

The draw functions are internally stored in Panel.draw._draw_funcs

You can remove them like you add them, the method is just called remove.

$\endgroup$
  • $\begingroup$ Hi, CoDEmanX. The goal is: When the Child Addon is enabled standalone then it should provide its own panel. But when it is used as "library of operators" it should not provide any panel because theen the master Addon will decide where the operators shall appear in the Master-Panels. And the Child Addon should not have any knowledge about the master Addon.Could you provide a small code example to show how i can achieve that with the draw function append ? thanks $\endgroup$ – Gaia Clary Sep 6 '14 at 22:06
  • $\begingroup$ You should not create such dependencies actually, but if you really want it this way, use hasattr() as I suggested. Based on the return value, append a draw function or register a new panel. $\endgroup$ – CoDEmanX Sep 7 '14 at 14:56
  • $\begingroup$ I added an example how you could either append a draw func or register a new panel. Note that bpy.utils.register_module(__name__) will break it! A possible solution is to always register a separate panel, but poll()-hide it. $\endgroup$ – CoDEmanX Sep 7 '14 at 15:08

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.