How to create submenus with dynamic content? I am trying to loop over all the cameras in my scene to populate a menu, and be able to access properties of each camera from the submenu of each menu item. For the minute, I'm just trying to get it to work with one property, the focal length:



I am able to loop over each camera and create a menu item by simply using the camera.name property in the text property of the menu item:

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

    for camera in bpy.data.cameras:

        row = layout.row()

        row.menu(CamerasSubMenu.bl_idname, text=camera.name)


However, what I don't really know how to do is make each submenu's properties relate to whichever camera I selected in the first menu. I managed to pass the camera_name to the submenu class with this in the last line of the camera loop:

CamerasSubMenu.camera_name = camera.name  


And then access it with:

class CamerasSubMenu(bpy.types.Menu):
    bl_label = "Camera Properties"
    bl_idname = "view3d.camera_properties_menu"

    camera_name = bpy.props.StringProperty()

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

        row = layout.row()
        row.prop(bpy.data.cameras[self.camera_name], "lens")    


But the lens property (the focal length) is always the focal length of whichever camera name got passed in last in the loop, e.g. 'Camera.001'. So all submenus are accessing Camera.001's focal length instead of the individual focal lengths. 

I presume I'm going about this completely the wrong way, but I attempted it in a similar way I would pass data to an operator (which works).

So, is there a way to have a unique submenu per camera or should I just try something completely different?

Full code:

import bpy

class CamerasSubMenu(bpy.types.Menu):
    bl_label = "Camera Properties"
    bl_idname = "view3d.camera_properties_menu"

    camera_name = bpy.props.StringProperty()

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

        row = layout.row()
        row.prop(bpy.data.cameras[self.camera_name], "lens")    


class CamerasMenu(bpy.types.Menu):
    bl_label = "Cameras"
    bl_idname = "view3d.cameras_menu"

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

        for camera in bpy.data.cameras:

            row = layout.row()

            row.menu(CamerasSubMenu.bl_idname,text=camera.name)

            CamerasSubMenu.camera_name = camera.name      


def register():
    bpy.utils.register_module(__name__)    


def unregister():
    bpy.utils.unregister_module(__name__)  

if __name__ == "__main__":
    register()


bpy.ops.wm.call_menu(name=CamerasMenu.bl_idname)
You can make each submenu have its own context by setting a variable in the UILayout.

The script can be made to work as expected with only minor changes.


Define a context member for each menu, using UILayout.context_pointer_set
In the menus draw function, access this variable directly.


Modified example script:

import bpy

class CamerasSubMenu(bpy.types.Menu):
    bl_label = "Camera Properties"
    bl_idname = "view3d.camera_properties_menu"

    # &lt;-- Line removed (menu's don't store properties)

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

        row = layout.row()
        row.prop(context.my_camera, "lens")  # &lt;-- Changed from original


class CamerasMenu(bpy.types.Menu):
    bl_label = "Cameras"
    bl_idname = "view3d.cameras_menu"

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

        for camera in bpy.data.cameras:

            row = layout.row()

            row.context_pointer_set("my_camera", camera)  # &lt;-- Added to original
            row.menu(CamerasSubMenu.bl_idname, text=camera.name)
            # &lt;-- Line removed (camera set above)

def register():
    bpy.utils.register_module(__name__)    


def unregister():
    bpy.utils.unregister_module(__name__)  

if __name__ == "__main__":
    register()


bpy.ops.wm.call_menu(name=CamerasMenu.bl_idname)