diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2015-06-29 17:41:00 +0300 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2015-06-29 18:18:11 +0300 |
commit | d140e70c496122915eb5c05aba83153e2e0d7998 (patch) | |
tree | 1e589247d69da64aa7b0e7802319237ec050b5d6 /release | |
parent | 147bd16ed1bb3415b30408b0eab110d0854eadd2 (diff) | |
parent | 295d0c52a26730edc6d4ed1276e4051cce006be5 (diff) |
Merge branch 'master' into temp-ghash-experimentstemp-ghash-experiments
Note that 'store hash' feature was removed for now - to complex to maintain (conflicts)
and relatively easy to re-add if we ever really want this one day.
Conflicts:
source/blender/blenlib/BLI_ghash.h
source/blender/blenlib/intern/BLI_ghash.c
source/blender/blenlib/intern/hash_mm2a.c
source/blender/bmesh/tools/bmesh_region_match.c
tests/gtests/blenlib/BLI_ghash_performance_test.cc
tests/gtests/blenlib/BLI_ghash_test.cc
tests/gtests/blenlib/CMakeLists.txt
Diffstat (limited to 'release')
81 files changed, 1518 insertions, 456 deletions
diff --git a/release/bin/blender-thumbnailer.py b/release/bin/blender-thumbnailer.py index 779c6156e70..d3b31d63fad 100755 --- a/release/bin/blender-thumbnailer.py +++ b/release/bin/blender-thumbnailer.py @@ -170,7 +170,7 @@ def write_png(buf, width, height): png_pack(b'IEND', b'')]) -if __name__ == '__main__': +def main(): import sys if len(sys.argv) < 3: @@ -186,3 +186,7 @@ if __name__ == '__main__': f = open(file_out, "wb") f.write(write_png(buf, width, height)) f.close() + + +if __name__ == '__main__': + main() diff --git a/release/datafiles/fonts/droidsans.ttf.gz b/release/datafiles/fonts/droidsans.ttf.gz Binary files differindex c76cf67992c..a685c590795 100644 --- a/release/datafiles/fonts/droidsans.ttf.gz +++ b/release/datafiles/fonts/droidsans.ttf.gz diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png Binary files differindex a3e9afe6403..cfadec07b09 100644 --- a/release/datafiles/splash.png +++ b/release/datafiles/splash.png diff --git a/release/datafiles/splash_2x.png b/release/datafiles/splash_2x.png Binary files differindex d45b9952633..79a6f2781ff 100644 --- a/release/datafiles/splash_2x.png +++ b/release/datafiles/splash_2x.png diff --git a/release/scripts/freestyle/modules/freestyle/functions.py b/release/scripts/freestyle/modules/freestyle/functions.py index 48d9b2e2b39..426d344e8ab 100644 --- a/release/scripts/freestyle/modules/freestyle/functions.py +++ b/release/scripts/freestyle/modules/freestyle/functions.py @@ -189,11 +189,13 @@ class CurveMaterialF0D(UnaryFunction0DMaterial): priority is used to pick one of the two materials at material boundaries. - Note: expects instances of CurvePoint to be iterated over + Notes: expects instances of CurvePoint to be iterated over + can return None if no fedge can be found """ def __call__(self, inter): fe = inter.object.fedge - assert(fe is not None), "CurveMaterialF0D: fe is None" + if fe is None: + return None if fe.is_smooth: return fe.material else: diff --git a/release/scripts/freestyle/modules/freestyle/predicates.py b/release/scripts/freestyle/modules/freestyle/predicates.py index 54656e450d9..5cbe577b956 100644 --- a/release/scripts/freestyle/modules/freestyle/predicates.py +++ b/release/scripts/freestyle/modules/freestyle/predicates.py @@ -43,6 +43,7 @@ __all__ = ( "FalseUP0D", "FalseUP1D", "Length2DBP1D", + "MaterialBP1D", "NotBP1D", "NotUP1D", "ObjectNamesUP1D", @@ -150,12 +151,13 @@ from freestyle.functions import ( pyViewMapGradientNormF1D, ) +from freestyle.utils import material_from_fedge + import random # -- Unary predicates for 0D elements (vertices) -- # - class pyHigherCurvature2DAngleUP0D(UnaryPredicate0D): def __init__(self, a): UnaryPredicate0D.__init__(self) @@ -234,9 +236,10 @@ class AndUP1D(UnaryPredicate1D): def __init__(self, *predicates): UnaryPredicate1D.__init__(self) self.predicates = predicates - # there are cases in which only one predicate is supplied (in the parameter editor) - if len(self.predicates) < 1: - raise ValueError("Expected one or more UnaryPredicate1D, got ", len(predicates)) + correct_types = all(isinstance(p, UnaryPredicate1D) for p in self.predicates) + if not (correct_types and predicates): + raise TypeError("%s: Expected one or more UnaryPredicate1D, got %r" % + (self.__class__.__name__, self.predicates)) def __call__(self, inter): return all(pred(inter) for pred in self.predicates) @@ -246,9 +249,10 @@ class OrUP1D(UnaryPredicate1D): def __init__(self, *predicates): UnaryPredicate1D.__init__(self) self.predicates = predicates - # there are cases in which only one predicate is supplied (in the parameter editor) - if len(self.predicates) < 1: - raise ValueError("Expected one or more UnaryPredicate1D, got ", len(predicates)) + correct_types = all(isinstance(p, UnaryPredicate1D) for p in self.predicates) + if not (correct_types and predicates): + raise TypeError("%s: Expected one or more UnaryPredicate1D, got %r" % + (self.__class__.__name__, self.predicates)) def __call__(self, inter): return any(pred(inter) for pred in self.predicates) @@ -257,10 +261,10 @@ class OrUP1D(UnaryPredicate1D): class NotUP1D(UnaryPredicate1D): def __init__(self, pred): UnaryPredicate1D.__init__(self) - self.__pred = pred + self.predicate = pred def __call__(self, inter): - return not self.__pred(inter) + return not self.predicate(inter) class ObjectNamesUP1D(UnaryPredicate1D): @@ -563,32 +567,36 @@ class pyClosedCurveUP1D(UnaryPredicate1D): class AndBP1D(BinaryPredicate1D): def __init__(self, *predicates): BinaryPredicate1D.__init__(self) - self._predicates = predicates - if len(self.predicates) < 2: - raise ValueError("Expected two or more BinaryPredicate1D, got ", len(predictates)) + self.predicates = tuple(predicates) + correct_types = all(isinstance(p, BinaryPredicate1D) for p in self.predicates) + if not (correct_types and predicates): + raise TypeError("%s: Expected one or more BinaryPredicate1D, got %r" % + (self.__class__.__name__, self.predicates)) def __call__(self, i1, i2): - return all(pred(i1, i2) for pred in self._predicates) + return all(pred(i1, i2) for pred in self.predicates) class OrBP1D(BinaryPredicate1D): def __init__(self, *predicates): BinaryPredicate1D.__init__(self) - self._predicates = predicates - if len(self.predicates) < 2: - raise ValueError("Expected two or more BinaryPredicate1D, got ", len(predictates)) + self.predicates = tuple(predicates) + correct_types = all(isinstance(p, BinaryPredicate1D) for p in self.predicates) + if not (correct_types and predicates): + raise TypeError("%s: Expected one or more BinaryPredicate1D, got %r" % + (self.__class__.__name__, self.predicates)) def __call__(self, i1, i2): - return any(pred(i1, i2) for pred in self._predicates) + return any(pred(i1, i2) for pred in self.predicates) class NotBP1D(BinaryPredicate1D): def __init__(self, predicate): BinaryPredicate1D.__init__(self) - self._predicate = predicate + self.predicate = predicate def __call__(self, i1, i2): - return (not self._predicate(i1, i2)) + return (not self.predicate(i1, i2)) class pyZBP1D(BinaryPredicate1D): @@ -663,3 +671,10 @@ class pyShuffleBP1D(BinaryPredicate1D): def __call__(self, inter1, inter2): return (random.uniform(0, 1) < random.uniform(0, 1)) + +class MaterialBP1D(BinaryPredicate1D): + """Checks whether the two supplied ViewEdges have the same material.""" + def __call__(self, i1, i2): + fedges = (fe for ve in (i1, i2) for fe in (ve.first_fedge, ve.last_fedge)) + materials = {material_from_fedge(fe) for fe in fedges} + return len(materials) < 2 diff --git a/release/scripts/freestyle/modules/freestyle/shaders.py b/release/scripts/freestyle/modules/freestyle/shaders.py index 61365e8dd87..127db3fcd4b 100644 --- a/release/scripts/freestyle/modules/freestyle/shaders.py +++ b/release/scripts/freestyle/modules/freestyle/shaders.py @@ -138,7 +138,7 @@ from freestyle.predicates import ( from freestyle.utils import ( bound, - bounding_box, + BoundingBox, phase_to_direction, ) @@ -865,7 +865,7 @@ class pyBluePrintCirclesShader(StrokeShader): def shade(self, stroke): # get minimum and maximum coordinates - p_min, p_max = bounding_box(stroke) + p_min, p_max = BoundingBox.from_sequence(svert.point for svert in stroke).corners stroke.resample(32 * self.__turns) sv_nb = len(stroke) // self.__turns @@ -917,7 +917,7 @@ class pyBluePrintEllipsesShader(StrokeShader): self.__random_radius = random_radius def shade(self, stroke): - p_min, p_max = bounding_box(stroke) + p_min, p_max = BoundingBox.from_sequence(svert.point for svert in stroke).corners stroke.resample(32 * self.__turns) sv_nb = len(stroke) // self.__turns @@ -964,7 +964,7 @@ class pyBluePrintSquaresShader(StrokeShader): return # get minimum and maximum coordinates - p_min, p_max = bounding_box(stroke) + p_min, p_max = BoundingBox.from_sequence(svert.point for svert in stroke).corners stroke.resample(32 * self.__turns) num_segments = len(stroke) // self.__turns diff --git a/release/scripts/freestyle/modules/freestyle/utils.py b/release/scripts/freestyle/modules/freestyle/utils.py index 224734d5bfb..41d2297f723 100644 --- a/release/scripts/freestyle/modules/freestyle/utils.py +++ b/release/scripts/freestyle/modules/freestyle/utils.py @@ -22,24 +22,29 @@ writing. """ __all__ = ( - "ContextFunctions", "bound", - "bounding_box", + "BoundingBox", + "ContextFunctions", "find_matching_vertex", - "getCurrentScene", "get_chain_length", + "get_object_name", + "get_strokes", "get_test_stroke", + "getCurrentScene", "integrate", + "is_poly_clockwise", "iter_distance_along_stroke", "iter_distance_from_camera", "iter_distance_from_object", "iter_material_value", "iter_t2d_along_stroke", + "material_from_fedge", "pairwise", "phase_to_direction", "rgb_to_bw", "stroke_curvature", "stroke_normal", + "StrokeCollector", "tripplewise", ) @@ -55,6 +60,7 @@ from _freestyle import ( from freestyle.types import ( Interface0DIterator, Stroke, + StrokeShader, StrokeVertexIterator, ) @@ -79,12 +85,38 @@ def bound(lower, x, higher): return (lower if x <= lower else higher if x >= higher else x) -def bounding_box(stroke): - """ - Returns the maximum and minimum coordinates (the bounding box) of the stroke's vertices - """ - x, y = zip(*(svert.point for svert in stroke)) - return (Vector((min(x), min(y))), Vector((max(x), max(y)))) +def get_strokes(): + """Get all strokes that are currently available""" + return tuple(map(Operators().get_stroke_from_index, range(Operators().get_strokes_size()))) + + +def is_poly_clockwise(stroke): + """True if the stroke is orientated in a clockwise way, False otherwise""" + v = sum((v2.point.x - v1.point.x) * (v1.point.y + v2.point.y) for v1, v2 in pairwise(stroke)) + v1, v2 = stroke[0], stroke[-1] + if (v1.point - v2.point).length > 1e-3: + v += (v2.point.x - v1.point.x) * (v1.point.y + v2.point.y) + return v > 0 + + +def get_object_name(stroke): + """Returns the name of the object that this stroke is drawn on.""" + fedge = stroke[0].fedge + if fedge is None: + return None + return fedge.viewedge.viewshape.name + + +def material_from_fedge(fe): + "get the diffuse rgba color from an FEdge" + if fe is None: + return None + if fe.is_smooth: + material = fe.material + else: + right, left = fe.material_right, fe.material_left + material = right if (right.priority > left.priority) else left + return material # -- General helper functions -- # @@ -106,6 +138,54 @@ def phase_to_direction(length): # lower bound (e.g., thickness, range and certain values) BoundedProperty = namedtuple("BoundedProperty", ["min", "max", "delta"]) + +class BoundingBox: + """Object representing a bounding box consisting out of 2 2D vectors""" + + __slots__ = ( + "minimum", + "maximum", + "size", + "corners", + ) + + def __init__(self, minimum: Vector, maximum: Vector): + self.minimum = minimum + self.maximum = maximum + if len(minimum) != len(maximum): + raise TypeError("Expected two vectors of size 2, got", minimum, maximum) + self.size = len(minimum) + self.corners = (minimum, maximum) + + def __repr__(self): + return "BoundingBox(%r, %r)" % (self.minimum, self.maximum) + + @classmethod + def from_sequence(cls, sequence): + """BoundingBox from sequence of 2D or 3D Vector objects""" + x, y = zip(*sequence) + mini = Vector((min(x), min(y))) + maxi = Vector((max(x), max(y))) + return cls(mini, maxi) + + def inside(self, other): + """True if self inside other, False otherwise""" + if self.size != other.size: + raise TypeError("Expected two BoundingBox of the same size, got", self, other) + return (self.minimum.x >= other.minimum.x and self.minimum.y >= other.minimum.y and + self.maximum.x <= other.maximum.x and self.maximum.y <= other.maximum.y) + + +class StrokeCollector(StrokeShader): + "Collects and Stores stroke objects" + def __init__(self): + StrokeShader.__init__(self) + self.strokes = [] + + def shade(self, stroke): + self.strokes.append(stroke) + + # -- helper functions for chaining -- # def get_chain_length(ve, orientation): @@ -147,6 +227,7 @@ def find_matching_vertex(id, it): """Finds the matching vertex, or returns None.""" return next((ve for ve in it if ve.id == id), None) + # -- helper functions for iterating -- # def pairwise(iterable, types={Stroke, StrokeVertexIterator}): @@ -210,7 +291,7 @@ def iter_distance_from_object(stroke, location, range_min, range_max, normfac): def iter_material_value(stroke, func, attribute): - "Yields a specific material attribute from the vertex' underlying material." + """Yields a specific material attribute from the vertex' underlying material.""" it = Interface0DIterator(stroke) for svert in it: material = func(it) @@ -252,8 +333,9 @@ def iter_material_value(stroke, func, attribute): raise ValueError("unexpected material attribute: " + attribute) yield (svert, value) + def iter_distance_along_stroke(stroke): - "Yields the absolute distance along the stroke up to the current vertex." + """Yields the absolute distance along the stroke up to the current vertex.""" distance = 0.0 # the positions need to be copied, because they are changed in the calling function points = tuple(svert.point.copy() for svert in stroke) @@ -295,6 +377,7 @@ def stroke_curvature(it): yield abs(K) + def stroke_normal(stroke): """ Compute the 2D normal at the stroke vertex pointed by the iterator @@ -304,7 +387,7 @@ def stroke_normal(stroke): The returned normals are dynamic: they update when the vertex position (and therefore the vertex normal) changes. - for use in geometry modifiers it is advised to + for use in geometry modifiers it is advised to cast this generator function to a tuple or list """ n = len(stroke) - 1 @@ -323,12 +406,13 @@ def stroke_normal(stroke): n2 = Vector((e2[1], -e2[0])).normalized() yield (n1 + n2).normalized() + def get_test_stroke(): """Returns a static stroke object for testing """ from freestyle.types import Stroke, Interface0DIterator, StrokeVertexIterator, SVertex, Id, StrokeVertex # points for our fake stroke points = (Vector((1.0, 5.0, 3.0)), Vector((1.0, 2.0, 9.0)), - Vector((6.0, 2.0, 3.0)), Vector((7.0, 2.0, 3.0)), + Vector((6.0, 2.0, 3.0)), Vector((7.0, 2.0, 3.0)), Vector((2.0, 6.0, 3.0)), Vector((2.0, 8.0, 3.0))) ids = (Id(0, 0), Id(1, 1), Id(2, 2), Id(3, 3), Id(4, 4), Id(5, 5)) diff --git a/release/scripts/freestyle/modules/parameter_editor.py b/release/scripts/freestyle/modules/parameter_editor.py index d4765847450..3c11c33a39d 100644 --- a/release/scripts/freestyle/modules/parameter_editor.py +++ b/release/scripts/freestyle/modules/parameter_editor.py @@ -960,11 +960,11 @@ def process(layer_name, lineset_name): if lineset.select_external_contour: upred = ExternalContourUP1D() edge_type_criteria.append(NotUP1D(upred) if lineset.exclude_external_contour else upred) - if lineset.edge_type_combination == 'OR': - upred = OrUP1D(*edge_type_criteria) - else: - upred = AndUP1D(*edge_type_criteria) - if upred is not None: + if edge_type_criteria: + if lineset.edge_type_combination == 'OR': + upred = OrUP1D(*edge_type_criteria) + else: + upred = AndUP1D(*edge_type_criteria) if lineset.edge_type_negation == 'EXCLUSIVE': upred = NotUP1D(upred) selection_criteria.append(upred) @@ -989,8 +989,9 @@ def process(layer_name, lineset_name): upred = WithinImageBoundaryUP1D(*ContextFunctions.get_border()) selection_criteria.append(upred) # select feature edges - upred = AndUP1D(*selection_criteria) - if upred is None: + if selection_criteria: + upred = AndUP1D(*selection_criteria) + else: upred = TrueUP1D() Operators.select(upred) # join feature edges to form chains diff --git a/release/scripts/modules/addon_utils.py b/release/scripts/modules/addon_utils.py index 8c86f31022c..123b3cb953c 100644 --- a/release/scripts/modules/addon_utils.py +++ b/release/scripts/modules/addon_utils.py @@ -74,40 +74,43 @@ def modules_refresh(module_cache=addons_fake_modules): print("fake_module", mod_path, mod_name) import ast ModuleType = type(ast) - file_mod = open(mod_path, "r", encoding='UTF-8') - if speedy: - lines = [] - line_iter = iter(file_mod) - l = "" - while not l.startswith("bl_info"): - try: - l = line_iter.readline() - except UnicodeDecodeError as e: - if not error_encoding: - error_encoding = True - print("Error reading file as UTF-8:", mod_path, e) - file_mod.close() - return None - - if len(l) == 0: - break - while l.rstrip(): - lines.append(l) - try: - l = line_iter.readline() - except UnicodeDecodeError as e: - if not error_encoding: - error_encoding = True - print("Error reading file as UTF-8:", mod_path, e) - file_mod.close() - return None - - data = "".join(lines) + try: + file_mod = open(mod_path, "r", encoding='UTF-8') + except OSError as e: + print("Error opening file %r: %s" % (mod_path, e)) + return None - else: - data = file_mod.read() + with file_mod: + if speedy: + lines = [] + line_iter = iter(file_mod) + l = "" + while not l.startswith("bl_info"): + try: + l = line_iter.readline() + except UnicodeDecodeError as e: + if not error_encoding: + error_encoding = True + print("Error reading file as UTF-8:", mod_path, e) + return None + + if len(l) == 0: + break + while l.rstrip(): + lines.append(l) + try: + l = line_iter.readline() + except UnicodeDecodeError as e: + if not error_encoding: + error_encoding = True + print("Error reading file as UTF-8:", mod_path, e) + return None + + data = "".join(lines) - file_mod.close() + else: + data = file_mod.read() + del file_mod try: ast_data = ast.parse(data, filename=mod_path) @@ -216,7 +219,8 @@ def check(module_name): loaded_default = module_name in _user_preferences.addons mod = sys.modules.get(module_name) - loaded_state = mod and getattr(mod, "__addon_enabled__", Ellipsis) + loaded_state = ((mod is not None) and + getattr(mod, "__addon_enabled__", Ellipsis)) if loaded_state is Ellipsis: print("Warning: addon-module %r found module " @@ -274,6 +278,20 @@ def enable(module_name, default_set=False, persistent=False, handle_error=None): mod = sys.modules.get(module_name) # chances of the file _not_ existing are low, but it could be removed if mod and os.path.exists(mod.__file__): + + if getattr(mod, "__addon_enabled__", False): + # This is an unlikely situation, + # re-register if the module is enabled. + # Note: the UI doesn't allow this to happen, + # in most cases the caller should 'check()' first. + try: + mod.unregister() + except: + print("Exception in module unregister(): %r" % + getattr(mod, "__file__", module_name)) + handle_error() + return None + mod.__addon_enabled__ = False mtime_orig = getattr(mod, "__time__", 0) mtime_new = os.path.getmtime(mod.__file__) @@ -342,7 +360,7 @@ def enable(module_name, default_set=False, persistent=False, handle_error=None): return mod -def disable(module_name, default_set=True, handle_error=None): +def disable(module_name, default_set=False, handle_error=None): """ Disables an addon by name. @@ -416,19 +434,21 @@ def reset_all(reload_scripts=False): disable(mod_name) -def module_bl_info(mod, info_basis={"name": "", - "author": "", - "version": (), - "blender": (), - "location": "", - "description": "", - "wiki_url": "", - "support": 'COMMUNITY', - "category": "", - "warning": "", - "show_expanded": False, - } - ): +def module_bl_info(mod, info_basis=None): + if info_basis is None: + info_basis = { + "name": "", + "author": "", + "version": (), + "blender": (), + "location": "", + "description": "", + "wiki_url": "", + "support": 'COMMUNITY', + "category": "", + "warning": "", + "show_expanded": False, + } addon_info = getattr(mod, "bl_info", {}) diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py index c3b2ae9908b..43a09a1acbd 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -64,7 +64,7 @@ def _gen_check_ctxt(settings): def _diff_check_ctxt(check_ctxt, minus_check_ctxt): - """Returns check_ctxt - minus_check_ctxt""" + """Removes minus_check_ctxt from check_ctxt""" for key in check_ctxt: if isinstance(check_ctxt[key], set): for warning in minus_check_ctxt[key]: @@ -576,8 +576,9 @@ def dump_py_messages_from_files(msgs, reports, files, settings): #print(func_translate_args) # Break recursive nodes look up on some kind of nodes. - # E.g. we don’t want to get strings inside subscripts (blah["foo"])! - stopper_nodes = {ast.Subscript} + # E.g. we don't want to get strings inside subscripts (blah["foo"])! + # we don't want to get strings from comparisons (foo.type == 'BAR'). + stopper_nodes = {ast.Subscript, ast.Compare} # Consider strings separate: ("a" if test else "b") separate_nodes = {ast.IfExp} @@ -897,7 +898,7 @@ def dump_addon_messages(module_name, do_checks, settings): del msgs[key] if check_ctxt: - check_ctxt = _diff_check_ctxt(check_ctxt, minus_check_ctxt) + _diff_check_ctxt(check_ctxt, minus_check_ctxt) # and we are done with those! del minus_pot @@ -924,18 +925,18 @@ def main(): return import sys - back_argv = sys.argv + import argparse + # Get rid of Blender args! - sys.argv = sys.argv[sys.argv.index("--") + 1:] + argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] - import argparse parser = argparse.ArgumentParser(description="Process UI messages from inside Blender.") parser.add_argument('-c', '--no_checks', default=True, action="store_false", help="No checks over UI messages.") parser.add_argument('-m', '--no_messages', default=True, action="store_false", help="No export of UI messages.") parser.add_argument('-o', '--output', default=None, help="Output POT file path.") parser.add_argument('-s', '--settings', default=None, help="Override (some) default settings. Either a JSon file name, or a JSon string.") - args = parser.parse_args() + args = parser.parse_args(argv) settings = settings_i18n.I18nSettings() settings.from_json(args.settings) @@ -945,8 +946,6 @@ def main(): dump_messages(do_messages=args.no_messages, do_checks=args.no_checks, settings=settings) - sys.argv = back_argv - if __name__ == "__main__": print("\n\n *** Running {} *** \n".format(__file__)) diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index 920a56a628b..1c960a217de 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -88,6 +88,7 @@ LANGUAGES = ( (38, "Uzbek (Oʻzbek)", "uz_UZ"), (39, "Uzbek Cyrillic (Ўзбек)", "uz_UZ@cyrillic"), (40, "Hindi (मानक हिन्दी)", "hi_IN"), + (41, "Vietnamese (tiếng Việt)", "vi_VN"), ) # Default context, in py! @@ -324,6 +325,7 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "y", # Sub-strings. "available with", + "brown fox", "can't save image while rendering", "expected a timeline/animation area to be active", "expected a view3d region", @@ -332,6 +334,8 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "image file not found", "image path can't be written to", "in memory to enable editing!", + "jumps over", + "the lazy dog", "unable to load movie clip", "unable to load text", "unable to open the file", @@ -412,6 +416,15 @@ REL_TRUNK_PO_DIR = os.path.join(REL_TRUNK_DIR, "po") # The /trunk/mo path (relative to I18N_DIR). REL_TRUNK_MO_DIR = os.path.join(REL_TRUNK_DIR, "locale") + +# The path to the *git* translation repository (relative to SOURCE_DIR). +REL_GIT_I18N_DIR = os.path.join("release/datafiles/locale") + + +# The /po path of the *git* translation repository (relative to REL_GIT_I18N_DIR). +REL_GIT_I18N_PO_DIR = os.path.join("po") + + # The Blender source path to check for i18n macros (relative to SOURCE_DIR). REL_POTFILES_SOURCE_DIR = os.path.join("source") @@ -493,14 +506,6 @@ def _gen_get_set_path(ref, name): return _get, _set -def _gen_get_set_paths(ref, name): - def _get(self): - return [_do_get(getattr(self, ref), p) for p in getattr(self, name)] - def _set(self, value): - setattr(self, name, [_do_set(getattr(self, ref), p) for p in value]) - return _get, _set - - class I18nSettings: """ Class allowing persistence of our settings! @@ -552,6 +557,8 @@ class I18nSettings: TRUNK_DIR = property(*(_gen_get_set_path("I18N_DIR", "REL_TRUNK_DIR"))) TRUNK_PO_DIR = property(*(_gen_get_set_path("I18N_DIR", "REL_TRUNK_PO_DIR"))) TRUNK_MO_DIR = property(*(_gen_get_set_path("I18N_DIR", "REL_TRUNK_MO_DIR"))) + GIT_I18N_ROOT = property(*(_gen_get_set_path("SOURCE_DIR", "REL_GIT_I18N_DIR"))) + GIT_I18N_PO_DIR = property(*(_gen_get_set_path("GIT_I18N_ROOT", "REL_GIT_I18N_PO_DIR"))) POTFILES_SOURCE_DIR = property(*(_gen_get_set_path("SOURCE_DIR", "REL_POTFILES_SOURCE_DIR"))) FILE_NAME_POT = property(*(_gen_get_set_path("I18N_DIR", "REL_FILE_NAME_POT"))) MO_PATH_ROOT = property(*(_gen_get_set_path("I18N_DIR", "REL_MO_PATH_ROOT"))) diff --git a/release/scripts/modules/bl_i18n_utils/utils.py b/release/scripts/modules/bl_i18n_utils/utils.py index 524fef909e8..5fdb6b88cbf 100644 --- a/release/scripts/modules/bl_i18n_utils/utils.py +++ b/release/scripts/modules/bl_i18n_utils/utils.py @@ -162,7 +162,7 @@ def get_po_files_from_dir(root_dir, langs=set()): yield uid, po_file -def enable_addons(addons={}, support={}, disable=False, check_only=False): +def enable_addons(addons=None, support=None, disable=False, check_only=False): """ Enable (or disable) addons based either on a set of names, or a set of 'support' types. Returns the list of all affected addons (as fake modules)! @@ -170,6 +170,11 @@ def enable_addons(addons={}, support={}, disable=False, check_only=False): """ import addon_utils + if addons is None: + addons = {} + if support is None: + support = {} + userpref = bpy.context.user_preferences used_ext = {ext.module for ext in userpref.addons} @@ -212,13 +217,13 @@ class I18nMessage: __slots__ = ("msgctxt_lines", "msgid_lines", "msgstr_lines", "comment_lines", "is_fuzzy", "is_commented", "settings") - def __init__(self, msgctxt_lines=[], msgid_lines=[], msgstr_lines=[], comment_lines=[], + def __init__(self, msgctxt_lines=None, msgid_lines=None, msgstr_lines=None, comment_lines=None, is_commented=False, is_fuzzy=False, settings=settings): self.settings = settings - self.msgctxt_lines = msgctxt_lines - self.msgid_lines = msgid_lines - self.msgstr_lines = msgstr_lines - self.comment_lines = comment_lines + self.msgctxt_lines = msgctxt_lines or [] + self.msgid_lines = msgid_lines or [] + self.msgstr_lines = msgstr_lines or [] + self.comment_lines = comment_lines or [] self.is_fuzzy = is_fuzzy self.is_commented = is_commented @@ -976,13 +981,13 @@ class I18nMessages: def write(self, kind, dest): self.writers[kind](self, dest) - def write_messages_to_po(self, fname): + def write_messages_to_po(self, fname, compact=False): """ Write messages in fname po file. """ default_context = self.settings.DEFAULT_CONTEXT - def _write(self, f): + def _write(self, f, compact): _msgctxt = self.settings.PO_MSGCTXT _msgid = self.settings.PO_MSGID _msgstr = self.settings.PO_MSGSTR @@ -991,9 +996,12 @@ class I18nMessages: self.escape() for num, msg in enumerate(self.msgs.values()): - f.write("\n".join(msg.comment_lines)) + if compact and (msg.is_commented or msg.is_fuzzy or not msg.msgstr_lines): + continue + if not compact: + f.write("\n".join(msg.comment_lines)) # Only mark as fuzzy if msgstr is not empty! - if msg.is_fuzzy and msg.msgstr: + if msg.is_fuzzy and msg.msgstr_lines: f.write("\n" + self.settings.PO_COMMENT_FUZZY) _p = _comm if msg.is_commented else "" chunks = [] @@ -1030,10 +1038,10 @@ class I18nMessages: self.normalize(max_len=0) # No wrapping for now... if isinstance(fname, str): with open(fname, 'w', encoding="utf-8") as f: - _write(self, f) + _write(self, f, compact) # Else assume fname is already a file(like) object! else: - _write(self, fname) + _write(self, fname, compact) def write_messages_to_mo(self, fname): """ @@ -1112,6 +1120,7 @@ class I18nMessages: writers = { "PO": write_messages_to_po, + "PO_COMPACT": lambda s, fn: s.write_messages_to_po(fn, True), "MO": write_messages_to_mo, } diff --git a/release/scripts/modules/bl_i18n_utils/utils_languages_menu.py b/release/scripts/modules/bl_i18n_utils/utils_languages_menu.py index 24255d9be61..4f499476ad9 100755 --- a/release/scripts/modules/bl_i18n_utils/utils_languages_menu.py +++ b/release/scripts/modules/bl_i18n_utils/utils_languages_menu.py @@ -95,3 +95,5 @@ def gen_menu_file(stats, settings): data_lines.append("# {} #{}:{}:{}".format(FLAG_MESSAGES[flag], uid_num, label, uid)) with open(os.path.join(settings.TRUNK_MO_DIR, settings.LANGUAGES_FILE), 'w') as f: f.write("\n".join(data_lines)) + with open(os.path.join(settings.GIT_I18N_ROOT, settings.LANGUAGES_FILE), 'w') as f: + f.write("\n".join(data_lines)) diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index 4126dde1225..42a23c8c041 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -125,6 +125,7 @@ class SpellChecker: "multisampling", "multitexture", "multiuser", + "multiview", "namespace", "keyconfig", "online", @@ -186,6 +187,7 @@ class SpellChecker: "unhide", "unindent", "unkeyed", + "unmute", "unpremultiply", "unprojected", "unreacted", @@ -576,6 +578,8 @@ class SpellChecker: "btx", "cineon", "dpx", + "dwaa", + "dwab", "dxf", "eps", "exr", diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py index 25a6c7242e0..d5b64933165 100644 --- a/release/scripts/modules/bpy/path.py +++ b/release/scripts/modules/bpy/path.py @@ -116,7 +116,11 @@ def is_subdir(path, directory): from os.path import normpath, normcase path = normpath(normcase(path)) directory = normpath(normcase(directory)) - return path.startswith(directory) + if len(path) > len(directory): + if path.startswith(directory): + sep = ord(_os.sep) if isinstance(directory, bytes) else _os.sep + return (path[len(directory)] == sep) + return False def clean_name(name, replace="_"): @@ -128,23 +132,47 @@ def clean_name(name, replace="_"): or the replace argument if defined. """ - bad_chars = ("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e" - "\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d" - "\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c" - "\x2e\x2f\x3a\x3b\x3c\x3d\x3e\x3f\x40\x5b\x5c\x5d\x5e\x60\x7b" - "\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a" - "\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99" - "\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8" - "\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7" - "\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6" - "\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5" - "\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4" - "\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3" - "\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe") - - for ch in bad_chars: - name = name.replace(ch, replace) - return name + if replace != "_": + if len(replace) != 1 or ord(replace) > 255: + raise ValueError("Value must be a single ascii character") + + def maketrans_init(): + trans_cache = clean_name._trans_cache + trans = trans_cache.get(replace) + if trans is None: + bad_chars = ( + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2e, 0x2f, 0x3a, + 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x5b, 0x5c, + 0x5d, 0x5e, 0x60, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, + ) + trans = str.maketrans({char: replace for char in bad_chars}) + trans_cache[replace] = trans + return trans + + trans = maketrans_init() + return name.translate(trans) +clean_name._trans_cache = {} def _clean_utf8(name): @@ -222,7 +250,8 @@ def resolve_ncase(path): if _os.path.isdir(dirpath): try: files = _os.listdir(dirpath) - except PermissionError: # We might not have the permission to list dirpath... + except PermissionError: + # We might not have the permission to list dirpath... return path, False else: return path, False diff --git a/release/scripts/modules/bpy/utils.py b/release/scripts/modules/bpy/utils/__init__.py index 5f235ae3958..7a1224db226 100644 --- a/release/scripts/modules/bpy/utils.py +++ b/release/scripts/modules/bpy/utils/__init__.py @@ -38,6 +38,7 @@ __all__ = ( "unregister_manual_map", "make_rna_paths", "manual_map", + "previews", "resource_path", "script_path_user", "script_path_pref", @@ -160,7 +161,7 @@ def load_scripts(reload_scripts=False, refresh_scripts=False): # modification time changes. This `won't` work for packages so... # its not perfect. for module_name in [ext.module for ext in _user_preferences.addons]: - _addon_utils.disable(module_name, default_set=False) + _addon_utils.disable(module_name) def register_module_call(mod): register = getattr(mod, "register", None) @@ -640,10 +641,10 @@ def unregister_module(module, verbose=False): # we start with the built-in default mapping def _blender_default_map(): import sys - import rna_wiki_reference as ref_mod + import rna_manual_reference as ref_mod ret = (ref_mod.url_manual_prefix, ref_mod.url_manual_mapping) # avoid storing in memory - del sys.modules["rna_wiki_reference"] + del sys.modules["rna_manual_reference"] return ret # hooks for doc lookups diff --git a/release/scripts/modules/bpy/utils/previews.py b/release/scripts/modules/bpy/utils/previews.py new file mode 100644 index 00000000000..965971139e4 --- /dev/null +++ b/release/scripts/modules/bpy/utils/previews.py @@ -0,0 +1,153 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +""" +This module contains utility functions to handle custom previews. + +It behaves as a high-level 'cached' previews manager. + +This allows scripts to generate their own previews, and use them as icons in UI widgets +('icon_value' for UILayout functions). + + +Custom Icon Example +------------------- + +.. literalinclude:: ../../../release/scripts/templates_py/ui_previews_custom_icon.py +""" + +__all__ = ( + "new", + "remove", + "ImagePreviewCollection", + ) + +import _bpy +_utils_previews = _bpy._utils_previews +del _bpy + + +_uuid_open = set() + + +# High-level previews manager. +# not accessed directly +class ImagePreviewCollection(dict): + """ + Dictionary-like class of previews. + + This is a subclass of Python's built-in dict type, + used to store multiple image previews. + + .. note:: + + - instance with :mod:`bpy.utils.previews.new` + - keys must be ``str`` type. + - values will be :class:`bpy.types.ImagePreview` + """ + + # Internal notes: + # - Blender's internal 'PreviewImage' struct uses 'self._uuid' prefix. + + def __init__(self): + super().__init__() + self._uuid = hex(id(self)) + _uuid_open.add(self._uuid) + + def __del__(self): + if self._uuid not in _uuid_open: + return + + raise ResourceWarning( + "<%s id=%s[%d]>: left open, remove with " + "'bpy.utils.previews.remove()'" % + (self.__class__.__name__, self._uuid, len(self))) + self.close() + + def _gen_key(self, name): + return ":".join((self._uuid, name)) + + def new(self, name): + if name in self: + raise KeyException("key %r already exists") + p = self[name] = _utils_previews.new( + self._gen_key(name)) + return p + new.__doc__ = _utils_previews.new.__doc__ + + def load(self, name, path, path_type, force_reload=False): + if name in self: + raise KeyException("key %r already exists") + p = self[name] = _utils_previews.load( + self._gen_key(name), path, path_type, force_reload) + return p + load.__doc__ = _utils_previews.load.__doc__ + + def clear(self): + """Clear all previews.""" + for name in self.keys(): + _utils_previews.release(self._gen_key(name)) + super().clear() + + def close(self): + """Close the collection and clear all previews.""" + self.clear() + _uuid_open.remove(self._uuid) + + def __delitem__(self, key): + _utils_previews.release(self._gen_key(key)) + super().__delitem__(key) + + def __repr__(self): + return "<%s id=%s[%d], %s>" % ( + self.__class__.__name__, + self._uuid, + len(self), + super().__repr__()) + + +def new(): + """ + :return: a new preview collection. + :rtype: :class:`ImagePreviewCollection` + """ + + return ImagePreviewCollection() + + +def remove(pcoll): + """ + Remove the specified previews collection. + + :arg pcoll: Preview collection to close. + :type pcoll: :class:`ImagePreviewCollection` + """ + pcoll.close() + + +# don't complain about resources on exit (only unregister) +import atexit + + +def exit_clear_warning(): + del ImagePreviewCollection.__del__ + +atexit.register(exit_clear_warning) +del atexit, exit_clear_warning diff --git a/release/scripts/modules/bpy_extras/io_utils.py b/release/scripts/modules/bpy_extras/io_utils.py index 81de0d7c6f0..65ccc3f8dc3 100644 --- a/release/scripts/modules/bpy_extras/io_utils.py +++ b/release/scripts/modules/bpy_extras/io_utils.py @@ -21,7 +21,7 @@ __all__ = ( "ExportHelper", "ImportHelper", - "OrientationHelper", + "orientation_helper_factory", "axis_conversion", "axis_conversion_ensure", "create_derived_objects", @@ -35,7 +35,11 @@ __all__ = ( ) import bpy -from bpy.props import StringProperty, BoolProperty, EnumProperty +from bpy.props import ( + StringProperty, + BoolProperty, + EnumProperty, + ) def _check_axis_conversion(op): @@ -61,6 +65,12 @@ class ExportHelper: options={'HIDDEN'}, ) + # needed for mix-ins + order = [ + "filepath", + "check_existing", + ] + # subclasses can override with decorator # True == use ext, False == no ext, None == do nothing. check_extension = True @@ -109,6 +119,11 @@ class ImportHelper: subtype='FILE_PATH', ) + # needed for mix-ins + order = [ + "filepath", + ] + def invoke(self, context, event): context.window_manager.fileselect_add(self) return {'RUNNING_MODAL'} @@ -117,13 +132,14 @@ class ImportHelper: return _check_axis_conversion(self) -class OrientationHelper: +def orientation_helper_factory(name, axis_forward='Y', axis_up='Z'): + members = {} def _update_axis_forward(self, context): if self.axis_forward[-1] == self.axis_up[-1]: self.axis_up = self.axis_up[0:-1] + 'XYZ'[('XYZ'.index(self.axis_up[-1]) + 1) % 3] - axis_forward = EnumProperty( + members['axis_forward'] = EnumProperty( name="Forward", items=(('X', "X Forward", ""), ('Y', "Y Forward", ""), @@ -132,7 +148,7 @@ class OrientationHelper: ('-Y', "-Y Forward", ""), ('-Z', "-Z Forward", ""), ), - default='-Z', + default=axis_forward, update=_update_axis_forward, ) @@ -140,7 +156,7 @@ class OrientationHelper: if self.axis_up[-1] == self.axis_forward[-1]: self.axis_forward = self.axis_forward[0:-1] + 'XYZ'[('XYZ'.index(self.axis_forward[-1]) + 1) % 3] - axis_up = EnumProperty( + members['axis_up'] = EnumProperty( name="Up", items=(('X', "X Up", ""), ('Y', "Y Up", ""), @@ -149,10 +165,17 @@ class OrientationHelper: ('-Y', "-Y Up", ""), ('-Z', "-Z Up", ""), ), - default='Y', + default=axis_up, update=_update_axis_up, ) + members["order"] = [ + "axis_forward", + "axis_up", + ] + + return type(name, (object,), members) + # Axis conversion function, not pretty LUT # use lookup table to convert between any axis @@ -349,7 +372,7 @@ def unpack_list(list_of_tuples): # same as above except that it adds 0 for triangle faces def unpack_face_list(list_of_tuples): - #allocate the entire list + # allocate the entire list flat_ls = [0] * (len(list_of_tuples) * 4) i = 0 diff --git a/release/scripts/modules/bpy_extras/object_utils.py b/release/scripts/modules/bpy_extras/object_utils.py index 13ef86b23c0..78fb6aa8fa2 100644 --- a/release/scripts/modules/bpy_extras/object_utils.py +++ b/release/scripts/modules/bpy_extras/object_utils.py @@ -31,7 +31,10 @@ __all__ = ( import bpy -from bpy.props import BoolProperty, FloatVectorProperty +from bpy.props import ( + BoolProperty, + FloatVectorProperty, + ) def add_object_align_init(context, operator): @@ -171,7 +174,7 @@ def object_data_add(context, obdata, operator=None, use_active_layer=True, name= obj_act.select = True scene.update() # apply location - #scene.objects.active = obj_new + # scene.objects.active = obj_new bpy.ops.object.join() # join into the active. if obdata: @@ -287,7 +290,8 @@ def world_to_camera_view(scene, obj, coord): Returns the camera space coords for a 3d point. (also known as: normalized device coordinates - NDC). - Where (0, 0) is the bottom left and (1, 1) is the top right of the camera frame. + Where (0, 0) is the bottom left and (1, 1) + is the top right of the camera frame. values outside 0-1 are also supported. A negative 'z' value means the point is behind the camera. @@ -300,7 +304,8 @@ def world_to_camera_view(scene, obj, coord): :type obj: :class:`bpy.types.Object` :arg coord: World space location. :type coord: :class:`mathutils.Vector` - :return: a vector where X and Y map to the view plane and Z is the depth on the view axis. + :return: a vector where X and Y map to the view plane and + Z is the depth on the view axis. :rtype: :class:`mathutils.Vector` """ from mathutils import Vector diff --git a/release/scripts/modules/bpy_extras/view3d_utils.py b/release/scripts/modules/bpy_extras/view3d_utils.py index ec4a395f1e1..4aa06262970 100644 --- a/release/scripts/modules/bpy_extras/view3d_utils.py +++ b/release/scripts/modules/bpy_extras/view3d_utils.py @@ -69,11 +69,13 @@ def region_2d_to_origin_3d(region, rv3d, coord, clamp=None): .. note:: - Orthographic views have a less obvious origin, the far clip is used to define the viewport near/far extents. - Since far clip can be a very large value, the result may give with numeric precision issues. + Orthographic views have a less obvious origin, + the far clip is used to define the viewport near/far extents. + Since far clip can be a very large value, + the result may give with numeric precision issues. - To avoid this problem, you can optionally clamp the far clip to a smaller value - based on the data you're operating on. + To avoid this problem, you can optionally clamp the far clip to a + smaller value based on the data you're operating on. :arg region: region of the 3D viewport, typically bpy.context.region. :type region: :class:`bpy.types.Region` @@ -160,7 +162,7 @@ def region_2d_to_location_3d(region, rv3d, coord, depth_location): )[0] -def location_3d_to_region_2d(region, rv3d, coord): +def location_3d_to_region_2d(region, rv3d, coord, default=None): """ Return the *region* relative 2d location of a 3d position. @@ -170,8 +172,10 @@ def location_3d_to_region_2d(region, rv3d, coord): :type rv3d: :class:`bpy.types.RegionView3D` :arg coord: 3d worldspace location. :type coord: 3d vector + :arg default: Return this value if ``coord`` + is behind the origin of a perspective view. :return: 2d location - :rtype: :class:`mathutils.Vector` + :rtype: :class:`mathutils.Vector` or ``default`` argument. """ from mathutils import Vector @@ -184,4 +188,4 @@ def location_3d_to_region_2d(region, rv3d, coord): height_half + height_half * (prj.y / prj.w), )) else: - return None + return default diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index c7ec7e1e54a..92dbd2dbd0e 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -715,7 +715,7 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta): __slots__ = () def path_menu(self, searchpaths, operator, - props_default={}, filter_ext=None): + props_default=None, filter_ext=None): layout = self.layout # hard coded to set the operators 'filepath' to the filename. @@ -745,8 +745,9 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta): text=bpy.path.display_name(f), translate=False) - for attr, value in props_default.items(): - setattr(props, attr, value) + if props_default is not None: + for attr, value in props_default.items(): + setattr(props, attr, value) props.filepath = filepath if operator == "script.execute_preset": @@ -754,14 +755,21 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta): def draw_preset(self, context): """ - Define these on the subclass - - preset_operator - - preset_subdir + Define these on the subclass: + - preset_operator (string) + - preset_subdir (string) + + Optionally: + - preset_extensions (set of strings) + - preset_operator_defaults (dict of keyword args) """ import bpy + ext_valid = getattr(self, "preset_extensions", {".py", ".xml"}) + props_default = getattr(self, "preset_operator_defaults", None) self.path_menu(bpy.utils.preset_paths(self.preset_subdir), self.preset_operator, - filter_ext=lambda ext: ext.lower() in {".py", ".xml"}) + props_default=props_default, + filter_ext=lambda ext: ext.lower() in ext_valid) @classmethod def draw_collapsible(cls, context, layout): @@ -773,24 +781,6 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta): layout.menu(cls.__name__, icon='COLLAPSEMENU') -class Region(StructRNA): - __slots__ = () - - def callback_add(self, cb, args, draw_mode): - """ - Append a draw function to this region, - deprecated, instead use bpy.types.SpaceView3D.draw_handler_add - """ - for area in self.id_data.areas: - for region in area.regions: - if region == self: - spacetype = type(area.spaces[0]) - return spacetype.draw_handler_add(cb, args, self.type, - draw_mode) - - return None - - class NodeTree(bpy_types.ID, metaclass=RNAMetaPropGroup): __slots__ = () diff --git a/release/scripts/modules/nodeitems_utils.py b/release/scripts/modules/nodeitems_utils.py index 2882a08fbd4..2f69ea84386 100644 --- a/release/scripts/modules/nodeitems_utils.py +++ b/release/scripts/modules/nodeitems_utils.py @@ -37,13 +37,17 @@ class NodeCategory: else: def items_gen(context): for item in items: - if item.poll is None or item.poll(context): + if item.poll is None or context is None or item.poll(context): yield item self.items = items_gen class NodeItem: - def __init__(self, nodetype, label=None, settings={}, poll=None): + def __init__(self, nodetype, label=None, settings=None, poll=None): + + if settings is None: + settings = {} + self.nodetype = nodetype self._label = label self.settings = settings @@ -136,7 +140,7 @@ def register_node_categories(identifier, cat_list): def node_categories_iter(context): for cat_type in _node_categories.values(): for cat in cat_type[0]: - if cat.poll and cat.poll(context): + if cat.poll and ((context is None) or cat.poll(context)): yield cat diff --git a/release/scripts/modules/rna_info.py b/release/scripts/modules/rna_info.py index 353362ed168..dae262e93d7 100644 --- a/release/scripts/modules/rna_info.py +++ b/release/scripts/modules/rna_info.py @@ -268,7 +268,8 @@ class InfoPropertyRNA: self.default_str = "\"%s\"" % self.default elif self.type == "enum": if self.is_enum_flag: - self.default_str = "%r" % self.default # repr or set() + # self.default_str = "%r" % self.default # repr or set() + self.default_str = "{%s}" % repr(list(sorted(self.default)))[1:-1] else: self.default_str = "'%s'" % self.default elif self.array_length: @@ -695,7 +696,7 @@ def BuildRNAInfo(): return InfoStructRNA.global_lookup, InfoFunctionRNA.global_lookup, InfoOperatorRNA.global_lookup, InfoPropertyRNA.global_lookup -if __name__ == "__main__": +def main(): import rna_info struct = rna_info.BuildRNAInfo()[0] data = [] @@ -723,3 +724,7 @@ if __name__ == "__main__": else: text = bpy.data.texts.new(name="api.py") text.from_string(data) + + +if __name__ == "__main__": + main() diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index f4649453b46..44722fa7162 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -32,6 +32,13 @@ def rna_idprop_ui_get(item, create=True): return None +def rna_idprop_ui_del(item): + try: + del item['_RNA_UI'] + except KeyError: + pass + + def rna_idprop_ui_prop_get(item, prop, create=True): rna_ui = rna_idprop_ui_get(item, create) @@ -46,7 +53,7 @@ def rna_idprop_ui_prop_get(item, prop, create=True): return rna_ui[prop] -def rna_idprop_ui_prop_clear(item, prop): +def rna_idprop_ui_prop_clear(item, prop, remove=True): rna_ui = rna_idprop_ui_get(item, False) if rna_ui is None: @@ -54,8 +61,10 @@ def rna_idprop_ui_prop_clear(item, prop): try: del rna_ui[prop] - except: + except KeyError: pass + if remove and len(item.keys()) == 1: + rna_idprop_ui_del(item) def rna_idprop_context_value(context, context_member, property_type): diff --git a/release/scripts/modules/rna_xml.py b/release/scripts/modules/rna_xml.py index 729d6238ac3..ad4809efbe1 100644 --- a/release/scripts/modules/rna_xml.py +++ b/release/scripts/modules/rna_xml.py @@ -32,7 +32,7 @@ def build_property_typemap(skip_classes, skip_typemap): if issubclass(cls, skip_classes): continue - ## to support skip-save we cant get all props + # # to support skip-save we cant get all props # properties = cls.bl_rna.properties.keys() properties = [] for prop_id, prop in cls.bl_rna.properties.items(): diff --git a/release/scripts/presets/keyconfig/3dsmax.py b/release/scripts/presets/keyconfig/3dsmax.py index f85d3b43041..7694e338d68 100644 --- a/release/scripts/presets/keyconfig/3dsmax.py +++ b/release/scripts/presets/keyconfig/3dsmax.py @@ -754,8 +754,6 @@ kmi = km.keymap_items.new('anim.channels_move', 'PAGE_DOWN', 'PRESS', shift=True kmi.properties.direction = 'BOTTOM' kmi = km.keymap_items.new('anim.channels_group', 'G', 'PRESS', ctrl=True) kmi = km.keymap_items.new('anim.channels_ungroup', 'G', 'PRESS', alt=True) -kmi = km.keymap_items.new('anim.channels_visibility_set', 'V', 'PRESS') -kmi = km.keymap_items.new('anim.channels_visibility_toggle', 'V', 'PRESS', shift=True) # Map UV Editor km = kc.keymaps.new('UV Editor', space_type='EMPTY', region_type='WINDOW', modal=False) @@ -1055,7 +1053,6 @@ kmi.properties.name = 'NODE_MT_add' kmi = km.keymap_items.new('node.duplicate_move', 'D', 'PRESS', shift=True) kmi = km.keymap_items.new('node.duplicate_move_keep_inputs', 'D', 'PRESS', shift=True, ctrl=True) kmi = km.keymap_items.new('node.parent_set', 'P', 'PRESS', ctrl=True) -kmi = km.keymap_items.new('node.parent_clear', 'P', 'PRESS', alt=True) kmi = km.keymap_items.new('node.join', 'J', 'PRESS', ctrl=True) kmi = km.keymap_items.new('node.hide_toggle', 'H', 'PRESS') kmi = km.keymap_items.new('node.mute_toggle', 'M', 'PRESS') diff --git a/release/scripts/presets/keyconfig/maya.py b/release/scripts/presets/keyconfig/maya.py index 47a7a7c5f5d..cdd16f26877 100644 --- a/release/scripts/presets/keyconfig/maya.py +++ b/release/scripts/presets/keyconfig/maya.py @@ -698,10 +698,10 @@ kmi.properties.level = 5 km = kc.keymaps.new('Knife Tool Modal Map', space_type='EMPTY', region_type='WINDOW', modal=True) kmi = km.keymap_items.new_modal('CANCEL', 'ESC', 'ANY', any=True) +kmi = km.keymap_items.new_modal('ADD_CUT', 'LEFTMOUSE', 'ANY') kmi = km.keymap_items.new_modal('PANNING', 'LEFTMOUSE', 'ANY', alt=True) kmi = km.keymap_items.new_modal('PANNING', 'MIDDLEMOUSE', 'ANY', alt=True) kmi = km.keymap_items.new_modal('PANNING', 'RIGHTMOUSE', 'ANY', alt=True) -kmi = km.keymap_items.new_modal('ADD_CUT', 'LEFTMOUSE', 'PRESS', any=True) kmi = km.keymap_items.new_modal('CANCEL', 'RIGHTMOUSE', 'ANY') kmi = km.keymap_items.new_modal('CONFIRM', 'RET', 'PRESS', any=True) kmi = km.keymap_items.new_modal('CONFIRM', 'NUMPAD_ENTER', 'PRESS', any=True) @@ -1137,8 +1137,6 @@ kmi = km.keymap_items.new('anim.channels_move', 'PAGE_UP', 'PRESS', shift=True) kmi.properties.direction = 'TOP' kmi = km.keymap_items.new('anim.channels_move', 'PAGE_DOWN', 'PRESS', shift=True) kmi.properties.direction = 'BOTTOM' -kmi = km.keymap_items.new('anim.channels_visibility_set', 'V', 'PRESS') -kmi = km.keymap_items.new('anim.channels_visibility_toggle', 'V', 'PRESS', shift=True) # Map UV Editor km = kc.keymaps.new('UV Editor', space_type='EMPTY', region_type='WINDOW', modal=False) @@ -1499,11 +1497,9 @@ kmi = km.keymap_items.new('node.group_edit', 'TAB', 'PRESS') kmi = km.keymap_items.new('node.read_renderlayers', 'R', 'PRESS', ctrl=True) kmi = km.keymap_items.new('node.read_fullsamplelayers', 'R', 'PRESS', shift=True) kmi = km.keymap_items.new('node.render_changed', 'Z', 'PRESS') -kmi = km.keymap_items.new('transform.translate', 'W', 'PRESS') -kmi = km.keymap_items.new('transform.translate', 'EVT_TWEAK_A', 'ANY') -kmi.properties.release_confirm = True -kmi = km.keymap_items.new('transform.translate', 'EVT_TWEAK_S', 'ANY') -kmi.properties.release_confirm = True +kmi = km.keymap_items.new('node.translate_attach', 'W', 'PRESS') +kmi = km.keymap_items.new('node.translate_attach', 'EVT_TWEAK_A', 'ANY') +kmi = km.keymap_items.new('node.translate_attach', 'EVT_TWEAK_S', 'ANY') kmi = km.keymap_items.new('transform.rotate', 'E', 'PRESS') kmi = km.keymap_items.new('transform.resize', 'R', 'PRESS') kmi = km.keymap_items.new('node.move_detach_links', 'D', 'PRESS', alt=True) diff --git a/release/scripts/startup/bl_operators/__init__.py b/release/scripts/startup/bl_operators/__init__.py index 65f7bde1809..4047505652f 100644 --- a/release/scripts/startup/bl_operators/__init__.py +++ b/release/scripts/startup/bl_operators/__init__.py @@ -54,7 +54,7 @@ if bpy.app.build_options.freestyle: _modules.append("freestyle") __import__(name=__name__, fromlist=_modules) _namespace = globals() -_modules_loaded = {name: _namespace[name] for name in _modules if name != 'bpy'} +_modules_loaded = {name: _namespace[name] for name in _modules if name != "bpy"} del _namespace diff --git a/release/scripts/startup/bl_operators/add_mesh_torus.py b/release/scripts/startup/bl_operators/add_mesh_torus.py index dfb734e9b5b..82014c87be9 100644 --- a/release/scripts/startup/bl_operators/add_mesh_torus.py +++ b/release/scripts/startup/bl_operators/add_mesh_torus.py @@ -153,40 +153,40 @@ class AddTorus(Operator, object_utils.AddObjectHelper): col = layout.column(align=True) col.label(text="Location") - col.prop(self, 'location', text="") + col.prop(self, "location", text="") col = layout.column(align=True) col.label(text="Rotation") - col.prop(self, 'rotation', text="") + col.prop(self, "rotation", text="") col = layout.column(align=True) col.label(text="Major Segments") - col.prop(self, 'major_segments', text="") + col.prop(self, "major_segments", text="") col = layout.column(align=True) col.label(text="Minor Segments") - col.prop(self, 'minor_segments', text="") + col.prop(self, "minor_segments", text="") col = layout.column(align=True) col.label(text="Torus Dimensions") - col.row().prop(self, 'mode', expand=True) + col.row().prop(self, "mode", expand=True) if self.mode == 'MAJOR_MINOR': col = layout.column(align=True) col.label(text="Major Radius") - col.prop(self, 'major_radius', text="") + col.prop(self, "major_radius", text="") col = layout.column(align=True) col.label(text="Minor Radius") - col.prop(self, 'minor_radius', text="") + col.prop(self, "minor_radius", text="") else: col = layout.column(align=True) col.label(text="Exterior Radius") - col.prop(self, 'abso_major_rad', text="") + col.prop(self, "abso_major_rad", text="") col = layout.column(align=True) col.label(text="Interior Radius") - col.prop(self, 'abso_minor_rad', text="") + col.prop(self, "abso_minor_rad", text="") def invoke(self, context, event): object_utils.object_add_grid_scale_apply_operator(self, context) diff --git a/release/scripts/startup/bl_operators/anim.py b/release/scripts/startup/bl_operators/anim.py index 1b3e719b2bd..f3575f26890 100644 --- a/release/scripts/startup/bl_operators/anim.py +++ b/release/scripts/startup/bl_operators/anim.py @@ -210,6 +210,12 @@ class BakeAction(Operator): description="Bake animation onto the object then clear parents (objects only)", default=False, ) + use_current_action = BoolProperty( + name="Overwrite Current Action", + description="Bake animation into current action, instead of creating a new one " + "(useful for baking only part of bones in an armature)", + default=False, + ) bake_types = EnumProperty( name="Bake Data", description="Which data's transformations to bake", @@ -224,6 +230,12 @@ class BakeAction(Operator): from bpy_extras import anim_utils + action = None + if self.use_current_action: + obj = context.object + if obj.animation_data: + action = obj.animation_data.action + action = anim_utils.bake_action(self.frame_start, self.frame_end, frame_step=self.step, @@ -234,6 +246,7 @@ class BakeAction(Operator): do_constraint_clear=self.clear_constraints, do_parents_clear=self.clear_parents, do_clean=True, + action=action, ) if action is None: diff --git a/release/scripts/startup/bl_operators/clip.py b/release/scripts/startup/bl_operators/clip.py index 7e4e0ea9246..0c77ea2ab7e 100644 --- a/release/scripts/startup/bl_operators/clip.py +++ b/release/scripts/startup/bl_operators/clip.py @@ -225,7 +225,8 @@ class CLIP_OT_track_to_empty(Operator): bl_label = "Link Empty to Track" bl_options = {'UNDO', 'REGISTER'} - def _link_track(self, context, clip, tracking_object, track): + @staticmethod + def _link_track(context, clip, tracking_object, track): sc = context.space_data constraint = None ob = None @@ -331,7 +332,8 @@ class CLIP_OT_delete_proxy(Operator): return wm.invoke_confirm(self, event) - def _rmproxy(self, abspath): + @staticmethod + def _rmproxy(abspath): import shutil if not os.path.exists(abspath): @@ -552,8 +554,8 @@ class CLIP_OT_setup_tracking_scene(Operator): world.light_settings.sample_method = 'ADAPTIVE_QMC' world.light_settings.samples = 7 world.light_settings.threshold = 0.005 - if hasattr(scene, 'cycles'): - world.light_settings.ao_factor = 0.05 + if hasattr(scene, "cycles"): + world.light_settings.ao_factor = 0.05 @staticmethod def _findOrCreateCamera(context): @@ -841,7 +843,7 @@ class CLIP_OT_setup_tracking_scene(Operator): self._offsetNodes(tree) scene.render.alpha_mode = 'TRANSPARENT' - if hasattr(scene, 'cycles'): + if hasattr(scene, "cycles"): scene.cycles.film_transparent = True @staticmethod diff --git a/release/scripts/startup/bl_operators/image.py b/release/scripts/startup/bl_operators/image.py index 1653459bd71..f00f5d97c5e 100644 --- a/release/scripts/startup/bl_operators/image.py +++ b/release/scripts/startup/bl_operators/image.py @@ -33,7 +33,8 @@ class EditExternally(Operator): subtype='FILE_PATH', ) - def _editor_guess(self, context): + @staticmethod + def _editor_guess(context): import sys image_editor = context.user_preferences.filepaths.image_editor diff --git a/release/scripts/startup/bl_operators/mask.py b/release/scripts/startup/bl_operators/mask.py index 60208d27338..aa984659430 100644 --- a/release/scripts/startup/bl_operators/mask.py +++ b/release/scripts/startup/bl_operators/mask.py @@ -18,7 +18,6 @@ # <pep8-80 compliant> -import bpy from bpy.types import Menu diff --git a/release/scripts/startup/bl_operators/mesh.py b/release/scripts/startup/bl_operators/mesh.py index f86c31cd9cc..ea504d48448 100644 --- a/release/scripts/startup/bl_operators/mesh.py +++ b/release/scripts/startup/bl_operators/mesh.py @@ -75,7 +75,6 @@ class MeshMirrorUV(Operator): double_warn += co in mirror_lt mirror_lt[co] = i - #for i, v in enumerate(mesh.vertices): vmap = {} for mirror_a, mirror_b in ((mirror_gt, mirror_lt), (mirror_lt, mirror_gt)): diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py index e3ceeca8abe..b89890a223c 100644 --- a/release/scripts/startup/bl_operators/object.py +++ b/release/scripts/startup/bl_operators/object.py @@ -579,7 +579,8 @@ class MakeDupliFace(Operator): bl_label = "Make Dupli-Face" bl_options = {'REGISTER', 'UNDO'} - def _main(self, context): + @staticmethod + def _main(context): from mathutils import Vector SCALE_FAC = 0.01 @@ -643,6 +644,9 @@ class MakeDupliFace(Operator): ob_new.use_dupli_faces_scale = True ob_new.dupli_faces_scale = 1.0 / SCALE_FAC + ob_inst.select = True + ob_new.select = True + def execute(self, context): self._main(context) return {'FINISHED'} @@ -924,7 +928,7 @@ class LodGenerate(Operator): lod.location.y = ob.location.y + 3.0 * i if i == 1: - modifier = lod.modifiers.new("lod_decimate", "DECIMATE") + modifier = lod.modifiers.new("lod_decimate", 'DECIMATE') else: modifier = lod.modifiers[-1] diff --git a/release/scripts/startup/bl_operators/object_align.py b/release/scripts/startup/bl_operators/object_align.py index e843209da3c..3c84e5dc553 100644 --- a/release/scripts/startup/bl_operators/object_align.py +++ b/release/scripts/startup/bl_operators/object_align.py @@ -70,7 +70,8 @@ def GlobalBB_HQ(obj): # Initialize the variables with the last vertex - verts = obj.data.vertices + me = obj.to_mesh(scene=bpy.context.scene, apply_modifiers=True, settings='PREVIEW') + verts = me.vertices val = matrix_world * verts[-1].co @@ -111,6 +112,8 @@ def GlobalBB_HQ(obj): if val > up: up = val + bpy.data.meshes.remove(me) + return Vector((left, front, up)), Vector((right, back, down)) @@ -338,7 +341,10 @@ def align_objects(context, return True -from bpy.props import EnumProperty, BoolProperty +from bpy.props import ( + EnumProperty, + BoolProperty + ) class AlignObjects(Operator): diff --git a/release/scripts/startup/bl_operators/presets.py b/release/scripts/startup/bl_operators/presets.py index 7195b7819d1..63c1945d2d2 100644 --- a/release/scripts/startup/bl_operators/presets.py +++ b/release/scripts/startup/bl_operators/presets.py @@ -45,11 +45,28 @@ class AddPresetBase: options={'HIDDEN', 'SKIP_SAVE'}, ) + # needed for mix-ins + order = [ + "name", + "remove_active", + ] + @staticmethod def as_filename(name): # could reuse for other presets - for char in " !@#$%^&*(){}:\";'[]<>,.\\/?": - name = name.replace(char, '_') - return name.lower().strip() + + # lazy init maketrans + def maketrans_init(): + cls = AddPresetBase + attr = "_as_filename_trans" + + trans = getattr(cls, attr, None) + if trans is None: + trans = str.maketrans({char: "_" for char in " !@#$%^&*(){}:\";'[]<>,.\\/?"}) + setattr(cls, attr, trans) + return trans + + trans = maketrans_init() + return name.lower().strip().translate(trans) def execute(self, context): import os diff --git a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py index 3a7a9b99cde..a5565699364 100644 --- a/release/scripts/startup/bl_operators/screen_play_rendered_anim.py +++ b/release/scripts/startup/bl_operators/screen_play_rendered_anim.py @@ -107,11 +107,20 @@ class PlayRenderedAnim(Operator): del file_a, file_b, frame_tmp file = bpy.path.abspath(file) # expand '//' else: + path_valid = True # works for movies and images - file = rd.frame_path(frame=scene.frame_start) + file = rd.frame_path(frame=scene.frame_start, preview=scene.use_preview_range) file = bpy.path.abspath(file) # expand '//' if not os.path.exists(file): self.report({'WARNING'}, "File %r not found" % file) + path_valid = False + + #one last try for full range if we used preview range + if scene.use_preview_range and not path_valid: + file = rd.frame_path(frame=scene.frame_start, preview=False) + file = bpy.path.abspath(file) # expand '//' + if not os.path.exists(file): + self.report({'WARNING'}, "File %r not found" % file) cmd = [player_path] # extra options, fps controls etc. diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index a48415caa9b..edf60aa40e7 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -719,7 +719,7 @@ class WM_OT_context_modal_mouse(Operator): """Adjust arbitrary values with mouse input""" bl_idname = "wm.context_modal_mouse" bl_label = "Context Modal Mouse" - bl_options = {'GRAB_POINTER', 'BLOCKING', 'UNDO', 'INTERNAL'} + bl_options = {'GRAB_CURSOR', 'BLOCKING', 'UNDO', 'INTERNAL'} data_path_iter = data_path_iter data_path_item = data_path_item @@ -974,10 +974,12 @@ class WM_OT_doc_view_manual(Operator): url = self._lookup_rna_url(rna_id) if url is None: - self.report({'WARNING'}, "No reference available %r, " - "Update info in 'rna_wiki_reference.py' " - " or callback to bpy.utils.manual_map()" % - self.doc_id) + self.report( + {'WARNING'}, + "No reference available %r, " + "Update info in 'rna_manual_reference.py' " + "or callback to bpy.utils.manual_map()" % + self.doc_id) return {'CANCELLED'} else: import webbrowser @@ -1296,9 +1298,13 @@ class WM_OT_properties_remove(Operator): property = rna_property def execute(self, context): + from rna_prop_ui import rna_idprop_ui_prop_clear data_path = self.data_path item = eval("context.%s" % data_path) - del item[self.property] + prop = self.property + del item[prop] + rna_idprop_ui_prop_clear(item, prop) + return {'FINISHED'} @@ -1782,7 +1788,7 @@ class WM_OT_addon_disable(Operator): err_str = traceback.format_exc() print(err_str) - addon_utils.disable(self.module, handle_error=err_cb) + addon_utils.disable(self.module, default_set=True, handle_error=err_cb) if err_str: self.report({'ERROR'}, err_str) @@ -1998,7 +2004,7 @@ class WM_OT_addon_install(Operator): # disable any addons we may have enabled previously and removed. # this is unlikely but do just in case. bug [#23978] for new_addon in addons_new: - addon_utils.disable(new_addon) + addon_utils.disable(new_addon, default_set=True) # possible the zip contains multiple addons, we could disallow this # but for now just use the first @@ -2062,7 +2068,7 @@ class WM_OT_addon_remove(Operator): return {'CANCELLED'} # in case its enabled - addon_utils.disable(self.module) + addon_utils.disable(self.module, default_set=True) import shutil if isdir: @@ -2102,15 +2108,9 @@ class WM_OT_addon_expand(Operator): module_name = self.module - # unlikely to fail, module should have already been imported - try: - # mod = __import__(module_name) - mod = addon_utils.addons_fake_modules.get(module_name) - except: - import traceback - traceback.print_exc() - return {'CANCELLED'} + mod = addon_utils.addons_fake_modules.get(module_name) + if mod is not None: + info = addon_utils.module_bl_info(mod) + info["show_expanded"] = not info["show_expanded"] - info = addon_utils.module_bl_info(mod) - info["show_expanded"] = not info["show_expanded"] return {'FINISHED'} diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index 51117f68558..c110b429aad 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -86,7 +86,7 @@ if bpy.app.build_options.freestyle: _modules.append("properties_freestyle") __import__(name=__name__, fromlist=_modules) _namespace = globals() -_modules_loaded = {name: _namespace[name] for name in _modules if name != 'bpy'} +_modules_loaded = {name: _namespace[name] for name in _modules if name != "bpy"} del _namespace diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index 9aa67223fc4..b37feb82461 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -38,7 +38,8 @@ class ConstraintButtonsPanel: if con.type not in {'RIGID_BODY_JOINT', 'NULL'}: box.prop(con, "influence") - def space_template(self, layout, con, target=True, owner=True): + @staticmethod + def space_template(layout, con, target=True, owner=True): if target or owner: split = layout.split(percentage=0.2) @@ -55,7 +56,8 @@ class ConstraintButtonsPanel: if owner: row.prop(con, "owner_space", text="") - def target_template(self, layout, con, subtargets=True): + @staticmethod + def target_template(layout, con, subtargets=True): layout.prop(con, "target") # XXX limiting settings for only 'curves' or some type of object if con.target and subtargets: @@ -69,6 +71,7 @@ class ConstraintButtonsPanel: elif con.target.type in {'MESH', 'LATTICE'}: layout.prop_search(con, "subtarget", con.target, "vertex_groups", text="Vertex Group") + @staticmethod def ik_template(self, layout, con): # only used for iTaSC layout.prop(con, "pole_target") @@ -894,7 +897,7 @@ class OBJECT_PT_constraints(ConstraintButtonsPanel, Panel): obj = context.object - if obj.type == 'ARMATURE' and obj.mode in {'POSE'}: + if obj.type == 'ARMATURE' and obj.mode == 'POSE': box = layout.box() box.alert = True # XXX: this should apply to the box background box.label(icon='INFO', text="Constraints for active bone do not live here") diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 106e31ea89a..24a67a22c1f 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -135,6 +135,35 @@ class DATA_PT_lens(CameraButtonsPanel, Panel): col.prop(cam, "clip_end", text="End") +class DATA_PT_camera_stereoscopy(CameraButtonsPanel, Panel): + bl_label = "Stereoscopy" + COMPAT_ENGINES = {'BLENDER_RENDER'} + + @classmethod + def poll(cls, context): + render = context.scene.render + return (super().poll(context) and render.use_multiview and + render.views_format == 'STEREO_3D') + + def draw(self, context): + layout = self.layout + # render = context.scene.render + st = context.camera.stereo + + col = layout.column() + col.row().prop(st, "convergence_mode", expand=True) + + if st.convergence_mode == 'PARALLEL': + col.prop(st, "viewport_convergence") + else: + col.prop(st, "convergence_distance") + + col.prop(st, "interocular_distance") + + col.label(text="Pivot:") + col.row().prop(st, "pivot", expand=True) + + class DATA_PT_camera(CameraButtonsPanel, Panel): bl_label = "Camera" COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} @@ -184,13 +213,20 @@ class DATA_PT_camera_dof(CameraButtonsPanel, Panel): col = split.column() col.label(text="Focus:") col.prop(cam, "dof_object", text="") - - col = split.column() - col.prop(dof_options, "fstop") sub = col.column() - sub.active = cam.dof_object is None + sub.active = (cam.dof_object is None) sub.prop(cam, "dof_distance", text="Distance") + hq_support = dof_options.is_hq_supported + col = split.column(align=True) + col.label("Viewport:") + sub = col.column() + sub.active = hq_support + sub.prop(dof_options, "use_high_quality") + col.prop(dof_options, "fstop") + if dof_options.use_high_quality and hq_support: + col.prop(dof_options, "blades") + class DATA_PT_camera_display(CameraButtonsPanel, Panel): bl_label = "Display" diff --git a/release/scripts/startup/bl_ui/properties_data_curve.py b/release/scripts/startup/bl_ui/properties_data_curve.py index 9277bbe20f9..eb8ffa1fb60 100644 --- a/release/scripts/startup/bl_ui/properties_data_curve.py +++ b/release/scripts/startup/bl_ui/properties_data_curve.py @@ -321,7 +321,7 @@ class DATA_PT_font(CurveButtonsPanelText, Panel): row.label(text="Bold & Italic") row.template_ID(text, "font_bold_italic", open="font.open", unlink="font.unlink") - #layout.prop(text, "font") + # layout.prop(text, "font") split = layout.split() diff --git a/release/scripts/startup/bl_ui/properties_data_lamp.py b/release/scripts/startup/bl_ui/properties_data_lamp.py index 6fef30fa3e6..0121ad46c86 100644 --- a/release/scripts/startup/bl_ui/properties_data_lamp.py +++ b/release/scripts/startup/bl_ui/properties_data_lamp.py @@ -355,6 +355,7 @@ class DATA_PT_spot(DataButtonsPanel, Panel): col = split.column() + col.active = (lamp.shadow_method != 'BUFFER_SHADOW' or lamp.shadow_buffer_type != 'DEEP') col.prop(lamp, "use_halo") sub = col.column(align=True) sub.active = lamp.use_halo diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index 8efd14afb47..5416735494b 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -29,8 +29,8 @@ class MESH_MT_vertex_group_specials(Menu): def draw(self, context): layout = self.layout - layout.operator("object.vertex_group_sort", icon='SORTALPHA', text="Sort by Name").sort_type = "NAME" - layout.operator("object.vertex_group_sort", icon='ARMATURE_DATA', text="Sort by Bone Hierarchy").sort_type = "BONE_HIERARCHY" + layout.operator("object.vertex_group_sort", icon='SORTALPHA', text="Sort by Name").sort_type = 'NAME' + layout.operator("object.vertex_group_sort", icon='ARMATURE_DATA', text="Sort by Bone Hierarchy").sort_type = 'BONE_HIERARCHY' layout.operator("object.vertex_group_copy", icon='COPY_ID') layout.operator("object.vertex_group_copy_to_linked", icon='LINK_AREA') layout.operator("object.vertex_group_copy_to_selected", icon='LINK_AREA') @@ -70,7 +70,7 @@ class MESH_UL_vgroups(UIList): layout.prop(vgroup, "name", text="", emboss=False, icon_value=icon) icon = 'LOCKED' if vgroup.lock_weight else 'UNLOCKED' layout.prop(vgroup, "lock_weight", text="", icon=icon, emboss=False) - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) @@ -94,7 +94,7 @@ class MESH_UL_shape_keys(UIList): else: row.label(text="") row.prop(key_block, "mute", text="", emboss=False) - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) @@ -106,7 +106,7 @@ class MESH_UL_uvmaps_vcols(UIList): layout.prop(item, "name", text="", emboss=False, icon_value=icon) icon = 'RESTRICT_RENDER_OFF' if item.active_render else 'RESTRICT_RENDER_ON' layout.prop(item, "active_render", text="", icon=icon, emboss=False) - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) @@ -369,8 +369,8 @@ class DATA_PT_customdata(MeshButtonsPanel, Panel): me = context.mesh col = layout.column() - col.operator("mesh.customdata_clear_mask", icon='X') - col.operator("mesh.customdata_clear_skin", icon='X') + col.operator("mesh.customdata_mask_clear", icon='X') + col.operator("mesh.customdata_skin_clear", icon='X') if me.has_custom_normals: col.operator("mesh.customdata_custom_splitnormals_clear", icon='X') diff --git a/release/scripts/startup/bl_ui/properties_data_modifier.py b/release/scripts/startup/bl_ui/properties_data_modifier.py index ec60f1caec8..381c9add34f 100644 --- a/release/scripts/startup/bl_ui/properties_data_modifier.py +++ b/release/scripts/startup/bl_ui/properties_data_modifier.py @@ -262,24 +262,37 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): row.prop(md, "decimate_type", expand=True) if decimate_type == 'COLLAPSE': + has_vgroup = bool(md.vertex_group) layout.prop(md, "ratio") split = layout.split() - row = split.row(align=True) + + col = split.column() + row = col.row(align=True) row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') - split.prop(md, "use_collapse_triangulate") + layout_info = col + + col = split.column() + row = col.row() + row.active = has_vgroup + row.prop(md, "vertex_group_factor") + + col.prop(md, "use_collapse_triangulate") + elif decimate_type == 'UNSUBDIV': layout.prop(md, "iterations") + layout_info = layout else: # decimate_type == 'DISSOLVE': layout.prop(md, "angle_limit") layout.prop(md, "use_dissolve_boundaries") layout.label("Delimit:") row = layout.row() row.prop(md, "delimit") + layout_info = layout - layout.label(text=iface_("Face Count: %d") % md.face_count, translate=False) + layout_info.label(text=iface_("Faces: %d") % md.face_count, translate=False) def DISPLACE(self, layout, ob, md): has_texture = (md.texture is not None) @@ -1131,13 +1144,15 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): self.vertex_weight_mask(layout, ob, md) def SKIN(self, layout, ob, md): - layout.operator("object.skin_armature_create", text="Create Armature") + row = layout.row() + row.operator("object.skin_armature_create", text="Create Armature") + row.operator("mesh.customdata_skin_add") layout.separator() - col = layout.column(align=True) - col.prop(md, "branch_smoothing") - col.prop(md, "use_smooth_shade") + row = layout.row(align=True) + row.prop(md, "branch_smoothing") + row.prop(md, "use_smooth_shade") split = layout.split() @@ -1384,7 +1399,32 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel): row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") sub = row.row(align=True) sub.active = has_vgroup - sub.prop(md, "use_invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') + sub.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') + + def CORRECTIVE_SMOOTH(self, layout, ob, md): + is_bind = md.is_bind + + layout.prop(md, "factor", text="Factor") + layout.prop(md, "iterations") + + row = layout.row() + row.prop(md, "smooth_type") + + split = layout.split() + + col = split.column() + col.label(text="Vertex Group:") + row = col.row(align=True) + row.prop_search(md, "vertex_group", ob, "vertex_groups", text="") + row.prop(md, "invert_vertex_group", text="", icon='ARROW_LEFTRIGHT') + + col = split.column() + col.prop(md, "use_only_smooth") + col.prop(md, "use_pin_boundary") + + layout.prop(md, "rest_source") + if md.rest_source == 'BIND': + layout.operator("object.correctivesmooth_bind", text="Unbind" if is_bind else "Bind") if __name__ == "__main__": # only for live edit. diff --git a/release/scripts/startup/bl_ui/properties_freestyle.py b/release/scripts/startup/bl_ui/properties_freestyle.py index 2d91c998007..995136b0d97 100644 --- a/release/scripts/startup/bl_ui/properties_freestyle.py +++ b/release/scripts/startup/bl_ui/properties_freestyle.py @@ -74,8 +74,8 @@ class RenderLayerFreestyleButtonsPanel: rd = context.scene.render with_freestyle = bpy.app.build_options.freestyle - return (scene and with_freestyle and rd.use_freestyle - and rd.layers.active and(scene.render.engine in cls.COMPAT_ENGINES)) + return (scene and with_freestyle and rd.use_freestyle and + rd.layers.active and(scene.render.engine in cls.COMPAT_ENGINES)) class RenderLayerFreestyleEditorButtonsPanel(RenderLayerFreestyleButtonsPanel): @@ -95,7 +95,7 @@ class RENDERLAYER_UL_linesets(UIList): if self.layout_type in {'DEFAULT', 'COMPACT'}: layout.prop(lineset, "name", text="", emboss=False, icon_value=icon) layout.prop(lineset, "show_render", text="", index=index) - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label("", icon_value=icon) @@ -690,10 +690,11 @@ class RENDERLAYER_PT_freestyle_linestyle(RenderLayerFreestyleEditorButtonsPanel, row.prop(linestyle, "texture_spacing", text="Spacing Along Stroke") row = layout.row() - op = row.operator("wm.properties_context_change", - text="Go to Linestyle Textures Properties", - icon='TEXTURE') - op.context = 'TEXTURE' + props = row.operator( + "wm.properties_context_change", + text="Go to Linestyle Textures Properties", + icon='TEXTURE') + props.context = 'TEXTURE' elif linestyle.panel == 'MISC': pass diff --git a/release/scripts/startup/bl_ui/properties_game.py b/release/scripts/startup/bl_ui/properties_game.py index 32a8e734ab6..fa57bf2115f 100644 --- a/release/scripts/startup/bl_ui/properties_game.py +++ b/release/scripts/startup/bl_ui/properties_game.py @@ -89,10 +89,14 @@ class PHYSICS_PT_game_physics(PhysicsButtonsPanel, Panel): split = layout.split() col = split.column() - col.label(text="Velocity:") + col.label(text="Linear velocity:") sub = col.column(align=True) sub.prop(game, "velocity_min", text="Minimum") sub.prop(game, "velocity_max", text="Maximum") + col.label(text="Angular velocity:") + sub = col.column(align=True) + sub.prop(game, "angular_velocity_min", text="Minimum") + sub.prop(game, "angular_velocity_max", text="Maximum") col = split.column() col.label(text="Damping:") @@ -100,7 +104,6 @@ class PHYSICS_PT_game_physics(PhysicsButtonsPanel, Panel): sub.prop(game, "damping", text="Translation", slider=True) sub.prop(game, "rotation_damping", text="Rotation", slider=True) - if physics_type == 'RIGID_BODY': layout.separator() split = layout.split() @@ -111,6 +114,7 @@ class PHYSICS_PT_game_physics(PhysicsButtonsPanel, Panel): col.prop(game, "lock_location_y", text="Y") col.prop(game, "lock_location_z", text="Z") + if physics_type == 'RIGID_BODY': col = split.column() col.label(text="Lock Rotation:") col.prop(game, "lock_rotation_x", text="X") @@ -213,15 +217,17 @@ class PHYSICS_PT_game_collision_bounds(PhysicsButtonsPanel, Panel): layout = self.layout game = context.active_object.game - layout.active = game.use_collision_bounds + split = layout.split() + split.active = game.use_collision_bounds - layout.prop(game, "collision_bounds_type", text="Bounds") + col = split.column() + col.prop(game, "collision_bounds_type", text="Bounds") - row = layout.row() + row = col.row() row.prop(game, "collision_margin", text="Margin", slider=True) sub = row.row() - sub.active = game.physics_type not in {'SOFT_BODY', 'CHARACTER'} + sub.active = game.physics_type not in {'SOFT_BODY', 'CHARACTER'} sub.prop(game, "use_collision_compound", text="Compound") layout.separator() @@ -397,7 +403,6 @@ class RENDER_PT_game_shading(RenderButtonsPanel, Panel): col.prop(gs, "use_glsl_lights", text="Lights") col.prop(gs, "use_glsl_shaders", text="Shaders") col.prop(gs, "use_glsl_shadows", text="Shadows") - col.prop(gs, "use_glsl_color_management", text="Color Management") col = split.column() col.prop(gs, "use_glsl_ramps", text="Ramps") @@ -523,6 +528,26 @@ class SCENE_PT_game_navmesh(SceneButtonsPanel, Panel): row.prop(rd, "sample_max_error") +class SCENE_PT_game_hysteresis(SceneButtonsPanel, Panel): + bl_label = "Level of Detail" + COMPAT_ENGINES = {'BLENDER_GAME'} + + @classmethod + def poll(cls, context): + scene = context.scene + return (scene and scene.render.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + gs = context.scene.game_settings + + row = layout.row() + row.prop(gs, "use_scene_hysteresis", text="Hysteresis") + row = layout.row() + row.active = gs.use_scene_hysteresis + row.prop(gs, "scene_hysteresis_percentage", text="") + + class WorldButtonsPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' @@ -712,6 +737,8 @@ class DATA_PT_shadow_game(DataButtonsPanel, Panel): col = split.column() col.prop(lamp, "shadow_color", text="") + if lamp.type == 'SUN': + col.prop(lamp, "show_shadow_box") col = split.column() col.prop(lamp, "use_shadow_layer", text="This Layer Only") @@ -765,6 +792,7 @@ class OBJECT_PT_levels_of_detail(ObjectButtonsPanel, Panel): def draw(self, context): layout = self.layout ob = context.object + gs = context.scene.game_settings col = layout.column() @@ -782,6 +810,13 @@ class OBJECT_PT_levels_of_detail(ObjectButtonsPanel, Panel): row.prop(level, "use_mesh", text="") row.prop(level, "use_material", text="") + row = box.row() + row.active = gs.use_scene_hysteresis + row.prop(level, "use_object_hysteresis", text="Hysteresis Override") + row = box.row() + row.active = gs.use_scene_hysteresis and level.use_object_hysteresis + row.prop(level, "object_hysteresis_percentage", text="") + row = col.row(align=True) row.operator("object.lod_add", text="Add", icon='ZOOMIN') row.menu("OBJECT_MT_lod_tools", text="", icon='TRIA_DOWN') diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index 5b5a7648d83..e86fc79e343 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -321,7 +321,7 @@ class GPENCIL_UL_layer(UIList): row = layout.row(align=True) row.prop(gpl, "lock", text="", emboss=False) row.prop(gpl, "hide", text="", emboss=False) - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) @@ -414,9 +414,7 @@ class GreasePencilDataPanel: col = split.column(align=True) col.prop(gpl, "show_x_ray") - - # if debug: - # layout.prop(gpl, "show_points") + col.prop(gpl, "show_points", text="Points") layout.separator() diff --git a/release/scripts/startup/bl_ui/properties_mask_common.py b/release/scripts/startup/bl_ui/properties_mask_common.py index cb4a0f5bb85..f4836da50bc 100644 --- a/release/scripts/startup/bl_ui/properties_mask_common.py +++ b/release/scripts/startup/bl_ui/properties_mask_common.py @@ -36,7 +36,7 @@ class MASK_UL_layers(UIList): row.prop(mask, "hide", text="", emboss=False) row.prop(mask, "hide_select", text="", emboss=False) row.prop(mask, "hide_render", text="", emboss=False) - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) @@ -190,7 +190,7 @@ class MASK_PT_point: col.prop_search(parent, "parent", tracking, "objects", icon='OBJECT_DATA', text="Object:") - tracks_list = "tracks" if parent.type == 'POINT_TRACK' else 'plane_tracks' + tracks_list = "tracks" if parent.type == 'POINT_TRACK' else "plane_tracks" if parent.parent in tracking.objects: object = tracking.objects[parent.parent] diff --git a/release/scripts/startup/bl_ui/properties_material.py b/release/scripts/startup/bl_ui/properties_material.py index 20143b81d14..d916007ea41 100644 --- a/release/scripts/startup/bl_ui/properties_material.py +++ b/release/scripts/startup/bl_ui/properties_material.py @@ -87,7 +87,7 @@ class MATERIAL_UL_matslots(UIList): layout.label(text=iface_("Node %s") % manode.name, translate=False, icon_value=layout.icon(manode)) elif ma.use_nodes: layout.label(text="Node <none>") - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) @@ -123,11 +123,16 @@ class MATERIAL_PT_context_material(MaterialButtonsPanel, Panel): ob = context.object slot = context.material_slot space = context.space_data + is_sortable = (len(ob.material_slots) > 1) if ob: + rows = 1 + if is_sortable: + rows = 4 + row = layout.row() - row.template_list("MATERIAL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=1) + row.template_list("MATERIAL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=rows) col = row.column(align=True) col.operator("object.material_slot_add", icon='ZOOMIN', text="") @@ -135,6 +140,12 @@ class MATERIAL_PT_context_material(MaterialButtonsPanel, Panel): col.menu("MATERIAL_MT_specials", icon='DOWNARROW_HLT', text="") + if is_sortable: + col.separator() + + col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP' + col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN' + if ob.mode == 'EDIT': row = layout.row(align=True) row.operator("object.material_slot_assign", text="Assign") @@ -867,12 +878,14 @@ class MATERIAL_PT_transp_game(MaterialButtonsPanel, Panel): base_mat = context.material mat = active_node_mat(base_mat) + layout.active = mat.use_transparency + if simple_material(base_mat): row = layout.row() - row.active = mat.use_transparency row.prop(mat, "transparency_method", expand=True) layout.prop(mat, "alpha") + layout.prop(mat, "specular_alpha", text="Specular") class VolumeButtonsPanel: diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index b0da65063d1..cca142b645c 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -133,7 +133,7 @@ def brush_texpaint_common(panel, context, layout, brush, settings, projpaint=Fal else: row = col.row(align=True) panel.prop_unified_color(row, context, brush, "color", text="") - if brush.image_tool == 'FILL': + if brush.image_tool == 'FILL' and not projpaint: col.prop(brush, "fill_threshold") else: panel.prop_unified_color(row, context, brush, "secondary_color", text="") @@ -216,7 +216,9 @@ def brush_texpaint_common(panel, context, layout, brush, settings, projpaint=Fal col = layout.column(align=True) col.prop(brush, "use_accumulate") - col.prop(brush, "use_alpha") + if projpaint: + col.prop(brush, "use_alpha") + col.prop(brush, "use_gradient") col.separator() diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index 29be9ac2158..e294f5487a6 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -118,7 +118,7 @@ class PARTICLE_UL_particle_systems(bpy.types.UIList): layout.prop(md, "show_render", emboss=False, icon_only=True, icon='RESTRICT_RENDER_OFF' if md.show_render else 'RESTRICT_RENDER_ON') layout.prop(md, "show_viewport", emboss=False, icon_only=True, icon='RESTRICT_VIEW_OFF' if md.show_viewport else 'RESTRICT_VIEW_ON') - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) @@ -1243,7 +1243,7 @@ class PARTICLE_PT_children(ParticleButtonsPanel, Panel): split = layout.split() split.active = part.kink != 'NO' - if part.kink in {'SPIRAL'}: + if part.kink == 'SPIRAL': col = split.column() sub = col.column(align=True) sub.prop(part, "kink_amplitude", text="Radius") diff --git a/release/scripts/startup/bl_ui/properties_physics_common.py b/release/scripts/startup/bl_ui/properties_physics_common.py index bb0f0cfc796..82eecf0fb5a 100644 --- a/release/scripts/startup/bl_ui/properties_physics_common.py +++ b/release/scripts/startup/bl_ui/properties_physics_common.py @@ -191,7 +191,7 @@ def point_cache_ui(self, context, cache, enabled, cachetype): col.operator("ptcache.bake", text="Bake").bake = True sub = col.row() - sub.enabled = (cache.frames_skipped or cache.is_outdated) and enabled + sub.enabled = (cache.is_frame_skip or cache.is_outdated) and enabled sub.operator("ptcache.bake", text="Calculate To Frame").bake = False sub = col.column() diff --git a/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py b/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py index 01dcf837546..269ffa6d371 100644 --- a/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py +++ b/release/scripts/startup/bl_ui/properties_physics_dynamicpaint.py @@ -40,7 +40,7 @@ class PHYSICS_UL_dynapaint_surfaces(UIList): row.prop(surf, "show_preview", text="", emboss=False, icon='RESTRICT_VIEW_OFF' if surf.show_preview else 'RESTRICT_VIEW_ON') row.prop(surf, "is_active", text="") - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' row = layout.row(align=True) row.label(text="", icon_value=icon) diff --git a/release/scripts/startup/bl_ui/properties_physics_smoke.py b/release/scripts/startup/bl_ui/properties_physics_smoke.py index 63268bc6672..85d3c1d7dc4 100644 --- a/release/scripts/startup/bl_ui/properties_physics_smoke.py +++ b/release/scripts/startup/bl_ui/properties_physics_smoke.py @@ -81,12 +81,12 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel): layout.prop(flow, "smoke_flow_type", expand=False) - if flow.smoke_flow_type != "OUTFLOW": + if flow.smoke_flow_type != 'OUTFLOW': split = layout.split() col = split.column() col.label(text="Flow Source:") col.prop(flow, "smoke_flow_source", expand=False, text="") - if flow.smoke_flow_source == "PARTICLES": + if flow.smoke_flow_source == 'PARTICLES': col.label(text="Particle System:") col.prop_search(flow, "particle_system", ob, "particle_systems", text="") col.prop(flow, "use_particle_size", text="Set Size") @@ -103,7 +103,7 @@ class PHYSICS_PT_smoke(PhysicButtonsPanel, Panel): sub = sub.column() sub.active = flow.use_initial_velocity sub.prop(flow, "velocity_factor") - if flow.smoke_flow_source == "MESH": + if flow.smoke_flow_source == 'MESH': sub.prop(flow, "velocity_normal") #sub.prop(flow, "velocity_random") @@ -135,7 +135,7 @@ class PHYSICS_PT_smoke_flow_advanced(PhysicButtonsPanel, Panel): @classmethod def poll(cls, context): md = context.smoke - return md and (md.smoke_type == 'FLOW') and (md.flow_settings.smoke_flow_source == "MESH") + return md and (md.smoke_type == 'FLOW') and (md.flow_settings.smoke_flow_source == 'MESH') def draw(self, context): layout = self.layout @@ -151,9 +151,9 @@ class PHYSICS_PT_smoke_flow_advanced(PhysicButtonsPanel, Panel): sub.prop(flow, "noise_texture", text="") sub.label(text="Mapping:") sub.prop(flow, "texture_map_type", expand=False, text="") - if flow.texture_map_type == "UV": + if flow.texture_map_type == 'UV': sub.prop_search(flow, "uv_layer", ob.data, "uv_textures", text="") - if flow.texture_map_type == "AUTO": + if flow.texture_map_type == 'AUTO': sub.prop(flow, "texture_size") sub.prop(flow, "texture_offset") diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index b596fe48b49..0c68d98a8e1 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -274,7 +274,7 @@ class RENDER_PT_performance(RenderButtonsPanel, Panel): col.prop(rd, "tile_y", text="Y") col.separator() - col.prop(rd, 'preview_start_resolution') + col.prop(rd, "preview_start_resolution") col = split.column() col.label(text="Memory:") @@ -334,28 +334,25 @@ class RENDER_PT_post_processing(RenderButtonsPanel, Panel): class RENDER_PT_stamp(RenderButtonsPanel, Panel): - bl_label = "Stamp" + bl_label = "Metadata" bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER'} - def draw_header(self, context): - rd = context.scene.render - - self.layout.prop(rd, "use_stamp", text="") - def draw(self, context): layout = self.layout rd = context.scene.render - layout.active = rd.use_stamp + layout.prop(rd, "use_stamp", text="Stamp Output") + col = layout.column() + col.active = rd.use_stamp + col.prop(rd, "stamp_font_size", text="Font Size") - layout.prop(rd, "stamp_font_size", text="Font Size") - - row = layout.row() + row = col.row() row.column().prop(rd, "stamp_foreground", slider=True) row.column().prop(rd, "stamp_background", slider=True) + layout.label("Enabled Metadata") split = layout.split() col = split.column() @@ -404,6 +401,8 @@ class RENDER_PT_output(RenderButtonsPanel, Panel): col.prop(rd, "use_render_cache") layout.template_image_settings(image_settings, color_management=False) + if rd.use_multiview: + layout.template_image_views(image_settings) if file_format == 'QUICKTIME': quicktime = rd.quicktime diff --git a/release/scripts/startup/bl_ui/properties_render_layer.py b/release/scripts/startup/bl_ui/properties_render_layer.py index 35032d38933..8a3e33f1936 100644 --- a/release/scripts/startup/bl_ui/properties_render_layer.py +++ b/release/scripts/startup/bl_ui/properties_render_layer.py @@ -40,7 +40,7 @@ class RENDERLAYER_UL_renderlayers(UIList): if self.layout_type in {'DEFAULT', 'COMPACT'}: layout.prop(layer, "name", text="", icon_value=icon, emboss=False) layout.prop(layer, "use", text="", index=index) - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label("", icon_value=icon) @@ -87,7 +87,7 @@ class RENDERLAYER_PT_layer_options(RenderLayerButtonsPanel, Panel): col = split.column() col.prop(scene, "layers", text="Scene") col.label(text="") - col.prop(rl, "light_override", text="Light") + col.prop(rl, "light_override", text="Lights") col.prop(rl, "material_override", text="Material") col = split.column() @@ -126,7 +126,8 @@ class RENDERLAYER_PT_layer_passes(RenderLayerButtonsPanel, Panel): bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'BLENDER_RENDER'} - def draw_pass_type_buttons(self, box, rl, pass_type): + @staticmethod + def draw_pass_type_buttons(box, rl, pass_type): # property names use_pass_type = "use_pass_" + pass_type exclude_pass_type = "exclude_" + pass_type @@ -167,5 +168,63 @@ class RENDERLAYER_PT_layer_passes(RenderLayerButtonsPanel, Panel): self.draw_pass_type_buttons(col, rl, "refraction") +class RENDERLAYER_UL_renderviews(UIList): + def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): + # assert(isinstance(item, bpy.types.SceneRenderView) + view = item + if self.layout_type in {'DEFAULT', 'COMPACT'}: + if view.name in {'left', 'right'}: + layout.label(view.name, icon_value=icon + (not view.use)) + else: + layout.prop(view, "name", text="", index=index, icon_value=icon, emboss=False) + layout.prop(view, "use", text="", index=index) + + elif self.layout_type == 'GRID': + layout.alignment = 'CENTER' + layout.label("", icon_value=icon + (not view.use)) + + +class RENDERLAYER_PT_views(RenderLayerButtonsPanel, Panel): + bl_label = "Views" + COMPAT_ENGINES = {'BLENDER_RENDER'} + + def draw_header(self, context): + rd = context.scene.render + self.layout.prop(rd, "use_multiview", text="") + + def draw(self, context): + layout = self.layout + + scene = context.scene + rd = scene.render + rv = rd.views.active + + layout.active = rd.use_multiview + basic_stereo = rd.views_format == 'STEREO_3D' + + row = layout.row() + row.prop(rd, "views_format", expand=True) + + if basic_stereo: + row = layout.row() + row.template_list("RENDERLAYER_UL_renderviews", "name", rd, "stereo_views", rd.views, "active_index", rows=2) + + row = layout.row() + row.label(text="File Suffix:") + row.prop(rv, "file_suffix", text="") + + else: + row = layout.row() + row.template_list("RENDERLAYER_UL_renderviews", "name", rd, "views", rd.views, "active_index", rows=2) + + col = row.column(align=True) + col.operator("scene.render_view_add", icon='ZOOMIN', text="") + col.operator("scene.render_view_remove", icon='ZOOMOUT', text="") + + row = layout.row() + row.label(text="Camera Suffix:") + row.prop(rv, "camera_suffix", text="") + + if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py index facf54d358a..bbf11abe6d9 100644 --- a/release/scripts/startup/bl_ui/properties_scene.py +++ b/release/scripts/startup/bl_ui/properties_scene.py @@ -35,7 +35,7 @@ class SCENE_UL_keying_set_paths(UIList): if self.layout_type in {'DEFAULT', 'COMPACT'}: # Do not make this one editable in uiList for now... layout.label(text=kspath.data_path, translate=False, icon_value=icon) - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) @@ -86,19 +86,25 @@ class SCENE_PT_unit(SceneButtonsPanel, Panel): class SceneKeyingSetsPanel: - def draw_keyframing_settings(self, context, layout, ks, ksp): - self.draw_keyframing_setting(context, layout, ks, ksp, "Needed", - "use_insertkey_override_needed", "use_insertkey_needed", - userpref_fallback="use_keyframe_insert_needed") - self.draw_keyframing_setting(context, layout, ks, ksp, "Visual", - "use_insertkey_override_visual", "use_insertkey_visual", - userpref_fallback="use_visual_keying") + @staticmethod + def draw_keyframing_settings(context, layout, ks, ksp): + SceneKeyingSetsPanel._draw_keyframing_setting( + context, layout, ks, ksp, "Needed", + "use_insertkey_override_needed", "use_insertkey_needed", + userpref_fallback="use_keyframe_insert_needed") - self.draw_keyframing_setting(context, layout, ks, ksp, "XYZ to RGB", - "use_insertkey_override_xyz_to_rgb", "use_insertkey_xyz_to_rgb") + SceneKeyingSetsPanel._draw_keyframing_setting( + context, layout, ks, ksp, "Visual", + "use_insertkey_override_visual", "use_insertkey_visual", + userpref_fallback="use_visual_keying") - def draw_keyframing_setting(self, context, layout, ks, ksp, label, toggle_prop, prop, userpref_fallback=None): + SceneKeyingSetsPanel._draw_keyframing_setting( + context, layout, ks, ksp, "XYZ to RGB", + "use_insertkey_override_xyz_to_rgb", "use_insertkey_xyz_to_rgb") + + @staticmethod + def _draw_keyframing_setting(context, layout, ks, ksp, label, toggle_prop, prop, userpref_fallback=None): if ksp: item = ksp @@ -392,14 +398,17 @@ class SCENE_PT_simplify(SceneButtonsPanel, Panel): split = layout.split() col = split.column() + col.label(text="Viewport:") col.prop(rd, "simplify_subdivision", text="Subdivision") col.prop(rd, "simplify_child_particles", text="Child Particles") - col.prop(rd, "use_simplify_triangulate") - col = split.column() + col.label(text="Render:") + col.prop(rd, "simplify_subdivision_render", text="Subdivision") + col.prop(rd, "simplify_child_particles_render", text="Child Particles") col.prop(rd, "simplify_shadow_samples", text="Shadow Samples") col.prop(rd, "simplify_ao_sss", text="AO and SSS") + col.prop(rd, "use_simplify_triangulate") class SCENE_PT_custom_props(SceneButtonsPanel, PropertyPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_texture.py b/release/scripts/startup/bl_ui/properties_texture.py index a48e06249fb..78ea4fa6c3f 100644 --- a/release/scripts/startup/bl_ui/properties_texture.py +++ b/release/scripts/startup/bl_ui/properties_texture.py @@ -72,7 +72,7 @@ class TEXTURE_UL_texslots(UIList): layout.label(text="", icon_value=icon) if tex and isinstance(item, bpy.types.MaterialTextureSlot): layout.prop(ma, "use_textures", text="", index=index) - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon_value=icon) @@ -676,8 +676,7 @@ class TEXTURE_PT_musgrave(TextureTypePanel, Panel): col = split.column() if musgrave_type in {'HETERO_TERRAIN', 'RIDGED_MULTIFRACTAL', 'HYBRID_MULTIFRACTAL'}: col.prop(tex, "offset") - if musgrave_type in {'MULTIFRACTAL', 'RIDGED_MULTIFRACTAL', 'HYBRID_MULTIFRACTAL'}: - col.prop(tex, "noise_intensity", text="Intensity") + col.prop(tex, "noise_intensity", text="Intensity") if musgrave_type in {'RIDGED_MULTIFRACTAL', 'HYBRID_MULTIFRACTAL'}: col.prop(tex, "gain") diff --git a/release/scripts/startup/bl_ui/space_clip.py b/release/scripts/startup/bl_ui/space_clip.py index a9fcdea35c7..ed4a78420d8 100644 --- a/release/scripts/startup/bl_ui/space_clip.py +++ b/release/scripts/startup/bl_ui/space_clip.py @@ -37,7 +37,7 @@ class CLIP_UL_tracking_objects(UIList): layout.prop(tobj, "name", text="", emboss=False, icon='CAMERA_DATA' if tobj.is_camera else 'OBJECT_DATA') - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="", icon='CAMERA_DATA' if tobj.is_camera @@ -147,7 +147,7 @@ class CLIP_HT_header(Header): sc = context.space_data - if sc.mode in {'TRACKING'}: + if sc.mode == 'TRACKING': self._draw_tracking(context) else: self._draw_masking(context) @@ -716,7 +716,7 @@ class CLIP_PT_tracking_camera(Panel): if CLIP_PT_clip_view_panel.poll(context): sc = context.space_data - return sc.mode in {'TRACKING'} and sc.clip + return sc.mode == 'TRACKING' and sc.clip return False @@ -756,7 +756,7 @@ class CLIP_PT_tracking_lens(Panel): if CLIP_PT_clip_view_panel.poll(context): sc = context.space_data - return sc.mode in {'TRACKING'} and sc.clip + return sc.mode == 'TRACKING' and sc.clip return False @@ -893,7 +893,7 @@ class CLIP_PT_stabilization(CLIP_PT_reconstruction_panel, Panel): if CLIP_PT_clip_view_panel.poll(context): sc = context.space_data - return sc.mode in {'TRACKING'} and sc.clip + return sc.mode == 'TRACKING' and sc.clip return False @@ -987,7 +987,9 @@ class CLIP_PT_proxy(CLIP_PT_clip_view_panel, Panel): if clip.use_proxy_custom_directory: col.prop(clip.proxy, "directory") - col.operator("clip.rebuild_proxy", text="Build Proxy") + col.operator("clip.rebuild_proxy", + text="Build Proxy / Timecode" if clip.source == 'MOVIE' + else "Build Proxy") if clip.source == 'MOVIE': col2 = col.column() @@ -1147,12 +1149,15 @@ class CLIP_MT_view(Menu): layout.operator("clip.view_selected") layout.operator("clip.view_all") + layout.operator("clip.view_all", text="View Fit").fit_view = True layout.separator() layout.operator("clip.view_zoom_in") layout.operator("clip.view_zoom_out") layout.separator() + layout.prop(sc, "show_metadata") + layout.separator() ratios = ((1, 8), (1, 4), (1, 2), (1, 1), (2, 1), (4, 1), (8, 1)) @@ -1277,8 +1282,8 @@ class CLIP_MT_reconstruction(Menu): layout.operator("clip.set_plane", text="Set Floor").plane = 'FLOOR' layout.operator("clip.set_plane", text="Set Wall").plane = 'WALL' - layout.operator("clip.set_axis", text="Set X Axis").axis = "X" - layout.operator("clip.set_axis", text="Set Y Axis").axis = "Y" + layout.operator("clip.set_axis", text="Set X Axis").axis = 'X' + layout.operator("clip.set_axis", text="Set Y Axis").axis = 'Y' layout.operator("clip.set_scale") diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py index 5358670c2f2..0b7502b585b 100644 --- a/release/scripts/startup/bl_ui/space_dopesheet.py +++ b/release/scripts/startup/bl_ui/space_dopesheet.py @@ -51,6 +51,11 @@ def dopesheet_filter(layout, context, genericFiltersOnly=False): row.prop(dopesheet, "show_only_matching_fcurves", text="") if dopesheet.show_only_matching_fcurves: row.prop(dopesheet, "filter_fcurve_name", text="") + else: + row = layout.row(align=True) + row.prop(dopesheet, "use_filter_text", text="") + if dopesheet.use_filter_text: + row.prop(dopesheet, "filter_text", text="") if not genericFiltersOnly: row = layout.row(align=True) @@ -105,6 +110,7 @@ class DOPESHEET_HT_header(Header): layout = self.layout st = context.space_data + toolsettings = context.tool_settings row = layout.row(align=True) row.template_header() @@ -112,6 +118,18 @@ class DOPESHEET_HT_header(Header): DOPESHEET_MT_editor_menus.draw_collapsible(context, layout) layout.prop(st, "mode", text="") + + if st.mode in {'ACTION', 'SHAPEKEY'}: + row = layout.row(align=True) + row.operator("action.layer_prev", text="", icon='TRIA_DOWN') + row.operator("action.layer_next", text="", icon='TRIA_UP') + + layout.template_ID(st, "action", new="action.new", unlink="action.unlink") + + row = layout.row(align=True) + row.operator("action.push_down", text="Push Down", icon='NLA_PUSHDOWN') + row.operator("action.stash", text="Stash", icon='FREEZE') + layout.prop(st.dopesheet, "show_summary", text="Summary") if st.mode == 'DOPESHEET': @@ -121,12 +139,12 @@ class DOPESHEET_HT_header(Header): # filters which will work here and are useful (especially for character animation) dopesheet_filter(layout, context, genericFiltersOnly=True) - if st.mode in {'ACTION', 'SHAPEKEY'}: - layout.template_ID(st, "action", new="action.new") - - row = layout.row(align=True) - row.operator("action.push_down", text="Push Down", icon='NLA_PUSHDOWN') - row.operator("action.stash", text="Stash", icon='FREEZE') + row = layout.row(align=True) + row.prop(toolsettings, "use_proportional_action", + text="", icon_only=True) + if toolsettings.use_proportional_action: + row.prop(toolsettings, "proportional_edit_falloff", + text="", icon_only=True) # Grease Pencil mode doesn't need snapping, as it's frame-aligned only if st.mode != 'GPENCIL': @@ -190,6 +208,7 @@ class DOPESHEET_MT_view(Menu): layout.separator() layout.operator("action.view_all") layout.operator("action.view_selected") + layout.operator("action.view_frame") layout.separator() layout.operator("screen.area_dupli") @@ -390,5 +409,18 @@ class DOPESHEET_MT_gpencil_frame(Menu): #layout.operator("action.copy") #layout.operator("action.paste") + +class DOPESHEET_MT_delete(Menu): + bl_label = "Delete" + + def draw(self, context): + layout = self.layout + + layout.operator("action.delete") + + layout.separator() + + layout.operator("action.clean") + if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 841e169b080..0d900a41f25 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -44,15 +44,19 @@ class FILEBROWSER_HT_header(Header): row.separator() row = layout.row(align=True) - layout.operator_context = "EXEC_DEFAULT" + layout.operator_context = 'EXEC_DEFAULT' row.operator("file.directory_new", icon='NEWFOLDER') - layout.operator_context = "INVOKE_DEFAULT" + layout.operator_context = 'INVOKE_DEFAULT' params = st.params # can be None when save/reload with a file selector open if params: layout.prop(params, "display_type", expand=True, text="") + + if params.display_type == 'FILE_IMGDISPLAY': + layout.prop(params, "thumbnail_size", text="") + layout.prop(params, "sort_method", expand=True, text="") layout.prop(params, "show_hidden", text="", icon='FILE_HIDDEN') @@ -84,7 +88,7 @@ class FILEBROWSER_HT_header(Header): class FILEBROWSER_UL_dir(bpy.types.UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): direntry = item - space = context.space_data + # space = context.space_data icon = 'NONE' if active_propname == "system_folders_active": icon = 'DISK_DRIVE' @@ -99,12 +103,12 @@ class FILEBROWSER_UL_dir(bpy.types.UIList): row = layout.row(align=True) row.enabled = direntry.is_valid # Non-editable entries would show grayed-out, which is bad in this specific case, so switch to mere label. - if direntry.is_property_readonly('name'): + if direntry.is_property_readonly("name"): row.label(text=direntry.name, icon=icon) else: row.prop(direntry, "name", text="", emboss=False, icon=icon) - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.prop(direntry, "path", text="") @@ -124,6 +128,7 @@ class FILEBROWSER_PT_system_folders(Panel): row.template_list("FILEBROWSER_UL_dir", "system_folders", space, "system_folders", space, "system_folders_active", item_dyntip_propname="path", rows=1, maxrows=10) + class FILEBROWSER_PT_system_bookmarks(Panel): bl_space_type = 'FILE_BROWSER' bl_region_type = 'TOOLS' diff --git a/release/scripts/startup/bl_ui/space_graph.py b/release/scripts/startup/bl_ui/space_graph.py index d3e1a866e43..2f5381e58c8 100644 --- a/release/scripts/startup/bl_ui/space_graph.py +++ b/release/scripts/startup/bl_ui/space_graph.py @@ -29,6 +29,7 @@ class GRAPH_HT_header(Header): from bl_ui.space_dopesheet import dopesheet_filter layout = self.layout + toolsettings = context.tool_settings st = context.space_data @@ -46,6 +47,14 @@ class GRAPH_HT_header(Header): row.active = st.use_normalization row.prop(st, "use_auto_normalization", text="Auto") + row = layout.row(align=True) + + row.prop(toolsettings, "use_proportional_fcurve", + text="", icon_only=True) + if toolsettings.use_proportional_fcurve: + row.prop(toolsettings, "proportional_edit_falloff", + text="", icon_only=True) + layout.prop(st, "auto_snap", text="") layout.prop(st, "pivot_point", icon_only=True) @@ -116,6 +125,7 @@ class GRAPH_MT_view(Menu): layout.separator() layout.operator("graph.view_all") layout.operator("graph.view_selected") + layout.operator("graph.view_frame") layout.separator() layout.operator("screen.area_dupli") @@ -271,5 +281,19 @@ class GRAPH_MT_key_transform(Menu): layout.operator("transform.rotate", text="Rotate") layout.operator("transform.resize", text="Scale") + +class GRAPH_MT_delete(Menu): + bl_label = "Delete" + + def draw(self, context): + layout = self.layout + + layout.operator("graph.delete") + + layout.separator() + + layout.operator("graph.clean") + + if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index a75e0916c09..1345a2c51fb 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -84,6 +84,7 @@ class IMAGE_MT_view(Menu): layout.prop(toolsettings, "show_uv_local_view") layout.prop(uv, "show_other_objects") + layout.prop(uv, "show_metadata") if paint.brush and (context.image_paint_object or sima.mode == 'PAINT'): layout.prop(uv, "show_texpaint") layout.prop(toolsettings, "show_uv_local_view", text="Show same material") @@ -106,6 +107,7 @@ class IMAGE_MT_view(Menu): layout.operator("image.view_selected") layout.operator("image.view_all") + layout.operator("image.view_all", text="View Fit").fit_view = True layout.separator() @@ -127,6 +129,7 @@ class IMAGE_MT_select(Menu): layout.operator("uv.select_border").pinned = False layout.operator("uv.select_border", text="Border Select Pinned").pinned = True + layout.operator("uv.circle_select") layout.separator() @@ -335,7 +338,8 @@ class IMAGE_MT_uvs(Menu): layout.operator("uv.average_islands_scale") layout.operator("uv.minimize_stretch") layout.operator("uv.stitch") - layout.operator("uv.mark_seam") + layout.operator("uv.mark_seam").clear = False + layout.operator("uv.mark_seam", text="Clear Seam").clear = True layout.operator("uv.seams_from_islands") layout.operator("mesh.faces_mirror_uv") @@ -458,6 +462,10 @@ class IMAGE_HT_header(Header): layout.prop_search(mesh.uv_textures, "active", mesh, "uv_textures", text="") if ima: + if ima.is_stereo_3d: + row = layout.row() + row.prop(sima, "show_stereo_3d", text="") + # layers layout.template_image_layers(ima, iuser) @@ -566,7 +574,7 @@ class IMAGE_PT_image_properties(Panel): sima = context.space_data iuser = sima.image_user - layout.template_image(sima, "image", iuser) + layout.template_image(sima, "image", iuser, multiview=True) class IMAGE_PT_game_properties(Panel): @@ -625,6 +633,8 @@ class IMAGE_PT_view_properties(Panel): sima = context.space_data ima = sima.image + + show_render = sima.show_render show_uvedit = sima.show_uvedit show_maskedit = sima.show_maskedit uvedit = sima.uv_editor @@ -669,7 +679,7 @@ class IMAGE_PT_view_properties(Panel): sub.active = uvedit.show_stretch sub.row().prop(uvedit, "draw_stretch_type", expand=True) - if ima: + if show_render and ima: layout.separator() render_slot = ima.render_slots.active layout.prop(render_slot, "name", text="Slot Name") @@ -1051,10 +1061,10 @@ class ImageScopesPanel: if not (sima and sima.image): return False # scopes are not updated in paint modes, hide - if sima.mode in {'PAINT'}: + if sima.mode == 'PAINT': return False ob = context.active_object - if ob and ob.mode in {'TEXTURE_PAINT'}: + if ob and ob.mode in {'TEXTURE_PAINT', 'EDIT'}: return False return True diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py index 927967c8bc6..48a1b28289e 100644 --- a/release/scripts/startup/bl_ui/space_info.py +++ b/release/scripts/startup/bl_ui/space_info.py @@ -282,6 +282,10 @@ class INFO_MT_window(Menu): layout.separator() layout.operator("wm.console_toggle", icon='CONSOLE') + if context.scene.render.use_multiview: + layout.separator() + layout.operator("wm.set_stereo_3d", icon='CAMERA_STEREO') + class INFO_MT_help(Menu): bl_label = "Help" diff --git a/release/scripts/startup/bl_ui/space_logic.py b/release/scripts/startup/bl_ui/space_logic.py index 16182da1018..48b54feba17 100644 --- a/release/scripts/startup/bl_ui/space_logic.py +++ b/release/scripts/startup/bl_ui/space_logic.py @@ -54,11 +54,11 @@ class LOGIC_PT_properties(Panel): row.label("See Text Object") else: props = layout.operator("object.game_property_new", text="Add Text Game Property", icon='ZOOMIN') - props.name = 'Text' + props.name = "Text" props.type = 'STRING' props = layout.operator("object.game_property_new", text="Add Game Property", icon='ZOOMIN') - props.name = '' + props.name = "" for i, prop in enumerate(game.properties): @@ -71,6 +71,13 @@ class LOGIC_PT_properties(Panel): row.prop(prop, "type", text="") row.prop(prop, "value", text="") row.prop(prop, "show_debug", text="", toggle=True, icon='INFO') + sub = row.row(align=True) + props = sub.operator("object.game_property_move", text="", icon='TRIA_UP') + props.index = i + props.direction = 'UP' + props = sub.operator("object.game_property_move", text="", icon='TRIA_DOWN') + props.index = i + props.direction = 'DOWN' row.operator("object.game_property_remove", text="", icon='X', emboss=False).index = i diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py index ae432c44bf6..c083907f017 100644 --- a/release/scripts/startup/bl_ui/space_nla.py +++ b/release/scripts/startup/bl_ui/space_nla.py @@ -168,8 +168,10 @@ class NLA_MT_edit(Menu): layout.separator() # TODO: names of these tools for 'tweak-mode' need changing? if scene.is_nla_tweakmode: + layout.operator("nla.tweakmode_exit", text="Stop Editing Stashed Action").isolate_action = True layout.operator("nla.tweakmode_exit", text="Stop Tweaking Strip Actions") else: + layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions") diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index a5ab6898b13..d0d1376fe05 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -19,6 +19,7 @@ # <pep8 compliant> import bpy from bpy.types import Header, Menu, Panel +from bpy.app.translations import pgettext_iface as iface_ from bl_ui.properties_grease_pencil_common import ( GreasePencilDrawingToolsPanel, GreasePencilStrokeEditPanel, @@ -66,7 +67,7 @@ class NODE_HT_header(Header): if snode_id and not (scene.render.use_shading_nodes == 0 and ob.type == 'LAMP'): layout.prop(snode_id, "use_nodes") - if snode.shader_type == 'WORLD': + if scene.render.use_shading_nodes and snode.shader_type == 'WORLD': row = layout.row() row.enabled = not snode.pin row.template_ID(scene, "world", new="world.new") @@ -208,8 +209,8 @@ class NODE_MT_select(Menu): layout.separator() layout.operator("node.select_grouped").extend = False - layout.operator("node.select_same_type_step").prev = True - layout.operator("node.select_same_type_step").prev = False + layout.operator("node.select_same_type_step", text="Activate Same Type Previous").prev = True + layout.operator("node.select_same_type_step", text="Activate Same Type Next").prev = False layout.separator() @@ -358,7 +359,7 @@ class NODE_PT_active_node_properties(Panel): layout.label("Inputs:") for socket in value_inputs: row = layout.row() - socket.draw(context, row, node, socket.name) + socket.draw(context, row, node, iface_(socket.name, socket.bl_rna.translation_context)) # Node Backdrop options @@ -440,7 +441,7 @@ class NODE_UL_interface_sockets(bpy.types.UIList): if socket.is_output: row.template_node_socket(color) - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.template_node_socket(color) diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 0be1bf0b90a..43224412009 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -19,6 +19,7 @@ # <pep8 compliant> import bpy from bpy.types import Header, Menu, Panel +from rna_prop_ui import PropertyPanel from bl_ui.properties_grease_pencil_common import GreasePencilDataPanel, GreasePencilToolsPanel from bpy.app.translations import pgettext_iface as iface_ @@ -206,6 +207,7 @@ class SEQUENCER_MT_view(Menu): if is_preview: if st.display_mode == 'IMAGE': layout.prop(st, "show_safe_areas") + layout.prop(st, "show_metadata") elif st.display_mode == 'WAVEFORM': layout.prop(st, "show_separate_color") @@ -228,12 +230,12 @@ class SEQUENCER_MT_select(Menu): layout.operator("sequencer.select_active_side", text="Strips to the Left").side = 'LEFT' layout.operator("sequencer.select_active_side", text="Strips to the Right").side = 'RIGHT' - op = layout.operator("sequencer.select", text="All strips to the Left") - op.left_right = 'LEFT' - op.linked_time = True - op = layout.operator("sequencer.select", text="All strips to the Right") - op.left_right = 'RIGHT' - op.linked_time = True + props = layout.operator("sequencer.select", text="All strips to the Left") + props.left_right = 'LEFT' + props.linked_time = True + props = layout.operator("sequencer.select", text="All strips to the Right") + props.left_right = 'RIGHT' + props.linked_time = True layout.separator() layout.operator("sequencer.select_handles", text="Surrounding Handles").side = 'BOTH' @@ -472,7 +474,7 @@ class SEQUENCER_PT_edit(SequencerButtonsPanel, Panel): split.label(text="Type:") split.prop(strip, "type", text="") - if strip.type not in {'SOUND'}: + if strip.type != 'SOUND': split = layout.split(percentage=0.3) split.label(text="Blend:") split.prop(strip, "blend_type", text="") @@ -658,6 +660,7 @@ class SEQUENCER_PT_input(SequencerButtonsPanel, Panel): def draw(self, context): layout = self.layout + scene = context.scene strip = act_strip(context) @@ -671,7 +674,7 @@ class SEQUENCER_PT_input(SequencerButtonsPanel, Panel): # Current element for the filename - elem = strip.strip_elem_from_frame(context.scene.frame_current) + elem = strip.strip_elem_from_frame(scene.frame_current) if elem: split = layout.split(percentage=0.2) split.label(text="File:") @@ -717,6 +720,19 @@ class SEQUENCER_PT_input(SequencerButtonsPanel, Panel): col.prop(strip, "frame_offset_start", text="Start") col.prop(strip, "frame_offset_end", text="End") + if scene.render.use_multiview and seq_type in {'IMAGE', 'MOVIE'}: + layout.prop(strip, "use_multiview") + + col = layout.column() + col.active = strip.use_multiview + + col.label(text="Views Format:") + col.row().prop(strip, "views_format", expand=True) + + box = col.box() + box.active = strip.views_format == 'STEREO_3D' + box.template_image_stereo_3d(strip.stereo_3d_format) + class SEQUENCER_PT_sound(SequencerButtonsPanel, Panel): bl_label = "Sound" @@ -913,44 +929,43 @@ class SEQUENCER_PT_proxy(SequencerButtonsPanel, Panel): def draw(self, context): layout = self.layout + sequencer = context.scene.sequence_editor + strip = act_strip(context) - flow = layout.column_flow() - flow.prop(strip, "use_proxy_custom_directory") - flow.prop(strip, "use_proxy_custom_file") if strip.proxy: - if strip.use_proxy_custom_directory and not strip.use_proxy_custom_file: - flow.prop(strip.proxy, "directory") - if strip.use_proxy_custom_file: - flow.prop(strip.proxy, "filepath") + proxy = strip.proxy - layout.label("Enabled Proxies:") - enabled = "" - row = layout.row() - if (strip.proxy.build_25): - enabled += "25% " - if (strip.proxy.build_50): - enabled += "50% " - if (strip.proxy.build_75): - enabled += "75% " - if (strip.proxy.build_100): - enabled += "100% " - - row.label(enabled) - if (strip.proxy.use_overwrite): - layout.label("Overwrite On") + flow = layout.column_flow() + flow.prop(sequencer, "proxy_storage") + if sequencer.proxy_storage == 'PROJECT': + flow.prop(sequencer, "proxy_dir") else: - layout.label("Overwrite Off") + flow.prop(proxy, "use_proxy_custom_directory") + flow.prop(proxy, "use_proxy_custom_file") + + if proxy.use_proxy_custom_directory and not proxy.use_proxy_custom_file: + flow.prop(proxy, "directory") + if proxy.use_proxy_custom_file: + flow.prop(proxy, "filepath") + + row = layout.row() + row.prop(strip.proxy, "build_25") + row.prop(strip.proxy, "build_50") + row.prop(strip.proxy, "build_75") + row.prop(strip.proxy, "build_100") + + layout.prop(proxy, "use_overwrite") col = layout.column() col.label(text="Build JPEG quality") - col.prop(strip.proxy, "quality") + col.prop(proxy, "quality") if strip.type == 'MOVIE': col = layout.column() col.label(text="Use timecode index:") - col.prop(strip.proxy, "timecode") + col.prop(proxy, "timecode") col = layout.column() col.operator("sequencer.enable_proxies") @@ -1036,6 +1051,7 @@ class SEQUENCER_PT_modifiers(SequencerButtonsPanel, Panel): layout.prop(strip, "use_linear_modifiers") layout.operator_menu_enum("sequencer.strip_modifier_add", "type") + layout.operator("sequencer.strip_modifier_copy") for mod in strip.modifiers: box = layout.box() @@ -1098,5 +1114,11 @@ class SEQUENCER_PT_grease_pencil_tools(GreasePencilToolsPanel, SequencerButtonsP # toolbar, which doesn't exist here... +class SEQUENCER_PT_custom_props(SequencerButtonsPanel, PropertyPanel, Panel): + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'} + _context_path = "scene.sequence_editor.active_strip" + _property_type = (bpy.types.Sequence,) + + if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) diff --git a/release/scripts/startup/bl_ui/space_time.py b/release/scripts/startup/bl_ui/space_time.py index 27fcf94666a..c4c4097315c 100644 --- a/release/scripts/startup/bl_ui/space_time.py +++ b/release/scripts/startup/bl_ui/space_time.py @@ -30,6 +30,7 @@ class TIME_HT_header(Header): scene = context.scene toolsettings = context.tool_settings screen = context.screen + userprefs = context.user_preferences row = layout.row(align=True) row.template_header() @@ -82,7 +83,7 @@ class TIME_HT_header(Header): if toolsettings.use_keyframe_insert_auto: row.prop(toolsettings, "use_keyframe_insert_keyingset", text="", toggle=True) - if screen.is_animation_playing: + if screen.is_animation_playing and not userprefs.edit.use_keyframe_insert_available: subsub = row.row(align=True) subsub.prop(toolsettings, "use_record_with_nla", toggle=True) @@ -253,6 +254,10 @@ def marker_menu_generic(layout): layout.operator("screen.marker_jump", text="Jump to Next Marker").next = True layout.operator("screen.marker_jump", text="Jump to Previous Marker").next = False + layout.separator() + ts = bpy.context.tool_settings + layout.prop(ts, "lock_markers") + if __name__ == "__main__": # only for live edit. bpy.utils.register_module(__name__) diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 3b17e40c294..0272f752bab 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -113,11 +113,11 @@ class USERPREF_MT_splash(Menu): row.label("") row = split.row() row.label("Interaction:") - # XXX, no redraws - # text = bpy.path.display_name(context.window_manager.keyconfigs.active.name) - # if not text: - # text = "Blender (default)" - row.menu("USERPREF_MT_appconfigs", text="Preset") + + text = bpy.path.display_name(context.window_manager.keyconfigs.active.name) + if not text: + text = "Blender (default)" + row.menu("USERPREF_MT_appconfigs", text=text) # only for addons @@ -200,6 +200,11 @@ class USERPREF_PT_interface(Panel): col.label(text="2D Viewports:") col.prop(view, "view2d_grid_spacing_min", text="Minimum Grid Spacing") col.prop(view, "timecode_style") + col.prop(view, "view_frame_type") + if (view.view_frame_type == 'SECONDS'): + col.prop(view, "view_frame_seconds") + elif (view.view_frame_type == 'KEYFRAMES'): + col.prop(view, "view_frame_keyframes") row.separator() row.separator() @@ -1041,7 +1046,8 @@ class USERPREF_PT_input(Panel): userpref = context.user_preferences return (userpref.active_section == 'INPUT') - def draw_input_prefs(self, inputs, layout): + @staticmethod + def draw_input_prefs(inputs, layout): import sys # General settings diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index c65cebfde10..1047d12fa5b 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -86,7 +86,7 @@ class VIEW3D_HT_header(Header): row = layout.row(align=True) row.prop(toolsettings, "use_snap", text="") row.prop(toolsettings, "snap_element", icon_only=True) - if snap_element != 'INCREMENT': + if snap_element not in {'INCREMENT', 'GRID'}: row.prop(toolsettings, "snap_target", text="") if obj: if mode in {'OBJECT', 'POSE'} and snap_element != 'VOLUME': @@ -158,7 +158,7 @@ class VIEW3D_MT_editor_menus(Menu): if edit_object: layout.menu("VIEW3D_MT_edit_%s" % edit_object.type.lower()) elif obj: - if mode_string not in {'PAINT_TEXTURE'}: + if mode_string != 'PAINT_TEXTURE': layout.menu("VIEW3D_MT_%s" % mode_string.lower()) if mode_string in {'SCULPT', 'PAINT_VERTEX', 'PAINT_WEIGHT', 'PAINT_TEXTURE'}: layout.menu("VIEW3D_MT_brush") @@ -208,8 +208,10 @@ class VIEW3D_MT_transform_base(Menu): layout.operator("transform.shear", text="Shear") layout.operator("transform.bend", text="Bend") layout.operator("transform.push_pull", text="Push/Pull") - layout.operator("object.vertex_warp", text="Warp") - layout.operator("object.vertex_random", text="Randomize") + + if context.mode != 'OBJECT': + layout.operator("transform.vertex_warp", text="Warp") + layout.operator("transform.vertex_random", text="Randomize") # Generic transform menu - geometry types @@ -935,7 +937,7 @@ class VIEW3D_MT_angle_control(Menu): settings = UnifiedPaintPanel.paint_settings(context) brush = settings.brush - sculpt = (context.sculpt_object != None) + sculpt = (context.sculpt_object is not None) tex_slot = brush.texture_slot @@ -1043,6 +1045,17 @@ class INFO_MT_armature_add(Menu): layout.operator("object.armature_add", text="Single Bone", icon='BONE_DATA') +class INFO_MT_lamp_add(Menu): + bl_idname = "INFO_MT_lamp_add" + bl_label = "Lamp" + + def draw(self, context): + layout = self.layout + + layout.operator_context = 'INVOKE_REGION_WIN' + layout.operator_enum("object.lamp_add", "type") + + class INFO_MT_add(Menu): bl_label = "Add" @@ -1075,7 +1088,7 @@ class INFO_MT_add(Menu): layout.separator() layout.operator("object.camera_add", text="Camera", icon='OUTLINER_OB_CAMERA') - layout.operator_menu_enum("object.lamp_add", "type", text="Lamp", icon='OUTLINER_OB_LAMP') + layout.menu("INFO_MT_lamp_add", icon='OUTLINER_OB_LAMP') layout.separator() layout.operator_menu_enum("object.effector_add", "type", text="Force Field", icon='OUTLINER_OB_EMPTY') @@ -1654,10 +1667,10 @@ class VIEW3D_MT_paint_weight(Menu): layout.operator("object.vertex_group_clean", text="Clean") layout.operator("object.vertex_group_quantize", text="Quantize") layout.operator("object.vertex_group_levels", text="Levels") - layout.operator("object.vertex_group_blend", text="Blend") - prop = layout.operator("object.data_transfer", text="Transfer Weights") - prop.use_reverse_transfer = True - prop.data_type = 'VGROUP_WEIGHTS' + layout.operator("object.vertex_group_smooth", text="Smooth") + props = layout.operator("object.data_transfer", text="Transfer Weights") + props.use_reverse_transfer = True + props.data_type = 'VGROUP_WEIGHTS' layout.operator("object.vertex_group_limit_total", text="Limit Total") layout.operator("object.vertex_group_fix", text="Fix Deforms") @@ -1933,6 +1946,10 @@ class VIEW3D_MT_pose_propagate(Menu): layout.separator() + layout.operator("pose.propagate", text="On Selected Keyframes").mode = 'SELECTED_KEYS' + + layout.separator() + layout.operator("pose.propagate", text="On Selected Markers").mode = 'SELECTED_MARKERS' @@ -2262,10 +2279,10 @@ class VIEW3D_MT_edit_mesh_vertices(Menu): layout.separator() - op = layout.operator("mesh.mark_sharp", text="Shade Smooth") - op.use_verts = True - op.clear = True - layout.operator("mesh.mark_sharp", text="Shade Sharp").use_verts = True + layout.operator("mesh.mark_sharp", text="Mark Sharp Edges").use_verts = True + props = layout.operator("mesh.mark_sharp", text="Clear Sharp Edges") + props.use_verts = True + props.clear = True layout.separator() @@ -2276,7 +2293,7 @@ class VIEW3D_MT_edit_mesh_vertices(Menu): layout.operator("mesh.blend_from_shape") - layout.operator("object.vertex_group_blend") + layout.operator("object.vertex_group_smooth") layout.operator("mesh.shape_propagate_to_all") layout.separator() @@ -2419,6 +2436,7 @@ class VIEW3D_MT_edit_mesh_clean(Menu): layout.operator("mesh.dissolve_degenerate") layout.operator("mesh.dissolve_limited") + layout.operator("mesh.face_make_planar") layout.operator("mesh.vert_connect_nonplanar") layout.operator("mesh.vert_connect_concave") layout.operator("mesh.fill_holes") @@ -2467,6 +2485,7 @@ def draw_curve(self, context): layout.separator() layout.operator("curve.extrude_move") + layout.operator("curve.spin") layout.operator("curve.duplicate_move") layout.operator("curve.split") layout.operator("curve.separate") @@ -2568,10 +2587,6 @@ class VIEW3D_MT_edit_font(Menu): layout.operator("font.style_toggle", text="Toggle Underline").style = 'UNDERLINE' layout.operator("font.style_toggle", text="Toggle Small Caps").style = 'SMALL_CAPS' - layout.separator() - - layout.operator("font.insert_lorem") - class VIEW3D_MT_edit_text_chars(Menu): bl_label = "Special Characters" @@ -2710,6 +2725,7 @@ class VIEW3D_MT_edit_armature(Menu): layout.separator() layout.operator_context = 'EXEC_AREA' + layout.operator("armature.symmetrize") layout.operator("armature.autoside_names", text="AutoName Left/Right").type = 'XAXIS' layout.operator("armature.autoside_names", text="AutoName Front/Back").type = 'YAXIS' layout.operator("armature.autoside_names", text="AutoName Top/Bottom").type = 'ZAXIS' @@ -2748,6 +2764,7 @@ class VIEW3D_MT_armature_specials(Menu): layout.operator("armature.autoside_names", text="AutoName Front/Back").type = 'YAXIS' layout.operator("armature.autoside_names", text="AutoName Top/Bottom").type = 'ZAXIS' layout.operator("armature.flip_names", text="Flip Names") + layout.operator("armature.symmetrize") class VIEW3D_MT_edit_armature_parent(Menu): @@ -2772,6 +2789,20 @@ class VIEW3D_MT_edit_armature_roll(Menu): layout.operator("transform.transform", text="Set Roll").mode = 'BONE_ROLL' + +class VIEW3D_MT_edit_armature_delete(Menu): + bl_label = "Delete" + + def draw(self, context): + layout = self.layout + + layout.operator("armature.delete", text="Delete Bones") + + layout.separator() + + layout.operator("armature.dissolve", text="Dissolve") + + # ********** Panel ********** @@ -2934,16 +2965,50 @@ class VIEW3D_PT_view3d_display(Panel): row.prop(region, "use_box_clip") -class VIEW3D_PT_view3d_shading(Panel): +class VIEW3D_PT_view3d_stereo(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'UI' - bl_label = "Shading" + bl_label = "Stereoscopy" bl_options = {'DEFAULT_CLOSED'} @classmethod def poll(cls, context): + scene = context.scene + + multiview = scene.render.use_multiview + return context.space_data and multiview + + def draw(self, context): + layout = self.layout view = context.space_data - return (view) + + basic_stereo = context.scene.render.views_format == 'STEREO_3D' + + col = layout.column() + col.row().prop(view, "stereo_3d_camera", expand=True) + + col.label(text="Display:") + row = col.row() + row.active = basic_stereo + row.prop(view, "show_stereo_3d_cameras") + row = col.row() + row.active = basic_stereo + split = row.split() + split.prop(view, "show_stereo_3d_convergence_plane") + split = row.split() + split.prop(view, "stereo_3d_convergence_plane_alpha", text="Alpha") + split.active = view.show_stereo_3d_convergence_plane + row = col.row() + split = row.split() + split.prop(view, "show_stereo_3d_volume") + split = row.split() + split.prop(view, "stereo_3d_volume_alpha", text="Alpha") + + +class VIEW3D_PT_view3d_shading(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_label = "Shading" def draw(self, context): layout = self.layout @@ -2973,12 +3038,12 @@ class VIEW3D_PT_view3d_shading(Panel): if obj and obj.mode == 'EDIT': col.prop(view, "show_occlude_wire") - fx_settings = view.fx_settings + fx_settings = view.fx_settings + if view.viewport_shade not in {'BOUNDBOX', 'WIREFRAME'}: sub = col.column() sub.active = view.region_3d.view_perspective == 'CAMERA' sub.prop(fx_settings, "use_dof") - col.prop(fx_settings, "use_ssao", text="Ambient Occlusion") if fx_settings.use_ssao: ssao_settings = fx_settings.ssao @@ -3171,6 +3236,7 @@ class VIEW3D_PT_background_image(Panel): layout = self.layout view = context.space_data + use_multiview = context.scene.render.use_multiview col = layout.column() col.operator("view3d.background_image_add", text="Add Image") @@ -3208,6 +3274,19 @@ class VIEW3D_PT_background_image(Panel): box.template_image(bg, "image", bg.image_user, compact=True) has_bg = True + if use_multiview and bg.view_axis in {'CAMERA','ALL'}: + box.prop(bg.image, "use_multiview") + + column = box.column() + column.active = bg.image.use_multiview + + column.label(text="Views Format:") + column.row().prop(bg.image, "views_format", expand=True) + + sub = column.box() + sub.active = bg.image.views_format == 'STEREO_3D' + sub.template_image_stereo_3d(bg.image.stereo_3d_format) + elif bg.source == 'MOVIE_CLIP': box.prop(bg, "use_camera_clip") diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 70874460d4d..e381800ae02 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -100,7 +100,7 @@ class VIEW3D_PT_tools_object(View3DPanel, Panel): row.operator("object.shade_smooth", text="Smooth") row.operator("object.shade_flat", text="Flat") - if obj_type in {'MESH'}: + if obj_type == 'MESH': col = layout.column(align=True) col.label(text="Data Transfer:") row = col.row(align=True) @@ -315,7 +315,7 @@ class VIEW3D_PT_tools_meshedit(View3DPanel, Panel): row.operator("transform.vert_slide", text="Vertex") col.operator("mesh.noise") col.operator("mesh.vertices_smooth") - col.operator("object.vertex_random") + col.operator("transform.vertex_random") col = layout.column(align=True) col.label(text="Add:") @@ -327,6 +327,7 @@ class VIEW3D_PT_tools_meshedit(View3DPanel, Panel): col.operator("mesh.edge_face_add") col.operator("mesh.subdivide") col.operator("mesh.loopcut_slide") + col.operator("mesh.offset_edge_loops_slide") col.operator("mesh.duplicate_move", text="Duplicate") row = col.row(align=True) row.operator("mesh.spin") @@ -366,7 +367,7 @@ class VIEW3D_PT_tools_meshweight(View3DPanel, Panel): col.operator("object.vertex_group_clean", text="Clean") col.operator("object.vertex_group_quantize", text="Quantize") col.operator("object.vertex_group_levels", text="Levels") - col.operator("object.vertex_group_blend", text="Blend") + col.operator("object.vertex_group_smooth", text="Smooth") col.operator("object.vertex_group_limit_total", text="Limit Total") col.operator("object.vertex_group_fix", text="Fix Deforms") @@ -407,9 +408,9 @@ class VIEW3D_PT_tools_shading(View3DPanel, Panel): row.operator("mesh.mark_sharp", text="Sharp") col.label(text="Vertices:") row = col.row(align=True) - op = row.operator("mesh.mark_sharp", text="Smooth") - op.use_verts = True - op.clear = True + props = row.operator("mesh.mark_sharp", text="Smooth") + props.use_verts = True + props.clear = True row.operator("mesh.mark_sharp", text="Sharp").use_verts = True col = layout.column(align=True) @@ -523,7 +524,7 @@ class VIEW3D_PT_tools_curveedit(View3DPanel, Panel): col.operator("curve.extrude_move", text="Extrude") col.operator("curve.subdivide") col.operator("curve.smooth") - col.operator("object.vertex_random") + col.operator("transform.vertex_random") class VIEW3D_PT_tools_add_curve_edit(View3DPanel, Panel): @@ -573,11 +574,12 @@ class VIEW3D_PT_tools_surfaceedit(View3DPanel, Panel): col = layout.column(align=True) col.label(text="Modeling:") col.operator("curve.extrude", text="Extrude") + col.operator("curve.spin") col.operator("curve.subdivide") col = layout.column(align=True) col.label(text="Deform:") - col.operator("object.vertex_random") + col.operator("transform.vertex_random") class VIEW3D_PT_tools_add_surface_edit(View3DPanel, Panel): @@ -654,7 +656,7 @@ class VIEW3D_PT_tools_armatureedit(View3DPanel, Panel): col = layout.column(align=True) col.label(text="Deform:") - col.operator("object.vertex_random") + col.operator("transform.vertex_random") class VIEW3D_PT_tools_armatureedit_options(View3DPanel, Panel): @@ -687,7 +689,7 @@ class VIEW3D_PT_tools_mballedit(View3DPanel, Panel): col = layout.column(align=True) col.label(text="Deform:") - col.operator("object.vertex_random") + col.operator("transform.vertex_random") class VIEW3D_PT_tools_add_mball_edit(View3DPanel, Panel): @@ -725,7 +727,7 @@ class VIEW3D_PT_tools_latticeedit(View3DPanel, Panel): col = layout.column(align=True) col.label(text="Deform:") - col.operator("object.vertex_random") + col.operator("transform.vertex_random") # ********** default tools for pose-mode **************** @@ -758,6 +760,10 @@ class VIEW3D_PT_tools_posemode(View3DPanel, Panel): row.operator("pose.copy", text="Copy") row.operator("pose.paste", text="Paste") + row = layout.row(align=True) + row.operator("pose.propagate", text="Propagate") + row.menu("VIEW3D_MT_pose_propagate", icon='TRIA_RIGHT', text="") + col = layout.column(align=True) col.operator("poselib.pose_add", text="Add To Library") @@ -1071,7 +1077,7 @@ class TEXTURE_UL_texpaintslots(UIList): if (not mat.use_nodes) and context.scene.render.engine in {'BLENDER_RENDER', 'BLENDER_GAME'}: mtex_index = mat.texture_paint_slots[index].index layout.prop(mat, "use_textures", text="", index=mtex_index) - elif self.layout_type in {'GRID'}: + elif self.layout_type == 'GRID': layout.alignment = 'CENTER' layout.label(text="") @@ -1467,6 +1473,8 @@ class VIEW3D_PT_sculpt_dyntopo(Panel, View3DPaintPanel): row = sub.row(align=True) row.prop(sculpt, "constant_detail") row.operator("sculpt.sample_detail_size", text="", icon='EYEDROPPER') + elif (sculpt.detail_type_method == 'BRUSH'): + sub.prop(sculpt, "detail_percent") else: sub.prop(sculpt, "detail_size") sub.prop(sculpt, "detail_refine_method", text="") @@ -1601,9 +1609,9 @@ class VIEW3D_PT_tools_weightpaint(View3DPanel, Panel): col = layout.column() col.operator("paint.weight_gradient") - prop = col.operator("object.data_transfer", text="Transfer Weights") - prop.use_reverse_transfer = True - prop.data_type = 'VGROUP_WEIGHTS' + props = col.operator("object.data_transfer", text="Transfer Weights") + props.use_reverse_transfer = True + props.data_type = 'VGROUP_WEIGHTS' class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel): @@ -1694,6 +1702,25 @@ class VIEW3D_PT_tools_imagepaint_external(Panel, View3DPaintPanel): col.operator("paint.project_image", text="Apply Camera Image") +class VIEW3D_PT_tools_imagepaint_symmetry(Panel, View3DPaintPanel): + bl_category = "Tools" + bl_context = "imagepaint" + bl_label = "Symmetry" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + layout = self.layout + + toolsettings = context.tool_settings + ipaint = toolsettings.image_paint + + col = layout.column(align=True) + row = col.row(align=True) + row.prop(ipaint, "use_symmetry_x", text="X", toggle=True) + row.prop(ipaint, "use_symmetry_y", text="Y", toggle=True) + row.prop(ipaint, "use_symmetry_z", text="Z", toggle=True) + + class VIEW3D_PT_tools_projectpaint(View3DPaintPanel, Panel): bl_category = "Options" bl_context = "imagepaint" @@ -1854,7 +1881,7 @@ class VIEW3D_PT_tools_history(View3DPanel, Panel): row = col.row(align=True) row.operator("ed.undo") row.operator("ed.redo") - if obj is None or obj.mode not in {'SCULPT'}: + if obj is None or obj.mode != 'SCULPT': # Sculpt mode does not generate an undo menu it seems... col.operator("ed.undo_history") diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 1590bd49763..bee48de42f5 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -66,6 +66,8 @@ node_tree_group_type = { # generic node group items generator for shader, compositor and texture node groups def node_group_items(context): + if context is None: + return space = context.space_data if not space: return @@ -328,6 +330,7 @@ compositor_node_categories = [ NodeItem("CompositorNodeCombYUVA"), NodeItem("CompositorNodeSepYCCA"), NodeItem("CompositorNodeCombYCCA"), + NodeItem("CompositorNodeSwitchView"), ]), CompositorNodeCategory("CMP_OP_FILTER", "Filter", items=[ NodeItem("CompositorNodeBlur"), diff --git a/release/scripts/templates_py/addon_add_object.py b/release/scripts/templates_py/addon_add_object.py index f0d8bede6d5..8e57d7ef8f8 100644 --- a/release/scripts/templates_py/addon_add_object.py +++ b/release/scripts/templates_py/addon_add_object.py @@ -2,12 +2,13 @@ bl_info = { "name": "New Object", "author": "Your Name Here", "version": (1, 0), - "blender": (2, 65, 0), + "blender": (2, 75, 0), "location": "View3D > Add > Mesh > New Object", "description": "Adds a new Mesh Object", "warning": "", "wiki_url": "", - "category": "Add Mesh"} + "category": "Add Mesh", + } import bpy diff --git a/release/scripts/templates_py/operator_modal_timer.py b/release/scripts/templates_py/operator_modal_timer.py index 4d36860b9e3..df4d10e656b 100644 --- a/release/scripts/templates_py/operator_modal_timer.py +++ b/release/scripts/templates_py/operator_modal_timer.py @@ -10,7 +10,8 @@ class ModalTimerOperator(bpy.types.Operator): def modal(self, context, event): if event.type in {'RIGHTMOUSE', 'ESC'}: - return self.cancel(context) + self.cancel(context) + return {'CANCELLED'} if event.type == 'TIMER': # change theme color, silly! diff --git a/release/scripts/templates_py/ui_previews_custom_icon.py b/release/scripts/templates_py/ui_previews_custom_icon.py new file mode 100644 index 00000000000..53f84b2982f --- /dev/null +++ b/release/scripts/templates_py/ui_previews_custom_icon.py @@ -0,0 +1,76 @@ +# This sample script demonstrates how to place a custom icon on a button or +# menu entry. +# +# IMPORTANT NOTE: if you run this sample, there will be no icon in the button +# You need to replace the image path with a real existing one. +# For distributable scripts, it is recommended to place the icons inside the +# addon folder and access it relative to the py script file for portability +# +# +# Other use cases for UI-previews: +# - provide a fixed list of previews to select from +# - provide a dynamic list of preview (eg. calculated from reading a directory) +# +# For the above use cases, see the template 'ui_previews_dynamic_enum.py" + + +import os +import bpy + + +class PreviewsExamplePanel(bpy.types.Panel): + """Creates a Panel in the Object properties window""" + bl_label = "Previews Example Panel" + bl_idname = "OBJECT_PT_previews" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "object" + + def draw(self, context): + layout = self.layout + pcoll = preview_collections["main"] + + row = layout.row() + my_icon = pcoll["my_icon"] + row.operator("render.render", icon_value=my_icon.icon_id) + + # my_icon.icon_id can be used in any UI function that accepts + # icon_value # try also setting text="" + # to get an icon only operator button + + +# We can store multiple preview collections here, +# however in this example we only store "main" +preview_collections = {} + + +def register(): + + # Note that preview collections returned by bpy.utils.previews + # are regular py objects - you can use them to store custom data. + import bpy.utils.previews + pcoll = bpy.utils.previews.new() + + # path to the folder where the icon is + # the path is calculated relative to this py file inside the addon folder + my_icons_dir = os.path.join(os.path.dirname(__file__), "icons") + + # load a preview thumbnail of a file and store in the previews collection + pcoll.load("my_icon", os.path.join(my_icons_dir, "icon-image.png"), 'IMAGE') + + preview_collections["main"] = pcoll + + bpy.utils.register_class(PreviewsExamplePanel) + + +def unregister(): + + for pcoll in preview_collections.values(): + bpy.utils.previews.remove(pcoll) + preview_collections.clear() + + bpy.utils.unregister_class(PreviewsExamplePanel) + + +if __name__ == "__main__": + register() diff --git a/release/scripts/templates_py/ui_previews_dynamic_enum.py b/release/scripts/templates_py/ui_previews_dynamic_enum.py new file mode 100644 index 00000000000..0cfa232116d --- /dev/null +++ b/release/scripts/templates_py/ui_previews_dynamic_enum.py @@ -0,0 +1,137 @@ +# This sample script demonstrates a dynamic EnumProperty with custom icons. +# The EnumProperty is populated dynamically with thumbnails of the contents of +# a chosen directory in 'enum_previews_from_directory_items'. +# Then, the same enum is displayed with different interfaces. Note that the +# generated icon previews do not have Blender IDs, which means that they can +# not be used with UILayout templates that require IDs, +# such as template_list and template_ID_preview. +# +# Other use cases: +# - make a fixed list of enum_items instead of calculating them in a function +# - generate isolated thumbnails to use as custom icons in buttons +# and menu items +# +# For custom icons, see the template "ui_previews_custom_icon.py". +# +# For distributable scripts, it is recommended to place the icons inside the +# script directory and access it relative to the py script file for portability: +# +# os.path.join(os.path.dirname(__file__), "images") + + +import os +import bpy + + +def enum_previews_from_directory_items(self, context): + """EnumProperty callback""" + enum_items = [] + + if context is None: + return enum_items + + wm = context.window_manager + directory = wm.my_previews_dir + + # Get the preview collection (defined in register func). + pcoll = preview_collections["main"] + + if directory == pcoll.my_previews_dir: + return pcoll.my_previews + + print("Scanning directory: %s" % directory) + + if directory and os.path.exists(directory): + # Scan the directory for png files + image_paths = [] + for fn in os.listdir(directory): + if fn.lower().endswith(".png"): + image_paths.append(fn) + + for i, name in enumerate(image_paths): + # generates a thumbnail preview for a file. + filepath = os.path.join(directory, name) + thumb = pcoll.load(filepath, filepath, 'IMAGE') + enum_items.append((name, name, "", thumb.icon_id, i)) + + pcoll.my_previews = enum_items + pcoll.my_previews_dir = directory + return pcoll.my_previews + + +class PreviewsExamplePanel(bpy.types.Panel): + """Creates a Panel in the Object properties window""" + bl_label = "Previews Example Panel" + bl_idname = "OBJECT_PT_previews" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' + bl_context = "object" + + def draw(self, context): + layout = self.layout + wm = context.window_manager + + row = layout.row() + row.prop(wm, "my_previews_dir") + + row = layout.row() + row.template_icon_view(wm, "my_previews") + + row = layout.row() + row.prop(wm, "my_previews") + + +# We can store multiple preview collections here, +# however in this example we only store "main" +preview_collections = {} + + +def register(): + from bpy.types import WindowManager + from bpy.props import ( + StringProperty, + EnumProperty, + ) + + WindowManager.my_previews_dir = StringProperty( + name="Folder Path", + subtype='DIR_PATH', + default="" + ) + + WindowManager.my_previews = EnumProperty( + items=enum_previews_from_directory_items, + ) + + # Note that preview collections returned by bpy.utils.previews + # are regular Python objects - you can use them to store custom data. + # + # This is especially useful here, since: + # - It avoids us regenerating the whole enum over and over. + # - It can store enum_items' strings + # (remember you have to keep those strings somewhere in py, + # else they get freed and Blender references invalid memory!). + import bpy.utils.previews + pcoll = bpy.utils.previews.new() + pcoll.my_previews_dir = "" + pcoll.my_previews = () + + preview_collections["main"] = pcoll + + bpy.utils.register_class(PreviewsExamplePanel) + + +def unregister(): + from bpy.types import WindowManager + + del WindowManager.my_previews + + for pcoll in preview_collections.values(): + bpy.utils.previews.remove(pcoll) + preview_collections.clear() + + bpy.utils.unregister_class(PreviewsExamplePanel) + + +if __name__ == "__main__": + register() diff --git a/release/windows/installer/00.sconsblender.nsi b/release/windows/installer/00.sconsblender.nsi index f6e5b783faa..2d91b9da7f1 100644 --- a/release/windows/installer/00.sconsblender.nsi +++ b/release/windows/installer/00.sconsblender.nsi @@ -70,6 +70,13 @@ InstallDir $INSTDIR ; $INSTDIR is set inside .onInit BrandingText "Blender Foundation | http://www.blender.org" ComponentText "This will install Blender [VERSION] on your computer." +VIAddVersionKey "ProductName" "Blender" +VIAddVersionKey "CompanyName" "http://www.blender.org" +VIAddVersionKey "FileDescription" "Free open source 3D content creation suite." +VIAddVersionKey "FileVersion" "[SHORTVERSION].0.0" + +VIProductVersion "[SHORTVERSION].0.0" + DirText "Use the field below to specify the folder where you want Blender to be copied to. To specify a different folder, type a new name or use the Browse button to select an existing folder." SilentUnInstall normal |