0
$\begingroup$

So I've had a look at this blender stack exchange thread: How do a create a foldout UI panel? and this reference here https://wiki.blender.org/wiki/Reference/Release_Notes/2.80/Python_API/UI_API#Sub_Panels from the official documentation.

But I'm getting an error on this line:

class LOADUESHADERSCRIPT_PT_sub_select_preset_in_folder_2(LOADUESHADERSCRIPT_shared_panel, bpy.types.panel)

which is AttributeError: 'RNA_Types' object has no attribute 'panel' when trying to follow their code and create subpanels

My code is the following:

class LOADUESHADERSCRIPT_shared_panel(bpy.types.Panel):
    #bl_label = "Load UE Shaders"
    #bl_idname = "LOADUESHADERSCRIPT_PT_main_panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "UE Shaders"


class LOADUESHADERSCRIPT_PT_main_panel_1(LOADUESHADERSCRIPT_shared_panel, bpy.types.Panel):
    bl_label = "Load UE Shaders"
    bl_idname = "LOADUESHADERSCRIPT_PT_main_panel_1"
        

#sub panel 2
class LOADUESHADERSCRIPT_PT_sub_select_preset_in_folder_2(LOADUESHADERSCRIPT_shared_panel, bpy.types.panel):
    bl_label = "Select Preset in Folder"
    bl_parent_id = "LOADUESHADERSCRIPT_PT_main_panel_1"

    def draw(self, context):
        layout = self.layout
        
        #store active/selected scene to variable
        scene = context.scene
        
        #set isOverridePackage to override __package__ variable as it does
        #not work for imported functions
        isOverridePackage = True
        preferences = get_preferences(isOverridePackage)

        #make folder section
        box = layout.box()
        row = box.row()
        row.label(text="Folders")
        row = box.row()
        left = row.column()
        left.alignment = "RIGHT"
        left.prop(preferences, 'folders', expand=False)


        selected_folders_presets = get_selected_folder_presets(isOverridePackage)

        #create the list of current presets
        layout.template_list("SHADER_PRESETS_UL_items", "", selected_folders_presets,
                                "presets", selected_folders_presets, "preset_index", rows=5)


#sub panel 3
class LOADUESHADERSCRIPT_PT_sub_load_shader_map_settings_3(LOADUESHADERSCRIPT_shared_panel, bpy.types.panel):
    bl_label = "Load Shader Map Settings"
    bl_parent_id = "LOADUESHADERSCRIPT_PT_main_panel_1"

    def draw(self, context):
        layout = self.layout
        
        #store active/selected scene to variable
        scene = context.scene
        #allow access to user inputted properties through pointer
        #to properties
        pathtool = scene.path_tool

        #--------make load shader map settings section
        layout.prop(pathtool, "is_load_img_textures")

        #option to replace or keep existing nodes in materials
        layout.prop(pathtool, "is_replace_nodes")

        #option to delete image texture nodes which have not had a texture
        #loaded into them
        layout.prop(pathtool, "is_delete_unused_img_texture_nodes")
        if(pathtool.is_load_img_textures):
            #only show this option if delete unused_img_texture_nodes is checked
            if(pathtool.is_delete_unused_img_texture_nodes):
                layout.prop(pathtool, "is_delete_unused_related_nodes")

            layout.prop(pathtool, "texture_file_type_enum")
            layout.prop(pathtool, "clipping_method_enum")

            layout.prop(pathtool, "is_reuse_node_group_with_same_name")
            layout.prop(pathtool, "is_reuse_img_texture_with_same_name")

            layout.prop(pathtool, "is_normal_non_colour")
            layout.prop(pathtool, "is_m_non_colour")
            
            layout.prop(pathtool, "is_change_principle_bsdf_emission_strength")

            if(pathtool.is_change_principle_bsdf_emission_strength):
                layout.prop(pathtool, "principled_bsdf_emission_strength_float")
            layout.prop(pathtool, "material_alpha_threshold")

            #Roman Noodles related settings
            layout.prop(pathtool, "is_orm_non_colour")
            layout.prop(pathtool, "is_add_skin_map")

        layout.operator("loadueshaderscript.reset_settings_main_panel_operator")

#sub panel 4
class LOADUESHADERSCRIPT_PT_sub_load_shader_map_methods_4(LOADUESHADERSCRIPT_shared_panel, bpy.types.panel):
    bl_label = "Load Shader Map Methods"
    bl_parent_id = "LOADUESHADERSCRIPT_PT_main_panel_1"

    def draw(self, context):
        layout = self.layout
        
        #store active/selected scene to variable
        scene = context.scene
        #allow access to user inputted properties through pointer
        #to properties
        pathtool = scene.path_tool

        #--------------draw load shader map methods
        #formatting
        #layout.use_property_split means that it will try and display 
        #the property fully
        layout.use_property_split = True

        
        #create box for all related inputs adding shader map to multiple material
        box = layout.box()
        box.label(text = "ADD SHADER MAP TO MULTIPLE MATERIALS (MULTIPLE MATERIALS)")
        box.label(text = "Select a mesh, enter material indexes separated by a space and add shader maps to multiple materials")
        
        if(pathtool.is_load_img_textures):
            box.prop(pathtool, "material_folder_path")
            box.prop(pathtool, "material_indices_list_string")
            box.prop(pathtool, "export_folder_path")
            if(pathtool.is_add_skin_map):
                box.prop(pathtool, "skin_map_path")
        else:
            box.prop(pathtool, "material_indices_list_string")
        box.operator("loadueshaderscript.add_to_multiple_materials_operator")
                
        #Create a box for adding shader maps to all materials
        #to the selected mesh with all
        #related inputs and operators 
        box = layout.box()
        box.label(text = "ADD SHADER MAP TO ALL MATERIALS ON SELECTED MESHES (ALL MATERIALS)")
        box.label(text = "Select multiple meshes and add shader maps to all the materials on the selected meshes")
        if(pathtool.is_load_img_textures):
            box.prop(pathtool, "material_folder_path")
            box.prop(pathtool, "export_folder_path")
            if(pathtool.is_add_skin_map):
                box.prop(pathtool, "skin_map_path")
        box.operator("loadueshaderscript.add_to_selected_meshes_operator" )

        layout.use_property_split = False

        #create box for all related input adding shader map to one selected material
        #should hide by default, but show if is_show_add_one_material_operator is checked
        #use this because some props.txt files do not have the same name as their materials
        layout.prop(pathtool, "is_show_add_one_material_operator")

        #formatting
        #layout.use_property_split means that it will try and display 
        #the property fully
        layout.use_property_split = True

        if (pathtool.is_show_add_one_material_operator):
            box = layout.box()
            box.label(text = "ADD SHADER MAP TO SELECTED MATERIAL (ONE MATERIAL)")
            box.label(text = "Select a mesh and a material and add a shader map to the selected material")
            box.label(text = "Used for Materials which have different names to their props.txt files")
            if(pathtool.is_load_img_textures):
                box.prop(pathtool, "props_txt_path")
                box.prop(pathtool, "export_folder_path")
                if(pathtool.is_add_skin_map):
                    box.prop(pathtool, "skin_map_path")
            box.operator("loadueshaderscript.add_to_one_material_operator")

I have also registered the classes down below as follows:

classes = [PathProperties, 

LOADUESHADERSCRIPT_shared_panel, LOADUESHADERSCRIPT_PT_main_panel_1,
LOADUESHADERSCRIPT_PT_sub_select_preset_in_folder_2,
LOADUESHADERSCRIPT_PT_sub_load_shader_map_settings_3, 
LOADUESHADERSCRIPT_PT_sub_load_shader_map_methods_4,

LOADUESHADERSCRIPT_OT_add_to_one_material, LOADUESHADERSCRIPT_OT_add_to_multiple_materials, 
LOADUESHADERSCRIPT_OT_add_to_selected_meshes, LOADUESHADERSCRIPT_OT_reset_settings_main_panel]
 
def register():
    for cls in classes:
        bpy.utils.register_class(cls)
        
        #register path_tool as a type which has all
        #the user input properties from the properties class 
        bpy.types.Scene.path_tool = bpy.props.PointerProperty(type = PathProperties)
 
def unregister():
    for cls in classes:
        bpy.utils.unregister_class(cls)
        
        #unregister path_tool as a type
        del bpy.types.Scene.path_tool
 
 
if __name__ == "__main__":
    register()

I've tried adding a draw function to the _main_panel_1 but the same error occurs I'm not sure what I might be missing.

$\endgroup$

1 Answer 1

0
$\begingroup$

Okay nevermind I found the errors I had made several errors which I have now fixed.

  1. I accidentally typed bpy.types.panel with a lowercase "p", it needs to uppercase in the class definitions.

  2. In the class definition for the LOADUESHADERSCRIPT_shared_panel there shouldn't have been a (bpy.types.Panel), there should be no parameters.

  3. You should not register the class LOADUESHADERSCRIPT_shared_panel, trying to register it will give you an error.

  4. The class LOADUESHADERSCRIPT_PT_main_panel_1 needs a draw function since it is a panel, but you can just put a pass in the draw function like so:

    def draw(self, context):
        pass

Thus, the corrected code is:

import bpy
#code for drawing main panel in the 3D View
class LOADUESHADERSCRIPT_shared_panel:
    #bl_label = "Load UE Shaders"
    #bl_idname = "LOADUESHADERSCRIPT_PT_main_panel"
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_category = "UE Shaders"


class LOADUESHADERSCRIPT_PT_main_panel_1(LOADUESHADERSCRIPT_shared_panel, bpy.types.Panel):
    bl_label = "Load UE Shaders"
    bl_idname = "LOADUESHADERSCRIPT_PT_main_panel_1"

    def draw(self, context):
        pass
        

#sub panel 1
class LOADUESHADERSCRIPT_PT_sub_select_preset_in_folder_2(LOADUESHADERSCRIPT_shared_panel, bpy.types.Panel):
    bl_label = "Select Preset in Folder"
    bl_parent_id = "LOADUESHADERSCRIPT_PT_main_panel_1"

    def draw(self, context):
        layout = self.layout
        
        #store active/selected scene to variable
        scene = context.scene
        
        #set isOverridePackage to override __package__ variable as it does
        #not work for imported functions
        isOverridePackage = True
        preferences = get_preferences(isOverridePackage)

        #make folder section
        box = layout.box()
        row = box.row()
        row.label(text="Folders")
        row = box.row()
        left = row.column()
        left.alignment = "RIGHT"
        left.prop(preferences, 'folders', expand=False)


        selected_folders_presets = get_selected_folder_presets(isOverridePackage)

        #create the list of current presets
        layout.template_list("SHADER_PRESETS_UL_items", "", selected_folders_presets,
                                "presets", selected_folders_presets, "preset_index", rows=5)


#sub panel 2
class LOADUESHADERSCRIPT_PT_sub_load_shader_map_settings_3(LOADUESHADERSCRIPT_shared_panel, bpy.types.Panel):
    bl_label = "Load Shader Map Settings"
    bl_parent_id = "LOADUESHADERSCRIPT_PT_main_panel_1"

    def draw(self, context):
        layout = self.layout
        
        #store active/selected scene to variable
        scene = context.scene
        #allow access to user inputted properties through pointer
        #to properties
        pathtool = scene.path_tool

        #--------make load shader map settings section
        layout.prop(pathtool, "is_load_img_textures")

        #option to replace or keep existing nodes in materials
        layout.prop(pathtool, "is_replace_nodes")

        #option to delete image texture nodes which have not had a texture
        #loaded into them
        layout.prop(pathtool, "is_delete_unused_img_texture_nodes")
        if(pathtool.is_load_img_textures):
            #only show this option if delete unused_img_texture_nodes is checked
            if(pathtool.is_delete_unused_img_texture_nodes):
                layout.prop(pathtool, "is_delete_unused_related_nodes")

            layout.prop(pathtool, "texture_file_type_enum")
            layout.prop(pathtool, "clipping_method_enum")

            layout.prop(pathtool, "is_reuse_node_group_with_same_name")
            layout.prop(pathtool, "is_reuse_img_texture_with_same_name")

            layout.prop(pathtool, "is_normal_non_colour")
            layout.prop(pathtool, "is_m_non_colour")
            
            layout.prop(pathtool, "is_change_principle_bsdf_emission_strength")

            if(pathtool.is_change_principle_bsdf_emission_strength):
                layout.prop(pathtool, "principled_bsdf_emission_strength_float")
            layout.prop(pathtool, "material_alpha_threshold")

            #Roman Noodles related settings
            layout.prop(pathtool, "is_orm_non_colour")
            layout.prop(pathtool, "is_add_skin_map")

        layout.operator("loadueshaderscript.reset_settings_main_panel_operator")

#sub panel 3
class LOADUESHADERSCRIPT_PT_sub_load_shader_map_methods_4(LOADUESHADERSCRIPT_shared_panel, bpy.types.Panel):
    bl_label = "Load Shader Map Methods"
    bl_parent_id = "LOADUESHADERSCRIPT_PT_main_panel_1"

    def draw(self, context):
        layout = self.layout
        
        #store active/selected scene to variable
        scene = context.scene
        #allow access to user inputted properties through pointer
        #to properties
        pathtool = scene.path_tool

        #--------------draw load shader map methods
        #formatting
        #layout.use_property_split means that it will try and display 
        #the property fully
        layout.use_property_split = True

        
        #create box for all related inputs adding shader map to multiple material
        box = layout.box()
        box.label(text = "ADD SHADER MAP TO MULTIPLE MATERIALS (MULTIPLE MATERIALS)")
        box.label(text = "Select a mesh, enter material indexes separated by a space and add shader maps to multiple materials")
        
        if(pathtool.is_load_img_textures):
            box.prop(pathtool, "material_folder_path")
            box.prop(pathtool, "material_indices_list_string")
            box.prop(pathtool, "export_folder_path")
            if(pathtool.is_add_skin_map):
                box.prop(pathtool, "skin_map_path")
        else:
            box.prop(pathtool, "material_indices_list_string")
        box.operator("loadueshaderscript.add_to_multiple_materials_operator")
                
        #Create a box for adding shader maps to all materials
        #to the selected mesh with all
        #related inputs and operators 
        box = layout.box()
        box.label(text = "ADD SHADER MAP TO ALL MATERIALS ON SELECTED MESHES (ALL MATERIALS)")
        box.label(text = "Select multiple meshes and add shader maps to all the materials on the selected meshes")
        if(pathtool.is_load_img_textures):
            box.prop(pathtool, "material_folder_path")
            box.prop(pathtool, "export_folder_path")
            if(pathtool.is_add_skin_map):
                box.prop(pathtool, "skin_map_path")
        box.operator("loadueshaderscript.add_to_selected_meshes_operator" )

        layout.use_property_split = False

        #create box for all related input adding shader map to one selected material
        #should hide by default, but show if is_show_add_one_material_operator is checked
        #use this because some props.txt files do not have the same name as their materials
        layout.prop(pathtool, "is_show_add_one_material_operator")

        #formatting
        #layout.use_property_split means that it will try and display 
        #the property fully
        layout.use_property_split = True

        if (pathtool.is_show_add_one_material_operator):
            box = layout.box()
            box.label(text = "ADD SHADER MAP TO SELECTED MATERIAL (ONE MATERIAL)")
            box.label(text = "Select a mesh and a material and add a shader map to the selected material")
            box.label(text = "Used for Materials which have different names to their props.txt files")
            if(pathtool.is_load_img_textures):
                box.prop(pathtool, "props_txt_path")
                box.prop(pathtool, "export_folder_path")
                if(pathtool.is_add_skin_map):
                    box.prop(pathtool, "skin_map_path")
            box.operator("loadueshaderscript.add_to_one_material_operator")

#don't need path properties if you're implementing this 
#yourself it's only something I had to store properties
classes = [PathProperties, 

LOADUESHADERSCRIPT_PT_main_panel_1,
LOADUESHADERSCRIPT_PT_sub_select_preset_in_folder_2,
LOADUESHADERSCRIPT_PT_sub_load_shader_map_settings_3, 
LOADUESHADERSCRIPT_PT_sub_load_shader_map_methods_4]
 
def register():
    for cls in classes:
        bpy.utils.register_class(cls)
        
        #register path_tool as a type which has all
        #the user input properties from the properties class 
        bpy.types.Scene.path_tool = bpy.props.PointerProperty(type = PathProperties)
 
def unregister():
    #unregister in reverse so classes relying on other classes
    #will not lead to an error
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)
        
        #unregister path_tool as a type
        del bpy.types.Scene.path_tool
 
 
if __name__ == "__main__":
    register()


$\endgroup$
1
  • $\begingroup$ For what has turned out to be somewhat, loosely related to blender, generic python script errors (Attribute Error re Q title) , consider deleting question rather than answering. Voted to close question based on this. $\endgroup$
    – batFINGER
    May 16, 2021 at 11:34

You must log in to answer this question.

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