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.