remove duplicate import
[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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8-80 compliant>
20
21 """
22 This module contains utility functions specific to blender but
23 not assosiated with blenders internal data.
24 """
25
26 __all__ = (
27     "blend_paths",
28     "keyconfig_set",
29     "load_scripts",
30     "modules_from_path",
31     "preset_find",
32     "preset_paths",
33     "refresh_script_paths",
34     "register_class",
35     "register_module",
36     "resource_path",
37     "script_paths",
38     "smpte_from_frame",
39     "smpte_from_seconds",
40     "unregister_class",
41     "unregister_module",
42     "user_resource",
43     "user_script_path",
44     )
45
46 from _bpy import register_class, unregister_class, blend_paths, resource_path
47 from _bpy import script_paths as _bpy_script_paths
48 from _bpy import user_resource as _user_resource
49
50 import bpy as _bpy
51 import os as _os
52 import sys as _sys
53
54 import addon_utils as _addon_utils
55
56 _script_module_dirs = "startup", "modules"
57
58
59 def _test_import(module_name, loaded_modules):
60     use_time = _bpy.app.debug_python
61
62     if module_name in loaded_modules:
63         return None
64     if "." in module_name:
65         print("Ignoring '%s', can't import files containing "
66               "multiple periods" % module_name)
67         return None
68
69     if use_time:
70         import time
71         t = time.time()
72
73     try:
74         mod = __import__(module_name)
75     except:
76         import traceback
77         traceback.print_exc()
78         return None
79
80     if use_time:
81         print("time %s %.4f" % (module_name, time.time() - t))
82
83     loaded_modules.add(mod.__name__)  # should match mod.__name__ too
84     return mod
85
86
87 def _sys_path_ensure(path):
88     if path not in _sys.path:  # reloading would add twice
89         _sys.path.insert(0, path)
90
91
92 def modules_from_path(path, loaded_modules):
93     """
94     Load all modules in a path and return them as a list.
95
96     :arg path: this path is scanned for scripts and packages.
97     :type path: string
98     :arg loaded_modules: already loaded module names, files matching these
99        names will be ignored.
100     :type loaded_modules: set
101     :return: all loaded modules.
102     :rtype: list
103     """
104     modules = []
105
106     for mod_name, mod_path in _bpy.path.module_names(path):
107         mod = _test_import(mod_name, loaded_modules)
108         if mod:
109             modules.append(mod)
110
111     return modules
112
113
114 _global_loaded_modules = []  # store loaded module names for reloading.
115 import bpy_types as _bpy_types  # keep for comparisons, never ever reload this.
116
117
118 def load_scripts(reload_scripts=False, refresh_scripts=False):
119     """
120     Load scripts and run each modules register function.
121
122     :arg reload_scripts: Causes all scripts to have their unregister method
123        called before loading.
124     :type reload_scripts: bool
125     :arg refresh_scripts: only load scripts which are not already loaded
126        as modules.
127     :type refresh_scripts: bool
128     """
129     use_time = _bpy.app.debug_python
130
131     prefs = _bpy.context.user_preferences
132
133     if use_time:
134         import time
135         t_main = time.time()
136
137     loaded_modules = set()
138
139     if refresh_scripts:
140         original_modules = _sys.modules.values()
141
142     if reload_scripts:
143         _bpy_types.TypeMap.clear()
144
145         # just unload, don't change user defaults, this means we can sync
146         # to reload. note that they will only actually reload of the
147         # modification time changes. This `won't` work for packages so...
148         # its not perfect.
149         for module_name in [ext.module for ext in prefs.addons]:
150             _addon_utils.disable(module_name, default_set=False)
151
152     def register_module_call(mod):
153         register = getattr(mod, "register", None)
154         if register:
155             try:
156                 register()
157             except:
158                 import traceback
159                 traceback.print_exc()
160         else:
161             print("\nWarning! '%s' has no register function, "
162                   "this is now a requirement for registerable scripts" %
163                   mod.__file__)
164
165     def unregister_module_call(mod):
166         unregister = getattr(mod, "unregister", None)
167         if unregister:
168             try:
169                 unregister()
170             except:
171                 import traceback
172                 traceback.print_exc()
173
174     def test_reload(mod):
175         import imp
176         # reloading this causes internal errors
177         # because the classes from this module are stored internally
178         # possibly to refresh internal references too but for now, best not to.
179         if mod == _bpy_types:
180             return mod
181
182         try:
183             return imp.reload(mod)
184         except:
185             import traceback
186             traceback.print_exc()
187
188     def test_register(mod):
189
190         if refresh_scripts and mod in original_modules:
191             return
192
193         if reload_scripts and mod:
194             print("Reloading:", mod)
195             mod = test_reload(mod)
196
197         if mod:
198             register_module_call(mod)
199             _global_loaded_modules.append(mod.__name__)
200
201     if reload_scripts:
202
203         # module names -> modules
204         _global_loaded_modules[:] = [_sys.modules[mod_name]
205                                      for mod_name in _global_loaded_modules]
206
207         # loop over and unload all scripts
208         _global_loaded_modules.reverse()
209         for mod in _global_loaded_modules:
210             unregister_module_call(mod)
211
212         for mod in _global_loaded_modules:
213             test_reload(mod)
214
215         _global_loaded_modules[:] = []
216
217     for base_path in script_paths():
218         for path_subdir in _script_module_dirs:
219             path = _os.path.join(base_path, path_subdir)
220             if _os.path.isdir(path):
221                 _sys_path_ensure(path)
222
223                 # only add this to sys.modules, don't run
224                 if path_subdir == "modules":
225                     continue
226
227                 for mod in modules_from_path(path, loaded_modules):
228                     test_register(mod)
229
230     # deal with addons separately
231     _addon_utils.reset_all(reload_scripts)
232
233     # run the active integration preset
234     filepath = preset_find(prefs.inputs.active_keyconfig, "keyconfig")
235
236     if filepath:
237         keyconfig_set(filepath)
238
239     if reload_scripts:
240         import gc
241         print("gc.collect() -> %d" % gc.collect())
242
243     if use_time:
244         print("Python Script Load Time %.4f" % (time.time() - t_main))
245
246
247 # base scripts
248 _scripts = _os.path.join(_os.path.dirname(__file__),
249                          _os.path.pardir,
250                          _os.path.pardir,
251                          )
252 _scripts = (_os.path.normpath(_scripts), )
253
254
255 def user_script_path():
256     prefs = _bpy.context.user_preferences
257     path = prefs.filepaths.script_directory
258
259     if path:
260         path = _os.path.normpath(path)
261         return path
262     else:
263         return None
264
265
266 def script_paths(subdir=None, user_pref=True, check_all=False):
267     """
268     Returns a list of valid script paths.
269
270     :arg subdir: Optional subdir.
271     :type subdir: string
272     :arg user_pref: Include the user preference script path.
273     :type user_pref: bool
274     :arg check_all: Include local, user and system paths rather just the paths
275        blender uses.
276     :type check_all: bool
277     :return: script paths.
278     :rtype: list
279     """
280     scripts = list(_scripts)
281     prefs = _bpy.context.user_preferences
282
283     # add user scripts dir
284     user_script = prefs.filepaths.script_directory if user_pref else None
285
286     if check_all:
287         # all possible paths
288         base_paths = tuple(_os.path.join(resource_path(res), "scripts")
289                            for res in ('LOCAL', 'USER', 'SYSTEM'))
290     else:
291         # only paths blender uses
292         base_paths = _bpy_script_paths()
293
294     for path in base_paths + (user_script, ):
295         if path:
296             path = _os.path.normpath(path)
297             if path not in scripts and _os.path.isdir(path):
298                 scripts.append(path)
299
300     if subdir is None:
301         return scripts
302
303     scripts_subdir = []
304     for path in scripts:
305         path_subdir = _os.path.join(path, subdir)
306         if _os.path.isdir(path_subdir):
307             scripts_subdir.append(path_subdir)
308
309     return scripts_subdir
310
311
312 def refresh_script_paths():
313     """
314     Run this after creating new script paths to update sys.path
315     """
316
317     for base_path in script_paths():
318         for path_subdir in _script_module_dirs:
319             path = _os.path.join(base_path, path_subdir)
320             if _os.path.isdir(path):
321                 _sys_path_ensure(path)
322
323     for path in _addon_utils.paths():
324         _sys_path_ensure(path)
325         path = _os.path.join(path, "modules")
326         if _os.path.isdir(path):
327             _sys_path_ensure(path)
328
329
330 def preset_paths(subdir):
331     """
332     Returns a list of paths for a specific preset.
333
334     :arg subdir: preset subdirectory (must not be an absolute path).
335     :type subdir: string
336     :return: script paths.
337     :rtype: list
338     """
339     dirs = []
340     for path in script_paths("presets", check_all=True):
341         directory = _os.path.join(path, subdir)
342         if not directory.startswith(path):
343             raise Exception("invalid subdir given %r" % subdir)
344         elif _os.path.isdir(directory):
345             dirs.append(directory)
346
347     # Find addons preset paths
348     for path in _addon_utils.paths():
349         directory = _os.path.join(path, "presets", subdir)
350         if _os.path.isdir(directory):
351             dirs.append(directory)
352
353     return dirs
354
355
356 def smpte_from_seconds(time, fps=None):
357     """
358     Returns an SMPTE formatted string from the time in seconds: "HH:MM:SS:FF".
359
360     If the *fps* is not given the current scene is used.
361     """
362     import math
363
364     if fps is None:
365         fps = _bpy.context.scene.render.fps
366
367     hours = minutes = seconds = frames = 0
368
369     if time < 0:
370         time = - time
371         neg = "-"
372     else:
373         neg = ""
374
375     if time >= 3600.0:  # hours
376         hours = int(time / 3600.0)
377         time = time % 3600.0
378     if time >= 60.0:  # minutes
379         minutes = int(time / 60.0)
380         time = time % 60.0
381
382     seconds = int(time)
383     frames = int(round(math.floor(((time - seconds) * fps))))
384
385     return "%s%02d:%02d:%02d:%02d" % (neg, hours, minutes, seconds, frames)
386
387
388 def smpte_from_frame(frame, fps=None, fps_base=None):
389     """
390     Returns an SMPTE formatted string from the frame: "HH:MM:SS:FF".
391
392     If *fps* and *fps_base* are not given the current scene is used.
393
394     :arg time: time in seconds.
395     :type time: number or timedelta object
396     :return: the frame.
397     :rtype: float
398     """
399
400     if fps is None:
401         fps = _bpy.context.scene.render.fps
402
403     if fps_base is None:
404         fps_base = _bpy.context.scene.render.fps_base
405
406     return smpte_from_seconds((frame * fps_base) / fps, fps)
407
408
409 def time_from_frame(frame, fps=None, fps_base=None):
410     """
411     Returns the time from a frame number .
412
413     If *fps* and *fps_base* are not given the current scene is used.
414
415     :arg frame: number.
416     :type frame: the frame number
417     :return: the time in seconds.
418     :rtype: timedate.timedelta
419     """
420
421     if fps is None:
422         fps = _bpy.context.scene.render.fps
423
424     if fps_base is None:
425         fps_base = _bpy.context.scene.render.fps_base
426
427     from datetime import timedelta
428
429     return timedelta((frame * fps_base) / fps)
430
431
432 def time_to_frame(time, fps=None, fps_base=None):
433     """
434     Returns a float frame number from a time given in seconds or
435     as a timedate.timedelta object.
436
437     If *fps* and *fps_base* are not given the current scene is used.
438
439     :arg time: time in seconds.
440     :type time: number or a timedate.timedelta object
441     :return: the frame.
442     :rtype: float
443     """
444
445     if fps is None:
446         fps = _bpy.context.scene.render.fps
447
448     if fps_base is None:
449         fps_base = _bpy.context.scene.render.fps_base
450
451     from datetime import timedelta
452
453     if isinstance(time, timedelta):
454         time = time.total_seconds()
455
456     return (time / fps_base) * fps
457
458
459 def preset_find(name, preset_path, display_name=False, ext=".py"):
460     if not name:
461         return None
462
463     for directory in preset_paths(preset_path):
464
465         if display_name:
466             filename = ""
467             for fn in _os.listdir(directory):
468                 if fn.endswith(ext) and name == _bpy.path.display_name(fn):
469                     filename = fn
470                     break
471         else:
472             filename = name + ext
473
474         if filename:
475             filepath = _os.path.join(directory, filename)
476             if _os.path.exists(filepath):
477                 return filepath
478
479
480 def keyconfig_set(filepath):
481     from os.path import basename, splitext
482
483     if _bpy.app.debug_python:
484         print("loading preset:", filepath)
485
486     keyconfigs = _bpy.context.window_manager.keyconfigs
487
488     keyconfigs_old = keyconfigs[:]
489
490     try:
491         keyfile = open(filepath)
492         exec(compile(keyfile.read(), filepath, 'exec'), {"__file__": filepath})
493         keyfile.close()
494     except:
495         import traceback
496         traceback.print_exc()
497
498     kc_new = [kc for kc in keyconfigs if kc not in keyconfigs_old][0]
499
500     kc_new.name = ""
501
502     # remove duplicates
503     name = splitext(basename(filepath))[0]
504     while True:
505         kc_dupe = keyconfigs.get(name)
506         if kc_dupe:
507             keyconfigs.remove(kc_dupe)
508         else:
509             break
510
511     kc_new.name = name
512     keyconfigs.active = kc_new
513
514
515 def user_resource(resource_type, path="", create=False):
516     """
517     Return a user resource path (normally from the users home directory).
518
519     :arg type: Resource type in ['DATAFILES', 'CONFIG', 'SCRIPTS', 'AUTOSAVE'].
520     :type type: string
521     :arg subdir: Optional subdirectory.
522     :type subdir: string
523     :arg create: Treat the path as a directory and create
524        it if its not existing.
525     :type create: boolean
526     :return: a path.
527     :rtype: string
528     """
529
530     target_path = _user_resource(resource_type, path)
531
532     if create:
533         # should always be true.
534         if target_path:
535             # create path if not existing.
536             if not _os.path.exists(target_path):
537                 try:
538                     _os.makedirs(target_path)
539                 except:
540                     import traceback
541                     traceback.print_exc()
542                     target_path = ""
543             elif not _os.path.isdir(target_path):
544                 print("Path %r found but isn't a directory!" % target_path)
545                 target_path = ""
546
547     return target_path
548
549
550 def _bpy_module_classes(module, is_registered=False):
551     typemap_list = _bpy_types.TypeMap.get(module, ())
552     i = 0
553     while i < len(typemap_list):
554         cls_weakref = typemap_list[i]
555         cls = cls_weakref()
556
557         if cls is None:
558             del typemap_list[i]
559         else:
560             if is_registered == cls.is_registered:
561                 yield cls
562             i += 1
563
564
565 def register_module(module, verbose=False):
566     if verbose:
567         print("bpy.utils.register_module(%r): ..." % module)
568     cls = None
569     for cls in _bpy_module_classes(module, is_registered=False):
570         if verbose:
571             print("    %r" % cls)
572         try:
573             register_class(cls)
574         except:
575             print("bpy.utils.register_module(): "
576                   "failed to registering class %r" % cls)
577             import traceback
578             traceback.print_exc()
579     if verbose:
580         print("done.\n")
581     if cls is None:
582         raise Exception("register_module(%r): defines no classes" % module)
583
584
585 def unregister_module(module, verbose=False):
586     if verbose:
587         print("bpy.utils.unregister_module(%r): ..." % module)
588     for cls in _bpy_module_classes(module, is_registered=True):
589         if verbose:
590             print("    %r" % cls)
591         try:
592             unregister_class(cls)
593         except:
594             print("bpy.utils.unregister_module(): "
595                   "failed to unregistering class %r" % cls)
596             import traceback
597             traceback.print_exc()
598     if verbose:
599         print("done.\n")