17

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:

enter image description here

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)                
ideasman42
  • 47,387
  • 10
  • 141
  • 223
Ray Mairlot
  • 29,192
  • 11
  • 103
  • 125
  • 1
    You are changing the class attribute (on line 29) and that changes it for all the instances. See: http://www.python-course.eu/python3_class_and_instance_attributes.php – Jaroslav Jerryno Novotny Jan 28 '16 at 20:54
  • @Jerryno I don't know if that solves my problem, but if it does it would be good to see that as an answer. – Ray Mairlot Jan 28 '16 at 21:59

1 Answers1

17

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.

Modified example script:

import bpy

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

# <-- Line removed (menu's don't store properties)

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

    row = layout.row()
    row.prop(context.my_camera, "lens")  # <-- 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)  # <-- Added to original
        row.menu(CamerasSubMenu.bl_idname, text=camera.name)
        # <-- Line removed (camera set above)

def register(): bpy.utils.register_class(CamerasSubMenu) bpy.utils.register_class(CamerasMenu)

def unregister(): bpy.utils.unregister_class(CamerasMenu) bpy.utils.unregister_class(CamerasSubMenu)

if name == "main": register()

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

Gorgious
  • 30,723
  • 2
  • 44
  • 101
ideasman42
  • 47,387
  • 10
  • 141
  • 223