1

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()
Tiles
  • 2,028
  • 16
  • 27
  • 2
    Go to the section marked Restricted Context here https://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) – batFINGER Oct 05 '16 at 09:37
  • 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. – Tiles Oct 05 '16 at 09:46
  • 1
    This is explained here: http://blender.stackexchange.com/questions/32335/how-to-implement-custom-icons-for-my-script-addon/32336#32336. You will find answers how to get the path for an addon and for running it within Blender. – Jaroslav Jerryno Novotny Oct 05 '16 at 09:51
  • 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? :) – Tiles Oct 05 '16 at 09:56
  • I have already understood that bpy.data.texts does not work. But i still search for a working solution, the substitute for it. Also your linked solution does not work, it gives me a UnboundLocalError: local variable 'bpy' referenced before assignment at the line import bpy.utils.previews. – Tiles Oct 05 '16 at 16:07
  • Thanks zeffi. I thought it is already reduced enough. But i will try to reduce it. Give me a few moments ... – Tiles Oct 06 '16 at 08:30
  • 1
    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 https://github.com/zeffii/mesh_tiny_cad – zeffii Oct 06 '16 at 08:34
  • Thanks for the advice. The reduced example is up. I have edited the first post. Now i will try to follow the advice in your link. – Tiles Oct 06 '16 at 09:09
  • @brockmann lol here's another one re the confusion over that icons path from textblock question we edited a while back – batFINGER Mar 22 '21 at 14:53

1 Answers1

2

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()
Tiles
  • 2,028
  • 16
  • 27