UI: add description methods for `wm.context_*` operators

Generic context operators now look-up the RNA properties to extract
their description (when it's available).

Add `bl_rna_utils.data_path.property_definition_from_data_path()`
to handle the details of accessing the RNA property definition.
This commit is contained in:
Campbell Barton 2021-03-31 00:44:29 +11:00
parent 88d94d89fa
commit 5a6d5d20de
3 changed files with 170 additions and 0 deletions

View File

@ -0,0 +1,80 @@
# ##### 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",
)
class _TokenizeDataPath:
"""Class to split up tokens of a data-path."""
__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 (
repr(args)[1:-1],
", ".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 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``.
"""
base_tokenize = _TokenizeDataPath(())
data = list(eval("base_tokenize" + data_path))
del base_tokenize
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

View File

@ -95,6 +95,28 @@ def context_path_validate(context, data_path):
return value
def context_path_description(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:
description = rna_prop.description
if description:
return description
return None
def description_from_data_path(base, data_path, *, prefix, value=Ellipsis):
if context_path_validate(base, data_path) is Ellipsis:
return None
description = context_path_description(base, data_path)
if 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
@ -168,6 +190,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 +211,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 +231,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 +269,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 +312,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 +332,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 +352,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 +372,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 +393,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 +436,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 +472,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 +512,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)
@ -498,6 +572,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 +601,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)
@ -550,6 +632,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
@ -587,6 +673,10 @@ 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