0

I have the below addon installed in Blender 3.3. I get AttributeError:'Mesh' object has no attribute 'subdiv_prop' on line 137 when I click the 'Project Image' button. How do I resolve this?

enter image description here

enter image description here

bl_info = {
    "name": "Tools",
    "description": "Tools",
    "author": "Tools",
    "version": (1, 0, 1),
    "blender": (3, 3, 0),
    "location": "View3D > 'N' Panel > PRT",
    "warning": "",
    "wiki_url": "",
    "doc_url": "",
    "tracker_url": "",
    "category": "3D View"
}

############### IMPORTS import bpy from bpy.utils import previews import os import math from bpy.props import * import bmesh

############### USER PANEL class SNA_PT_Create_a_Product_Render(bpy.types.Panel): bl_label = "Create a Product Render" bl_idname = "SNA_PT_Create_a_Product_Render" bl_space_type = "VIEW_3D" bl_region_type = "UI" bl_category = 'PRT' bl_order = 0

@classmethod
def poll(cls, context):
    return True

def draw_header(self, context):
    try:
        layout = self.layout
    except Exception as exc:
        print(str(exc) + " | Error in Create a Product Promo panel header")

def draw(self, context):
    try:
        layout = self.layout
        scene = context.scene
        obj = context.object

        box = layout.box()
        box.enabled = True
        box.alert = False
        box.scale_x = 1.0
        box.scale_y = 1.0
        box.label(text=r"IMPORT ASSETS FROM OTHER FILES",icon= 'MESH_MONKEY')
        row = box.row(align=True)
        row.enabled = True
        row.alert = False
        row.scale_x = 1.0
        row.scale_y = 1.0
        op = row.operator("sna.append_model",text=r"Append Model",emboss=True,depress=False,icon_value=0)
        op = row.operator("sna.link_model",text=r"Link Model",emboss=True,depress=False,icon_value=0)
        row = box.row(align=True)
        row.enabled = True
        row.alert = False
        row.scale_x = 1.0
        row.scale_y = 1.0
        op = row.operator("sna.import_fbx",text=r"Import FBX",emboss=True,depress=False,icon_value=0)
        op = row.operator("sna.import_obj",text=r"Import OBJ",emboss=True,depress=False,icon_value=0)
        box = layout.box()
        box.enabled = True
        box.alert = False
        box.scale_x = 1.0
        box.scale_y = 1.0
        box.label(text=r"ADD DECAL / LABEL",icon= 'FILE_IMAGE')
        row = box.row(align=True)
        row.enabled = True
        row.alert = False
        row.scale_x = 1.0
        row.scale_y = 1.0
        op = row.operator("sna.import_image",text=r"Import Image",emboss=True,depress=False,icon_value=0)
        row = layout.column()
        row.alert = False if scene.shrink_target else True
        box.label(text=r"Project Image to a Mesh")
        op = box.prop(scene, "shrink_target")
        row = layout.column()
        op = box.operator(SNA_OT_Projector_Operator.bl_idname)
        box = layout.box()
        box.enabled = True
        box.alert = False
        box.scale_x = 1.0
        box.scale_y = 1.0
        box.label(text=r"MORE TOOLS",icon= 'TOOL_SETTINGS')
        row = box.row(align=True)
        row.prop(bpy.context.active_object,'is_shadow_catcher',icon_value=0,text=r"Turn Selected Object Into A Shadow Catcher",emboss=True,toggle=False,invert_checkbox=False,)
        box = layout.box()
        box.enabled = True
        box.alert = False
        box.scale_x = 1.0
        box.scale_y = 1.0
        box.label(text=r"RENDER YOUR PRODUCT",icon= 'RESTRICT_RENDER_OFF')
        op = box.operator("sna.render_image",text=r"Render Image",emboss=True,depress=False,icon_value=0)
        op = box.operator("sna.render_animation",text=r"Render Animation",emboss=True,depress=False,icon_value=0)
        box = layout.box()
        box.enabled = True
        box.alert = True
        box.scale_x = 1.0
        box.scale_y = 1.0
        box.label(text=r"NEED MORE TOOLS FOR YOUR PROJECTS?",icon= 'SHADERFX')
        row = box.row(align=True)
        row.operator("wm.url_open", text="See More Addons").url = ""
        row.operator("wm.url_open", text="Request A Tool").url = ""            
    except Exception as exc:
        print(str(exc) + " | Error in Create a Product Promo panel")



############### OPERATORS class SNA_OT_Projector_Operator(bpy.types.Operator): """Project Image onto Mesh""" bl_idname = "mesh.custom_subdiv" bl_label = "Project Image" bl_options = {'REGISTER', 'UNDO'}

subd_levels: IntProperty(default=30, options={'HIDDEN', 'SKIP_SAVE'})

@classmethod
def poll(cls, context):
    return context.scene.shrink_target != context.active_object and context.active_object.type=='MESH' and context.mode=='OBJECT'

def execute(self, context):
    obj = context.object
    ob = context.object
    me = ob.data

    if me.subdiv_prop >= self.subd_levels:
        self.report({'INFO'}, "Maximum reached")
        return {'CANCELLED'}
    else:
        shrink_mod = None
        for m in obj.modifiers:
            if m.type == 'SHRINKWRAP':
                shrink_mod = m
        if shrink_mod is None:
            shrink_mod = obj.modifiers.new(name="Projector", type='SHRINKWRAP')
        shrink_mod.offset = 0.01
        shrink_mod.target = context.scene.shrink_target
        if shrink_mod.target is None:
            self.report({"ERROR"},"Select a Target object above then click 'Project Image' button again")
            return {'CANCELLED'}
        bm = bmesh.new()
        bm.from_mesh(me)
        bmesh.ops.subdivide_edges(bm, edges=bm.edges, cuts=self.subd_levels, use_grid_fill=True)
        bm.to_mesh(me)
        me.update()
        self.report({'INFO'}, "{} levels applied".format(self.subd_levels))
        me.subdiv_prop =+ self.subd_levels
        return {'FINISHED'}



class SNA_OT_Append_Model(bpy.types.Operator): bl_idname = "sna.append_model" bl_label = "Append model" bl_description = "Import a model from another .blend file" bl_options = {"REGISTER", "UNDO"}

@classmethod
def poll(cls, context):
    return True

def execute(self, context):
    try:
        bpy.ops.wm.append('INVOKE_DEFAULT' if True else 'EXEC_DEFAULT',)
    except Exception as exc:
        print(str(exc) + " | Error in execute function of Append model")
    return {"FINISHED"}

def invoke(self, context, event):
    try:
        pass
    except Exception as exc:
        print(str(exc) + " | Error in invoke function of Append model")
    return self.execute(context)



class SNA_OT_Link_Model(bpy.types.Operator): bl_idname = "sna.link_model" bl_label = "Link model" bl_description = "Link a model from another .blend file" bl_options = {"REGISTER", "UNDO"}

@classmethod
def poll(cls, context):
    return True

def execute(self, context):
    try:
        bpy.ops.wm.link('INVOKE_DEFAULT' if True else 'EXEC_DEFAULT',)
    except Exception as exc:
        print(str(exc) + " | Error in execute function of New Operator")
    return {"FINISHED"}

def invoke(self, context, event):
    try:
        pass
    except Exception as exc:
        print(str(exc) + " | Error in invoke function of New Operator")
    return self.execute(context)



class SNA_OT_Import_Fbx(bpy.types.Operator): bl_idname = "sna.import_fbx" bl_label = "Import FBX" bl_description = "Import a model from a .FBX project file" bl_options = {"REGISTER", "UNDO"}

@classmethod
def poll(cls, context):
    return True

def execute(self, context):
    try:
        bpy.ops.import_scene.fbx('INVOKE_DEFAULT' if True else 'EXEC_DEFAULT',)
    except Exception as exc:
        print(str(exc) + " | Error in execute function of New Operator")
    return {"FINISHED"}

def invoke(self, context, event):
    try:
        pass
    except Exception as exc:
        print(str(exc) + " | Error in invoke function of New Operator")
    return self.execute(context)



class SNA_OT_Import_Obj(bpy.types.Operator): bl_idname = "sna.import_obj" bl_label = "Import OBJ" bl_description = "Import a model from a .OBJ project file" bl_options = {"REGISTER", "UNDO"}

@classmethod
def poll(cls, context):
    return True

def execute(self, context):
    try:
        bpy.ops.import_scene.obj('INVOKE_DEFAULT' if True else 'EXEC_DEFAULT',)
    except Exception as exc:
        print(str(exc) + " | Error in execute function of New Operator")
    return {"FINISHED"}

def invoke(self, context, event):
    try:
        pass
    except Exception as exc:
        print(str(exc) + " | Error in invoke function of New Operator")
    return self.execute(context)



class SNA_OT_Render_Image(bpy.types.Operator): bl_idname = "sna.render_image" bl_label = "Render image" bl_description = "Render your promo" bl_options = {"REGISTER", "UNDO"}

@classmethod
def poll(cls, context):
    return True

def execute(self, context):
    try:
        bpy.ops.render.render('INVOKE_DEFAULT' if True else 'EXEC_DEFAULT',animation=False,write_still=False,use_viewport=True,layer=r"",scene=r"",)
    except Exception as exc:
        print(str(exc) + " | Error in execute function of Render image")
    return {"FINISHED"}

def invoke(self, context, event):
    try:
        pass
    except Exception as exc:
        print(str(exc) + " | Error in invoke function of Render image")
    return self.execute(context)



class SNA_OT_Render_Animation(bpy.types.Operator): bl_idname = "sna.render_animation" bl_label = "Render animation" bl_description = "" bl_options = {"REGISTER", "UNDO"}

@classmethod
def poll(cls, context):
    return True

def execute(self, context):
    try:
        bpy.ops.render.render('INVOKE_DEFAULT' if True else 'EXEC_DEFAULT',write_still=False,use_viewport=True,layer=r"",scene=r"",)
    except Exception as exc:
        print(str(exc) + " | Error in execute function of Render animation")
    return {"FINISHED"}

def invoke(self, context, event):
    try:
        pass
    except Exception as exc:
        print(str(exc) + " | Error in invoke function of Render animation")
    return self.execute(context)



class SNA_OT_Import_Image(bpy.types.Operator): bl_idname = "sna.import_image" bl_label = "Import image" bl_description = "Import your label / decal / sticker" bl_options = {"REGISTER", "UNDO"}

@classmethod
def poll(cls, context):
    return True

def invoke(self, context, event):
    try:
        bpy.ops.preferences.addon_enable('INVOKE_DEFAULT' if False else 'EXEC_DEFAULT',module=r"io_import_images_as_planes",)
    except Exception as exc:
        print(str(exc) + " | Error in invoke function of New Operator")
    return self.execute(context)

def execute(self, context):
    try:
        bpy.ops.import_image.to_plane('INVOKE_DEFAULT' if True else 'EXEC_DEFAULT',)
    except Exception as exc:
        print(str(exc) + " | Error in execute function of New Operator")
    return {"FINISHED"}



class SNA_OT_Shadow_Catcher(bpy.types.Operator): bl_idname = "sna.shadow_catcher" bl_label = "Shadow Catcher" bl_description = "" bl_options = {"REGISTER", "UNDO"}

@classmethod
def poll(cls, context):
    return True

def execute(self, context):
    try:
        bpy.context.active_object.is_shadow_catcher = True
    except Exception as exc:
        print(str(exc) + " | Error in execute function of Shadow Catcher")
    return {"FINISHED"}

def invoke(self, context, event):
    try:
        pass
    except Exception as exc:
        print(str(exc) + " | Error in invoke function of Shadow Catcher")
    return self.execute(context)



def target_poll(self, object): return object.type == 'MESH'

############### REGISTER AND UNREGISTER ADDON def register(): bpy.utils.register_class(SNA_PT_Create_a_Product_Render) bpy.utils.register_class(SNA_OT_Append_Model) bpy.utils.register_class(SNA_OT_Link_Model) bpy.utils.register_class(SNA_OT_Import_Fbx) bpy.utils.register_class(SNA_OT_Import_Obj) bpy.utils.register_class(SNA_OT_Render_Image) bpy.utils.register_class(SNA_OT_Render_Animation) bpy.utils.register_class(SNA_OT_Import_Image) bpy.utils.register_class(SNA_OT_Shadow_Catcher) bpy.utils.register_class(SNA_OT_Projector_Operator) bpy.types.Scene.shrink_target = bpy.props.PointerProperty( name="Select Mesh", type=bpy.types.Object, poll=target_poll )

def unregister(): bpy.utils.unregister_class(SNA_OT_Shadow_Catcher) bpy.utils.unregister_class(SNA_OT_Import_Image) bpy.utils.unregister_class(SNA_OT_Render_Animation) bpy.utils.unregister_class(SNA_OT_Render_Image) bpy.utils.unregister_class(SNA_OT_Import_Obj) bpy.utils.unregister_class(SNA_OT_Import_Fbx) bpy.utils.unregister_class(SNA_OT_Link_Model) bpy.utils.unregister_class(SNA_OT_Append_Model) bpy.utils.unregister_class(SNA_PT_Create_a_Product_Render) bpy.utils.unregister_class(SNA_OT_Projector_Operator) del bpy.types.Scene.shrink_target

3vanse
  • 95
  • 6

1 Answers1

1

Your problem is that you haven't created the custom attribute for the bpy.types.Mesh object yet.

Similar to how you did here, in your register function:

bpy.types.Scene.shrink_target = bpy.props.PointerProperty(
        name="Select Mesh",
        type=bpy.types.Object,
        poll=target_poll
    )

You need to do somethings like:

class MeshSubdivProp(bpy.types.PropertyGroup):
    subdiv_prop: bpy.props.BoolProperty(default=False)
    other_prop: bpy.props.StringProperty()
    # etc

def register(): ... # your other register code bpy.types.Mesh.subdiv_props = bpy.props.PointerProperty(type=MeshSubdivProp)

Then in the operator you can check if the mesh has been subdivided like:

def execute(self, context):
    custom_props = me.subdiv_props
    if custom_props.subdiv_prop:
        # your code

Or you can go simpler and create a pointer to a single property (e.g. IntProperty, BoolProperty, etc.) without the whole PropertyGroup.

Jakemoyo
  • 4,375
  • 10
  • 20
  • Thanks for the response. I am new and learning python. On what lines do I place the following 2 codes?

    1 def execute(self, context): custom_props = me.subdiv_props if custom_props.subdiv_prop: # your code

    and

    2 class MeshSubdivProp(bpy.types.PropertyGroup): subdiv_prop: bpy.props.BoolProperty(default=False) other_prop: bpy.props.StringProperty() # etc

    – 3vanse Oct 02 '22 at 09:07
  • Don't place those anywhere. These are just examples. Look at the register function of the example that @Sietse Brouwer posted. You need to register the property as a custom property of the mesh object. Like def register(): bpy.types.Mesh.subdiv_prop = bpy.props.BoolProperty(default=False) or something like that. In my example I created a PropertyGroup but you can use an IntProperty/BoolProperty like in the other example. – Jakemoyo Oct 03 '22 at 10:46