3
$\begingroup$

I'm trying to combine Render-Presets with Camera-Presets. Why is this script causing an Error? What does it mean: "Python: Traceback (most recent call last): File "C:\Program Files\Blender Foundation\Blender 2.91\2.91\scripts\startup\bl_operators\presets.py", line 166, in execute exec(rna_path) File "", line 1, in AttributeError: 'NoneType' object has no attribute 'data'

location: :-1 " ?

This Error is coming also if a Camera is selected.

How can I make it work?

Here is the script:

import bpy
from bpy.types import Operator, Menu
from bl_operators.presets import AddPresetBase


class OBJECT_MT_display_presets(Menu):
    bl_label = "Object Display Presets"
    preset_subdir = "object/display"
    preset_operator = "script.execute_preset"
    draw = Menu.draw_preset


class AddPresetObjectDisplay(AddPresetBase, Operator):
    '''Add a Object Display Preset'''
    bl_idname = "camera.object_display_preset_add"
    bl_label = "Add Object Display Preset"
    preset_menu = "OBJECT_MT_display_presets"

    # variable used for all preset values
    preset_defines = [
        "render  = bpy.context.scene.render",
        "data = bpy.context.object.data"
    ]

    # properties to store in the preset
    preset_values = [
        "render.engine",
        "render.use_stamp",
        "data.cycles.fisheye_lens",
        "data.cycles.fisheye_fov",
        "data.shift_x",
        "data.shift_y"

    ]

    # where to store the preset
    preset_subdir = "Test_Folder"


# Display into an existing panel
def panel_func(self, context):
    layout = self.layout

    row = layout.row(align=True)
    row.menu(OBJECT_MT_display_presets.__name__, text=OBJECT_MT_display_presets.bl_label)
    row.operator(AddPresetObjectDisplay.bl_idname, text="", icon='ZOOM_IN')
    row.operator(AddPresetObjectDisplay.bl_idname, text="", icon='ZOOM_OUT').remove_active = True


classes = (
    OBJECT_MT_display_presets,
    AddPresetObjectDisplay,
)


def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.RENDER_PT_context.prepend(panel_func)


def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)
    bpy.types.RENDER_PT_context.remove(panel_func)


if __name__ == "__main__":
    register()
$\endgroup$

1 Answer 1

3
$\begingroup$

Use context.active_object

The issue, re that error message in question, is with the preset operator in question is it is recognizing only context.active_object rather than context.object, which it sees as None. Perhaps context.object is used for the pinid object if set, where there is a pin object. Test

context.scene.camera

Anyhow, for the context members being set we can instead look at the scene camera. Have added a poll method to the operator to have it only poll when the render engine is cycles and the scene has a camera.

Otherwise change poll such that, in addition to being cycles render engine

ob = context.object
return ob and ob.type == 'CAMERA' and ...

and in preset

"camera = bpy.context.active_object.data"

Made the preset_subdir same in menu and operator. Another mixin class could be used for this.

Wrapped the base class menu draw method into draw. This way can append prepend a menu item.

Only enabled preset row when it polls to avoid trying to save / set when not polling.

import bpy
from bpy.types import Operator, Menu
from bl_operators.presets import AddPresetBase


class OBJECT_MT_display_presets(Menu):
    bl_label = "Object Display Presets"
    preset_subdir = "Test_Folder"
    preset_operator = "script.execute_preset"
    #draw = Menu.draw_preset
    def draw(self, context):
        self.draw_preset(context)
        


class AddPresetObjectDisplay(AddPresetBase, Operator):
    '''Add a Object Display Preset'''
    bl_idname = "camera.object_display_preset_add"
    bl_label = "Add Object Display Preset"
    preset_menu = "OBJECT_MT_display_presets"

    # variable used for all preset values
    preset_defines = [
        "scene  = bpy.context.scene",
        "render = scene.render",
        "camera = scene.camera.data",
    ]

    # properties to store in the preset
    preset_values = [
        "render.engine",
        "render.use_stamp",
        "camera.cycles.fisheye_lens",
        "camera.cycles.fisheye_fov",
        "camera.shift_x",
        "camera.shift_y"

    ]

    # where to store the preset
    preset_subdir = "Test_Folder"
    @classmethod
    def poll(cls, context):
        scene = context.scene
        return (
                scene.camera
                and scene.render.engine == 'CYCLES'
                )

# Display into an existing panel
def panel_func(self, context):
    layout = self.layout
    
    
    row = layout.row(align=True)
    row.enabled =  bpy.ops.camera.object_display_preset_add.poll()
    
    row.menu(OBJECT_MT_display_presets.__name__, text=OBJECT_MT_display_presets.bl_label)
    
    row.operator(AddPresetObjectDisplay.bl_idname, text="", icon='ZOOM_IN')
    row.operator(AddPresetObjectDisplay.bl_idname, text="", icon='ZOOM_OUT').remove_active = True


classes = (
    OBJECT_MT_display_presets,
    AddPresetObjectDisplay,
)


def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.RENDER_PT_context.prepend(panel_func)


def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)
    bpy.types.RENDER_PT_context.remove(panel_func)


if __name__ == "__main__":
    register()

Can Python Operator Presets be shared between Operators?

$\endgroup$
1
  • $\begingroup$ Thank you so much batFINGER! Now it works everything fine. And finally I can release my first Addon. Hurray :D $\endgroup$
    – Andi
    Jan 29, 2021 at 15:49

You must log in to answer this question.

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