2
$\begingroup$

I've been trying to make an addon in Blender 3D, and have basically started out making a custom button that's going to generate a mesh. I want a custom icon for the mesh I'm going to generate, and I can get it to work as a script, but not as an addon. So when I click 'run script' a button shows up with my icon (which is in a folder called 'icons')

I've seen one code template that looks promising for an addon, but I can't get my icon to load when I try it:https://developer.blender.org/diffusion/B/browse/master/release/scripts/templates_py/ui_previews_custom_icon.py

If anyone has any examples of working addon code (like an example addon with a custom button with a custom icon) it would be very helpful.

I've already tried using one solution I found on this site, but it only works as a script, and not as an addon. Here's the code that works as a script:

   #----------------------------------------------------------
# File hello.py
#----------------------------------------------------------
import bpy

#
#    Menu in tools region
#
class ToolsPanel(bpy.types.Panel):
    bl_label = "MyAddon"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"


    def draw(self, context):
        self.layout.operator("hello.hello", icon_value=custom_icons["custom_icon"].icon_id)
        global custom_icons

#

class OBJECT_OT_HelloButton(bpy.types.Operator):
    bl_idname = "hello.hello"
    bl_label = "Generate MyMesh"
    bl_icon= "custom_icons[custom_icon].icon_id"
    country = bpy.props.StringProperty()

    def execute(self, context):
        if self.country == '':
            print("Hello world!")
        else:
            print("Hello world from %s!" % self.country)
        return{'FINISHED'}    

#
#    Registration
#   All panels and operators must be registered with Blender; otherwise
#   they do not show up. The simplest way to register everything in the
#   file is with a call to bpy.utils.register_module(__name__).
#

bpy.utils.register_module(__name__)

import os
import bpy
import bpy.utils.previews

class Panel(bpy.types.Panel):
    """Creates a Panel in the 3D view Tools panel"""
    bl_label = "Custom Icon Preview Panel"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"

    def draw(self, context):
        global custom_icons


# global variable to store icons in
custom_icons = None

def register():
    global custom_icons
    custom_icons = bpy.utils.previews.new()
    script_path = bpy.context.space_data.text.filepath
    icons_dir = os.path.join(os.path.dirname(script_path), "icons")
    custom_icons.load("custom_icon", os.path.join(icons_dir, "icon.png"), 'IMAGE')
    bpy.utils.register_module(__name__)

def unregister():
    global custom_icons
    bpy.utils.previews.remove(custom_icons)
    bpy.utils.unregister_module(__name__)

if __name__ == "__main__":
    register()
$\endgroup$
2
  • $\begingroup$ also related: blender.stackexchange.com/questions/41565/… $\endgroup$
    – zeffii
    Jan 14, 2016 at 6:58
  • $\begingroup$ i've edited the title so it reflects the problem. and that isn't problems with icons, but problems with setting the path to icons in the scenario where the code is part of an addon. perhaps we can include a line in the template to show what that should look like using __file__ $\endgroup$
    – zeffii
    Jan 14, 2016 at 14:18

1 Answer 1

3
$\begingroup$

in particular this line won't produce the same result when running it as an add-on:

script_path = bpy.context.space_data.text.filepath

because the code isn't located in a text data block inside the .blend any longer, but directly on your file-system/ harddisk


to get the script path as an addon you can do

script_path = os.path.dirname(__file__)

or as a one liner set icons_dir:

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

enter image description here

to complete your script. I reused the template in TextEditor, and some variable names may have changed. (good practice to figure out what's different). There's no need to use global keyword, using a dict for the preview_collections lets you avoid that complication.

bl_info = {
    "name": "FILL ME IN",
    "author": "fauxbar",
    "version": (0, 1),
    "blender": (2, 7, 6),
    "category": "3D View"
}

import os
import bpy

# global variable to store icons in
preview_collections = {}


class ToolsPanel(bpy.types.Panel):
    bl_label = "MyAddon"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"

    def draw(self, context):
        this_icon = preview_collections["custom_icons"]["custom_icon"].icon_id
        self.layout.operator("hello.hello", icon_value=this_icon)


class OBJECT_OT_HelloButton(bpy.types.Operator):
    bl_idname = "hello.hello"
    bl_label = "Generate MyMesh"
    # the icon is told to appear by layout.operator(...icon_value=this_icon) (above)

    def execute(self, context):
        print("Hello world, from space")
        return{'FINISHED'}


class Panel(bpy.types.Panel):
    """Creates a Panel in the 3D view Tools panel"""
    bl_label = "Custom Icon Preview Panel"
    bl_space_type = "VIEW_3D"
    bl_region_type = "TOOLS"

    def draw(self, context):
        self.layout.row().label('something')


def register():

    import bpy.utils.previews
    pcoll = bpy.utils.previews.new()
    icons_dir = os.path.join(os.path.dirname(__file__), "icons")
    pcoll.load("custom_icon", os.path.join(icons_dir, "icon.png"), 'IMAGE')
    preview_collections["custom_icons"] = pcoll

    bpy.utils.register_module(__name__)


def unregister():

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

    bpy.utils.unregister_module(__name__)


if __name__ == "__main__":
    register()
$\endgroup$

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