crash with viewing histogram on a blank image
[blender.git] / release / scripts / modules / bpy / utils.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 """
22 This module contains utility functions spesific to blender but
23 not assosiated with blenders internal data.
24 """
25
26 import bpy as _bpy
27 import os as _os
28 import sys as _sys
29
30 def load_scripts(reload_scripts=False, refresh_scripts=False):
31     import traceback
32     import time
33
34     t_main = time.time()
35
36     loaded_modules = set()
37
38     def test_import(module_name):
39         if module_name in loaded_modules:
40             return None
41         if "." in module_name:
42             print("Ignoring '%s', can't import files containing multiple periods." % module_name)
43             return None
44
45         try:
46             t = time.time()
47             ret = __import__(module_name)
48             if _bpy.app.debug:
49                 print("time %s %.4f" % (module_name, time.time() - t))
50             return ret
51         except:
52             traceback.print_exc()
53             return None
54
55     def test_reload(module):
56         try:
57             reload(module)
58         except:
59             traceback.print_exc()
60
61     if reload_scripts:
62         # reload modules that may not be directly included
63         for type_class_name in dir(_bpy.types):
64             type_class = getattr(_bpy.types, type_class_name)
65             module_name = getattr(type_class, "__module__", "")
66
67             if module_name and module_name != "bpy.types": # hard coded for C types
68                loaded_modules.add(module_name)
69
70         for module_name in loaded_modules:
71             print("Reloading:", module_name)
72             test_reload(_sys.modules[module_name])
73
74     for base_path in script_paths():
75         for path_subdir in ("ui", "op", "io"):
76             path = _os.path.join(base_path, path_subdir)
77             if _os.path.isdir(path):
78
79                 # needed to load scripts after the users script path changes
80                 # we should also support a full reload but since this is now unstable it can be postponed.
81                 if refresh_scripts and path in _sys.path:
82                     continue
83
84                 if path not in _sys.path: # reloading would add twice
85                     _sys.path.insert(0, path)
86                 for f in sorted(_os.listdir(path)):
87                     if f.endswith(".py"):
88                         # python module
89                         mod = test_import(f[0:-3])
90                     elif "." not in f:
91                         # python package
92                         mod = test_import(f)
93                     else:
94                         mod = None
95
96                     if reload_scripts and mod:
97                         print("Reloading:", mod)
98                         test_reload(mod)
99
100     if _bpy.app.debug:
101         print("Time %.4f" % (time.time() - t_main))
102
103
104 def expandpath(path):
105     if path.startswith("//"):
106         return _os.path.join(_os.path.dirname(_bpy.data.filename), path[2:])
107
108     return path
109
110
111 _unclean_chars = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, \
112     17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, \
113     35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 46, 47, 58, 59, 60, 61, 62, 63, \
114     64, 91, 92, 93, 94, 96, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, \
115     133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, \
116     147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, \
117     161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, \
118     175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, \
119     189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, \
120     203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, \
121     217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, \
122     231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, \
123     245, 246, 247, 248, 249, 250, 251, 252, 253, 254]
124
125 _unclean_chars = ''.join([chr(i) for i in _unclean_chars])
126
127
128 def clean_name(name, replace="_"):
129     """
130     Returns a name with characters replaced that may cause problems under various circumstances, such as writing to a file.
131     All characters besides A-Z/a-z, 0-9 are replaced with "_"
132     or the replace argumet if defined.
133     """
134     for ch in _unclean_chars:
135         name = name.replace(ch, replace)
136     return name
137
138
139 def display_name(name):
140     """
141     Creates a display string from name to be used menus and the user interface.
142     Capitalize the first letter in all lowercase names, mixed case names are kept as is.
143     Intended for use with filenames and module names.
144     """
145     name_base = _os.path.splitext(name)[0]
146
147     # string replacements
148     name_base = name_base.replace("_colon_", ":")
149
150     name_base = name_base.replace("_", " ")
151
152     if name_base.islower():
153         return name_base.capitalize()
154     else:
155         return name_base
156
157
158 # base scripts
159 _scripts = _os.path.join(_os.path.dirname(__file__), _os.path.pardir, _os.path.pardir)
160 _scripts = (_os.path.normpath(_scripts), )
161
162
163 def script_paths(*args):
164     """
165     Returns a list of valid script paths from the home directory and user preferences.
166
167     Accepts any number of string arguments which are joined to make a path.
168     """
169     scripts = list(_scripts)
170
171     # add user scripts dir
172     user_script_path = _bpy.context.user_preferences.filepaths.python_scripts_directory
173
174     if not user_script_path:
175         # XXX - WIN32 needs checking, perhaps better call a blender internal function.
176         user_script_path = _os.path.join(_os.path.expanduser("~"), ".blender", "scripts")
177
178     user_script_path = _os.path.normpath(user_script_path)
179
180     if user_script_path not in scripts and _os.path.isdir(user_script_path):
181         scripts.append(user_script_path)
182
183     if not args:
184         return scripts
185
186     subdir = _os.path.join(*args)
187     script_paths = []
188     for path in scripts:
189         path_subdir = _os.path.join(path, subdir)
190         if _os.path.isdir(path_subdir):
191             script_paths.append(path_subdir)
192
193     return script_paths
194
195
196 _presets = _os.path.join(_scripts[0], "presets") # FIXME - multiple paths
197
198
199 def preset_paths(subdir):
200     '''
201     Returns a list of paths for a spesific preset.
202     '''
203
204     return (_os.path.join(_presets, subdir), )