1 # ##### BEGIN GPL LICENSE BLOCK #####
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
22 This module contains utility functions specific to blender but
23 not assosiated with blenders internal data.
26 from _bpy import register_class
27 from _bpy import unregister_class
29 from _bpy import blend_paths
30 from _bpy import script_paths as _bpy_script_paths
31 from _bpy import user_resource as _user_resource
40 def _test_import(module_name, loaded_modules):
43 if module_name in loaded_modules:
45 if "." in module_name:
46 print("Ignoring '%s', can't import files containing multiple periods." % module_name)
51 mod = __import__(module_name)
57 print("time %s %.4f" % (module_name, time.time() - t))
59 loaded_modules.add(mod.__name__) # should match mod.__name__ too
63 def _sys_path_ensure(path):
64 if path not in _sys.path: # reloading would add twice
65 _sys.path.insert(0, path)
68 def modules_from_path(path, loaded_modules):
70 Load all modules in a path and return them as a list.
72 :arg path: this path is scanned for scripts and packages.
74 :arg loaded_modules: already loaded module names, files matching these names will be ignored.
75 :type loaded_modules: set
76 :return: all loaded modules.
81 for mod_name, mod_path in _bpy.path.module_names(path):
82 mod = _test_import(mod_name, loaded_modules)
89 _global_loaded_modules = [] # store loaded module names for reloading.
90 import bpy_types as _bpy_types # keep for comparisons, never ever reload this.
93 def load_scripts(reload_scripts=False, refresh_scripts=False):
95 Load scripts and run each modules register function.
97 :arg reload_scripts: Causes all scripts to have their unregister method called before loading.
98 :type reload_scripts: bool
99 :arg refresh_scripts: only load scripts which are not already loaded as modules.
100 :type refresh_scripts: bool
107 loaded_modules = set()
110 original_modules = _sys.modules.values()
113 _bpy_types.TypeMap.clear()
115 # just unload, dont change user defaults, this means we can sync to reload.
116 # note that they will only actually reload of the modification time changes.
117 # this `wont` work for packages so... its not perfect.
118 for module_name in [ext.module for ext in _bpy.context.user_preferences.addons]:
119 addon_utils.disable(module_name, default_set=False)
121 def register_module_call(mod):
122 register = getattr(mod, "register", None)
127 traceback.print_exc()
129 print("\nWarning! '%s' has no register function, this is now a requirement for registerable scripts." % mod.__file__)
131 def unregister_module_call(mod):
132 unregister = getattr(mod, "unregister", None)
137 traceback.print_exc()
139 def test_reload(mod):
141 # reloading this causes internal errors
142 # because the classes from this module are stored internally
143 # possibly to refresh internal references too but for now, best not to.
144 if mod == _bpy_types:
148 return imp.reload(mod)
150 traceback.print_exc()
152 def test_register(mod):
154 if refresh_scripts and mod in original_modules:
157 if reload_scripts and mod:
158 print("Reloading:", mod)
159 mod = test_reload(mod)
162 register_module_call(mod)
163 _global_loaded_modules.append(mod.__name__)
167 # module names -> modules
168 _global_loaded_modules[:] = [_sys.modules[mod_name] for mod_name in _global_loaded_modules]
170 # loop over and unload all scripts
171 _global_loaded_modules.reverse()
172 for mod in _global_loaded_modules:
173 unregister_module_call(mod)
175 for mod in _global_loaded_modules:
178 _global_loaded_modules[:] = []
180 user_path = user_script_path()
182 for base_path in script_paths():
183 for path_subdir in ("", "ui", "op", "io", "keyingsets", "modules"):
184 path = _os.path.join(base_path, path_subdir)
185 if _os.path.isdir(path):
186 _sys_path_ensure(path)
188 # only add this to sys.modules, dont run
189 if path_subdir == "modules":
192 if user_path != base_path and path_subdir == "":
193 continue # avoid loading 2.4x scripts
195 for mod in modules_from_path(path, loaded_modules):
198 # deal with addons seperately
199 addon_utils.reset_all(reload_scripts)
201 # run the active integration preset
202 filepath = preset_find(_bpy.context.user_preferences.inputs.active_keyconfig, "keyconfig")
204 keyconfig_set(filepath)
208 print("gc.collect() -> %d" % gc.collect())
211 print("Python Script Load Time %.4f" % (time.time() - t_main))
215 _scripts = _os.path.join(_os.path.dirname(__file__), _os.path.pardir, _os.path.pardir)
216 _scripts = (_os.path.normpath(_scripts), )
219 def user_script_path():
220 path = _bpy.context.user_preferences.filepaths.script_directory
223 path = _os.path.normpath(path)
229 def script_paths(subdir=None, user=True):
231 Returns a list of valid script paths from the home directory and user preferences.
233 Accepts any number of string arguments which are joined to make a path.
235 scripts = list(_scripts)
237 # add user scripts dir
239 user_script_path = _bpy.context.user_preferences.filepaths.script_directory
241 user_script_path = None
243 for path in _bpy_script_paths() + (user_script_path, ):
245 path = _os.path.normpath(path)
246 if path not in scripts and _os.path.isdir(path):
254 path_subdir = _os.path.join(path, subdir)
255 if _os.path.isdir(path_subdir):
256 script_paths.append(path_subdir)
261 _presets = _os.path.join(_scripts[0], "presets") # FIXME - multiple paths
264 def preset_paths(subdir):
266 Returns a list of paths for a specific preset.
269 for path in script_paths("presets"):
270 directory = _os.path.join(path, subdir)
271 if _os.path.isdir(directory):
272 dirs.append(directory)
276 def smpte_from_seconds(time, fps=None):
278 Returns an SMPTE formatted string from the time in seconds: "HH:MM:SS:FF".
280 If the *fps* is not given the current scene is used.
285 fps = _bpy.context.scene.render.fps
287 hours = minutes = seconds = frames = 0
295 if time >= 3600.0: # hours
296 hours = int(time / 3600.0)
298 if time >= 60.0: # mins
299 minutes = int(time / 60.0)
303 frames = int(round(math.floor(((time - seconds) * fps))))
305 return "%s%02d:%02d:%02d:%02d" % (neg, hours, minutes, seconds, frames)
308 def smpte_from_frame(frame, fps=None, fps_base=None):
310 Returns an SMPTE formatted string from the frame: "HH:MM:SS:FF".
312 If *fps* and *fps_base* are not given the current scene is used.
316 fps = _bpy.context.scene.render.fps
319 fps_base = _bpy.context.scene.render.fps_base
321 return smpte_from_seconds((frame * fps_base) / fps, fps)
324 def preset_find(name, preset_path, display_name=False):
328 for directory in preset_paths(preset_path):
332 for fn in _os.listdir(directory):
333 if fn.endswith(".py") and name == _bpy.path.display_name(fn):
337 filename = name + ".py"
340 filepath = _os.path.join(directory, filename)
341 if _os.path.exists(filepath):
345 def keyconfig_set(filepath):
346 from os.path import basename, splitext
348 print("loading preset:", filepath)
349 keyconfigs = _bpy.context.window_manager.keyconfigs
350 kc_orig = keyconfigs.active
352 keyconfigs_old = keyconfigs[:]
355 exec(compile(open(filepath).read(), filepath, 'exec'), {"__file__": filepath})
358 traceback.print_exc()
360 kc_new = [kc for kc in keyconfigs if kc not in keyconfigs_old][0]
365 name = splitext(basename(filepath))[0]
367 kc_dupe = keyconfigs.get(name)
369 keyconfigs.remove(kc_dupe)
374 keyconfigs.active = kc_new
377 def user_resource(type, path="", create=False):
379 Return a user resource path (normally from the users home directory).
381 :arg type: Resource type in ['DATAFILES', 'CONFIG', 'SCRIPTS', 'AUTOSAVE'].
383 :arg subdir: Optional subdirectory.
385 :arg create: Treat the path as a directory and create it if its not existing.
386 :type create: boolean
391 target_path = _user_resource(type, path)
394 # should always be true.
396 # create path if not existing.
397 if not _os.path.exists(target_path):
399 _os.makedirs(target_path)
402 traceback.print_exc()
404 elif not _os.path.isdir(target_path):
405 print("Path %r found but isn't a directory!" % target_path)
411 def _bpy_module_classes(module, is_registered=False):
412 typemap_list = _bpy_types.TypeMap.get(module, ())
414 while i < len(typemap_list):
415 cls_weakref, path, line = typemap_list[i]
421 if is_registered == cls.is_registered:
422 yield (cls, path, line)
426 def register_module(module, verbose=False):
429 print("bpy.utils.register_module(%r): ..." % module)
430 for cls, path, line in _bpy_module_classes(module, is_registered=False):
432 print(" %s of %s:%s" % (cls, path, line))
436 print("bpy.utils.register_module(): failed to registering class '%s.%s'" % (cls.__module__, cls.__name__))
437 print("\t", path, "line", line)
438 traceback.print_exc()
441 if "cls" not in locals():
442 raise Exception("register_module(%r): defines no classes" % module)
445 def unregister_module(module, verbose=False):
448 print("bpy.utils.unregister_module(%r): ..." % module)
449 for cls, path, line in _bpy_module_classes(module, is_registered=True):
451 print(" %s of %s:%s" % (cls, path, line))
453 unregister_class(cls)
455 print("bpy.utils.unregister_module(): failed to unregistering class '%s.%s'" % (cls.__module__, cls.__name__))
456 print("\t", path, "line", line)
457 traceback.print_exc()