1
$\begingroup$

I work at an addon that works with external icons. When I run the script from within Blender then all is fine. When I make it an add-on, and install it, and try to activate it, then I get an error:

AttributeError: '_RestrictContext' object has no attribute 'space_data'

The vital code part that throws the error is:

script_path = bpy.context.space_data.text.filepath

Why does it work when i run it inside Blender, and not as an addon?

The problem reduced to one icon button:

import bpy
import os
import bpy.utils.previews

bl_info = {
    "name": "Icon problem as addon",
    "author": "X",
    "version": (1, 0, 0),
    "blender": (2, 76, 0),
    "location": "",
    "description": "Test",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "User Interface"}

# ¸¸♫·¯·♪¸¸♩·¯·♬¸¸¸¸♫·¯·♪¸¸♩·¯·♬¸¸¸¸♫·¯·♪¸¸♩·¯·♬¸¸¸¸♫·¯·♪¸¸♩·¯·♬¸¸¸¸♫·¯·♪¸¸♩·¯·♬¸¸¸¸♫·¯·♪¸¸♩·¯·♬¸¸

def addon_button(self, context):
    layout = self.layout

    global custom_icons 

    row = layout.row(align=True)
    row.operator("object.mode_set", text="", icon_value = custom_icons["icon_not_available"].icon_id).mode = 'OBJECT'  

# global variable to store icons in
custom_icons = None

def register():

    bpy.types.VIEW3D_HT_header.prepend(addon_button) # Here we add our button in front of the View 3D header. 

    # Our external Icons
    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("icon_not_available", os.path.join(icons_dir, "NOT_AVAILABLE.png"), 'IMAGE')

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

# This allows you to run the script directly from blenders text editor
# to test the addon without having to install it.

if __name__ == "__main__":
    register()
$\endgroup$
9
  • 2
    $\begingroup$ Go to the section marked Restricted Context here wiki.blender.org/index.php/Extensions:2.6/Py/API_Changes You prob don't need script_path = bpy.context.space_data.text.filepath in an addon anyhow? For an addon consider using icon_dir = path.join(path.dirname(__file__), "icons") (path is os.path) $\endgroup$
    – batFINGER
    Oct 5, 2016 at 9:37
  • $\begingroup$ Well this line of code detects the script path, and so the path of the icons. How should the icon path be found without this? I tried to comment out the line, and made the icons dir to icons_dir = ( "icons"). The addon now loads. But then the icons doesn't show anymore. $\endgroup$
    – Tiles
    Oct 5, 2016 at 9:46
  • 1
    $\begingroup$ This is explained here: blender.stackexchange.com/questions/32335/…. You will find answers how to get the path for an addon and for running it within Blender. $\endgroup$ Oct 5, 2016 at 9:51
  • $\begingroup$ And this is what does not work. I overlook something it seems :/ . Wouldn't it be easier to tell me the solution instead of linking me around? :) $\endgroup$
    – Tiles
    Oct 5, 2016 at 9:56
  • 1
    $\begingroup$ Make it so it only tries to load / display one icon. The result of your reduction doesn't even have to be a useful add-on, as long as it attempts to display a single custom icon :) and then try to work the code linked to by Jerryno or the custom icons Template in TextEditor, or a working example add-on github.com/zeffii/mesh_tiny_cad $\endgroup$
    – zeffii
    Oct 6, 2016 at 8:34

1 Answer 1

2
$\begingroup$

The solution was as easy as replacing

script_path = bpy.context.space_data.text.filepath
icons_dir = os.path.join(os.path.dirname(script_path), "icons")

by

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

Problem solved.

Full example script:

import bpy
import os
import bpy.utils.previews

bl_info = {
    "name": "Icon problem as addon",
    "author": "X",
    "version": (1, 0, 0),
    "blender": (2, 76, 0),
    "location": "",
    "description": "Test",
    "warning": "",
    "wiki_url": "",
    "tracker_url": "",
    "category": "User Interface"}

# ¸¸♫·¯·♪¸¸♩·¯·♬¸¸¸¸♫·¯·♪¸¸♩·¯·♬¸¸¸¸♫·¯·♪¸¸♩·¯·♬¸¸¸¸♫·¯·♪¸¸♩·¯·♬¸¸¸¸♫·¯·♪¸¸♩·¯·♬¸¸¸¸♫·¯·♪¸¸♩·¯·♬¸¸

def addon_button(self, context):
    layout = self.layout

    global custom_icons 

    row = layout.row(align=True)
    row.operator("object.mode_set", text="", icon_value = custom_icons["icon_not_available"].icon_id).mode = 'OBJECT'  

# global variable to store icons in
custom_icons = None

def register():

    bpy.types.VIEW3D_HT_header.prepend(addon_button) # Here we add our button in front of the View 3D header. 

    # Our external Icons
    global custom_icons
    custom_icons = bpy.utils.previews.new()
    # Use this for an addon.
    icons_dir = os.path.join(os.path.dirname(__file__), "icons")

    # Use this inside a script
    #script_path = bpy.context.space_data.text.filepath
    #icons_dir = os.path.join(os.path.dirname(script_path), "icons")

    custom_icons.load("icon_not_available", os.path.join(icons_dir, "NOT_AVAILABLE.png"), 'IMAGE')

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

# This allows you to run the script directly from blenders text editor
# to test the addon without having to install it.

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

You must log in to answer this question.

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