Page MenuHome

Toolbar icons do not honour BLENDER_SYSTEM_DATAFILES
Open, Confirmed, MediumPublic


System Information
Operating system: FreeBSD-12.0-STABLE-amd64-64bit-ELF 64 Bits
Graphics card: GeForce GT 730/PCIe/SSE2 NVIDIA Corporation 4.5.0 NVIDIA 390.87

Blender Version
Broken: version: 2.80 (sub 58), branch: master, commit date: 2019-04-20 17:01, hash: rBd11d5403f0a4

Short description of error
The environment variable BLENDER_SYSTEM_DATAFILES can be set to allow placing datafiles in different locations. The toolbar icons fail to follow this path and can only be found using the 2.80/datafiles path construct.

Exact steps for others to reproduce the error
Move the 2.80/datafiles folder to a different location than blender and set BLENDER_SYSTEM_DATAFILES to match. Only the toolbar icons fail to be found.

Also setting BLENDER_USER_DATAFILES does not help. Could/should this also be included in the icon search path?



Event Timeline

first note(s):

  • looks for icon folder in datafiles via bpy.utils.resource_path('LOCAL'), then bpy.utils.resource_path('SYSTEM')
  • under the hood resource_path uses BKE_appdir_folder_id_version (not good -- doesnt check environment variables at all)
  • there is also bpy.utils.user_resource('DATAFILES')
  • under the hood user_resource uses BKE_appdir_folder_id_user_notest (this checks environment variables but could fail -- because it doesnt check for subfolder existance -- see rBfe7c7d2820f8)
  • also above would only work with BLENDER_USER_DATAFILES, not BLENDER_SYSTEM_DATAFILES

to reliably go over all possible directories [including checking if environment variables are set] it would be good to have the equivalent of what init_iconfile_list() does:
icondir = BKE_appdir_folder_id(BLENDER_DATAFILES, "icons")
[In a way, this was done prior to rBfe7c7d2820f8]

Needs a bit more thinking to get it right from bpy, stay tuned...

Any ideas that could fix this for the 2.80 release?

Looking at this again today

To recap: from the bpy side we have:

  • bpy.utils.resource_path('LOCAL'/'SYSTEM'/'USER')
    • this doesnt take environment variables into account, because this returns the toplevel resource folder, environment variables tweak the "sublevel" [config, datafiles, scripts]
  • bpy.utils.user_resource('CONFIG'/'DATAFILES'/'SCRIPTS')
    • checks _USER_ environment variables (BLENDER_USER_DATAFILES, BLENDER_USER_CONFIG, ...)
    • this works, but we cant use it in because we need a general path, not just the user path

So as it seems we have no way to query a resource path from python that takes environment variables into account other than the _USER_ ones.
We would need such functionality in bpy for to be able to reliably check where to get icons from.

How about adding:

  • bpy.utils.resource('CONFIG'/'DATAFILES'/'SCRIPTS')?
    • this would then use BKE_appdir_folder_id (which covers the environment variables)
      • in detail: we would use the BLENDER_DATAFILES folder_id in BKE_appdir_folder_id [this goes through the whole hierarchy of user first (envvar precedence), then local, then system (envvar precedence)]
      • for 'CONFIG' and 'SCRIPTS': we'd need to add BLENDER_CONFIG and BLENDER_SCRIPTS [to go through the whole hierarchy of user first (envvar precedence), then local, then system (envvar precedence)]
  • alternatively: bpy.utils.resource_datafiles() - takes no arguments
    • same as above, but does it only for DATAFILES (which is the most common usecase I guess)
    • can just use BKE_appdir_folder_id with BLENDER_DATAFILES folder_id (so we are fully covered already)

Sorry for the long blathering (I hope I am not overcomlicating things), but before implementing this, would like to get feedback from @Brecht Van Lommel (brecht) and @Campbell Barton (campbellbarton) here...

Note though that I'll be away next week, so from my side, this wont be ready for 2.80 (unless someone else jumps in)...

See the following for implementing (and using) bpy.utils.resource_datafiles()

3diff --git a/release/scripts/modules/bpy/utils/ b/release/scripts/modules/bpy/utils/
4index e6424de0742..8d913f54a70 100644
5--- a/release/scripts/modules/bpy/utils/
6+++ b/release/scripts/modules/bpy/utils/
7@@ -44,6 +44,7 @@ __all__ = (
8 "manual_map",
9 "previews",
10 "resource_path",
11+ "resource_datafiles",
12 "script_path_user",
13 "script_path_pref",
14 "script_paths",
15@@ -62,6 +63,7 @@ from _bpy import (
16 escape_identifier,
17 register_class,
18 resource_path,
19+ resource_datafiles,
20 script_paths as _bpy_script_paths,
21 unregister_class,
22 user_resource as _user_resource,
23diff --git a/release/scripts/startup/bl_ui/ b/release/scripts/startup/bl_ui/
24index e7e95c26b55..565ee121744 100644
25--- a/release/scripts/startup/bl_ui/
26+++ b/release/scripts/startup/bl_ui/
27@@ -195,11 +195,11 @@ class ToolSelectPanelHelper:
28 assert(type(icon_name) is str)
29 icon_value = _icon_cache.get(icon_name)
30 if icon_value is None:
31- dirname = bpy.utils.resource_path('LOCAL')
32+ dirname = bpy.utils.resource_datafiles()
33 if not os.path.exists(dirname):
34- # TODO(campbell): use a better way of finding datafiles.
35- dirname = bpy.utils.resource_path('SYSTEM')
36- filename = os.path.join(dirname, "datafiles", "icons", icon_name + ".dat")
37+ # TODO(philipp): what to do then?
38+ pass
39+ filename = os.path.join(dirname, "icons", icon_name + ".dat")
40 try:
41 icon_value =
42 except Exception as ex:
43diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c
44index 92ba7704b19..b436336844d 100644
45--- a/source/blender/python/intern/bpy.c
46+++ b/source/blender/python/intern/bpy.c
47@@ -231,6 +231,22 @@ static PyObject *bpy_resource_path(PyObject *UNUSED(self), PyObject *args, PyObj
48 return PyC_UnicodeFromByte(path ? path : "");
49 }
52+ ".. function:: script_paths()\n"
53+ "\n"
54+ " Return the resource datafiles path.\n"
55+ "\n"
56+ " :return: the resource datafiles path.\n"
57+ " :rtype: string\n");
58+static PyObject *bpy_resource_datafiles(PyObject *UNUSED(self))
60+ const char *path;
62+ path = BKE_appdir_folder_id(BLENDER_DATAFILES, NULL);
64+ return PyC_UnicodeFromByte(path ? path : "");
67 PyDoc_STRVAR(bpy_escape_identifier_doc,
68 ".. function:: escape_identifier(string)\n"
69 "\n"
70@@ -298,6 +314,12 @@ static PyMethodDef meth_bpy_resource_path = {
72 bpy_resource_path_doc,
73 };
74+static PyMethodDef meth_bpy_resource_datafiles = {
75+ "resource_datafiles",
76+ (PyCFunction)bpy_resource_datafiles,
78+ bpy_resource_datafiles_doc,
80 static PyMethodDef meth_bpy_escape_identifier = {
81 "escape_identifier",
82 (PyCFunction)bpy_escape_identifier,
83@@ -400,6 +422,9 @@ void BPy_init_modules(void)
84 PyModule_AddObject(mod,
85 meth_bpy_resource_path.ml_name,
86 (PyObject *)PyCFunction_New(&meth_bpy_resource_path, NULL));
87+ PyModule_AddObject(mod,
88+ meth_bpy_resource_datafiles.ml_name,
89+ (PyObject *)PyCFunction_New(&meth_bpy_resource_datafiles, NULL));
90 PyModule_AddObject(mod,
91 meth_bpy_escape_identifier.ml_name,
92 (PyObject *)PyCFunction_New(&meth_bpy_escape_identifier, NULL));

note we are also in API freeze, so above solution would be for 2.81 I guess...