diff options
author | Joseph Eagar <joeedh@gmail.com> | 2021-04-04 05:29:12 +0300 |
---|---|---|
committer | Joseph Eagar <joeedh@gmail.com> | 2021-04-04 05:29:12 +0300 |
commit | f5588dfb70332d761b09545139ab6f0a815f04a3 (patch) | |
tree | 57e20ae17d3cdd4aba0a78bf2c113333cc99c1ca /release | |
parent | 4b308888d8a14f94f8bb1709867ce4352083c723 (diff) | |
parent | 8681504f06127cf72ad67c4d056d04013d218ad5 (diff) |
Merge branch 'master' into temp_bmesh_multires
Diffstat (limited to 'release')
-rw-r--r-- | release/scripts/modules/animsys_refactor.py | 8 | ||||
-rw-r--r-- | release/scripts/modules/bl_rna_utils/__init__.py | 0 | ||||
-rw-r--r-- | release/scripts/modules/bl_rna_utils/data_path.py | 91 | ||||
-rw-r--r-- | release/scripts/startup/bl_operators/wm.py | 190 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/properties_data_armature.py | 9 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d.py | 6 | ||||
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d_toolbar.py | 22 |
7 files changed, 272 insertions, 54 deletions
diff --git a/release/scripts/modules/animsys_refactor.py b/release/scripts/modules/animsys_refactor.py index 97e8a8dd144..fd4952e2a53 100644 --- a/release/scripts/modules/animsys_refactor.py +++ b/release/scripts/modules/animsys_refactor.py @@ -32,12 +32,6 @@ import bpy IS_TESTING = False -def drepr(string): - # is there a less crappy way to do this in python?, re.escape also escapes - # single quotes strings so can't use it. - return '"%s"' % repr(string)[1:-1].replace("\"", "\\\"").replace("\\'", "'") - - def classes_recursive(base_type, clss=None): if clss is None: clss = [base_type] @@ -66,7 +60,7 @@ class DataPathBuilder: if type(key) is int: str_value = '[%d]' % key elif type(key) is str: - str_value = '[%s]' % drepr(key) + str_value = '["%s"]' % bpy.utils.escape_identifier(key) else: raise Exception("unsupported accessor %r of type %r (internal error)" % (key, type(key))) return DataPathBuilder(self.data_path + (str_value, )) diff --git a/release/scripts/modules/bl_rna_utils/__init__.py b/release/scripts/modules/bl_rna_utils/__init__.py new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/release/scripts/modules/bl_rna_utils/__init__.py diff --git a/release/scripts/modules/bl_rna_utils/data_path.py b/release/scripts/modules/bl_rna_utils/data_path.py new file mode 100644 index 00000000000..42942b7a295 --- /dev/null +++ b/release/scripts/modules/bl_rna_utils/data_path.py @@ -0,0 +1,91 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +__all__ = ( + "property_definition_from_data_path", + "decompose_data_path", +) + +class _TokenizeDataPath: + """ + Class to split up tokens of a data-path. + + Note that almost all access generates new objects with additional paths, + with the exception of iteration which is the intended way to access the resulting data.""" + __slots__ = ( + "data_path", + ) + + def __init__(self, attrs): + self.data_path = attrs + + def __getattr__(self, attr): + return _TokenizeDataPath(self.data_path + ((".%s" % attr),)) + + def __getitem__(self, key): + return _TokenizeDataPath(self.data_path + (("[%r]" % (key,)),)) + + def __call__(self, *args, **kw): + value_str = ", ".join([ + val for val in ( + ", ".join(repr(value) for value in args), + ", ".join(["%s=%r" % (key, value) for key, value in kw.items()]), + ) if val]) + return _TokenizeDataPath(self.data_path + ('(%s)' % value_str, )) + + def __iter__(self): + return iter(self.data_path) + + +def decompose_data_path(data_path): + """ + Return the components of a data path split into a list. + """ + ns = {"base": _TokenizeDataPath(())} + return list(eval("base" + data_path, ns, ns)) + + +def property_definition_from_data_path(base, data_path): + """ + Return an RNA property definition from an object and a data path. + + In Blender this is often used with ``context`` as the base and a + path that it references, for example ``.space_data.lock_camera``. + """ + data = decompose_data_path(data_path) + while data and (not data[-1].startswith(".")): + data.pop() + + if (not data) or (not data[-1].startswith(".")) or (len(data) < 2): + return None + + data_path_head = "".join(data[:-1]) + data_path_tail = data[-1] + + value_head = eval("base" + data_path_head) + value_head_rna = getattr(value_head, "bl_rna", None) + if value_head_rna is None: + return None + + value_tail = value_head.bl_rna.properties.get(data_path_tail[1:]) + if not value_tail: + return None + + return value_tail diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 2f97942faa4..35826cea860 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -95,6 +95,76 @@ def context_path_validate(context, data_path): return value +def context_path_to_rna_property(context, data_path): + from bl_rna_utils.data_path import property_definition_from_data_path + rna_prop = property_definition_from_data_path(context, "." + data_path) + if rna_prop is not None: + return rna_prop + return None + + +def context_path_decompose(data_path): + # Decompose a data_path into 3 components: + # base_path, prop_attr, prop_item, where: + # `"foo.bar["baz"].fiz().bob.buz[10][2]"`, returns... + # `("foo.bar["baz"].fiz().bob", "buz", "[10][2]")` + # + # This is useful as we often want the base and the property, ignoring any item access. + # Note that item access includes function calls since these aren't properties. + # + # Note that the `.` is removed from the start of the first and second values, + # this is done because `.attr` isn't convenient to use as an argument, + # also the convention is not to include this within the data paths or the operator logic for `bpy.ops.wm.*`. + from bl_rna_utils.data_path import decompose_data_path + path_split = decompose_data_path("." + data_path) + + # Find the last property that isn't a function call. + value_prev = "" + i = len(path_split) + while (i := i - 1) >= 0: + value = path_split[i] + if value.startswith("."): + if not value_prev.startswith("("): + break + value_prev = value + + if i != -1: + base_path = "".join(path_split[:i]) + prop_attr = path_split[i] + prop_item = "".join(path_split[i + 1:]) + + if base_path: + assert(base_path.startswith(".")) + base_path= base_path[1:] + if prop_attr: + assert(prop_attr.startswith(".")) + prop_attr = prop_attr[1:] + else: + # If there are no properties, everything is an item. + # Note that should not happen in practice with values which are added onto `context`, + # include since it's correct to account for this case and not doing so will create a confusing exception. + base_path = "" + prop_attr = "" + prop_item = "".join(path_split) + + return (base_path, prop_attr, prop_item) + + +def description_from_data_path(base, data_path, *, prefix, value=Ellipsis): + if context_path_validate(base, data_path) is Ellipsis: + return None + + if ( + (rna_prop := context_path_to_rna_property(base, data_path)) and + (description := rna_prop.description) + ): + description = "%s: %s" % (prefix, description) + if value != Ellipsis: + description = "%s\n%s: %s" % (description, iface_("Value"), str(value)) + return description + return None + + def operator_value_is_undo(value): if value in {None, Ellipsis}: return False @@ -120,12 +190,9 @@ def operator_value_is_undo(value): def operator_path_is_undo(context, data_path): - # note that if we have data paths that use strings this could fail - # luckily we don't do this! - # - # When we can't find the data owner assume no undo is needed. - data_path_head = data_path.rpartition(".")[0] + data_path_head, _, _ = context_path_decompose(data_path) + # When we can't find the data owner assume no undo is needed. if not data_path_head: return False @@ -168,6 +235,10 @@ class WM_OT_context_set_boolean(Operator): default=True, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value) + execute = execute_context_assign @@ -185,6 +256,10 @@ class WM_OT_context_set_int(Operator): # same as enum ) relative: rna_relative_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix="Assign", value=props.value) + execute = execute_context_assign @@ -201,6 +276,10 @@ class WM_OT_context_scale_float(Operator): default=1.0, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Scale"), value=props.value) + def execute(self, context): data_path = self.data_path if context_path_validate(context, data_path) is Ellipsis: @@ -235,6 +314,10 @@ class WM_OT_context_scale_int(Operator): options={'SKIP_SAVE'}, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Scale"), value=props.value) + def execute(self, context): data_path = self.data_path if context_path_validate(context, data_path) is Ellipsis: @@ -274,6 +357,10 @@ class WM_OT_context_set_float(Operator): # same as enum ) relative: rna_relative_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix="Assign", value=props.value) + execute = execute_context_assign @@ -290,6 +377,10 @@ class WM_OT_context_set_string(Operator): # same as enum maxlen=1024, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value) + execute = execute_context_assign @@ -306,6 +397,10 @@ class WM_OT_context_set_enum(Operator): maxlen=1024, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value) + execute = execute_context_assign @@ -322,6 +417,10 @@ class WM_OT_context_set_value(Operator): maxlen=1024, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value) + def execute(self, context): data_path = self.data_path if context_path_validate(context, data_path) is Ellipsis: @@ -339,6 +438,13 @@ class WM_OT_context_toggle(Operator): data_path: rna_path_prop module: rna_module_prop + @classmethod + def description(cls, context, props): + # Currently unsupported, it might be possible to extract this. + if props.module: + return None + return description_from_data_path(context, props.data_path, prefix=iface_("Toggle")) + def execute(self, context): data_path = self.data_path @@ -375,6 +481,11 @@ class WM_OT_context_toggle_enum(Operator): maxlen=1024, ) + @classmethod + def description(cls, context, props): + value = "(%r, %r)" % (props.value_1, props.value_2) + return description_from_data_path(context, props.data_path, prefix=iface_("Toggle"), value=value) + def execute(self, context): data_path = self.data_path @@ -406,6 +517,10 @@ class WM_OT_context_cycle_int(Operator): reverse: rna_reverse_prop wrap: rna_wrap_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Cycle")) + def execute(self, context): data_path = self.data_path value = context_path_validate(context, data_path) @@ -442,6 +557,10 @@ class WM_OT_context_cycle_enum(Operator): reverse: rna_reverse_prop wrap: rna_wrap_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Cycle")) + def execute(self, context): data_path = self.data_path value = context_path_validate(context, data_path) @@ -450,22 +569,11 @@ class WM_OT_context_cycle_enum(Operator): orig_value = value - # Have to get rna enum values - rna_struct_str, rna_prop_str = data_path.rsplit('.', 1) - i = rna_prop_str.find('[') - - # just in case we get "context.foo.bar[0]" - if i != -1: - rna_prop_str = rna_prop_str[0:i] - - rna_struct = eval("context.%s.rna_type" % rna_struct_str) - - rna_prop = rna_struct.properties[rna_prop_str] - + rna_prop = context_path_to_rna_property(context, data_path) if type(rna_prop) != bpy.types.EnumProperty: raise Exception("expected an enum property") - enums = rna_struct.properties[rna_prop_str].enum_items.keys() + enums = rna_prop.enum_items.keys() orig_index = enums.index(orig_value) # Have the info we need, advance to the next item. @@ -498,6 +606,10 @@ class WM_OT_context_cycle_array(Operator): data_path: rna_path_prop reverse: rna_reverse_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Cycle")) + def execute(self, context): data_path = self.data_path value = context_path_validate(context, data_path) @@ -523,6 +635,10 @@ class WM_OT_context_menu_enum(Operator): data_path: rna_path_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Menu")) + def execute(self, context): data_path = self.data_path value = context_path_validate(context, data_path) @@ -530,15 +646,15 @@ class WM_OT_context_menu_enum(Operator): if value is Ellipsis: return {'PASS_THROUGH'} - base_path, prop_string = data_path.rsplit(".", 1) + base_path, prop_attr, _ = context_path_decompose(data_path) value_base = context_path_validate(context, base_path) - prop = value_base.bl_rna.properties[prop_string] + rna_prop = context_path_to_rna_property(context, data_path) def draw_cb(self, context): layout = self.layout - layout.prop(value_base, prop_string, expand=True) + layout.prop(value_base, prop_attr, expand=True) - context.window_manager.popup_menu(draw_func=draw_cb, title=prop.name, icon=prop.icon) + context.window_manager.popup_menu(draw_func=draw_cb, title=rna_prop.name, icon=rna_prop.icon) return {'FINISHED'} @@ -550,6 +666,10 @@ class WM_OT_context_pie_enum(Operator): data_path: rna_path_prop + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Pie Menu")) + def invoke(self, context, event): wm = context.window_manager data_path = self.data_path @@ -558,15 +678,15 @@ class WM_OT_context_pie_enum(Operator): if value is Ellipsis: return {'PASS_THROUGH'} - base_path, prop_string = data_path.rsplit(".", 1) + base_path, prop_attr, _ = context_path_decompose(data_path) value_base = context_path_validate(context, base_path) - prop = value_base.bl_rna.properties[prop_string] + rna_prop = context_path_to_rna_property(context, data_path) def draw_cb(self, context): layout = self.layout - layout.prop(value_base, prop_string, expand=True) + layout.prop(value_base, prop_attr, expand=True) - wm.popup_menu_pie(draw_func=draw_cb, title=prop.name, icon=prop.icon, event=event) + wm.popup_menu_pie(draw_func=draw_cb, title=rna_prop.name, icon=rna_prop.icon, event=event) return {'FINISHED'} @@ -587,11 +707,15 @@ class WM_OT_operator_pie_enum(Operator): maxlen=1024, ) + @classmethod + def description(cls, context, props): + return description_from_data_path(context, props.data_path, prefix=iface_("Pie Menu")) + def invoke(self, context, event): wm = context.window_manager data_path = self.data_path - prop_string = self.prop_string + prop_attr = self.prop_string # same as eval("bpy.ops." + data_path) op_mod_str, ob_id_str = data_path.split(".", 1) @@ -607,7 +731,7 @@ class WM_OT_operator_pie_enum(Operator): def draw_cb(self, context): layout = self.layout pie = layout.menu_pie() - pie.operator_enum(data_path, prop_string) + pie.operator_enum(data_path, prop_attr) wm.popup_menu_pie(draw_func=draw_cb, title=op_rna.name, event=event) @@ -631,17 +755,17 @@ class WM_OT_context_set_id(Operator): value = self.value data_path = self.data_path - # match the pointer type from the target property to bpy.data.* + # Match the pointer type from the target property to `bpy.data.*` # so we lookup the correct list. - data_path_base, data_path_prop = data_path.rsplit(".", 1) - data_prop_rna = eval("context.%s" % data_path_base).rna_type.properties[data_path_prop] - data_prop_rna_type = data_prop_rna.fixed_type + + rna_prop = context_path_to_rna_property(context, data_path) + rna_prop_fixed_type = rna_prop.fixed_type id_iter = None for prop in bpy.data.rna_type.properties: if prop.rna_type.identifier == "CollectionProperty": - if prop.fixed_type == data_prop_rna_type: + if prop.fixed_type == rna_prop_fixed_type: id_iter = prop.identifier break diff --git a/release/scripts/startup/bl_ui/properties_data_armature.py b/release/scripts/startup/bl_ui/properties_data_armature.py index 4cdcab45926..87572fcd438 100644 --- a/release/scripts/startup/bl_ui/properties_data_armature.py +++ b/release/scripts/startup/bl_ui/properties_data_armature.py @@ -86,12 +86,19 @@ class DATA_PT_display(ArmatureButtonsPanel, Panel): col = layout.column(heading="Show") col.prop(arm, "show_names", text="Names") - col.prop(arm, "show_axes", text="Axes") col.prop(arm, "show_bone_custom_shapes", text="Shapes") col.prop(arm, "show_group_colors", text="Group Colors") + if ob: col.prop(ob, "show_in_front", text="In Front") + col = layout.column(align=False, heading="Axes") + row = col.row(align=True) + row.prop(arm, "show_axes", text="") + sub = row.row(align=True) + sub.active = arm.show_axes + sub.prop(arm, "axes_position", text="Position") + class DATA_MT_bone_group_context_menu(Menu): bl_label = "Bone Group Specials" diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index e68f006ccc1..ab20bce1756 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -161,9 +161,9 @@ class VIEW3D_HT_tool_header(Header): elif mode_string in {'EDIT_MESH', 'PAINT_WEIGHT', 'SCULPT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}: # Mesh Modes, Use Mesh Symmetry row, sub = row_for_mirror() - sub.prop(context.object.data, "use_mirror_x", text="X", toggle=True) - sub.prop(context.object.data, "use_mirror_y", text="Y", toggle=True) - sub.prop(context.object.data, "use_mirror_z", text="Z", toggle=True) + sub.prop(context.object, "use_mesh_mirror_x", text="X", toggle=True) + sub.prop(context.object, "use_mesh_mirror_y", text="Y", toggle=True) + sub.prop(context.object, "use_mesh_mirror_z", text="Z", toggle=True) if mode_string == 'EDIT_MESH': tool_settings = context.tool_settings layout.prop(tool_settings, "use_mesh_automerge", text="") diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index fb4553bd820..f857f9fef95 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -123,13 +123,15 @@ class View3DPanel: # **************** standard tool clusters ****************** # Used by vertex & weight paint -def draw_vpaint_symmetry(layout, vpaint, mesh): +def draw_vpaint_symmetry(layout, vpaint, obj): col = layout.column() row = col.row(heading="Mirror", align=True) - row.prop(mesh, "use_mirror_x", text="X", toggle=True) - row.prop(mesh, "use_mirror_y", text="Y", toggle=True) - row.prop(mesh, "use_mirror_z", text="Z", toggle=True) + row.prop(obj, "use_mesh_mirror_x", text="X", toggle=True) + row.prop(obj, "use_mesh_mirror_y", text="Y", toggle=True) + row.prop(obj, "use_mesh_mirror_z", text="Z", toggle=True) + col = layout.column() + col.active = not obj.data.use_mirror_vertex_groups col.prop(vpaint, "radial_symmetry", text="Radial") @@ -980,12 +982,12 @@ class VIEW3D_PT_tools_weightpaint_symmetry(Panel, View3DPaintPanel): wpaint = tool_settings.weight_paint mesh = context.object.data - draw_vpaint_symmetry(layout, wpaint, mesh) + layout.prop(mesh, 'use_mirror_vertex_groups') - col = layout.column(align=True) - col.prop(mesh, 'use_mirror_vertex_group_x', text="Vertex Group X") - row = col.row() - row.active = mesh.use_mirror_vertex_group_x + draw_vpaint_symmetry(layout, wpaint, context.object) + + row = layout.row() + row.active = mesh.use_mirror_vertex_groups row.prop(mesh, "use_mirror_topology") @@ -1060,7 +1062,7 @@ class VIEW3D_PT_tools_vertexpaint_symmetry(Panel, View3DPaintPanel): tool_settings = context.tool_settings vpaint = tool_settings.vertex_paint - draw_vpaint_symmetry(layout, vpaint, context.object.data) + draw_vpaint_symmetry(layout, vpaint, context.object) class VIEW3D_PT_tools_vertexpaint_symmetry_for_topbar(Panel): |