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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.