# Custom icons for my add-on doesn't work!

I am making custom icons for my Blender add-on. I read the bpy.utils.previews.ImagePreviewCollection already. I tried code and set the __file__ to bpy.context.space_data.text.filepath it works very well.

But, when I add the codes to my add-on file, it doesn't show the icon. Here is the code:


bl_info = {
"author": "me",
"version": (1, 0),
"blender": (2, 92, 0),
"location": "View3D > Toolbar > Shader",
"category": "3D View",
}

import os
import bpy
from bpy.types import Operator
from bpy.props import StringProperty, IntProperty
from bpy.types import Panel

# Main panel for adding colour to object
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'

def draw(self, context):
layout = self.layout
pcoll = preview_collections["main"]
row = layout.row()
my_icon = pcoll["my_icon"]
your_icon = pcoll["your_icon"]

row.operator("render.render", icon_value=your_icon.icon_id)
row.operator("render.render", icon_value=my_icon.icon_id)

preview_collections = {}

def register():
from bpy.types import WindowManager
from bpy.props import (
StringProperty,
EnumProperty,
)

############ Custom Icons ############
import bpy.utils.previews
pcoll = bpy.utils.previews.new()

# path to the folder where the icon is
# the path is calculated relative to this py file inside the addon folder
my_icons_dir = os.path.join(os.path.dirname(__file__), "icons")

# load a preview thumbnail of a file and store in the previews collection

preview_collections["main"] = pcoll
#######################################

for cls in classes:
bpy.utils.register_class(cls)

def unregister():

for pcoll in preview_collections.values():
bpy.utils.previews.remove(pcoll)
preview_collections.clear()

for cls in classes:
bpy.utils.unregister_class(cls)

if __name__ == "__main__":
__file__ = bpy.context.space_data.text.filepath
register()


I did not get error, but it doesn't show icon. Here is what I got:

Here is the folders and files/images location:

• Hello ! While it's nice to provide a working code example, could you restrict it to the part that's responsible for drawing the UI so it's easier to pinpoint it ? Thanks in advance :) Jan 24, 2022 at 6:40
• @Gorgious I don't understand what you want me to do. Do you mean there is too many codes on it that uneasy too read? Jan 24, 2022 at 6:43
• Do you know why my code doesn't work, but the code in documentation works? Jan 24, 2022 at 6:45
• Yes, I'm saying it would take me 10 mins to read through all of your addon code to try and find out what is wrong, while if you isolated the part that's responsible for drawing the UI in the panel, that time would be much faster Jan 24, 2022 at 6:48
• @Gorgious Now, I edited. I already put those unrelated codes away and pls see my codes and find what's the problem. thanks Jan 24, 2022 at 6:56

Your path to resources is wrong:

my_icons_dir = os.path.join(os.path.dirname(__file__), "icons")


See template ui_previews_custom_icon.py:

IMPORTANT NOTE: You need to replace the image path with a real existing one. For distributable scripts, it is recommended to place the icons inside the addon folder and access it relative to the py script file for portability

For this we can use bpy.utils.user_resource(resource_type, path='', create=False) link

Script:

#Add-on info
bl_info = {
"author": "me",
"version": (1, 0),
"blender": (2, 92, 0),
"location": "View3D > Toolbar > Shader",
"category": "3D View",
}

import os
import bpy
from bpy.types import Operator
from bpy.props import StringProperty, IntProperty
from bpy.types import Panel

# Main panel for adding colour to object
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'

def draw(self, context):
layout = self.layout
pcoll = preview_collections["main"]
row = layout.row()
my_icon = pcoll["my_icon"]
your_icon = pcoll["your_icon"]

row.operator("render.render", icon_value=your_icon.icon_id)
row.operator("render.render", icon_value=my_icon.icon_id)

preview_collections = {}

def register():
from bpy.types import WindowManager
from bpy.props import (
StringProperty,
EnumProperty,
)

############ Custom Icons ############
import bpy.utils.previews
pcoll = bpy.utils.previews.new()

# path to the folder where the icon is
# the path is calculated relative to this py file inside the addon folder
###    my_icons_dir = os.path.join(os.path.dirname(__file__), "icons")

# this path is for installed addons in user_resources ; doesn´t work as script , for script you need a path to your resources

# load a preview thumbnail of a file and store in the previews collection

preview_collections["main"] = pcoll
#######################################

for cls in classes:
bpy.utils.register_class(cls)

def unregister():

for pcoll in preview_collections.values():
bpy.utils.previews.remove(pcoll)
preview_collections.clear()

for cls in classes:
bpy.utils.unregister_class(cls)

if __name__ == "__main__":
__file__ = bpy.context.space_data.text.filepath
register()

• according to your answer, my code should change to my_icons_dir = bpy.utils.user_resource('SCRIPTS', path='Shader Adder\icons' , create=False) since my path is \Documents\Scripting\Blender\Shader Adder\icons but this doesn't work. Please tell me what is the problem, thx Jan 24, 2022 at 12:37
• Another question about your answer, you tell me to see template ui_previews_custom_icon.py, but my code is copy from it. You also note that I need to replace the image path with a real existing one, so my code has: __file__ = bpy.context.space_data.text.filepath . Jan 24, 2022 at 12:41
• print(bpy.utils.user_resource('SCRIPTS')) returns the folder where installed (!) user addons are saved in a subfolder called addons. For this path='addons\Shader Adder\icons'if addonfolder is Shader Adder and icons in subfolder icons. bpy.context.space_data.text.filepath is the path from where a file was loaded into the text editor. Jan 24, 2022 at 14:43
• Thank you very much for answering. I understand now. Jan 24, 2022 at 15:58