Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'release/scripts/modules')
-rw-r--r--release/scripts/modules/addon_utils.py114
-rw-r--r--release/scripts/modules/bl_i18n_utils/bl_extract_messages.py19
-rw-r--r--release/scripts/modules/bl_i18n_utils/settings.py23
-rw-r--r--release/scripts/modules/bl_i18n_utils/utils.py33
-rwxr-xr-xrelease/scripts/modules/bl_i18n_utils/utils_languages_menu.py2
-rw-r--r--release/scripts/modules/bl_i18n_utils/utils_spell_check.py2
-rw-r--r--release/scripts/modules/bpy/path.py67
-rw-r--r--release/scripts/modules/bpy/utils/__init__.py (renamed from release/scripts/modules/bpy/utils.py)7
-rw-r--r--release/scripts/modules/bpy/utils/previews.py153
-rw-r--r--release/scripts/modules/bpy_extras/io_utils.py39
-rw-r--r--release/scripts/modules/bpy_extras/object_utils.py13
-rw-r--r--release/scripts/modules/bpy_extras/view3d_utils.py18
-rw-r--r--release/scripts/modules/bpy_types.py40
-rw-r--r--release/scripts/modules/nodeitems_utils.py10
-rw-r--r--release/scripts/modules/rna_info.py9
-rw-r--r--release/scripts/modules/rna_prop_ui.py13
-rw-r--r--release/scripts/modules/rna_xml.py2
17 files changed, 413 insertions, 151 deletions
diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py
index 8c86f31022c..123b3cb953c 100644
--- a/release/scripts/modules/addon_utils.py
+++ b/release/scripts/modules/addon_utils.py
@@ -74,40 +74,43 @@ def modules_refresh(module_cache=addons_fake_modules):
print("fake_module", mod_path, mod_name)
import ast
ModuleType = type(ast)
- file_mod = open(mod_path, "r", encoding='UTF-8')
- if speedy:
- lines = []
- line_iter = iter(file_mod)
- l = ""
- while not l.startswith("bl_info"):
- try:
- l = line_iter.readline()
- except UnicodeDecodeError as e:
- if not error_encoding:
- error_encoding = True
- print("Error reading file as UTF-8:", mod_path, e)
- file_mod.close()
- return None
-
- if len(l) == 0:
- break
- while l.rstrip():
- lines.append(l)
- try:
- l = line_iter.readline()
- except UnicodeDecodeError as e:
- if not error_encoding:
- error_encoding = True
- print("Error reading file as UTF-8:", mod_path, e)
- file_mod.close()
- return None
-
- data = "".join(lines)
+ try:
+ file_mod = open(mod_path, "r", encoding='UTF-8')
+ except OSError as e:
+ print("Error opening file %r: %s" % (mod_path, e))
+ return None
- else:
- data = file_mod.read()
+ with file_mod:
+ if speedy:
+ lines = []
+ line_iter = iter(file_mod)
+ l = ""
+ while not l.startswith("bl_info"):
+ try:
+ l = line_iter.readline()
+ except UnicodeDecodeError as e:
+ if not error_encoding:
+ error_encoding = True
+ print("Error reading file as UTF-8:", mod_path, e)
+ return None
+
+ if len(l) == 0:
+ break
+ while l.rstrip():
+ lines.append(l)
+ try:
+ l = line_iter.readline()
+ except UnicodeDecodeError as e:
+ if not error_encoding:
+ error_encoding = True
+ print("Error reading file as UTF-8:", mod_path, e)
+ return None
+
+ data = "".join(lines)
- file_mod.close()
+ else:
+ data = file_mod.read()
+ del file_mod
try:
ast_data = ast.parse(data, filename=mod_path)
@@ -216,7 +219,8 @@ def check(module_name):
loaded_default = module_name in _user_preferences.addons
mod = sys.modules.get(module_name)
- loaded_state = mod and getattr(mod, "__addon_enabled__", Ellipsis)
+ loaded_state = ((mod is not None) and
+ getattr(mod, "__addon_enabled__", Ellipsis))
if loaded_state is Ellipsis:
print("Warning: addon-module %r found module "
@@ -274,6 +278,20 @@ def enable(module_name, default_set=False, persistent=False, handle_error=None):
mod = sys.modules.get(module_name)
# chances of the file _not_ existing are low, but it could be removed
if mod and os.path.exists(mod.__file__):
+
+ if getattr(mod, "__addon_enabled__", False):
+ # This is an unlikely situation,
+ # re-register if the module is enabled.
+ # Note: the UI doesn't allow this to happen,
+ # in most cases the caller should 'check()' first.
+ try:
+ mod.unregister()
+ except:
+ print("Exception in module unregister(): %r" %
+ getattr(mod, "__file__", module_name))
+ handle_error()
+ return None
+
mod.__addon_enabled__ = False
mtime_orig = getattr(mod, "__time__", 0)
mtime_new = os.path.getmtime(mod.__file__)
@@ -342,7 +360,7 @@ def enable(module_name, default_set=False, persistent=False, handle_error=None):
return mod
-def disable(module_name, default_set=True, handle_error=None):
+def disable(module_name, default_set=False, handle_error=None):
"""
Disables an addon by name.
@@ -416,19 +434,21 @@ def reset_all(reload_scripts=False):
disable(mod_name)
-def module_bl_info(mod, info_basis={"name": "",
- "author": "",
- "version": (),
- "blender": (),
- "location": "",
- "description": "",
- "wiki_url": "",
- "support": 'COMMUNITY',
- "category": "",
- "warning": "",
- "show_expanded": False,
- }
- ):
+def module_bl_info(mod, info_basis=None):
+ if info_basis is None:
+ info_basis = {
+ "name": "",
+ "author": "",
+ "version": (),
+ "blender": (),
+ "location": "",
+ "description": "",
+ "wiki_url": "",
+ "support": 'COMMUNITY',
+ "category": "",
+ "warning": "",
+ "show_expanded": False,
+ }
addon_info = getattr(mod, "bl_info", {})
diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py
index c3b2ae9908b..43a09a1acbd 100644
--- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py
+++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py
@@ -64,7 +64,7 @@ def _gen_check_ctxt(settings):
def _diff_check_ctxt(check_ctxt, minus_check_ctxt):
- """Returns check_ctxt - minus_check_ctxt"""
+ """Removes minus_check_ctxt from check_ctxt"""
for key in check_ctxt:
if isinstance(check_ctxt[key], set):
for warning in minus_check_ctxt[key]:
@@ -576,8 +576,9 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
#print(func_translate_args)
# Break recursive nodes look up on some kind of nodes.
- # E.g. we don’t want to get strings inside subscripts (blah["foo"])!
- stopper_nodes = {ast.Subscript}
+ # E.g. we don't want to get strings inside subscripts (blah["foo"])!
+ # we don't want to get strings from comparisons (foo.type == 'BAR').
+ stopper_nodes = {ast.Subscript, ast.Compare}
# Consider strings separate: ("a" if test else "b")
separate_nodes = {ast.IfExp}
@@ -897,7 +898,7 @@ def dump_addon_messages(module_name, do_checks, settings):
del msgs[key]
if check_ctxt:
- check_ctxt = _diff_check_ctxt(check_ctxt, minus_check_ctxt)
+ _diff_check_ctxt(check_ctxt, minus_check_ctxt)
# and we are done with those!
del minus_pot
@@ -924,18 +925,18 @@ def main():
return
import sys
- back_argv = sys.argv
+ import argparse
+
# Get rid of Blender args!
- sys.argv = sys.argv[sys.argv.index("--") + 1:]
+ argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []
- import argparse
parser = argparse.ArgumentParser(description="Process UI messages from inside Blender.")
parser.add_argument('-c', '--no_checks', default=True, action="store_false", help="No checks over UI messages.")
parser.add_argument('-m', '--no_messages', default=True, action="store_false", help="No export of UI messages.")
parser.add_argument('-o', '--output', default=None, help="Output POT file path.")
parser.add_argument('-s', '--settings', default=None,
help="Override (some) default settings. Either a JSon file name, or a JSon string.")
- args = parser.parse_args()
+ args = parser.parse_args(argv)
settings = settings_i18n.I18nSettings()
settings.from_json(args.settings)
@@ -945,8 +946,6 @@ def main():
dump_messages(do_messages=args.no_messages, do_checks=args.no_checks, settings=settings)
- sys.argv = back_argv
-
if __name__ == "__main__":
print("\n\n *** Running {} *** \n".format(__file__))
diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py
index 920a56a628b..1c960a217de 100644
--- a/release/scripts/modules/bl_i18n_utils/settings.py
+++ b/release/scripts/modules/bl_i18n_utils/settings.py
@@ -88,6 +88,7 @@ LANGUAGES = (
(38, "Uzbek (Oʻzbek)", "uz_UZ"),
(39, "Uzbek Cyrillic (Ўзбек)", "uz_UZ@cyrillic"),
(40, "Hindi (मानक हिन्दी)", "hi_IN"),
+ (41, "Vietnamese (tiếng Việt)", "vi_VN"),
)
# Default context, in py!
@@ -324,6 +325,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = {
"y",
# Sub-strings.
"available with",
+ "brown fox",
"can't save image while rendering",
"expected a timeline/animation area to be active",
"expected a view3d region",
@@ -332,6 +334,8 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = {
"image file not found",
"image path can't be written to",
"in memory to enable editing!",
+ "jumps over",
+ "the lazy dog",
"unable to load movie clip",
"unable to load text",
"unable to open the file",
@@ -412,6 +416,15 @@ REL_TRUNK_PO_DIR = os.path.join(REL_TRUNK_DIR, "po")
# The /trunk/mo path (relative to I18N_DIR).
REL_TRUNK_MO_DIR = os.path.join(REL_TRUNK_DIR, "locale")
+
+# The path to the *git* translation repository (relative to SOURCE_DIR).
+REL_GIT_I18N_DIR = os.path.join("release/datafiles/locale")
+
+
+# The /po path of the *git* translation repository (relative to REL_GIT_I18N_DIR).
+REL_GIT_I18N_PO_DIR = os.path.join("po")
+
+
# The Blender source path to check for i18n macros (relative to SOURCE_DIR).
REL_POTFILES_SOURCE_DIR = os.path.join("source")
@@ -493,14 +506,6 @@ def _gen_get_set_path(ref, name):
return _get, _set
-def _gen_get_set_paths(ref, name):
- def _get(self):
- return [_do_get(getattr(self, ref), p) for p in getattr(self, name)]
- def _set(self, value):
- setattr(self, name, [_do_set(getattr(self, ref), p) for p in value])
- return _get, _set
-
-
class I18nSettings:
"""
Class allowing persistence of our settings!
@@ -552,6 +557,8 @@ class I18nSettings:
TRUNK_DIR = property(*(_gen_get_set_path("I18N_DIR", "REL_TRUNK_DIR")))
TRUNK_PO_DIR = property(*(_gen_get_set_path("I18N_DIR", "REL_TRUNK_PO_DIR")))
TRUNK_MO_DIR = property(*(_gen_get_set_path("I18N_DIR", "REL_TRUNK_MO_DIR")))
+ GIT_I18N_ROOT = property(*(_gen_get_set_path("SOURCE_DIR", "REL_GIT_I18N_DIR")))
+ GIT_I18N_PO_DIR = property(*(_gen_get_set_path("GIT_I18N_ROOT", "REL_GIT_I18N_PO_DIR")))
POTFILES_SOURCE_DIR = property(*(_gen_get_set_path("SOURCE_DIR", "REL_POTFILES_SOURCE_DIR")))
FILE_NAME_POT = property(*(_gen_get_set_path("I18N_DIR", "REL_FILE_NAME_POT")))
MO_PATH_ROOT = property(*(_gen_get_set_path("I18N_DIR", "REL_MO_PATH_ROOT")))
diff --git a/release/scripts/modules/bl_i18n_utils/utils.py b/release/scripts/modules/bl_i18n_utils/utils.py
index 524fef909e8..5fdb6b88cbf 100644
--- a/release/scripts/modules/bl_i18n_utils/utils.py
+++ b/release/scripts/modules/bl_i18n_utils/utils.py
@@ -162,7 +162,7 @@ def get_po_files_from_dir(root_dir, langs=set()):
yield uid, po_file
-def enable_addons(addons={}, support={}, disable=False, check_only=False):
+def enable_addons(addons=None, support=None, disable=False, check_only=False):
"""
Enable (or disable) addons based either on a set of names, or a set of 'support' types.
Returns the list of all affected addons (as fake modules)!
@@ -170,6 +170,11 @@ def enable_addons(addons={}, support={}, disable=False, check_only=False):
"""
import addon_utils
+ if addons is None:
+ addons = {}
+ if support is None:
+ support = {}
+
userpref = bpy.context.user_preferences
used_ext = {ext.module for ext in userpref.addons}
@@ -212,13 +217,13 @@ class I18nMessage:
__slots__ = ("msgctxt_lines", "msgid_lines", "msgstr_lines", "comment_lines", "is_fuzzy", "is_commented",
"settings")
- def __init__(self, msgctxt_lines=[], msgid_lines=[], msgstr_lines=[], comment_lines=[],
+ def __init__(self, msgctxt_lines=None, msgid_lines=None, msgstr_lines=None, comment_lines=None,
is_commented=False, is_fuzzy=False, settings=settings):
self.settings = settings
- self.msgctxt_lines = msgctxt_lines
- self.msgid_lines = msgid_lines
- self.msgstr_lines = msgstr_lines
- self.comment_lines = comment_lines
+ self.msgctxt_lines = msgctxt_lines or []
+ self.msgid_lines = msgid_lines or []
+ self.msgstr_lines = msgstr_lines or []
+ self.comment_lines = comment_lines or []
self.is_fuzzy = is_fuzzy
self.is_commented = is_commented
@@ -976,13 +981,13 @@ class I18nMessages:
def write(self, kind, dest):
self.writers[kind](self, dest)
- def write_messages_to_po(self, fname):
+ def write_messages_to_po(self, fname, compact=False):
"""
Write messages in fname po file.
"""
default_context = self.settings.DEFAULT_CONTEXT
- def _write(self, f):
+ def _write(self, f, compact):
_msgctxt = self.settings.PO_MSGCTXT
_msgid = self.settings.PO_MSGID
_msgstr = self.settings.PO_MSGSTR
@@ -991,9 +996,12 @@ class I18nMessages:
self.escape()
for num, msg in enumerate(self.msgs.values()):
- f.write("\n".join(msg.comment_lines))
+ if compact and (msg.is_commented or msg.is_fuzzy or not msg.msgstr_lines):
+ continue
+ if not compact:
+ f.write("\n".join(msg.comment_lines))
# Only mark as fuzzy if msgstr is not empty!
- if msg.is_fuzzy and msg.msgstr:
+ if msg.is_fuzzy and msg.msgstr_lines:
f.write("\n" + self.settings.PO_COMMENT_FUZZY)
_p = _comm if msg.is_commented else ""
chunks = []
@@ -1030,10 +1038,10 @@ class I18nMessages:
self.normalize(max_len=0) # No wrapping for now...
if isinstance(fname, str):
with open(fname, 'w', encoding="utf-8") as f:
- _write(self, f)
+ _write(self, f, compact)
# Else assume fname is already a file(like) object!
else:
- _write(self, fname)
+ _write(self, fname, compact)
def write_messages_to_mo(self, fname):
"""
@@ -1112,6 +1120,7 @@ class I18nMessages:
writers = {
"PO": write_messages_to_po,
+ "PO_COMPACT": lambda s, fn: s.write_messages_to_po(fn, True),
"MO": write_messages_to_mo,
}
diff --git a/release/scripts/modules/bl_i18n_utils/utils_languages_menu.py b/release/scripts/modules/bl_i18n_utils/utils_languages_menu.py
index 24255d9be61..4f499476ad9 100755
--- a/release/scripts/modules/bl_i18n_utils/utils_languages_menu.py
+++ b/release/scripts/modules/bl_i18n_utils/utils_languages_menu.py
@@ -95,3 +95,5 @@ def gen_menu_file(stats, settings):
data_lines.append("# {} #{}:{}:{}".format(FLAG_MESSAGES[flag], uid_num, label, uid))
with open(os.path.join(settings.TRUNK_MO_DIR, settings.LANGUAGES_FILE), 'w') as f:
f.write("\n".join(data_lines))
+ with open(os.path.join(settings.GIT_I18N_ROOT, settings.LANGUAGES_FILE), 'w') as f:
+ f.write("\n".join(data_lines))
diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
index cdb3a3fedb5..42a23c8c041 100644
--- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
+++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py
@@ -125,6 +125,7 @@ class SpellChecker:
"multisampling",
"multitexture",
"multiuser",
+ "multiview",
"namespace",
"keyconfig",
"online",
@@ -186,6 +187,7 @@ class SpellChecker:
"unhide",
"unindent",
"unkeyed",
+ "unmute",
"unpremultiply",
"unprojected",
"unreacted",
diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py
index 25a6c7242e0..d5b64933165 100644
--- a/release/scripts/modules/bpy/path.py
+++ b/release/scripts/modules/bpy/path.py
@@ -116,7 +116,11 @@ def is_subdir(path, directory):
from os.path import normpath, normcase
path = normpath(normcase(path))
directory = normpath(normcase(directory))
- return path.startswith(directory)
+ if len(path) > len(directory):
+ if path.startswith(directory):
+ sep = ord(_os.sep) if isinstance(directory, bytes) else _os.sep
+ return (path[len(directory)] == sep)
+ return False
def clean_name(name, replace="_"):
@@ -128,23 +132,47 @@ def clean_name(name, replace="_"):
or the replace argument if defined.
"""
- bad_chars = ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e"
- "\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d"
- "\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c"
- "\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x40\x5b\x5c\x5d\x5e\x60\x7b"
- "\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a"
- "\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99"
- "\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8"
- "\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7"
- "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6"
- "\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5"
- "\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4"
- "\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3"
- "\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe")
-
- for ch in bad_chars:
- name = name.replace(ch, replace)
- return name
+ if replace != "_":
+ if len(replace) != 1 or ord(replace) > 255:
+ raise ValueError("Value must be a single ascii character")
+
+ def maketrans_init():
+ trans_cache = clean_name._trans_cache
+ trans = trans_cache.get(replace)
+ if trans is None:
+ bad_chars = (
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2e, 0x2f, 0x3a,
+ 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x5b, 0x5c,
+ 0x5d, 0x5e, 0x60, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe,
+ )
+ trans = str.maketrans({char: replace for char in bad_chars})
+ trans_cache[replace] = trans
+ return trans
+
+ trans = maketrans_init()
+ return name.translate(trans)
+clean_name._trans_cache = {}
def _clean_utf8(name):
@@ -222,7 +250,8 @@ def resolve_ncase(path):
if _os.path.isdir(dirpath):
try:
files = _os.listdir(dirpath)
- except PermissionError: # We might not have the permission to list dirpath...
+ except PermissionError:
+ # We might not have the permission to list dirpath...
return path, False
else:
return path, False
diff --git a/release/scripts/modules/bpy/utils.py b/release/scripts/modules/bpy/utils/__init__.py
index 5f235ae3958..7a1224db226 100644
--- a/release/scripts/modules/bpy/utils.py
+++ b/release/scripts/modules/bpy/utils/__init__.py
@@ -38,6 +38,7 @@ __all__ = (
"unregister_manual_map",
"make_rna_paths",
"manual_map",
+ "previews",
"resource_path",
"script_path_user",
"script_path_pref",
@@ -160,7 +161,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
# modification time changes. This `won't` work for packages so...
# its not perfect.
for module_name in [ext.module for ext in _user_preferences.addons]:
- _addon_utils.disable(module_name, default_set=False)
+ _addon_utils.disable(module_name)
def register_module_call(mod):
register = getattr(mod, "register", None)
@@ -640,10 +641,10 @@ def unregister_module(module, verbose=False):
# we start with the built-in default mapping
def _blender_default_map():
import sys
- import rna_wiki_reference as ref_mod
+ import rna_manual_reference as ref_mod
ret = (ref_mod.url_manual_prefix, ref_mod.url_manual_mapping)
# avoid storing in memory
- del sys.modules["rna_wiki_reference"]
+ del sys.modules["rna_manual_reference"]
return ret
# hooks for doc lookups
diff --git a/release/scripts/modules/bpy/utils/previews.py b/release/scripts/modules/bpy/utils/previews.py
new file mode 100644
index 00000000000..965971139e4
--- /dev/null
+++ b/release/scripts/modules/bpy/utils/previews.py
@@ -0,0 +1,153 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+"""
+This module contains utility functions to handle custom previews.
+
+It behaves as a high-level 'cached' previews manager.
+
+This allows scripts to generate their own previews, and use them as icons in UI widgets
+('icon_value' for UILayout functions).
+
+
+Custom Icon Example
+-------------------
+
+.. literalinclude:: ../../../release/scripts/templates_py/ui_previews_custom_icon.py
+"""
+
+__all__ = (
+ "new",
+ "remove",
+ "ImagePreviewCollection",
+ )
+
+import _bpy
+_utils_previews = _bpy._utils_previews
+del _bpy
+
+
+_uuid_open = set()
+
+
+# High-level previews manager.
+# not accessed directly
+class ImagePreviewCollection(dict):
+ """
+ Dictionary-like class of previews.
+
+ This is a subclass of Python's built-in dict type,
+ used to store multiple image previews.
+
+ .. note::
+
+ - instance with :mod:`bpy.utils.previews.new`
+ - keys must be ``str`` type.
+ - values will be :class:`bpy.types.ImagePreview`
+ """
+
+ # Internal notes:
+ # - Blender's internal 'PreviewImage' struct uses 'self._uuid' prefix.
+
+ def __init__(self):
+ super().__init__()
+ self._uuid = hex(id(self))
+ _uuid_open.add(self._uuid)
+
+ def __del__(self):
+ if self._uuid not in _uuid_open:
+ return
+
+ raise ResourceWarning(
+ "<%s id=%s[%d]>: left open, remove with "
+ "'bpy.utils.previews.remove()'" %
+ (self.__class__.__name__, self._uuid, len(self)))
+ self.close()
+
+ def _gen_key(self, name):
+ return ":".join((self._uuid, name))
+
+ def new(self, name):
+ if name in self:
+ raise KeyException("key %r already exists")
+ p = self[name] = _utils_previews.new(
+ self._gen_key(name))
+ return p
+ new.__doc__ = _utils_previews.new.__doc__
+
+ def load(self, name, path, path_type, force_reload=False):
+ if name in self:
+ raise KeyException("key %r already exists")
+ p = self[name] = _utils_previews.load(
+ self._gen_key(name), path, path_type, force_reload)
+ return p
+ load.__doc__ = _utils_previews.load.__doc__
+
+ def clear(self):
+ """Clear all previews."""
+ for name in self.keys():
+ _utils_previews.release(self._gen_key(name))
+ super().clear()
+
+ def close(self):
+ """Close the collection and clear all previews."""
+ self.clear()
+ _uuid_open.remove(self._uuid)
+
+ def __delitem__(self, key):
+ _utils_previews.release(self._gen_key(key))
+ super().__delitem__(key)
+
+ def __repr__(self):
+ return "<%s id=%s[%d], %s>" % (
+ self.__class__.__name__,
+ self._uuid,
+ len(self),
+ super().__repr__())
+
+
+def new():
+ """
+ :return: a new preview collection.
+ :rtype: :class:`ImagePreviewCollection`
+ """
+
+ return ImagePreviewCollection()
+
+
+def remove(pcoll):
+ """
+ Remove the specified previews collection.
+
+ :arg pcoll: Preview collection to close.
+ :type pcoll: :class:`ImagePreviewCollection`
+ """
+ pcoll.close()
+
+
+# don't complain about resources on exit (only unregister)
+import atexit
+
+
+def exit_clear_warning():
+ del ImagePreviewCollection.__del__
+
+atexit.register(exit_clear_warning)
+del atexit, exit_clear_warning
diff --git a/release/scripts/modules/bpy_extras/io_utils.py b/release/scripts/modules/bpy_extras/io_utils.py
index 81de0d7c6f0..65ccc3f8dc3 100644
--- a/release/scripts/modules/bpy_extras/io_utils.py
+++ b/release/scripts/modules/bpy_extras/io_utils.py
@@ -21,7 +21,7 @@
__all__ = (
"ExportHelper",
"ImportHelper",
- "OrientationHelper",
+ "orientation_helper_factory",
"axis_conversion",
"axis_conversion_ensure",
"create_derived_objects",
@@ -35,7 +35,11 @@ __all__ = (
)
import bpy
-from bpy.props import StringProperty, BoolProperty, EnumProperty
+from bpy.props import (
+ StringProperty,
+ BoolProperty,
+ EnumProperty,
+ )
def _check_axis_conversion(op):
@@ -61,6 +65,12 @@ class ExportHelper:
options={'HIDDEN'},
)
+ # needed for mix-ins
+ order = [
+ "filepath",
+ "check_existing",
+ ]
+
# subclasses can override with decorator
# True == use ext, False == no ext, None == do nothing.
check_extension = True
@@ -109,6 +119,11 @@ class ImportHelper:
subtype='FILE_PATH',
)
+ # needed for mix-ins
+ order = [
+ "filepath",
+ ]
+
def invoke(self, context, event):
context.window_manager.fileselect_add(self)
return {'RUNNING_MODAL'}
@@ -117,13 +132,14 @@ class ImportHelper:
return _check_axis_conversion(self)
-class OrientationHelper:
+def orientation_helper_factory(name, axis_forward='Y', axis_up='Z'):
+ members = {}
def _update_axis_forward(self, context):
if self.axis_forward[-1] == self.axis_up[-1]:
self.axis_up = self.axis_up[0:-1] + 'XYZ'[('XYZ'.index(self.axis_up[-1]) + 1) % 3]
- axis_forward = EnumProperty(
+ members['axis_forward'] = EnumProperty(
name="Forward",
items=(('X', "X Forward", ""),
('Y', "Y Forward", ""),
@@ -132,7 +148,7 @@ class OrientationHelper:
('-Y', "-Y Forward", ""),
('-Z', "-Z Forward", ""),
),
- default='-Z',
+ default=axis_forward,
update=_update_axis_forward,
)
@@ -140,7 +156,7 @@ class OrientationHelper:
if self.axis_up[-1] == self.axis_forward[-1]:
self.axis_forward = self.axis_forward[0:-1] + 'XYZ'[('XYZ'.index(self.axis_forward[-1]) + 1) % 3]
- axis_up = EnumProperty(
+ members['axis_up'] = EnumProperty(
name="Up",
items=(('X', "X Up", ""),
('Y', "Y Up", ""),
@@ -149,10 +165,17 @@ class OrientationHelper:
('-Y', "-Y Up", ""),
('-Z', "-Z Up", ""),
),
- default='Y',
+ default=axis_up,
update=_update_axis_up,
)
+ members["order"] = [
+ "axis_forward",
+ "axis_up",
+ ]
+
+ return type(name, (object,), members)
+
# Axis conversion function, not pretty LUT
# use lookup table to convert between any axis
@@ -349,7 +372,7 @@ def unpack_list(list_of_tuples):
# same as above except that it adds 0 for triangle faces
def unpack_face_list(list_of_tuples):
- #allocate the entire list
+ # allocate the entire list
flat_ls = [0] * (len(list_of_tuples) * 4)
i = 0
diff --git a/release/scripts/modules/bpy_extras/object_utils.py b/release/scripts/modules/bpy_extras/object_utils.py
index 13ef86b23c0..78fb6aa8fa2 100644
--- a/release/scripts/modules/bpy_extras/object_utils.py
+++ b/release/scripts/modules/bpy_extras/object_utils.py
@@ -31,7 +31,10 @@ __all__ = (
import bpy
-from bpy.props import BoolProperty, FloatVectorProperty
+from bpy.props import (
+ BoolProperty,
+ FloatVectorProperty,
+ )
def add_object_align_init(context, operator):
@@ -171,7 +174,7 @@ def object_data_add(context, obdata, operator=None, use_active_layer=True, name=
obj_act.select = True
scene.update() # apply location
- #scene.objects.active = obj_new
+ # scene.objects.active = obj_new
bpy.ops.object.join() # join into the active.
if obdata:
@@ -287,7 +290,8 @@ def world_to_camera_view(scene, obj, coord):
Returns the camera space coords for a 3d point.
(also known as: normalized device coordinates - NDC).
- Where (0, 0) is the bottom left and (1, 1) is the top right of the camera frame.
+ Where (0, 0) is the bottom left and (1, 1)
+ is the top right of the camera frame.
values outside 0-1 are also supported.
A negative 'z' value means the point is behind the camera.
@@ -300,7 +304,8 @@ def world_to_camera_view(scene, obj, coord):
:type obj: :class:`bpy.types.Object`
:arg coord: World space location.
:type coord: :class:`mathutils.Vector`
- :return: a vector where X and Y map to the view plane and Z is the depth on the view axis.
+ :return: a vector where X and Y map to the view plane and
+ Z is the depth on the view axis.
:rtype: :class:`mathutils.Vector`
"""
from mathutils import Vector
diff --git a/release/scripts/modules/bpy_extras/view3d_utils.py b/release/scripts/modules/bpy_extras/view3d_utils.py
index ec4a395f1e1..4aa06262970 100644
--- a/release/scripts/modules/bpy_extras/view3d_utils.py
+++ b/release/scripts/modules/bpy_extras/view3d_utils.py
@@ -69,11 +69,13 @@ def region_2d_to_origin_3d(region, rv3d, coord, clamp=None):
.. note::
- Orthographic views have a less obvious origin, the far clip is used to define the viewport near/far extents.
- Since far clip can be a very large value, the result may give with numeric precision issues.
+ Orthographic views have a less obvious origin,
+ the far clip is used to define the viewport near/far extents.
+ Since far clip can be a very large value,
+ the result may give with numeric precision issues.
- To avoid this problem, you can optionally clamp the far clip to a smaller value
- based on the data you're operating on.
+ To avoid this problem, you can optionally clamp the far clip to a
+ smaller value based on the data you're operating on.
:arg region: region of the 3D viewport, typically bpy.context.region.
:type region: :class:`bpy.types.Region`
@@ -160,7 +162,7 @@ def region_2d_to_location_3d(region, rv3d, coord, depth_location):
)[0]
-def location_3d_to_region_2d(region, rv3d, coord):
+def location_3d_to_region_2d(region, rv3d, coord, default=None):
"""
Return the *region* relative 2d location of a 3d position.
@@ -170,8 +172,10 @@ def location_3d_to_region_2d(region, rv3d, coord):
:type rv3d: :class:`bpy.types.RegionView3D`
:arg coord: 3d worldspace location.
:type coord: 3d vector
+ :arg default: Return this value if ``coord``
+ is behind the origin of a perspective view.
:return: 2d location
- :rtype: :class:`mathutils.Vector`
+ :rtype: :class:`mathutils.Vector` or ``default`` argument.
"""
from mathutils import Vector
@@ -184,4 +188,4 @@ def location_3d_to_region_2d(region, rv3d, coord):
height_half + height_half * (prj.y / prj.w),
))
else:
- return None
+ return default
diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py
index c7ec7e1e54a..92dbd2dbd0e 100644
--- a/release/scripts/modules/bpy_types.py
+++ b/release/scripts/modules/bpy_types.py
@@ -715,7 +715,7 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
__slots__ = ()
def path_menu(self, searchpaths, operator,
- props_default={}, filter_ext=None):
+ props_default=None, filter_ext=None):
layout = self.layout
# hard coded to set the operators 'filepath' to the filename.
@@ -745,8 +745,9 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
text=bpy.path.display_name(f),
translate=False)
- for attr, value in props_default.items():
- setattr(props, attr, value)
+ if props_default is not None:
+ for attr, value in props_default.items():
+ setattr(props, attr, value)
props.filepath = filepath
if operator == "script.execute_preset":
@@ -754,14 +755,21 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
def draw_preset(self, context):
"""
- Define these on the subclass
- - preset_operator
- - preset_subdir
+ Define these on the subclass:
+ - preset_operator (string)
+ - preset_subdir (string)
+
+ Optionally:
+ - preset_extensions (set of strings)
+ - preset_operator_defaults (dict of keyword args)
"""
import bpy
+ ext_valid = getattr(self, "preset_extensions", {".py", ".xml"})
+ props_default = getattr(self, "preset_operator_defaults", None)
self.path_menu(bpy.utils.preset_paths(self.preset_subdir),
self.preset_operator,
- filter_ext=lambda ext: ext.lower() in {".py", ".xml"})
+ props_default=props_default,
+ filter_ext=lambda ext: ext.lower() in ext_valid)
@classmethod
def draw_collapsible(cls, context, layout):
@@ -773,24 +781,6 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
layout.menu(cls.__name__, icon='COLLAPSEMENU')
-class Region(StructRNA):
- __slots__ = ()
-
- def callback_add(self, cb, args, draw_mode):
- """
- Append a draw function to this region,
- deprecated, instead use bpy.types.SpaceView3D.draw_handler_add
- """
- for area in self.id_data.areas:
- for region in area.regions:
- if region == self:
- spacetype = type(area.spaces[0])
- return spacetype.draw_handler_add(cb, args, self.type,
- draw_mode)
-
- return None
-
-
class NodeTree(bpy_types.ID, metaclass=RNAMetaPropGroup):
__slots__ = ()
diff --git a/release/scripts/modules/nodeitems_utils.py b/release/scripts/modules/nodeitems_utils.py
index 2882a08fbd4..2f69ea84386 100644
--- a/release/scripts/modules/nodeitems_utils.py
+++ b/release/scripts/modules/nodeitems_utils.py
@@ -37,13 +37,17 @@ class NodeCategory:
else:
def items_gen(context):
for item in items:
- if item.poll is None or item.poll(context):
+ if item.poll is None or context is None or item.poll(context):
yield item
self.items = items_gen
class NodeItem:
- def __init__(self, nodetype, label=None, settings={}, poll=None):
+ def __init__(self, nodetype, label=None, settings=None, poll=None):
+
+ if settings is None:
+ settings = {}
+
self.nodetype = nodetype
self._label = label
self.settings = settings
@@ -136,7 +140,7 @@ def register_node_categories(identifier, cat_list):
def node_categories_iter(context):
for cat_type in _node_categories.values():
for cat in cat_type[0]:
- if cat.poll and cat.poll(context):
+ if cat.poll and ((context is None) or cat.poll(context)):
yield cat
diff --git a/release/scripts/modules/rna_info.py b/release/scripts/modules/rna_info.py
index 353362ed168..dae262e93d7 100644
--- a/release/scripts/modules/rna_info.py
+++ b/release/scripts/modules/rna_info.py
@@ -268,7 +268,8 @@ class InfoPropertyRNA:
self.default_str = "\"%s\"" % self.default
elif self.type == "enum":
if self.is_enum_flag:
- self.default_str = "%r" % self.default # repr or set()
+ # self.default_str = "%r" % self.default # repr or set()
+ self.default_str = "{%s}" % repr(list(sorted(self.default)))[1:-1]
else:
self.default_str = "'%s'" % self.default
elif self.array_length:
@@ -695,7 +696,7 @@ def BuildRNAInfo():
return InfoStructRNA.global_lookup, InfoFunctionRNA.global_lookup, InfoOperatorRNA.global_lookup, InfoPropertyRNA.global_lookup
-if __name__ == "__main__":
+def main():
import rna_info
struct = rna_info.BuildRNAInfo()[0]
data = []
@@ -723,3 +724,7 @@ if __name__ == "__main__":
else:
text = bpy.data.texts.new(name="api.py")
text.from_string(data)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py
index f4649453b46..44722fa7162 100644
--- a/release/scripts/modules/rna_prop_ui.py
+++ b/release/scripts/modules/rna_prop_ui.py
@@ -32,6 +32,13 @@ def rna_idprop_ui_get(item, create=True):
return None
+def rna_idprop_ui_del(item):
+ try:
+ del item['_RNA_UI']
+ except KeyError:
+ pass
+
+
def rna_idprop_ui_prop_get(item, prop, create=True):
rna_ui = rna_idprop_ui_get(item, create)
@@ -46,7 +53,7 @@ def rna_idprop_ui_prop_get(item, prop, create=True):
return rna_ui[prop]
-def rna_idprop_ui_prop_clear(item, prop):
+def rna_idprop_ui_prop_clear(item, prop, remove=True):
rna_ui = rna_idprop_ui_get(item, False)
if rna_ui is None:
@@ -54,8 +61,10 @@ def rna_idprop_ui_prop_clear(item, prop):
try:
del rna_ui[prop]
- except:
+ except KeyError:
pass
+ if remove and len(item.keys()) == 1:
+ rna_idprop_ui_del(item)
def rna_idprop_context_value(context, context_member, property_type):
diff --git a/release/scripts/modules/rna_xml.py b/release/scripts/modules/rna_xml.py
index 729d6238ac3..ad4809efbe1 100644
--- a/release/scripts/modules/rna_xml.py
+++ b/release/scripts/modules/rna_xml.py
@@ -32,7 +32,7 @@ def build_property_typemap(skip_classes, skip_typemap):
if issubclass(cls, skip_classes):
continue
- ## to support skip-save we cant get all props
+ # # to support skip-save we cant get all props
# properties = cls.bl_rna.properties.keys()
properties = []
for prop_id, prop in cls.bl_rna.properties.items():