Merge branch 'blender2.8' into soc-2018-bevel
This commit is contained in:
@@ -78,7 +78,7 @@ def modules_refresh(module_cache=addons_fake_modules):
|
||||
try:
|
||||
file_mod = open(mod_path, "r", encoding='UTF-8')
|
||||
except OSError as ex:
|
||||
print("Error opening file %r: %s" % (mod_path, ex))
|
||||
print("Error opening file:", mod_path, ex)
|
||||
return None
|
||||
|
||||
with file_mod:
|
||||
@@ -116,7 +116,7 @@ def modules_refresh(module_cache=addons_fake_modules):
|
||||
try:
|
||||
ast_data = ast.parse(data, filename=mod_path)
|
||||
except:
|
||||
print("Syntax error 'ast.parse' can't read %r" % mod_path)
|
||||
print("Syntax error 'ast.parse' can't read:", repr(mod_path))
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
ast_data = None
|
||||
@@ -138,7 +138,7 @@ def modules_refresh(module_cache=addons_fake_modules):
|
||||
mod.__file__ = mod_path
|
||||
mod.__time__ = os.path.getmtime(mod_path)
|
||||
except:
|
||||
print("AST error parsing bl_info for %s" % mod_name)
|
||||
print("AST error parsing bl_info for:", mod_name)
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
raise
|
||||
@@ -148,8 +148,11 @@ def modules_refresh(module_cache=addons_fake_modules):
|
||||
|
||||
return mod
|
||||
else:
|
||||
print("fake_module: addon missing 'bl_info' "
|
||||
"gives bad performance!: %r" % mod_path)
|
||||
print(
|
||||
"fake_module: addon missing 'bl_info' "
|
||||
"gives bad performance!:",
|
||||
repr(mod_path),
|
||||
)
|
||||
return None
|
||||
|
||||
modules_stale = set(module_cache.keys())
|
||||
@@ -167,17 +170,21 @@ def modules_refresh(module_cache=addons_fake_modules):
|
||||
mod = module_cache.get(mod_name)
|
||||
if mod:
|
||||
if mod.__file__ != mod_path:
|
||||
print("multiple addons with the same name:\n %r\n %r" %
|
||||
(mod.__file__, mod_path))
|
||||
print(
|
||||
"multiple addons with the same name:\n"
|
||||
" " f"{mod.__file__!r}" "\n"
|
||||
" " f"{mod_path!r}"
|
||||
)
|
||||
error_duplicates.append((mod.bl_info["name"], mod.__file__, mod_path))
|
||||
|
||||
elif mod.__time__ != os.path.getmtime(mod_path):
|
||||
print("reloading addon:",
|
||||
mod_name,
|
||||
mod.__time__,
|
||||
os.path.getmtime(mod_path),
|
||||
mod_path,
|
||||
)
|
||||
print(
|
||||
"reloading addon:",
|
||||
mod_name,
|
||||
mod.__time__,
|
||||
os.path.getmtime(mod_path),
|
||||
repr(mod_path),
|
||||
)
|
||||
del module_cache[mod_name]
|
||||
mod = None
|
||||
|
||||
@@ -209,6 +216,8 @@ def modules(module_cache=addons_fake_modules, *, refresh=True):
|
||||
)
|
||||
)
|
||||
return mod_list
|
||||
|
||||
|
||||
modules._is_first = True
|
||||
|
||||
|
||||
@@ -231,10 +240,12 @@ def check(module_name):
|
||||
)
|
||||
|
||||
if loaded_state is Ellipsis:
|
||||
print("Warning: addon-module %r found module "
|
||||
"but without __addon_enabled__ field, "
|
||||
"possible name collision from file: %r" %
|
||||
(module_name, getattr(mod, "__file__", "<unknown>")))
|
||||
print(
|
||||
"Warning: addon-module " f"{module_name:s}" " found module "
|
||||
"but without '__addon_enabled__' field, "
|
||||
"possible name collision from file:",
|
||||
repr(getattr(mod, "__file__", "<unknown>")),
|
||||
)
|
||||
|
||||
loaded_state = False
|
||||
|
||||
@@ -301,8 +312,10 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
||||
try:
|
||||
mod.unregister()
|
||||
except Exception as ex:
|
||||
print("Exception in module unregister(): %r" %
|
||||
getattr(mod, "__file__", module_name))
|
||||
print(
|
||||
"Exception in module unregister():",
|
||||
repr(getattr(mod, "__file__", module_name)),
|
||||
)
|
||||
handle_error(ex)
|
||||
return None
|
||||
|
||||
@@ -311,7 +324,7 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
||||
mtime_new = os.path.getmtime(mod.__file__)
|
||||
if mtime_orig != mtime_new:
|
||||
import importlib
|
||||
print("module changed on disk:", mod.__file__, "reloading...")
|
||||
print("module changed on disk:", repr(mod.__file__), "reloading...")
|
||||
|
||||
try:
|
||||
importlib.reload(mod)
|
||||
@@ -341,7 +354,7 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
||||
except Exception as ex:
|
||||
# if the addon doesn't exist, dont print full traceback
|
||||
if type(ex) is ImportError and ex.name == module_name:
|
||||
print("addon not found: %r" % module_name)
|
||||
print("addon not found:", repr(module_name))
|
||||
else:
|
||||
handle_error(ex)
|
||||
|
||||
@@ -349,6 +362,29 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
||||
_addon_remove(module_name)
|
||||
return None
|
||||
|
||||
# 1.1) fail when add-on is too old
|
||||
# This is a temporary 2.8x migration check, so we can manage addons that are supported.
|
||||
|
||||
# Silent default, we know these need updating.
|
||||
if module_name in {
|
||||
"io_anim_bvh",
|
||||
"io_mesh_ply",
|
||||
"io_mesh_stl",
|
||||
"io_mesh_uv_layout",
|
||||
"io_scene_3ds",
|
||||
"io_scene_fbx",
|
||||
"io_scene_obj",
|
||||
"io_scene_x3d",
|
||||
}:
|
||||
return None
|
||||
|
||||
try:
|
||||
if mod.bl_info.get("blender", (0, 0, 0)) < (2, 80, 0):
|
||||
raise Exception(f"Add-on '{module_name:s}' has not been upgraded to 2.8, ignoring")
|
||||
except Exception as ex:
|
||||
handle_error(ex)
|
||||
return None
|
||||
|
||||
# 2) try register collected modules
|
||||
# removed, addons need to handle own registration now.
|
||||
|
||||
@@ -362,8 +398,10 @@ def enable(module_name, *, default_set=False, persistent=False, handle_error=Non
|
||||
try:
|
||||
mod.register()
|
||||
except Exception as ex:
|
||||
print("Exception in module register(): %r" %
|
||||
getattr(mod, "__file__", module_name))
|
||||
print(
|
||||
"Exception in module register():",
|
||||
getattr(mod, "__file__", module_name),
|
||||
)
|
||||
handle_error(ex)
|
||||
del sys.modules[module_name]
|
||||
if default_set:
|
||||
@@ -413,12 +451,15 @@ def disable(module_name, *, default_set=False, handle_error=None):
|
||||
try:
|
||||
mod.unregister()
|
||||
except Exception as ex:
|
||||
print("Exception in module unregister(): %r" %
|
||||
getattr(mod, "__file__", module_name))
|
||||
mod_path = getattr(mod, "__file__", module_name)
|
||||
print("Exception in module unregister():", repr(mod_path))
|
||||
del mod_path
|
||||
handle_error(ex)
|
||||
else:
|
||||
print("addon_utils.disable: %s not %s." %
|
||||
(module_name, "disabled" if mod is None else "loaded"))
|
||||
print(
|
||||
"addon_utils.disable: " f"{module_name:s}" " not",
|
||||
("disabled" if mod is None else "loaded")
|
||||
)
|
||||
|
||||
# could be in more than once, unlikely but better do this just in case.
|
||||
if default_set:
|
||||
|
||||
@@ -172,6 +172,7 @@ def ui_draw_filter_register(
|
||||
|
||||
class Wrapper(cls_real):
|
||||
__slots__ = ()
|
||||
|
||||
def __getattribute__(self, attr):
|
||||
if attr == "layout":
|
||||
return UILayout_Fake(self_real.layout)
|
||||
|
||||
@@ -120,8 +120,11 @@ def check(check_ctxt, msgs, key, msgsrc, settings):
|
||||
key[1][0].isalpha() and not key[1][0].isupper()):
|
||||
not_capitalized.add(key)
|
||||
if end_point is not None:
|
||||
if (key[1].strip().endswith('.') and not key[1].strip().endswith('...') and
|
||||
key[1] not in settings.WARN_MSGID_END_POINT_ALLOWED):
|
||||
if (
|
||||
key[1].strip().endswith('.') and
|
||||
(not key[1].strip().endswith('...')) and
|
||||
key[1] not in settings.WARN_MSGID_END_POINT_ALLOWED
|
||||
):
|
||||
end_point.add(key)
|
||||
if undoc_ops is not None:
|
||||
if key[1] == settings.UNDOC_OPS_STR:
|
||||
@@ -183,8 +186,10 @@ def print_info(reports, pot):
|
||||
# if py_in_rna and key in py_in_rna:
|
||||
# _print("\t\t-> RNA message also used in py UI code!")
|
||||
if spell_errors and spell_errors.get(key):
|
||||
lines = ["\t\t-> {}: misspelled, suggestions are ({})".format(w, "'" + "', '".join(errs) + "'")
|
||||
for w, errs in spell_errors[key]]
|
||||
lines = [
|
||||
"\t\t-> {}: misspelled, suggestions are ({})".format(w, "'" + "', '".join(errs) + "'")
|
||||
for w, errs in spell_errors[key]
|
||||
]
|
||||
_print("\n".join(lines))
|
||||
_print("\t\t{}".format("\n\t\t".join(pot.msgs[key].sources)))
|
||||
|
||||
@@ -215,13 +220,13 @@ def dump_rna_messages(msgs, reports, settings, verbose=False):
|
||||
"""
|
||||
def class_blacklist():
|
||||
blacklist_rna_class = {getattr(bpy.types, cls_id) for cls_id in (
|
||||
# core classes
|
||||
"Context", "Event", "Function", "UILayout", "UnknownType", "Property", "Struct",
|
||||
# registerable classes
|
||||
"Panel", "Menu", "Header", "RenderEngine", "Operator", "OperatorMacro", "Macro", "KeyingSetInfo",
|
||||
# window classes
|
||||
"Window",
|
||||
)
|
||||
# core classes
|
||||
"Context", "Event", "Function", "UILayout", "UnknownType", "Property", "Struct",
|
||||
# registerable classes
|
||||
"Panel", "Menu", "Header", "RenderEngine", "Operator", "OperatorMacro", "Macro", "KeyingSetInfo",
|
||||
# window classes
|
||||
"Window",
|
||||
)
|
||||
}
|
||||
|
||||
# More builtin classes we don't need to parse.
|
||||
@@ -380,12 +385,13 @@ def dump_rna_messages(msgs, reports, settings, verbose=False):
|
||||
|
||||
# Dump Messages
|
||||
operator_categories = {}
|
||||
|
||||
def process_cls_list(cls_list):
|
||||
if not cls_list:
|
||||
return
|
||||
|
||||
def full_class_id(cls):
|
||||
"""Gives us 'ID.Lamp.AreaLamp' which is best for sorting."""
|
||||
"""Gives us 'ID.Light.AreaLight' which is best for sorting."""
|
||||
# Always the same issue, some classes listed in blacklist should actually no more exist (they have been
|
||||
# unregistered), but are still listed by __subclasses__() calls... :/
|
||||
if cls in blacklist_rna_class:
|
||||
@@ -447,6 +453,7 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
|
||||
i18n_contexts = bpy.app.translations.contexts
|
||||
|
||||
root_paths = tuple(bpy.utils.resource_path(t) for t in ('USER', 'LOCAL', 'SYSTEM'))
|
||||
|
||||
def make_rel(path):
|
||||
for rp in root_paths:
|
||||
if path.startswith(rp):
|
||||
@@ -510,6 +517,7 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
|
||||
return [_extract_string_merge(estr_ls, nds_ls) for estr_ls, nds_ls in bag]
|
||||
|
||||
i18n_ctxt_ids = {v for v in bpy.app.translations.contexts_C_to_py.values()}
|
||||
|
||||
def _ctxt_to_ctxt(node):
|
||||
# We must try, to some extend, to get contexts from vars instead of only literal strings...
|
||||
ctxt = extract_strings(node)[0]
|
||||
@@ -561,9 +569,9 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
|
||||
translate_kw = {
|
||||
"text": ((("text_ctxt",), _ctxt_to_ctxt),
|
||||
(("operator",), _op_to_ctxt),
|
||||
),
|
||||
"msgid": ((("msgctxt",), _ctxt_to_ctxt),
|
||||
),
|
||||
"msgid": ((("msgctxt",), _ctxt_to_ctxt),
|
||||
),
|
||||
"message": (),
|
||||
}
|
||||
|
||||
@@ -611,7 +619,7 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
|
||||
func_translate_args[func_id] = pgettext_variants_args
|
||||
for func_id in func_ids:
|
||||
func_translate_args[func_id] = pgettext_variants_args
|
||||
#print(func_translate_args)
|
||||
# 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"])!
|
||||
@@ -666,7 +674,7 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
|
||||
if kw.arg == arg_kw:
|
||||
context_elements[arg_kw] = kw.value
|
||||
break
|
||||
#print(context_elements)
|
||||
# print(context_elements)
|
||||
for kws, proc in translate_kw[msgid]:
|
||||
if set(kws) <= context_elements.keys():
|
||||
args = tuple(context_elements[k] for k in kws)
|
||||
@@ -676,7 +684,7 @@ def dump_py_messages_from_files(msgs, reports, files, settings):
|
||||
msgctxts[msgid] = ctxt
|
||||
break
|
||||
|
||||
#print(translate_args)
|
||||
# print(translate_args)
|
||||
# do nothing if not found
|
||||
for arg_kw, (arg_pos, _) in func_args.items():
|
||||
msgctxt = msgctxts[arg_kw]
|
||||
@@ -706,7 +714,7 @@ def dump_py_messages(msgs, reports, addons, settings, addons_only=False):
|
||||
return []
|
||||
if os.path.isdir(path):
|
||||
return [os.path.join(dpath, fn) for dpath, _, fnames in os.walk(path) for fn in fnames
|
||||
if not fn.startswith("_") and fn.endswith(".py")]
|
||||
if not fn.startswith("_") and fn.endswith(".py")]
|
||||
return [path]
|
||||
|
||||
files = []
|
||||
@@ -738,7 +746,9 @@ def dump_src_messages(msgs, reports, settings):
|
||||
pygettexts = tuple(re.compile(r).search for r in settings.PYGETTEXT_KEYWORDS)
|
||||
|
||||
_clean_str = re.compile(settings.str_clean_re).finditer
|
||||
clean_str = lambda s: "".join(m.group("clean") for m in _clean_str(s))
|
||||
|
||||
def clean_str(s):
|
||||
return "".join(m.group("clean") for m in _clean_str(s))
|
||||
|
||||
def dump_src_file(path, rel_path, msgs, reports, settings):
|
||||
def process_entry(_msgctxt, _msgid):
|
||||
@@ -873,10 +883,10 @@ def dump_messages(do_messages, do_checks, settings):
|
||||
process_msg(msgs, settings.DEFAULT_CONTEXT, cat[1],
|
||||
"Language categories’ labels from bl_i18n_utils/settings.py", reports, None, settings)
|
||||
|
||||
#pot.check()
|
||||
# pot.check()
|
||||
pot.unescape() # Strings gathered in py/C source code may contain escaped chars...
|
||||
print_info(reports, pot)
|
||||
#pot.check()
|
||||
# pot.check()
|
||||
|
||||
if do_messages:
|
||||
print("Writing messages…")
|
||||
|
||||
@@ -43,14 +43,17 @@ else:
|
||||
# XXX This is a quick hack to make it work with new I18n... objects! To be reworked!
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description=
|
||||
"Merge one or more .po files into the first dest one.\n"
|
||||
"If a msgkey (msgctxt, msgid) is present in more than one merged po, the one in the first file "
|
||||
"wins, unless it’s marked as fuzzy and one later is not.\n"
|
||||
"The fuzzy flag is removed if necessary.\n"
|
||||
"All other comments are never modified.\n"
|
||||
"Commented messages in dst will always remain commented, and commented messages are never merged "
|
||||
"from sources.")
|
||||
parser = argparse.ArgumentParser(
|
||||
description=(
|
||||
"Merge one or more .po files into the first dest one.\n"
|
||||
"If a msgkey (msgctxt, msgid) is present in more than one merged po, the one in the first file "
|
||||
"wins, unless it’s marked as fuzzy and one later is not.\n"
|
||||
"The fuzzy flag is removed if necessary.\n"
|
||||
"All other comments are never modified.\n"
|
||||
"Commented messages in dst will always remain commented, and commented messages are never merged "
|
||||
"from sources."
|
||||
),
|
||||
)
|
||||
parser.add_argument('-s', '--stats', action="store_true", help="Show statistics info.")
|
||||
parser.add_argument('-r', '--replace', action="store_true",
|
||||
help="Replace existing messages of same \"level\" already in dest po.")
|
||||
|
||||
@@ -32,9 +32,9 @@ import tempfile
|
||||
#import time
|
||||
|
||||
from bl_i18n_utils import (
|
||||
settings,
|
||||
utils_rtl,
|
||||
)
|
||||
settings,
|
||||
utils_rtl,
|
||||
)
|
||||
|
||||
import bpy
|
||||
|
||||
@@ -44,6 +44,8 @@ from bpy.app.translations import locale_explode
|
||||
|
||||
|
||||
_valid_po_path_re = re.compile(r"^\S+:[0-9]+$")
|
||||
|
||||
|
||||
def is_valid_po_path(path):
|
||||
return bool(_valid_po_path_re.match(path))
|
||||
|
||||
@@ -57,9 +59,10 @@ def get_best_similar(data):
|
||||
# We also consider to never make a match when len differs more than -len_key / 2, +len_key * 2 (which is valid
|
||||
# as long as use_similar is not below ~0.7).
|
||||
# Gives an overall ~20% of improvement!
|
||||
#tmp = difflib.get_close_matches(key[1], similar_pool, n=1, cutoff=use_similar)
|
||||
#if tmp:
|
||||
#tmp = tmp[0]
|
||||
|
||||
# tmp = difflib.get_close_matches(key[1], similar_pool, n=1, cutoff=use_similar)
|
||||
# if tmp:
|
||||
# tmp = tmp[0]
|
||||
tmp = None
|
||||
s = difflib.SequenceMatcher()
|
||||
s.set_seq2(key[1])
|
||||
@@ -178,9 +181,11 @@ def enable_addons(addons=None, support=None, disable=False, check_only=False):
|
||||
userpref = bpy.context.user_preferences
|
||||
used_ext = {ext.module for ext in userpref.addons}
|
||||
|
||||
ret = [mod for mod in addon_utils.modules()
|
||||
if ((addons and mod.__name__ in addons) or
|
||||
(not addons and addon_utils.module_bl_info(mod)["support"] in support))]
|
||||
ret = [
|
||||
mod for mod in addon_utils.modules()
|
||||
if ((addons and mod.__name__ in addons) or
|
||||
(not addons and addon_utils.module_bl_info(mod)["support"] in support))
|
||||
]
|
||||
|
||||
if not check_only:
|
||||
for mod in ret:
|
||||
@@ -229,18 +234,21 @@ class I18nMessage:
|
||||
|
||||
def _get_msgctxt(self):
|
||||
return "".join(self.msgctxt_lines)
|
||||
|
||||
def _set_msgctxt(self, ctxt):
|
||||
self.msgctxt_lines = [ctxt]
|
||||
msgctxt = property(_get_msgctxt, _set_msgctxt)
|
||||
|
||||
def _get_msgid(self):
|
||||
return "".join(self.msgid_lines)
|
||||
|
||||
def _set_msgid(self, msgid):
|
||||
self.msgid_lines = [msgid]
|
||||
msgid = property(_get_msgid, _set_msgid)
|
||||
|
||||
def _get_msgstr(self):
|
||||
return "".join(self.msgstr_lines)
|
||||
|
||||
def _set_msgstr(self, msgstr):
|
||||
self.msgstr_lines = [msgstr]
|
||||
msgstr = property(_get_msgstr, _set_msgstr)
|
||||
@@ -250,12 +258,15 @@ class I18nMessage:
|
||||
lstrip2 = len(self.settings.PO_COMMENT_PREFIX_SOURCE_CUSTOM)
|
||||
return ([l[lstrip1:] for l in self.comment_lines if l.startswith(self.settings.PO_COMMENT_PREFIX_SOURCE)] +
|
||||
[l[lstrip2:] for l in self.comment_lines
|
||||
if l.startswith(self.settings.PO_COMMENT_PREFIX_SOURCE_CUSTOM)])
|
||||
if l.startswith(self.settings.PO_COMMENT_PREFIX_SOURCE_CUSTOM)])
|
||||
|
||||
def _set_sources(self, sources):
|
||||
cmmlines = self.comment_lines.copy()
|
||||
for l in cmmlines:
|
||||
if (l.startswith(self.settings.PO_COMMENT_PREFIX_SOURCE) or
|
||||
l.startswith(self.settings.PO_COMMENT_PREFIX_SOURCE_CUSTOM)):
|
||||
if (
|
||||
l.startswith(self.settings.PO_COMMENT_PREFIX_SOURCE) or
|
||||
l.startswith(self.settings.PO_COMMENT_PREFIX_SOURCE_CUSTOM)
|
||||
):
|
||||
self.comment_lines.remove(l)
|
||||
lines_src = []
|
||||
lines_src_custom = []
|
||||
@@ -791,7 +802,7 @@ class I18nMessages:
|
||||
if len(k) > 1 and src_rna in src_to_msg:
|
||||
k &= src_to_msg[src_rna]
|
||||
msgmap["rna_tip"]["key"] = k
|
||||
#print(k)
|
||||
# print(k)
|
||||
btip = getattr(msgs, msgmap["but_tip"]["msgstr"])
|
||||
#print("button tip: " + btip)
|
||||
if btip and btip not in {rtip, etip}:
|
||||
@@ -1051,12 +1062,13 @@ class I18nMessages:
|
||||
import subprocess
|
||||
with tempfile.NamedTemporaryFile(mode='w+', encoding="utf-8") as tmp_po_f:
|
||||
self.write_messages_to_po(tmp_po_f)
|
||||
cmd = (self.settings.GETTEXT_MSGFMT_EXECUTABLE,
|
||||
"--statistics", # show stats
|
||||
tmp_po_f.name,
|
||||
"-o",
|
||||
fname,
|
||||
)
|
||||
cmd = (
|
||||
self.settings.GETTEXT_MSGFMT_EXECUTABLE,
|
||||
"--statistics", # show stats
|
||||
tmp_po_f.name,
|
||||
"-o",
|
||||
fname,
|
||||
)
|
||||
print("Running ", " ".join(cmd))
|
||||
ret = subprocess.call(cmd)
|
||||
print("Finished.")
|
||||
@@ -1081,6 +1093,7 @@ class I18nMessages:
|
||||
EOT = b"0x04" # Used to concatenate context and msgid
|
||||
_msgid_offset = 0
|
||||
_msgstr_offset = 0
|
||||
|
||||
def _gen(v):
|
||||
nonlocal _msgid_offset, _msgstr_offset
|
||||
msgid = v.msgid.encode("utf-8")
|
||||
@@ -1188,6 +1201,7 @@ class I18n:
|
||||
|
||||
def _py_file_get(self):
|
||||
return self.src.get(self.settings.PARSER_PY_ID)
|
||||
|
||||
def _py_file_set(self, value):
|
||||
self.src[self.settings.PARSER_PY_ID] = value
|
||||
py_file = property(_py_file_get, _py_file_set)
|
||||
@@ -1252,7 +1266,8 @@ class I18n:
|
||||
_ctx_txt = "s are"
|
||||
else:
|
||||
_ctx_txt = " is"
|
||||
lines = (("",
|
||||
lines = ((
|
||||
"",
|
||||
"Average stats for all {} translations:\n".format(self.nbr_trans),
|
||||
" {:>6.1%} done!\n".format(self.lvl / self.nbr_trans),
|
||||
" {:>6.1%} of messages are tooltips.\n".format(self.lvl_ttips / self.nbr_trans),
|
||||
@@ -1350,10 +1365,10 @@ class I18n:
|
||||
comment_lines = [self.settings.PO_COMMENT_PREFIX + c for c in user_comments] + common_comment_lines
|
||||
self.trans[uid].msgs[key] = I18nMessage(ctxt, [key[1]], [msgstr], comment_lines, False, is_fuzzy,
|
||||
settings=self.settings)
|
||||
#key = self.settings.PO_HEADER_KEY
|
||||
#for uid, trans in self.trans.items():
|
||||
#if key not in trans.msgs:
|
||||
#trans.msgs[key]
|
||||
# key = self.settings.PO_HEADER_KEY
|
||||
# for uid, trans in self.trans.items():
|
||||
# if key not in trans.msgs:
|
||||
# trans.msgs[key]
|
||||
self.unescape()
|
||||
|
||||
def write(self, kind, langs=set()):
|
||||
|
||||
@@ -31,7 +31,7 @@ from mathutils import (
|
||||
)
|
||||
|
||||
|
||||
INTERN_PREVIEW_TYPES = {'MATERIAL', 'LAMP', 'WORLD', 'TEXTURE', 'IMAGE'}
|
||||
INTERN_PREVIEW_TYPES = {'MATERIAL', 'LIGHT', 'WORLD', 'TEXTURE', 'IMAGE'}
|
||||
OBJECT_TYPES_RENDER = {'MESH', 'CURVE', 'SURFACE', 'META', 'FONT'}
|
||||
|
||||
|
||||
@@ -73,15 +73,15 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
|
||||
|
||||
# Helpers.
|
||||
RenderContext = collections.namedtuple("RenderContext", (
|
||||
"scene", "world", "camera", "lamp", "camera_data", "lamp_data", "image", # All those are names!
|
||||
"backup_scene", "backup_world", "backup_camera", "backup_lamp", "backup_camera_data", "backup_lamp_data",
|
||||
"scene", "world", "camera", "light", "camera_data", "light_data", "image", # All those are names!
|
||||
"backup_scene", "backup_world", "backup_camera", "backup_light", "backup_camera_data", "backup_light_data",
|
||||
))
|
||||
|
||||
RENDER_PREVIEW_SIZE = bpy.app.render_preview_size
|
||||
|
||||
def render_context_create(engine, objects_ignored):
|
||||
if engine == '__SCENE':
|
||||
backup_scene, backup_world, backup_camera, backup_lamp, backup_camera_data, backup_lamp_data = [()] * 6
|
||||
backup_scene, backup_world, backup_camera, backup_light, backup_camera_data, backup_light_data = [()] * 6
|
||||
scene = bpy.context.screen.scene
|
||||
exclude_props = {('world',), ('camera',), ('tool_settings',), ('preview',)}
|
||||
backup_scene = tuple(rna_backup_gen(scene, exclude_props=exclude_props))
|
||||
@@ -96,20 +96,20 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
|
||||
camera.rotation_euler = Euler((1.1635528802871704, 0.0, 0.7853981852531433), 'XYZ') # (66.67, 0.0, 45.0)
|
||||
scene.camera = camera
|
||||
scene.objects.link(camera)
|
||||
# TODO: add lamp if none found in scene?
|
||||
lamp = None
|
||||
lamp_data = None
|
||||
# TODO: add light if none found in scene?
|
||||
light = None
|
||||
light_data = None
|
||||
else:
|
||||
backup_scene, backup_world, backup_camera, backup_lamp, backup_camera_data, backup_lamp_data = [None] * 6
|
||||
backup_scene, backup_world, backup_camera, backup_light, backup_camera_data, backup_light_data = [None] * 6
|
||||
|
||||
scene = bpy.data.scenes.new("TEMP_preview_render_scene")
|
||||
world = bpy.data.worlds.new("TEMP_preview_render_world")
|
||||
camera_data = bpy.data.cameras.new("TEMP_preview_render_camera")
|
||||
camera = bpy.data.objects.new("TEMP_preview_render_camera", camera_data)
|
||||
lamp_data = bpy.data.lamps.new("TEMP_preview_render_lamp", 'SPOT')
|
||||
lamp = bpy.data.objects.new("TEMP_preview_render_lamp", lamp_data)
|
||||
light_data = bpy.data.lights.new("TEMP_preview_render_light", 'SPOT')
|
||||
light = bpy.data.objects.new("TEMP_preview_render_light", light_data)
|
||||
|
||||
objects_ignored.add((camera.name, lamp.name))
|
||||
objects_ignored.add((camera.name, light.name))
|
||||
|
||||
scene.world = world
|
||||
|
||||
@@ -117,26 +117,14 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
|
||||
scene.camera = camera
|
||||
scene.objects.link(camera)
|
||||
|
||||
lamp.rotation_euler = Euler((0.7853981852531433, 0.0, 1.7453292608261108), 'XYZ') # (45.0, 0.0, 100.0)
|
||||
lamp_data.falloff_type = 'CONSTANT'
|
||||
lamp_data.spot_size = 1.0471975803375244 # 60
|
||||
scene.objects.link(lamp)
|
||||
light.rotation_euler = Euler((0.7853981852531433, 0.0, 1.7453292608261108), 'XYZ') # (45.0, 0.0, 100.0)
|
||||
light_data.falloff_type = 'CONSTANT'
|
||||
light_data.spot_size = 1.0471975803375244 # 60
|
||||
scene.objects.link(light)
|
||||
|
||||
if engine == 'BLENDER_RENDER':
|
||||
scene.render.engine = 'BLENDER_RENDER'
|
||||
scene.render.alpha_mode = 'TRANSPARENT'
|
||||
|
||||
world.use_sky_blend = True
|
||||
world.horizon_color = 0.9, 0.9, 0.9
|
||||
world.zenith_color = 0.5, 0.5, 0.5
|
||||
world.ambient_color = 0.1, 0.1, 0.1
|
||||
world.light_settings.use_environment_light = True
|
||||
world.light_settings.environment_energy = 1.0
|
||||
world.light_settings.environment_color = 'SKY_COLOR'
|
||||
elif engine == 'CYCLES':
|
||||
scene.render.engine = 'CYCLES'
|
||||
scene.cycles.film_transparent = True
|
||||
# TODO: define Cycles world?
|
||||
scene.render.engine = 'CYCLES'
|
||||
scene.cycles.film_transparent = True
|
||||
# TODO: define Cycles world?
|
||||
|
||||
scene.render.image_settings.file_format = 'PNG'
|
||||
scene.render.image_settings.color_depth = '8'
|
||||
@@ -154,9 +142,9 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
|
||||
image.filepath = scene.render.filepath
|
||||
|
||||
return RenderContext(
|
||||
scene.name, world.name if world else None, camera.name, lamp.name if lamp else None,
|
||||
camera_data.name, lamp_data.name if lamp_data else None, image.name,
|
||||
backup_scene, backup_world, backup_camera, backup_lamp, backup_camera_data, backup_lamp_data,
|
||||
scene.name, world.name if world else None, camera.name, light.name if light else None,
|
||||
camera_data.name, light_data.name if light_data else None, image.name,
|
||||
backup_scene, backup_world, backup_camera, backup_light, backup_camera_data, backup_light_data,
|
||||
)
|
||||
|
||||
def render_context_delete(render_context):
|
||||
@@ -171,8 +159,8 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
|
||||
scene.camera = None
|
||||
if render_context.camera:
|
||||
scene.objects.unlink(bpy.data.objects[render_context.camera, None])
|
||||
if render_context.lamp:
|
||||
scene.objects.unlink(bpy.data.objects[render_context.lamp, None])
|
||||
if render_context.light:
|
||||
scene.objects.unlink(bpy.data.objects[render_context.light, None])
|
||||
bpy.data.scenes.remove(scene, do_unlink=True)
|
||||
scene = None
|
||||
else:
|
||||
@@ -213,18 +201,18 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
|
||||
print("ERROR:", e)
|
||||
success = False
|
||||
|
||||
if render_context.lamp:
|
||||
if render_context.light:
|
||||
try:
|
||||
lamp = bpy.data.objects[render_context.lamp, None]
|
||||
if render_context.backup_lamp is None:
|
||||
light = bpy.data.objects[render_context.light, None]
|
||||
if render_context.backup_light is None:
|
||||
if scene is not None:
|
||||
scene.objects.unlink(lamp)
|
||||
lamp.user_clear()
|
||||
bpy.data.objects.remove(lamp)
|
||||
bpy.data.lamps.remove(bpy.data.lamps[render_context.lamp_data, None])
|
||||
scene.objects.unlink(light)
|
||||
light.user_clear()
|
||||
bpy.data.objects.remove(light)
|
||||
bpy.data.lights.remove(bpy.data.lights[render_context.light_data, None])
|
||||
else:
|
||||
rna_backup_restore(lamp, render_context.backup_lamp)
|
||||
rna_backup_restore(bpy.data.lamps[render_context.lamp_data, None], render_context.backup_lamp_data)
|
||||
rna_backup_restore(light, render_context.backup_light)
|
||||
rna_backup_restore(bpy.data.lights[render_context.light_data, None], render_context.backup_light_data)
|
||||
except Exception as e:
|
||||
print("ERROR:", e)
|
||||
success = False
|
||||
@@ -239,17 +227,6 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
|
||||
|
||||
return success
|
||||
|
||||
def objects_render_engine_guess(obs):
|
||||
for obname, libpath in obs:
|
||||
ob = bpy.data.objects[obname, libpath]
|
||||
for matslot in ob.material_slots:
|
||||
mat = matslot.material
|
||||
if mat and mat.use_nodes and mat.node_tree:
|
||||
for nd in mat.node_tree.nodes:
|
||||
if nd.shading_compatibility == {'NEW_SHADING'}:
|
||||
return 'CYCLES'
|
||||
return 'BLENDER_RENDER'
|
||||
|
||||
def object_bbox_merge(bbox, ob, ob_space, offset_matrix):
|
||||
# Take collections instances into account (including linked one in this case).
|
||||
if ob.type == 'EMPTY' and ob.dupli_type == 'COLLECTION':
|
||||
@@ -305,7 +282,7 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
|
||||
scene = bpy.data.scenes[render_context.scene, None]
|
||||
if objects is not None:
|
||||
camera = bpy.data.objects[render_context.camera, None]
|
||||
lamp = bpy.data.objects[render_context.lamp, None] if render_context.lamp is not None else None
|
||||
light = bpy.data.objects[render_context.light, None] if render_context.light is not None else None
|
||||
cos = objects_bbox_calc(camera, objects, offset_matrix)
|
||||
loc, ortho_scale = camera.camera_fit_coords(scene, cos)
|
||||
camera.location = loc
|
||||
@@ -320,9 +297,9 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
|
||||
max_dist = dist
|
||||
camera.data.clip_start = min_dist / 2
|
||||
camera.data.clip_end = max_dist * 2
|
||||
if lamp:
|
||||
loc, ortho_scale = lamp.camera_fit_coords(scene, cos)
|
||||
lamp.location = loc
|
||||
if light:
|
||||
loc, ortho_scale = light.camera_fit_coords(scene, cos)
|
||||
light.location = loc
|
||||
scene.update()
|
||||
|
||||
bpy.ops.render.render(write_still=True)
|
||||
@@ -360,11 +337,10 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
|
||||
continue
|
||||
objects = ((root.name, None),)
|
||||
|
||||
render_engine = objects_render_engine_guess(objects)
|
||||
render_context = render_contexts.get(render_engine, None)
|
||||
render_context = render_contexts.get('CYCLES', None)
|
||||
if render_context is None:
|
||||
render_context = render_context_create(render_engine, objects_ignored)
|
||||
render_contexts[render_engine] = render_context
|
||||
render_context = render_context_create('CYCLES', objects_ignored)
|
||||
render_contexts['CYCLES'] = render_context
|
||||
|
||||
scene = bpy.data.scenes[render_context.scene, None]
|
||||
bpy.context.screen.scene = scene
|
||||
@@ -405,11 +381,10 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
|
||||
# Here too, we do want to keep linked objects members of local collection...
|
||||
objects = tuple((ob.name, ob.library.filepath if ob.library else None) for ob in grp.objects)
|
||||
|
||||
render_engine = objects_render_engine_guess(objects)
|
||||
render_context = render_contexts.get(render_engine, None)
|
||||
render_context = render_contexts.get('CYCLES', None)
|
||||
if render_context is None:
|
||||
render_context = render_context_create(render_engine, objects_ignored)
|
||||
render_contexts[render_engine] = render_context
|
||||
render_context = render_context_create('CYCLES', objects_ignored)
|
||||
render_contexts['CYCLES'] = render_context
|
||||
|
||||
scene = bpy.data.scenes[render_context.scene, None]
|
||||
bpy.context.screen.scene = scene
|
||||
@@ -442,7 +417,7 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern):
|
||||
if not has_camera:
|
||||
# We had to add a temp camera, now we need to place it to see interesting objects!
|
||||
objects = tuple((ob.name, ob.library.filepath if ob.library else None) for ob in scene.objects
|
||||
if (not ob.hide_render) and (ob.type in OBJECT_TYPES_RENDER))
|
||||
if (not ob.hide_render) and (ob.type in OBJECT_TYPES_RENDER))
|
||||
|
||||
preview_render_do(render_context, 'scenes', scene.name, objects)
|
||||
|
||||
|
||||
@@ -98,5 +98,6 @@ def main():
|
||||
for value in read_blend_rend_chunk(arg):
|
||||
print("%d %d %s" % value)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@@ -228,4 +228,5 @@ class BPyOpsSubModOp:
|
||||
return ("<function bpy.ops.%s.%s at 0x%x'>" %
|
||||
(self._module, self._func, id(self)))
|
||||
|
||||
|
||||
ops_fake_module = BPyOps()
|
||||
|
||||
@@ -183,6 +183,8 @@ def clean_name(name, replace="_"):
|
||||
|
||||
trans = maketrans_init()
|
||||
return name.translate(trans)
|
||||
|
||||
|
||||
clean_name._trans_cache = {}
|
||||
|
||||
|
||||
@@ -223,6 +225,7 @@ def display_name(name):
|
||||
name = _clean_utf8(name)
|
||||
return name
|
||||
|
||||
|
||||
def display_name_to_filepath(name):
|
||||
"""
|
||||
Performs the reverse of display_name using literal versions of characters
|
||||
|
||||
@@ -34,7 +34,6 @@ __all__ = (
|
||||
"refresh_script_paths",
|
||||
"app_template_paths",
|
||||
"register_class",
|
||||
"register_module",
|
||||
"register_manual_map",
|
||||
"unregister_manual_map",
|
||||
"register_classes_factory",
|
||||
@@ -50,7 +49,6 @@ __all__ = (
|
||||
"smpte_from_seconds",
|
||||
"units",
|
||||
"unregister_class",
|
||||
"unregister_module",
|
||||
"user_resource",
|
||||
)
|
||||
|
||||
@@ -166,10 +164,6 @@ def load_scripts(reload_scripts=False, refresh_scripts=False):
|
||||
for module_name in [ext.module for ext in _user_preferences.addons]:
|
||||
_addon_utils.disable(module_name)
|
||||
|
||||
# *AFTER* unregistering all add-ons, otherwise all calls to
|
||||
# unregister_module() will silently fail (do nothing).
|
||||
_bpy_types.TypeMap.clear()
|
||||
|
||||
def register_module_call(mod):
|
||||
register = getattr(mod, "register", None)
|
||||
if register:
|
||||
@@ -488,11 +482,11 @@ def smpte_from_frame(frame, fps=None, fps_base=None):
|
||||
|
||||
return (
|
||||
"%s%02d:%02d:%02d:%02d" % (
|
||||
sign,
|
||||
int(frame / (3600 * fps)), # HH
|
||||
int((frame / (60 * fps)) % 60), # MM
|
||||
int((frame / fps) % 60), # SS
|
||||
int(frame % fps), # FF
|
||||
sign,
|
||||
int(frame / (3600 * fps)), # HH
|
||||
int((frame / (60 * fps)) % 60), # MM
|
||||
int((frame / fps) % 60), # SS
|
||||
int(frame % fps), # FF
|
||||
))
|
||||
|
||||
|
||||
@@ -581,8 +575,13 @@ def keyconfig_set(filepath, report=None):
|
||||
try:
|
||||
error_msg = ""
|
||||
with open(filepath, 'r', encoding='utf-8') as keyfile:
|
||||
exec(compile(keyfile.read(), filepath, "exec"),
|
||||
{"__file__": filepath})
|
||||
exec(
|
||||
compile(keyfile.read(), filepath, "exec"),
|
||||
{
|
||||
"__file__": filepath,
|
||||
"__name__": "__main__",
|
||||
}
|
||||
)
|
||||
except:
|
||||
import traceback
|
||||
error_msg = traceback.format_exc()
|
||||
@@ -650,58 +649,6 @@ def user_resource(resource_type, path="", create=False):
|
||||
return target_path
|
||||
|
||||
|
||||
def _bpy_module_classes(module, is_registered=False):
|
||||
typemap_list = _bpy_types.TypeMap.get(module, ())
|
||||
i = 0
|
||||
while i < len(typemap_list):
|
||||
cls_weakref = typemap_list[i]
|
||||
cls = cls_weakref()
|
||||
|
||||
if cls is None:
|
||||
del typemap_list[i]
|
||||
else:
|
||||
if is_registered == cls.is_registered:
|
||||
yield cls
|
||||
i += 1
|
||||
|
||||
|
||||
def register_module(module, verbose=False):
|
||||
if verbose:
|
||||
print("bpy.utils.register_module(%r): ..." % module)
|
||||
cls = None
|
||||
for cls in _bpy_module_classes(module, is_registered=False):
|
||||
if verbose:
|
||||
print(" %r" % cls)
|
||||
try:
|
||||
register_class(cls)
|
||||
except:
|
||||
print("bpy.utils.register_module(): "
|
||||
"failed to registering class %r" % cls)
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
if verbose:
|
||||
print("done.\n")
|
||||
if cls is None:
|
||||
raise Exception("register_module(%r): defines no classes" % module)
|
||||
|
||||
|
||||
def unregister_module(module, verbose=False):
|
||||
if verbose:
|
||||
print("bpy.utils.unregister_module(%r): ..." % module)
|
||||
for cls in _bpy_module_classes(module, is_registered=True):
|
||||
if verbose:
|
||||
print(" %r" % cls)
|
||||
try:
|
||||
unregister_class(cls)
|
||||
except:
|
||||
print("bpy.utils.unregister_module(): "
|
||||
"failed to unregistering class %r" % cls)
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
if verbose:
|
||||
print("done.\n")
|
||||
|
||||
|
||||
def register_classes_factory(classes):
|
||||
"""
|
||||
Utility function to create register and unregister functions
|
||||
@@ -773,6 +720,7 @@ def _blender_default_map():
|
||||
del _sys.modules["rna_manual_reference"]
|
||||
return ret
|
||||
|
||||
|
||||
# hooks for doc lookups
|
||||
_manual_map = [_blender_default_map]
|
||||
|
||||
|
||||
@@ -31,4 +31,4 @@ __all__ = (
|
||||
"mesh_utils",
|
||||
"node_utils",
|
||||
"view3d_utils",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -174,7 +174,7 @@ def bake_action_iter(
|
||||
|
||||
# Bendy Bones
|
||||
if pbone.bone.bbone_segments > 1:
|
||||
bbones[name] = {bb_prop : getattr(pbone, bb_prop) for bb_prop in BBONE_PROPS}
|
||||
bbones[name] = {bb_prop: getattr(pbone, bb_prop) for bb_prop in BBONE_PROPS}
|
||||
return matrix, bbones
|
||||
|
||||
if do_parents_clear:
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
__all__ = (
|
||||
"load_image",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# limited replacement for BPyImage.comprehensiveImageLoad
|
||||
|
||||
@@ -32,7 +32,7 @@ __all__ = (
|
||||
"path_reference_copy",
|
||||
"path_reference_mode",
|
||||
"unique_name"
|
||||
)
|
||||
)
|
||||
|
||||
import bpy
|
||||
from bpy.props import (
|
||||
@@ -52,24 +52,18 @@ def _check_axis_conversion(op):
|
||||
|
||||
|
||||
class ExportHelper:
|
||||
filepath = StringProperty(
|
||||
name="File Path",
|
||||
description="Filepath used for exporting the file",
|
||||
maxlen=1024,
|
||||
subtype='FILE_PATH',
|
||||
)
|
||||
check_existing = BoolProperty(
|
||||
name="Check Existing",
|
||||
description="Check and warn on overwriting existing files",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
||||
# needed for mix-ins
|
||||
order = [
|
||||
"filepath",
|
||||
"check_existing",
|
||||
]
|
||||
filepath: StringProperty(
|
||||
name="File Path",
|
||||
description="Filepath used for exporting the file",
|
||||
maxlen=1024,
|
||||
subtype='FILE_PATH',
|
||||
)
|
||||
check_existing: BoolProperty(
|
||||
name="Check Existing",
|
||||
description="Check and warn on overwriting existing files",
|
||||
default=True,
|
||||
options={'HIDDEN'},
|
||||
)
|
||||
|
||||
# subclasses can override with decorator
|
||||
# True == use ext, False == no ext, None == do nothing.
|
||||
@@ -112,17 +106,12 @@ class ExportHelper:
|
||||
|
||||
|
||||
class ImportHelper:
|
||||
filepath = StringProperty(
|
||||
name="File Path",
|
||||
description="Filepath used for importing the file",
|
||||
maxlen=1024,
|
||||
subtype='FILE_PATH',
|
||||
)
|
||||
|
||||
# needed for mix-ins
|
||||
order = [
|
||||
"filepath",
|
||||
]
|
||||
filepath: StringProperty(
|
||||
name="File Path",
|
||||
description="Filepath used for importing the file",
|
||||
maxlen=1024,
|
||||
subtype='FILE_PATH',
|
||||
)
|
||||
|
||||
def invoke(self, context, event):
|
||||
context.window_manager.fileselect_add(self)
|
||||
@@ -138,43 +127,40 @@ def orientation_helper_factory(name, axis_forward='Y', axis_up='Z'):
|
||||
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])
|
||||
'XYZ'[('XYZ'.index(self.axis_up[-1]) + 1) % 3])
|
||||
|
||||
members['axis_forward'] = EnumProperty(
|
||||
name="Forward",
|
||||
items=(('X', "X Forward", ""),
|
||||
('Y', "Y Forward", ""),
|
||||
('Z', "Z Forward", ""),
|
||||
('-X', "-X Forward", ""),
|
||||
('-Y', "-Y Forward", ""),
|
||||
('-Z', "-Z Forward", ""),
|
||||
),
|
||||
default=axis_forward,
|
||||
update=_update_axis_forward,
|
||||
)
|
||||
name="Forward",
|
||||
items=(
|
||||
('X', "X Forward", ""),
|
||||
('Y', "Y Forward", ""),
|
||||
('Z', "Z Forward", ""),
|
||||
('-X', "-X Forward", ""),
|
||||
('-Y', "-Y Forward", ""),
|
||||
('-Z', "-Z Forward", ""),
|
||||
),
|
||||
default=axis_forward,
|
||||
update=_update_axis_forward,
|
||||
)
|
||||
|
||||
def _update_axis_up(self, context):
|
||||
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])
|
||||
'XYZ'[('XYZ'.index(self.axis_forward[-1]) + 1) % 3])
|
||||
|
||||
members['axis_up'] = EnumProperty(
|
||||
name="Up",
|
||||
items=(('X', "X Up", ""),
|
||||
('Y', "Y Up", ""),
|
||||
('Z', "Z Up", ""),
|
||||
('-X', "-X Up", ""),
|
||||
('-Y', "-Y Up", ""),
|
||||
('-Z', "-Z Up", ""),
|
||||
),
|
||||
default=axis_up,
|
||||
update=_update_axis_up,
|
||||
)
|
||||
|
||||
members["order"] = [
|
||||
"axis_forward",
|
||||
"axis_up",
|
||||
]
|
||||
name="Up",
|
||||
items=(
|
||||
('X', "X Up", ""),
|
||||
('Y', "Y Up", ""),
|
||||
('Z', "Z Up", ""),
|
||||
('-X', "-X Up", ""),
|
||||
('-Y', "-Y Up", ""),
|
||||
('-Z', "-Z Up", ""),
|
||||
),
|
||||
default=axis_up,
|
||||
update=_update_axis_up,
|
||||
)
|
||||
|
||||
return type(name, (object,), members)
|
||||
|
||||
@@ -205,7 +191,7 @@ _axis_convert_matrix = (
|
||||
((1.0, 0.0, 0.0), (0.0, -1.0, 0.0), (0.0, 0.0, -1.0)),
|
||||
((1.0, 0.0, 0.0), (0.0, 0.0, 1.0), (0.0, -1.0, 0.0)),
|
||||
((1.0, 0.0, 0.0), (0.0, 0.0, -1.0), (0.0, 1.0, 0.0)),
|
||||
)
|
||||
)
|
||||
|
||||
# store args as a single int
|
||||
# (X Y Z -X -Y -Z) --> (0, 1, 2, 3, 4, 5)
|
||||
@@ -282,7 +268,7 @@ _axis_convert_lut = (
|
||||
{0x408, 0x810, 0xA20, 0x228, 0x081, 0x891, 0x699, 0x2A9, 0x102, 0x50A,
|
||||
0x71A, 0xB22, 0x4CB, 0x8D3, 0xAE3, 0x2EB, 0x144, 0x954, 0x75C, 0x36C,
|
||||
0x045, 0x44D, 0x65D, 0xA65},
|
||||
)
|
||||
)
|
||||
|
||||
_axis_convert_num = {'X': 0, 'Y': 1, 'Z': 2, '-X': 3, '-Y': 4, '-Z': 5}
|
||||
|
||||
@@ -303,11 +289,11 @@ def axis_conversion(from_forward='Y', from_up='Z', to_forward='Y', to_up='Z'):
|
||||
"can't use up/forward on the same axis")
|
||||
|
||||
value = reduce(int.__or__, (_axis_convert_num[a] << (i * 3)
|
||||
for i, a in enumerate((from_forward,
|
||||
from_up,
|
||||
to_forward,
|
||||
to_up,
|
||||
))))
|
||||
for i, a in enumerate((from_forward,
|
||||
from_up,
|
||||
to_forward,
|
||||
to_up,
|
||||
))))
|
||||
|
||||
for i, axis_lut in enumerate(_axis_convert_lut):
|
||||
if value in axis_lut:
|
||||
@@ -392,20 +378,21 @@ def unpack_face_list(list_of_tuples):
|
||||
|
||||
|
||||
path_reference_mode = EnumProperty(
|
||||
name="Path Mode",
|
||||
description="Method used to reference paths",
|
||||
items=(('AUTO', "Auto", "Use Relative paths with subdirectories only"),
|
||||
('ABSOLUTE', "Absolute", "Always write absolute paths"),
|
||||
('RELATIVE', "Relative", "Always write relative paths "
|
||||
"(where possible)"),
|
||||
('MATCH', "Match", "Match Absolute/Relative "
|
||||
"setting with input path"),
|
||||
('STRIP', "Strip Path", "Filename only"),
|
||||
('COPY', "Copy", "Copy the file to the destination path "
|
||||
"(or subdirectory)"),
|
||||
),
|
||||
default='AUTO',
|
||||
)
|
||||
name="Path Mode",
|
||||
description="Method used to reference paths",
|
||||
items=(
|
||||
('AUTO', "Auto", "Use Relative paths with subdirectories only"),
|
||||
('ABSOLUTE', "Absolute", "Always write absolute paths"),
|
||||
('RELATIVE', "Relative", "Always write relative paths "
|
||||
"(where possible)"),
|
||||
('MATCH', "Match", "Match Absolute/Relative "
|
||||
"setting with input path"),
|
||||
('STRIP', "Strip Path", "Filename only"),
|
||||
('COPY', "Copy", "Copy the file to the destination path "
|
||||
"(or subdirectory)"),
|
||||
),
|
||||
default='AUTO',
|
||||
)
|
||||
|
||||
|
||||
def path_reference(filepath,
|
||||
|
||||
@@ -177,8 +177,8 @@ def addon_keymap_register(wm, keymaps_description):
|
||||
for km_info, km_items in keymaps_description:
|
||||
km_name, km_sptype, km_regtype, km_ismodal = km_info
|
||||
kmap = [k for k in kconf.keymaps
|
||||
if k.name == km_name and k.region_type == km_regtype and
|
||||
k.space_type == km_sptype and k.is_modal == km_ismodal]
|
||||
if k.name == km_name and k.region_type == km_regtype and
|
||||
k.space_type == km_sptype and k.is_modal == km_ismodal]
|
||||
if kmap:
|
||||
kmap = kmap[0]
|
||||
else:
|
||||
@@ -202,8 +202,8 @@ def addon_keymap_unregister(wm, keymaps_description):
|
||||
for km_info, km_items in keymaps_description:
|
||||
km_name, km_sptype, km_regtype, km_ismodal = km_info
|
||||
kmaps = (k for k in kconf.keymaps
|
||||
if k.name == km_name and k.region_type == km_regtype and
|
||||
k.space_type == km_sptype and k.is_modal == km_ismodal)
|
||||
if k.name == km_name and k.region_type == km_regtype and
|
||||
k.space_type == km_sptype and k.is_modal == km_ismodal)
|
||||
for kmap in kmaps:
|
||||
for kmi_kwargs, props in km_items:
|
||||
idname = kmi_kwargs["idname"]
|
||||
@@ -240,9 +240,9 @@ def _export_properties(prefix, properties, kmi_id, lines=None):
|
||||
lines = []
|
||||
|
||||
def string_value(value):
|
||||
if isinstance(value, str) or isinstance(value, bool) or isinstance(value, float) or isinstance(value, int):
|
||||
if isinstance(value, (str, bool, float, int)):
|
||||
return repr(value)
|
||||
elif getattr(value, '__len__', False):
|
||||
elif hasattr(value, "__len__"):
|
||||
return repr(list(value))
|
||||
|
||||
print("Export key configuration: can't write ", value)
|
||||
@@ -296,8 +296,10 @@ def _kmistr(kmi, is_modal):
|
||||
return "".join(s)
|
||||
|
||||
|
||||
def keyconfig_export(wm, kc, filepath):
|
||||
|
||||
def keyconfig_export(
|
||||
wm, kc, filepath, *,
|
||||
all_keymaps=False,
|
||||
):
|
||||
f = open(filepath, "w")
|
||||
|
||||
f.write("import bpy\n")
|
||||
@@ -327,7 +329,7 @@ def keyconfig_export(wm, kc, filepath):
|
||||
keymaps = []
|
||||
edited_kc = FakeKeyConfig()
|
||||
for km in wm.keyconfigs.user.keymaps:
|
||||
if km.is_user_modified:
|
||||
if all_keymaps or km.is_user_modified:
|
||||
edited_kc.keymaps.append(km)
|
||||
# merge edited keymaps with non-default keyconfig, if it exists
|
||||
if kc != wm.keyconfigs.default:
|
||||
@@ -406,3 +408,11 @@ def keyconfig_test(kc):
|
||||
if testEntry(kc, entry):
|
||||
result = True
|
||||
return result
|
||||
|
||||
|
||||
# Note, we may eventually replace existing logic with this
|
||||
# so key configs are always data.
|
||||
from .keyconfig_utils_experimental import (
|
||||
keyconfig_export_as_data,
|
||||
keyconfig_import_from_data,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
# ##### 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>
|
||||
|
||||
__all__ = (
|
||||
"keyconfig_export_as_data",
|
||||
"keyconfig_import_from_data",
|
||||
)
|
||||
|
||||
|
||||
def indent(levels):
|
||||
return levels * " "
|
||||
|
||||
|
||||
def round_float_32(f):
|
||||
from struct import pack, unpack
|
||||
return unpack("f", pack("f", f))[0]
|
||||
|
||||
|
||||
def repr_f32(f):
|
||||
f_round = round_float_32(f)
|
||||
f_str = repr(f)
|
||||
f_str_frac = f_str.partition(".")[2]
|
||||
if not f_str_frac:
|
||||
return f_str
|
||||
for i in range(1, len(f_str_frac)):
|
||||
f_test = round(f, i)
|
||||
f_test_round = round_float_32(f_test)
|
||||
if f_test_round == f_round:
|
||||
return "%.*f" % (i, f_test)
|
||||
return f_str
|
||||
|
||||
|
||||
def kmi_args_as_data(kmi):
|
||||
s = [
|
||||
f"\"type\": '{kmi.type}'",
|
||||
f"\"value\": '{kmi.value}'"
|
||||
]
|
||||
|
||||
if kmi.any:
|
||||
s.append("\"any\": True")
|
||||
else:
|
||||
if kmi.shift:
|
||||
s.append("\"shift\": True")
|
||||
if kmi.ctrl:
|
||||
s.append("\"ctrl\": True")
|
||||
if kmi.alt:
|
||||
s.append("\"alt\": True")
|
||||
if kmi.oskey:
|
||||
s.append("\"oskey\": True")
|
||||
if kmi.key_modifier and kmi.key_modifier != 'NONE':
|
||||
s.append(f"\"key_modifier\": '{kmi.key_modifier}'")
|
||||
|
||||
return "{" + ", ".join(s) + "}"
|
||||
|
||||
|
||||
def _kmi_properties_to_lines_recursive(level, properties, lines):
|
||||
from bpy.types import OperatorProperties
|
||||
|
||||
def string_value(value):
|
||||
if isinstance(value, (str, bool, int)):
|
||||
return repr(value)
|
||||
elif isinstance(value, float):
|
||||
return repr_f32(value)
|
||||
elif getattr(value, '__len__', False):
|
||||
return repr(tuple(value))
|
||||
raise Exception(f"Export key configuration: can't write {value!r}")
|
||||
|
||||
for pname in properties.bl_rna.properties.keys():
|
||||
if pname != "rna_type":
|
||||
value = getattr(properties, pname)
|
||||
if isinstance(value, OperatorProperties):
|
||||
lines_test = []
|
||||
_kmi_properties_to_lines_recursive(level + 2, value, lines_test)
|
||||
if lines_test:
|
||||
lines.append(f"(")
|
||||
lines.append(f"\"{pname}\",\n")
|
||||
lines.append(f"{indent(level + 3)}" "[")
|
||||
lines.extend(lines_test)
|
||||
lines.append("],\n")
|
||||
lines.append(f"{indent(level + 3)}" "),\n" f"{indent(level + 2)}")
|
||||
del lines_test
|
||||
elif properties.is_property_set(pname):
|
||||
value = string_value(value)
|
||||
lines.append((f"(\"{pname}\", {value:s}),\n" f"{indent(level + 2)}"))
|
||||
|
||||
|
||||
def _kmi_properties_to_lines(level, kmi_props, lines):
|
||||
if kmi_props is None:
|
||||
return
|
||||
|
||||
lines_test = [f"\"properties\":\n" f"{indent(level + 1)}" "["]
|
||||
_kmi_properties_to_lines_recursive(level, kmi_props, lines_test)
|
||||
if len(lines_test) > 1:
|
||||
lines_test.append("],\n")
|
||||
lines.extend(lines_test)
|
||||
|
||||
|
||||
def _kmi_attrs_or_none(level, kmi):
|
||||
lines = []
|
||||
_kmi_properties_to_lines(level + 1, kmi.properties, lines)
|
||||
if kmi.active is False:
|
||||
lines.append(f"{indent(level)}\"active\":" "False,\n")
|
||||
if not lines:
|
||||
return None
|
||||
return "".join(lines)
|
||||
|
||||
|
||||
def keyconfig_export_as_data(wm, kc, filepath, *, all_keymaps=False):
|
||||
# Alternate foramt
|
||||
|
||||
# Generate a list of keymaps to export:
|
||||
#
|
||||
# First add all user_modified keymaps (found in keyconfigs.user.keymaps list),
|
||||
# then add all remaining keymaps from the currently active custom keyconfig.
|
||||
#
|
||||
# This will create a final list of keymaps that can be used as a "diff" against
|
||||
# the default blender keyconfig, recreating the current setup from a fresh blender
|
||||
# without needing to export keymaps which haven't been edited.
|
||||
|
||||
from .keyconfig_utils import keyconfig_merge
|
||||
|
||||
class FakeKeyConfig:
|
||||
keymaps = []
|
||||
edited_kc = FakeKeyConfig()
|
||||
for km in wm.keyconfigs.user.keymaps:
|
||||
if all_keymaps or km.is_user_modified:
|
||||
edited_kc.keymaps.append(km)
|
||||
# merge edited keymaps with non-default keyconfig, if it exists
|
||||
if kc != wm.keyconfigs.default:
|
||||
export_keymaps = keyconfig_merge(edited_kc, kc)
|
||||
else:
|
||||
export_keymaps = keyconfig_merge(edited_kc, edited_kc)
|
||||
|
||||
with open(filepath, "w") as fh:
|
||||
fw = fh.write
|
||||
fw("keyconfig_data = \\\n[")
|
||||
|
||||
for km, kc_x in export_keymaps:
|
||||
km = km.active()
|
||||
fw("(")
|
||||
fw(f"\"{km.name:s}\",\n")
|
||||
fw(f"{indent(2)}" "{")
|
||||
fw(f"\"space_type\": '{km.space_type:s}'")
|
||||
fw(f", \"region_type\": '{km.region_type:s}'")
|
||||
# We can detect from the kind of items.
|
||||
if km.is_modal:
|
||||
fw(", \"modal\": True")
|
||||
fw("},\n")
|
||||
fw(f"{indent(2)}" "{")
|
||||
is_modal = km.is_modal
|
||||
fw(f"\"items\":\n")
|
||||
fw(f"{indent(3)}[")
|
||||
for kmi in km.keymap_items:
|
||||
if is_modal:
|
||||
kmi_id = kmi.propvalue
|
||||
else:
|
||||
kmi_id = kmi.idname
|
||||
fw(f"(")
|
||||
kmi_args = kmi_args_as_data(kmi)
|
||||
kmi_data = _kmi_attrs_or_none(4, kmi)
|
||||
fw(f"\"{kmi_id:s}\"")
|
||||
if kmi_data is None:
|
||||
fw(f", ")
|
||||
else:
|
||||
fw(",\n" f"{indent(5)}")
|
||||
|
||||
fw(kmi_args)
|
||||
if kmi_data is None:
|
||||
fw(", None),\n")
|
||||
else:
|
||||
fw(",\n")
|
||||
fw(f"{indent(5)}" "{")
|
||||
fw(kmi_data)
|
||||
fw(f"{indent(6)}")
|
||||
fw("},\n" f"{indent(5)}")
|
||||
fw("),\n")
|
||||
fw(f"{indent(4)}")
|
||||
fw("],\n" f"{indent(3)}")
|
||||
fw("},\n" f"{indent(2)}")
|
||||
fw("),\n" f"{indent(1)}")
|
||||
|
||||
fw("]\n")
|
||||
fw("\n\n")
|
||||
fw("if __name__ == \"__main__\":\n")
|
||||
fw(" import os\n")
|
||||
fw(" from bpy_extras.keyconfig_utils import keyconfig_import_from_data\n")
|
||||
fw(" keyconfig_import_from_data(os.path.splitext(os.path.basename(__file__))[0], keyconfig_data)\n")
|
||||
|
||||
|
||||
def keyconfig_import_from_data(name, keyconfig_data):
|
||||
# Load data in the format defined above.
|
||||
#
|
||||
# Runs at load time, keep this fast!
|
||||
|
||||
def kmi_props_setattr(kmi_props, attr, value):
|
||||
if type(value) is list:
|
||||
kmi_subprop = getattr(kmi_props, attr)
|
||||
for subattr, subvalue in value:
|
||||
kmi_props_setattr(kmi_subprop, subattr, subvalue)
|
||||
return
|
||||
|
||||
try:
|
||||
setattr(kmi_props, attr, value)
|
||||
except AttributeError:
|
||||
print(f"Warning: property '{attr}' not found in keymap item '{kmi_props.__class__.__name__}'")
|
||||
except Exception as ex:
|
||||
print(f"Warning: {ex!r}")
|
||||
|
||||
import bpy
|
||||
wm = bpy.context.window_manager
|
||||
kc = wm.keyconfigs.new(name)
|
||||
del name
|
||||
|
||||
for (km_name, km_args, km_content) in keyconfig_data:
|
||||
km = kc.keymaps.new(km_name, **km_args)
|
||||
is_modal = km_args.get("modal", False)
|
||||
new_fn = getattr(km.keymap_items, "new_modal" if is_modal else "new")
|
||||
for (kmi_idname, kmi_args, kmi_data) in km_content["items"]:
|
||||
kmi = new_fn(kmi_idname, **kmi_args)
|
||||
if kmi_data is not None:
|
||||
if not kmi_data.get("active", True):
|
||||
kmi.active = False
|
||||
kmi_props_data = kmi_data.get("properties", None)
|
||||
if kmi_props_data is not None:
|
||||
kmi_props = kmi.properties
|
||||
for attr, value in kmi_props_data:
|
||||
kmi_props_setattr(kmi_props, attr, value)
|
||||
@@ -27,7 +27,7 @@ __all__ = (
|
||||
"edge_loops_from_edges",
|
||||
"ngon_tessellate",
|
||||
"face_random_points",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def mesh_linked_uv_islands(mesh):
|
||||
@@ -286,7 +286,7 @@ def edge_loops_from_edges(mesh, edges=None):
|
||||
ok = True
|
||||
while ok:
|
||||
ok = False
|
||||
#for i, ed in enumerate(edges):
|
||||
# for i, ed in enumerate(edges):
|
||||
i = len(edges)
|
||||
while i:
|
||||
i -= 1
|
||||
@@ -303,7 +303,7 @@ def edge_loops_from_edges(mesh, edges=None):
|
||||
vert_end = line_poly[-1]
|
||||
ok = 1
|
||||
del edges[i]
|
||||
#break
|
||||
# break
|
||||
elif v1 == vert_start:
|
||||
line_poly.insert(0, v2)
|
||||
vert_start = line_poly[0]
|
||||
@@ -315,7 +315,7 @@ def edge_loops_from_edges(mesh, edges=None):
|
||||
vert_start = line_poly[0]
|
||||
ok = 1
|
||||
del edges[i]
|
||||
#break
|
||||
# break
|
||||
line_polys.append(line_poly)
|
||||
|
||||
return line_polys
|
||||
@@ -481,7 +481,7 @@ def ngon_tessellate(from_data, indices, fix_loops=True):
|
||||
ii += len(verts)
|
||||
|
||||
fill = tessellate_polygon([[v[0] for v in loop] for loop in loop_list])
|
||||
#draw_loops(loop_list)
|
||||
# draw_loops(loop_list)
|
||||
#raise Exception("done loop")
|
||||
# map to original indices
|
||||
fill = [[vert_map[i] for i in reversed(f)] for f in fill]
|
||||
|
||||
@@ -20,8 +20,7 @@
|
||||
|
||||
__all__ = (
|
||||
"find_node_input",
|
||||
"find_output_node",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# XXX Names are not unique. Returns the first match.
|
||||
@@ -31,20 +30,3 @@ def find_node_input(node, name):
|
||||
return input
|
||||
|
||||
return None
|
||||
|
||||
# Return the output node to display in the UI. In case multiple node types are
|
||||
# specified, node types earlier in the list get priority.
|
||||
def find_output_node(ntree, nodetypes):
|
||||
if ntree:
|
||||
output_node = None
|
||||
for nodetype in nodetypes:
|
||||
for node in ntree.nodes:
|
||||
if getattr(node, "type", None) == nodetype:
|
||||
if getattr(node, "is_active_output", True):
|
||||
return node
|
||||
if not output_node:
|
||||
output_node = node
|
||||
if output_node:
|
||||
return output_node
|
||||
|
||||
return None
|
||||
|
||||
@@ -26,7 +26,7 @@ __all__ = (
|
||||
"object_add_grid_scale_apply_operator",
|
||||
"object_image_guess",
|
||||
"world_to_camera_view",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
import bpy
|
||||
@@ -194,19 +194,19 @@ class AddObjectHelper:
|
||||
if not self.view_align:
|
||||
self.rotation.zero()
|
||||
|
||||
view_align = BoolProperty(
|
||||
name="Align to View",
|
||||
default=False,
|
||||
update=view_align_update_callback,
|
||||
)
|
||||
location = FloatVectorProperty(
|
||||
name="Location",
|
||||
subtype='TRANSLATION',
|
||||
)
|
||||
rotation = FloatVectorProperty(
|
||||
name="Rotation",
|
||||
subtype='EULER',
|
||||
)
|
||||
view_align: BoolProperty(
|
||||
name="Align to View",
|
||||
default=False,
|
||||
update=view_align_update_callback,
|
||||
)
|
||||
location: FloatVectorProperty(
|
||||
name="Location",
|
||||
subtype='TRANSLATION',
|
||||
)
|
||||
rotation: FloatVectorProperty(
|
||||
name="Rotation",
|
||||
subtype='EULER',
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(self, context):
|
||||
|
||||
@@ -61,7 +61,7 @@ class Library(bpy_types.ID):
|
||||
# we could make this an attribute in rna.
|
||||
attr_links = ("actions", "armatures", "brushes", "cameras",
|
||||
"curves", "grease_pencil", "collections", "images",
|
||||
"lamps", "lattices", "materials", "metaballs",
|
||||
"lights", "lattices", "materials", "metaballs",
|
||||
"meshes", "node_groups", "objects", "scenes",
|
||||
"sounds", "speakers", "textures", "texts",
|
||||
"fonts", "worlds")
|
||||
@@ -144,9 +144,15 @@ class WindowManager(bpy_types.ID):
|
||||
finally:
|
||||
self.popmenu_end__internal(popup)
|
||||
|
||||
def popover(self, draw_func, keymap=None):
|
||||
def popover(
|
||||
self, draw_func, *,
|
||||
ui_units_x=0,
|
||||
keymap=None,
|
||||
):
|
||||
import bpy
|
||||
popup = self.popover_begin__internal()
|
||||
popup = self.popover_begin__internal(
|
||||
ui_units_x=ui_units_x,
|
||||
)
|
||||
|
||||
try:
|
||||
draw_func(popup, bpy.context)
|
||||
@@ -527,10 +533,6 @@ class Text(bpy_types.ID):
|
||||
self.write(string)
|
||||
|
||||
|
||||
# values are module: [(cls, path, line), ...]
|
||||
TypeMap = {}
|
||||
|
||||
|
||||
class Sound(bpy_types.ID):
|
||||
__slots__ = ()
|
||||
|
||||
@@ -542,59 +544,19 @@ class Sound(bpy_types.ID):
|
||||
|
||||
|
||||
class RNAMeta(type):
|
||||
|
||||
def __new__(cls, name, bases, classdict, **args):
|
||||
result = type.__new__(cls, name, bases, classdict)
|
||||
if bases and bases[0] is not StructRNA:
|
||||
from _weakref import ref as ref
|
||||
module = result.__module__
|
||||
|
||||
# first part of packages only
|
||||
if "." in module:
|
||||
module = module[:module.index(".")]
|
||||
|
||||
TypeMap.setdefault(module, []).append(ref(result))
|
||||
|
||||
return result
|
||||
|
||||
# TODO(campbell): move to C-API
|
||||
@property
|
||||
def is_registered(cls):
|
||||
return "bl_rna" in cls.__dict__
|
||||
|
||||
|
||||
class OrderedDictMini(dict):
|
||||
|
||||
def __init__(self, *args):
|
||||
self.order = []
|
||||
dict.__init__(self, args)
|
||||
|
||||
def __setitem__(self, key, val):
|
||||
dict.__setitem__(self, key, val)
|
||||
if key not in self.order:
|
||||
self.order.append(key)
|
||||
|
||||
def __delitem__(self, key):
|
||||
dict.__delitem__(self, key)
|
||||
self.order.remove(key)
|
||||
|
||||
|
||||
class RNAMetaPropGroup(StructMetaPropGroup, RNAMeta):
|
||||
pass
|
||||
|
||||
|
||||
class OrderedMeta(RNAMeta):
|
||||
|
||||
def __init__(cls, name, bases, attributes):
|
||||
if attributes.__class__ is OrderedDictMini:
|
||||
cls.order = attributes.order
|
||||
|
||||
def __prepare__(name, bases, **kwargs):
|
||||
return OrderedDictMini() # collections.OrderedDict()
|
||||
|
||||
|
||||
# Same as 'Operator'
|
||||
# only without 'as_keywords'
|
||||
class Manipulator(StructRNA, metaclass=OrderedMeta):
|
||||
class Gizmo(StructRNA):
|
||||
__slots__ = ()
|
||||
|
||||
def __getattribute__(self, attr):
|
||||
@@ -619,24 +581,24 @@ class Manipulator(StructRNA, metaclass=OrderedMeta):
|
||||
return super().__delattr__(attr)
|
||||
|
||||
from _bpy import (
|
||||
_rna_manipulator_target_set_handler as target_set_handler,
|
||||
_rna_manipulator_target_get_value as target_get_value,
|
||||
_rna_manipulator_target_set_value as target_set_value,
|
||||
_rna_manipulator_target_get_range as target_get_range,
|
||||
_rna_gizmo_target_set_handler as target_set_handler,
|
||||
_rna_gizmo_target_get_value as target_get_value,
|
||||
_rna_gizmo_target_set_value as target_set_value,
|
||||
_rna_gizmo_target_get_range as target_get_range,
|
||||
)
|
||||
|
||||
# Convenience wrappers around private `_gawain` module.
|
||||
# Convenience wrappers around private `_gpu` module.
|
||||
def draw_custom_shape(self, shape, *, matrix=None, select_id=None):
|
||||
"""
|
||||
Draw a shape created form :class:`bpy.types.Manipulator.draw_custom_shape`.
|
||||
Draw a shape created form :class:`bpy.types.Gizmo.draw_custom_shape`.
|
||||
|
||||
:arg shape: The cached shape to draw.
|
||||
:type shape: Undefined.
|
||||
:arg matrix: 4x4 matrix, when not given
|
||||
:class:`bpy.types.Manipulator.matrix_world` is used.
|
||||
:class:`bpy.types.Gizmo.matrix_world` is used.
|
||||
:type matrix: :class:`mathutils.Matrix`
|
||||
:arg select_id: The selection id.
|
||||
Only use when drawing within :class:`bpy.types.Manipulator.draw_select`.
|
||||
Only use when drawing within :class:`bpy.types.Gizmo.draw_select`.
|
||||
:type select_it: int
|
||||
"""
|
||||
import gpu
|
||||
@@ -665,7 +627,7 @@ class Manipulator(StructRNA, metaclass=OrderedMeta):
|
||||
@staticmethod
|
||||
def new_custom_shape(type, verts):
|
||||
"""
|
||||
Create a new shape that can be passed to :class:`bpy.types.Manipulator.draw_custom_shape`.
|
||||
Create a new shape that can be passed to :class:`bpy.types.Gizmo.draw_custom_shape`.
|
||||
|
||||
:arg type: The type of shape to create in (POINTS, LINES, TRIS, LINE_STRIP).
|
||||
:type type: string
|
||||
@@ -676,25 +638,25 @@ class Manipulator(StructRNA, metaclass=OrderedMeta):
|
||||
:return: The newly created shape.
|
||||
:rtype: Undefined (it may change).
|
||||
"""
|
||||
from _gawain.types import (
|
||||
Gwn_Batch,
|
||||
Gwn_VertBuf,
|
||||
Gwn_VertFormat,
|
||||
from _gpu.types import (
|
||||
GPUBatch,
|
||||
GPUVertBuf,
|
||||
GPUVertFormat,
|
||||
)
|
||||
dims = len(verts[0])
|
||||
if dims not in {2, 3}:
|
||||
raise ValueError("Expected 2D or 3D vertex")
|
||||
fmt = Gwn_VertFormat()
|
||||
fmt = GPUVertFormat()
|
||||
pos_id = fmt.attr_add(id="pos", comp_type='F32', len=dims, fetch_mode='FLOAT')
|
||||
vbo = Gwn_VertBuf(len=len(verts), format=fmt)
|
||||
vbo = GPUVertBuf(len=len(verts), format=fmt)
|
||||
vbo.fill(id=pos_id, data=verts)
|
||||
batch = Gwn_Batch(type=type, buf=vbo)
|
||||
batch = GPUBatch(type=type, buf=vbo)
|
||||
return (batch, dims)
|
||||
|
||||
|
||||
# Only defined so operators members can be used by accessing self.order
|
||||
# with doc generation 'self.properties.bl_rna.properties' can fail
|
||||
class Operator(StructRNA, metaclass=OrderedMeta):
|
||||
class Operator(StructRNA):
|
||||
__slots__ = ()
|
||||
|
||||
def __getattribute__(self, attr):
|
||||
@@ -726,7 +688,7 @@ class Operator(StructRNA, metaclass=OrderedMeta):
|
||||
if attr not in ignore}
|
||||
|
||||
|
||||
class Macro(StructRNA, metaclass=OrderedMeta):
|
||||
class Macro(StructRNA):
|
||||
# bpy_types is imported before ops is defined
|
||||
# so we have to do a local import on each run
|
||||
__slots__ = ()
|
||||
@@ -942,7 +904,6 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
|
||||
props = row.operator(add_operator, text="", icon='ZOOMIN')
|
||||
props.name = wm.preset_name
|
||||
|
||||
|
||||
def draw_preset(self, context):
|
||||
"""
|
||||
Define these on the subclass:
|
||||
|
||||
@@ -153,6 +153,7 @@ def topretty_py(py_data, indent=" "):
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# testing code.
|
||||
|
||||
|
||||
@@ -19,12 +19,12 @@
|
||||
# ##### END GPL LICENSE BLOCK #####
|
||||
|
||||
# Original copyright (see docstring):
|
||||
#*****************************************************************************
|
||||
# ****************************************************************************
|
||||
# Copyright (C) 2001-2006 Fernando Perez <fperez@colorado.edu>
|
||||
#
|
||||
# Distributed under the terms of the BSD License. The full license is in
|
||||
# the file COPYING, distributed as part of this software.
|
||||
#*****************************************************************************
|
||||
# ****************************************************************************
|
||||
|
||||
# <pep8 compliant>
|
||||
|
||||
|
||||
@@ -224,6 +224,7 @@ def execute(context, is_interactive):
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
execute.hooks = []
|
||||
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ def shell_run(text):
|
||||
|
||||
add_scrollback(output, style)
|
||||
|
||||
|
||||
PROMPT = "$ "
|
||||
|
||||
|
||||
@@ -64,7 +65,7 @@ def execute(context, is_interactive):
|
||||
|
||||
|
||||
def autocomplete(context):
|
||||
#~ sc = context.space_data
|
||||
# sc = context.space_data
|
||||
# TODO
|
||||
return {'CANCELLED'}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ def graph_armature(obj, filepath, FAKE_PARENT=True, CONSTRAINTS=True, DRIVERS=Tr
|
||||
return None
|
||||
|
||||
#rna_path_bone = rna_path[:rna_path.index("]") + 1]
|
||||
#return obj.path_resolve(rna_path_bone)
|
||||
# return obj.path_resolve(rna_path_bone)
|
||||
bone_name = rna_path.split("[")[1].split("]")[0]
|
||||
return obj.pose.bones[bone_name[1:-1]]
|
||||
|
||||
@@ -179,6 +179,7 @@ def graph_armature(obj, filepath, FAKE_PARENT=True, CONSTRAINTS=True, DRIVERS=Tr
|
||||
print("\nSaved:", filepath)
|
||||
return True
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import os
|
||||
tmppath = "/tmp/test.dot"
|
||||
|
||||
@@ -36,7 +36,7 @@ __all__ = (
|
||||
"RKS_GEN_rotation",
|
||||
"RKS_GEN_scaling",
|
||||
"RKS_GEN_bendy_bones",
|
||||
)
|
||||
)
|
||||
|
||||
import bpy
|
||||
|
||||
@@ -221,6 +221,7 @@ def RKS_GEN_scaling(ksi, context, ks, data):
|
||||
|
||||
# ------
|
||||
|
||||
|
||||
# Property identifiers for Bendy Bones
|
||||
bbone_property_ids = (
|
||||
"bbone_curveinx",
|
||||
|
||||
@@ -121,7 +121,7 @@ def register_node_categories(identifier, cat_list):
|
||||
"category": cat,
|
||||
"poll": cat.poll,
|
||||
"draw": draw_node_item,
|
||||
})
|
||||
})
|
||||
panel_type = type("NODE_PT_category_" + cat.identifier, (bpy.types.Panel,), {
|
||||
"bl_space_type": 'NODE_EDITOR',
|
||||
"bl_region_type": 'TOOLS',
|
||||
@@ -130,7 +130,7 @@ def register_node_categories(identifier, cat_list):
|
||||
"category": cat,
|
||||
"poll": cat.poll,
|
||||
"draw": draw_node_item,
|
||||
})
|
||||
})
|
||||
|
||||
menu_types.append(menu_type)
|
||||
panel_types.append(panel_type)
|
||||
|
||||
@@ -25,7 +25,7 @@ __all__ = (
|
||||
"draw_filtered",
|
||||
"draw_hierarchy",
|
||||
"draw_keymaps",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
import bpy
|
||||
@@ -174,7 +174,7 @@ def draw_kmi(display_keymaps, kc, km, kmi, layout, level):
|
||||
sub.prop(kmi, "propvalue", text="")
|
||||
else:
|
||||
# One day...
|
||||
#~ sub.prop_search(kmi, "idname", bpy.context.window_manager, "operators_all", text="")
|
||||
# sub.prop_search(kmi, "idname", bpy.context.window_manager, "operators_all", text="")
|
||||
sub.prop(kmi, "idname", text="")
|
||||
|
||||
if map_type not in {'TEXTINPUT', 'TIMER'}:
|
||||
@@ -207,6 +207,7 @@ def draw_kmi(display_keymaps, kc, km, kmi, layout, level):
|
||||
draw_km(display_keymaps, kc, kmm, None, layout, level + 1)
|
||||
layout.context_pointer_set("keymap", km)
|
||||
|
||||
|
||||
_EVENT_TYPES = set()
|
||||
_EVENT_TYPE_MAP = {}
|
||||
_EVENT_TYPE_MAP_EXTRA = {}
|
||||
@@ -234,10 +235,10 @@ def draw_filtered(display_keymaps, filter_type, filter_text, layout):
|
||||
"RMB": 'RIGHTMOUSE',
|
||||
"LMB": 'LEFTMOUSE',
|
||||
"MMB": 'MIDDLEMOUSE',
|
||||
})
|
||||
})
|
||||
_EVENT_TYPE_MAP_EXTRA.update({
|
||||
"%d" % i: "NUMPAD_%d" % i for i in range(10)
|
||||
})
|
||||
})
|
||||
# done with once off init
|
||||
|
||||
filter_text_split = filter_text.strip()
|
||||
@@ -251,7 +252,7 @@ def draw_filtered(display_keymaps, filter_type, filter_text, layout):
|
||||
"cmd": "oskey",
|
||||
"oskey": "oskey",
|
||||
"any": "any",
|
||||
}
|
||||
}
|
||||
# KeyMapItem like dict, use for comparing against
|
||||
# attr: {states, ...}
|
||||
kmi_test_dict = {}
|
||||
@@ -260,8 +261,8 @@ def draw_filtered(display_keymaps, filter_type, filter_text, layout):
|
||||
kmi_test_type = []
|
||||
|
||||
# initialize? - so if a if a kmi has a MOD assigned it wont show up.
|
||||
#~ for kv in key_mod.values():
|
||||
#~ kmi_test_dict[kv] = {False}
|
||||
# for kv in key_mod.values():
|
||||
# kmi_test_dict[kv] = {False}
|
||||
|
||||
# altname: attr
|
||||
for kk, kv in key_mod.items():
|
||||
@@ -374,7 +375,7 @@ def draw_keymaps(context, layout):
|
||||
|
||||
row = subcol.row(align=True)
|
||||
|
||||
#~ row.prop_search(wm.keyconfigs, "active", wm, "keyconfigs", text="Key Config")
|
||||
# row.prop_search(wm.keyconfigs, "active", wm, "keyconfigs", text="Key Config")
|
||||
text = bpy.path.display_name(wm.keyconfigs.active.name)
|
||||
if not text:
|
||||
text = "Blender (default)"
|
||||
@@ -382,8 +383,8 @@ def draw_keymaps(context, layout):
|
||||
row.operator("wm.keyconfig_preset_add", text="", icon='ZOOMIN')
|
||||
row.operator("wm.keyconfig_preset_add", text="", icon='ZOOMOUT').remove_active = True
|
||||
|
||||
#~ layout.context_pointer_set("keyconfig", wm.keyconfigs.active)
|
||||
#~ row.operator("wm.keyconfig_remove", text="", icon='X')
|
||||
# layout.context_pointer_set("keyconfig", wm.keyconfigs.active)
|
||||
# row.operator("wm.keyconfig_remove", text="", icon='X')
|
||||
row.separator()
|
||||
rowsub = row.split(align=True, percentage=0.33)
|
||||
# postpone drawing into rowsub, so we can set alert!
|
||||
|
||||
@@ -129,14 +129,25 @@ def draw(layout, context, context_member, property_type, use_edit=True):
|
||||
props.data_path = context_member
|
||||
del row
|
||||
|
||||
show_developer_ui = context.user_preferences.view.show_developer_ui
|
||||
rna_properties = {prop.identifier for prop in rna_item.bl_rna.properties if prop.is_runtime} if items else None
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False # No animation.
|
||||
|
||||
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
|
||||
|
||||
for key, val in items:
|
||||
|
||||
if key == '_RNA_UI':
|
||||
continue
|
||||
|
||||
row = layout.row()
|
||||
is_rna = (key in rna_properties)
|
||||
|
||||
# only show API defined for developers
|
||||
if is_rna and not show_developer_ui:
|
||||
continue
|
||||
|
||||
to_dict = getattr(val, "to_dict", None)
|
||||
to_list = getattr(val, "to_list", None)
|
||||
|
||||
@@ -150,19 +161,20 @@ def draw(layout, context, context_member, property_type, use_edit=True):
|
||||
else:
|
||||
val_draw = val
|
||||
|
||||
row = flow.row(align=True)
|
||||
box = row.box()
|
||||
|
||||
if use_edit:
|
||||
split = box.split(percentage=0.75)
|
||||
row = split.row()
|
||||
row = split.row(align=True)
|
||||
else:
|
||||
row = box.row()
|
||||
row = box.row(align=True)
|
||||
|
||||
row.alignment = 'RIGHT'
|
||||
|
||||
row.label(text=key, translate=False)
|
||||
|
||||
# explicit exception for arrays
|
||||
is_rna = (key in rna_properties)
|
||||
|
||||
# explicit exception for arrays.
|
||||
if to_dict or to_list:
|
||||
row.label(text=val_draw, translate=False)
|
||||
else:
|
||||
@@ -181,6 +193,8 @@ def draw(layout, context, context_member, property_type, use_edit=True):
|
||||
else:
|
||||
row.label(text="API Defined")
|
||||
|
||||
del flow
|
||||
|
||||
|
||||
class PropertyPanel:
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user