Merge branch 'blender2.8' into soc-2018-bevel

This commit is contained in:
Rohan Rathi
2018-07-21 19:29:15 +05:30
1444 changed files with 59674 additions and 41167 deletions

View File

@@ -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:

View File

@@ -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)

View File

@@ -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…")

View File

@@ -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 its 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 its 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.")

View File

@@ -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()):

View File

@@ -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)

View File

@@ -98,5 +98,6 @@ def main():
for value in read_blend_rend_chunk(arg):
print("%d %d %s" % value)
if __name__ == '__main__':
main()

View File

@@ -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()

View File

@@ -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

View File

@@ -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]

View File

@@ -31,4 +31,4 @@ __all__ = (
"mesh_utils",
"node_utils",
"view3d_utils",
)
)

View File

@@ -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:

View File

@@ -20,7 +20,7 @@
__all__ = (
"load_image",
)
)
# limited replacement for BPyImage.comprehensiveImageLoad

View File

@@ -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,

View File

@@ -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,
)

View File

@@ -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)

View File

@@ -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]

View File

@@ -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

View File

@@ -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):

View File

@@ -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:

View File

@@ -153,6 +153,7 @@ def topretty_py(py_data, indent=" "):
return "\n".join(lines)
if __name__ == "__main__":
# testing code.

View File

@@ -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>

View File

@@ -224,6 +224,7 @@ def execute(context, is_interactive):
return {'FINISHED'}
execute.hooks = []

View File

@@ -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'}

View File

@@ -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"

View File

@@ -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",

View File

@@ -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)

View File

@@ -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!

View File

@@ -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:
"""