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$
  • 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 '16 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 '16 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$ – Jaroslav Jerryno Novotny Oct 5 '16 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 '16 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 '16 at 8:34
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$

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.