2009-11-04 20:50:09 +00:00
|
|
|
# ##### 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.
|
2009-11-21 23:55:14 +00:00
|
|
|
#
|
2009-11-04 20:50:09 +00:00
|
|
|
# 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.
|
2009-11-21 23:55:14 +00:00
|
|
|
#
|
2009-11-04 20:50:09 +00:00
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
|
# along with this program; if not, write to the Free Software Foundation,
|
2010-02-12 13:34:04 +00:00
|
|
|
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
2009-11-04 20:50:09 +00:00
|
|
|
#
|
|
|
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
|
|
|
|
2010-06-09 19:12:03 +00:00
|
|
|
# <pep8 compliant>
|
2021-02-21 21:21:18 +11:00
|
|
|
from __future__ import annotations
|
2009-11-04 20:50:09 +00:00
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
import bpy
|
2018-06-07 16:01:57 +02:00
|
|
|
from bpy.types import (
|
2018-09-18 17:44:14 +02:00
|
|
|
Menu,
|
2018-06-07 16:01:57 +02:00
|
|
|
Operator,
|
|
|
|
|
)
|
2015-01-27 17:46:07 +11:00
|
|
|
from bpy.props import (
|
2017-03-25 11:07:05 +11:00
|
|
|
BoolProperty,
|
2019-09-01 03:16:58 +10:00
|
|
|
CollectionProperty,
|
2017-03-25 11:07:05 +11:00
|
|
|
EnumProperty,
|
2017-11-29 18:00:41 +11:00
|
|
|
FloatProperty,
|
|
|
|
|
IntProperty,
|
|
|
|
|
StringProperty,
|
2017-03-25 11:07:05 +11:00
|
|
|
)
|
2020-12-06 10:29:26 -08:00
|
|
|
from bpy.app.translations import pgettext_iface as iface_
|
2011-06-27 07:51:52 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_path_prop = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Context Attributes",
|
|
|
|
|
description="RNA context string",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_reverse_prop = BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Reverse",
|
|
|
|
|
description="Cycle backwards",
|
|
|
|
|
default=False,
|
2021-01-26 08:04:25 +11:00
|
|
|
options={'SKIP_SAVE'},
|
2018-06-26 19:41:37 +02:00
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2015-12-18 16:17:13 +11:00
|
|
|
rna_wrap_prop = BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Wrap",
|
|
|
|
|
description="Wrap back to the first/last values",
|
|
|
|
|
default=False,
|
2021-01-26 08:04:25 +11:00
|
|
|
options={'SKIP_SAVE'},
|
2018-06-26 19:41:37 +02:00
|
|
|
)
|
2015-12-18 16:17:13 +11:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_relative_prop = BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Relative",
|
|
|
|
|
description="Apply relative to the current value (delta)",
|
|
|
|
|
default=False,
|
2021-01-26 08:04:25 +11:00
|
|
|
options={'SKIP_SAVE'},
|
2018-06-26 19:41:37 +02:00
|
|
|
)
|
2010-01-31 14:46:28 +00:00
|
|
|
|
2018-05-30 22:15:10 +02:00
|
|
|
rna_space_type_prop = EnumProperty(
|
2018-06-26 22:56:39 +02:00
|
|
|
name="Type",
|
|
|
|
|
items=tuple(
|
|
|
|
|
(e.identifier, e.name, "", e. value)
|
|
|
|
|
for e in bpy.types.Space.bl_rna.properties["type"].enum_items
|
|
|
|
|
),
|
|
|
|
|
default='EMPTY',
|
|
|
|
|
)
|
2018-05-30 22:15:10 +02:00
|
|
|
|
2019-06-12 12:17:50 +10:00
|
|
|
# Note, this can be used for more operators,
|
|
|
|
|
# currently not used for all "WM_OT_context_" operators.
|
|
|
|
|
rna_module_prop = StringProperty(
|
|
|
|
|
name="Module",
|
|
|
|
|
description="Optionally override the context with a module",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
)
|
|
|
|
|
|
2009-12-13 13:59:16 +00:00
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
def context_path_validate(context, data_path):
|
2009-11-04 20:21:08 +00:00
|
|
|
try:
|
2011-02-19 04:28:07 +00:00
|
|
|
value = eval("context.%s" % data_path) if data_path else Ellipsis
|
2018-02-01 13:58:44 +11:00
|
|
|
except AttributeError as ex:
|
|
|
|
|
if str(ex).startswith("'NoneType'"):
|
2009-11-04 20:21:08 +00:00
|
|
|
# One of the items in the rna path is None, just ignore this
|
2009-12-14 01:58:08 +00:00
|
|
|
value = Ellipsis
|
2009-11-04 20:21:08 +00:00
|
|
|
else:
|
2020-04-22 08:59:37 +02:00
|
|
|
# Print invalid path, but don't show error to the users and fully
|
|
|
|
|
# break the UI if the operator is bound to an event like left click.
|
|
|
|
|
print("context_path_validate error: context.%s not found (invalid keymap entry?)" % data_path)
|
|
|
|
|
value = Ellipsis
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
|
|
2021-03-31 15:07:44 +11:00
|
|
|
def context_path_to_rna_property(context, data_path):
|
2021-03-31 00:44:29 +11:00
|
|
|
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:
|
2021-03-31 15:07:44 +11:00
|
|
|
return rna_prop
|
2021-03-31 00:44:29 +11:00
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
2021-03-31 15:07:44 +11:00
|
|
|
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("."))
|
2021-07-06 12:05:27 +10:00
|
|
|
base_path = base_path[1:]
|
2021-03-31 15:07:44 +11:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
def description_from_data_path(base, data_path, *, prefix, value=Ellipsis):
|
|
|
|
|
if context_path_validate(base, data_path) is Ellipsis:
|
|
|
|
|
return None
|
2021-03-31 15:07:44 +11:00
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
(rna_prop := context_path_to_rna_property(base, data_path)) and
|
|
|
|
|
(description := rna_prop.description)
|
|
|
|
|
):
|
2021-03-31 00:44:29 +11:00
|
|
|
description = "%s: %s" % (prefix, description)
|
|
|
|
|
if value != Ellipsis:
|
|
|
|
|
description = "%s\n%s: %s" % (description, iface_("Value"), str(value))
|
|
|
|
|
return description
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
def operator_value_is_undo(value):
|
|
|
|
|
if value in {None, Ellipsis}:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# typical properties or objects
|
|
|
|
|
id_data = getattr(value, "id_data", Ellipsis)
|
|
|
|
|
|
|
|
|
|
if id_data is None:
|
|
|
|
|
return False
|
|
|
|
|
elif id_data is Ellipsis:
|
|
|
|
|
# handle mathutils types
|
|
|
|
|
id_data = getattr(getattr(value, "owner", None), "id_data", None)
|
|
|
|
|
|
|
|
|
|
if id_data is None:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
# return True if its a non window ID type
|
|
|
|
|
return (isinstance(id_data, bpy.types.ID) and
|
|
|
|
|
(not isinstance(id_data, (bpy.types.WindowManager,
|
|
|
|
|
bpy.types.Screen,
|
|
|
|
|
bpy.types.Brush,
|
|
|
|
|
))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def operator_path_is_undo(context, data_path):
|
2021-03-31 15:07:44 +11:00
|
|
|
data_path_head, _, _ = context_path_decompose(data_path)
|
2011-08-18 16:01:11 +00:00
|
|
|
|
2021-03-31 15:07:44 +11:00
|
|
|
# When we can't find the data owner assume no undo is needed.
|
2011-08-18 16:01:11 +00:00
|
|
|
if not data_path_head:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
value = context_path_validate(context, data_path_head)
|
|
|
|
|
|
|
|
|
|
return operator_value_is_undo(value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def operator_path_undo_return(context, data_path):
|
|
|
|
|
return {'FINISHED'} if operator_path_is_undo(context, data_path) else {'CANCELLED'}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def operator_value_undo_return(value):
|
|
|
|
|
return {'FINISHED'} if operator_value_is_undo(value) else {'CANCELLED'}
|
|
|
|
|
|
|
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
def execute_context_assign(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
|
if context_path_validate(context, data_path) is Ellipsis:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2010-01-04 08:24:24 +00:00
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
if getattr(self, "relative", False):
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s += self.value" % data_path)
|
2010-01-04 08:24:24 +00:00
|
|
|
else:
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = self.value" % data_path)
|
2010-01-04 08:24:24 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_boolean(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_set_boolean"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set Boolean"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
value: BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
|
description="Assignment value",
|
|
|
|
|
default=True,
|
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
|
|
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
execute = execute_context_assign
|
|
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_int(Operator): # same as enum
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_set_int"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
value: IntProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
|
description="Assign value",
|
|
|
|
|
default=0,
|
|
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
relative: rna_relative_prop
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix="Assign", value=props.value)
|
|
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
execute = execute_context_assign
|
|
|
|
|
|
|
|
|
|
|
2014-02-12 15:31:23 +11:00
|
|
|
class WM_OT_context_scale_float(Operator):
|
|
|
|
|
"""Scale a float context value"""
|
|
|
|
|
bl_idname = "wm.context_scale_float"
|
|
|
|
|
bl_label = "Context Scale Float"
|
|
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
value: FloatProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
|
description="Assign value",
|
|
|
|
|
default=1.0,
|
|
|
|
|
)
|
2014-02-12 15:31:23 +11:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix=iface_("Scale"), value=props.value)
|
|
|
|
|
|
2014-02-12 15:31:23 +11:00
|
|
|
def execute(self, context):
|
|
|
|
|
data_path = self.data_path
|
|
|
|
|
if context_path_validate(context, data_path) is Ellipsis:
|
|
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
|
|
|
|
|
|
value = self.value
|
|
|
|
|
|
|
|
|
|
if value == 1.0: # nothing to do
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
|
|
exec("context.%s *= value" % data_path)
|
|
|
|
|
|
|
|
|
|
return operator_path_undo_return(context, data_path)
|
|
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_scale_int(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Scale an int context value"""
|
2010-03-08 23:34:38 +00:00
|
|
|
bl_idname = "wm.context_scale_int"
|
2014-02-12 15:31:23 +11:00
|
|
|
bl_label = "Context Scale Int"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2010-03-08 23:34:38 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
value: FloatProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
|
description="Assign value",
|
|
|
|
|
default=1.0,
|
|
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
always_step: BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Always Step",
|
|
|
|
|
description="Always adjust the value by a minimum of 1 when 'value' is not 1.0",
|
|
|
|
|
default=True,
|
2021-01-26 08:04:25 +11:00
|
|
|
options={'SKIP_SAVE'},
|
2018-06-26 19:41:37 +02:00
|
|
|
)
|
2010-03-08 23:34:38 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix=iface_("Scale"), value=props.value)
|
|
|
|
|
|
2010-03-08 23:34:38 +00:00
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
|
if context_path_validate(context, data_path) is Ellipsis:
|
2010-03-08 23:34:38 +00:00
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
value = self.value
|
2010-03-08 23:34:38 +00:00
|
|
|
|
2010-09-07 15:17:42 +00:00
|
|
|
if value == 1.0: # nothing to do
|
2010-03-08 23:34:38 +00:00
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
if getattr(self, "always_step", False):
|
2010-03-08 23:34:38 +00:00
|
|
|
if value > 1.0:
|
|
|
|
|
add = "1"
|
|
|
|
|
func = "max"
|
|
|
|
|
else:
|
|
|
|
|
add = "-1"
|
|
|
|
|
func = "min"
|
2011-08-18 12:20:10 +00:00
|
|
|
exec("context.%s = %s(round(context.%s * value), context.%s + %s)" %
|
|
|
|
|
(data_path, func, data_path, data_path, add))
|
2010-03-08 23:34:38 +00:00
|
|
|
else:
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s *= value" % data_path)
|
2010-03-08 23:34:38 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2010-03-08 23:34:38 +00:00
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_float(Operator): # same as enum
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context value"""
|
2009-11-17 16:04:17 +00:00
|
|
|
bl_idname = "wm.context_set_float"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set Float"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
value: FloatProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
|
description="Assignment value",
|
|
|
|
|
default=0.0,
|
|
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
relative: rna_relative_prop
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix="Assign", value=props.value)
|
|
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
execute = execute_context_assign
|
|
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_string(Operator): # same as enum
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_set_string"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set String"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
value: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
|
description="Assign value",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
|
|
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
execute = execute_context_assign
|
|
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_enum(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_set_enum"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set Enum"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
value: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
|
description="Assignment value (as a string)",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
|
|
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
execute = execute_context_assign
|
|
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_value(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context value"""
|
2009-12-22 16:11:11 +00:00
|
|
|
bl_idname = "wm.context_set_value"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Set Value"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-12-22 16:11:11 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
value: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
|
description="Assignment value (as a string)",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
)
|
2009-12-22 16:11:11 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix=iface_("Assign"), value=props.value)
|
|
|
|
|
|
2009-12-22 16:11:11 +00:00
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
|
if context_path_validate(context, data_path) is Ellipsis:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = %s" % (data_path, self.value))
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2009-12-22 16:11:11 +00:00
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_toggle(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Toggle a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_toggle"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Toggle"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-12-22 16:11:11 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
2019-06-12 12:17:50 +10:00
|
|
|
module: rna_module_prop
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@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"))
|
|
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2019-06-12 12:17:50 +10:00
|
|
|
module = self.module
|
|
|
|
|
if not module:
|
|
|
|
|
base = context
|
|
|
|
|
else:
|
|
|
|
|
from importlib import import_module
|
|
|
|
|
base = import_module(self.module)
|
|
|
|
|
|
|
|
|
|
if context_path_validate(base, data_path) is Ellipsis:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2019-06-12 12:17:50 +10:00
|
|
|
exec("base.%s = not (base.%s)" % (data_path, data_path))
|
2009-12-13 13:59:16 +00:00
|
|
|
|
2019-06-12 12:17:50 +10:00
|
|
|
return operator_path_undo_return(base, data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_toggle_enum(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Toggle a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_toggle_enum"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Toggle Values"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
value_1: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
|
description="Toggle enum",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
value_2: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
|
description="Toggle enum",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@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)
|
|
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
if context_path_validate(context, data_path) is Ellipsis:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2014-01-11 00:59:22 +01:00
|
|
|
# failing silently is not ideal, but we don't want errors for shortcut
|
|
|
|
|
# keys that some values that are only available in a particular context
|
|
|
|
|
try:
|
|
|
|
|
exec("context.%s = ('%s', '%s')[context.%s != '%s']" %
|
|
|
|
|
(data_path, self.value_1,
|
|
|
|
|
self.value_2, data_path,
|
|
|
|
|
self.value_2,
|
|
|
|
|
))
|
|
|
|
|
except:
|
|
|
|
|
return {'PASS_THROUGH'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_cycle_int(Operator):
|
2012-07-07 13:51:07 +00:00
|
|
|
"""Set a context value (useful for cycling active material, """ \
|
2018-06-26 19:41:37 +02:00
|
|
|
"""vertex keys, groups, etc.)"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_cycle_int"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Int Cycle"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-12-22 16:11:11 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
reverse: rna_reverse_prop
|
|
|
|
|
wrap: rna_wrap_prop
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix=iface_("Cycle"))
|
|
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
def execute(self, context):
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path = self.data_path
|
2010-06-14 03:52:10 +00:00
|
|
|
value = context_path_validate(context, data_path)
|
2009-12-14 01:58:08 +00:00
|
|
|
if value is Ellipsis:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
if self.reverse:
|
2010-01-03 02:24:53 +00:00
|
|
|
value -= 1
|
2009-11-04 20:21:08 +00:00
|
|
|
else:
|
2010-01-03 02:24:53 +00:00
|
|
|
value += 1
|
|
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = value" % data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2015-12-18 16:17:13 +11:00
|
|
|
if self.wrap:
|
|
|
|
|
if value != eval("context.%s" % data_path):
|
|
|
|
|
# relies on rna clamping integers out of the range
|
|
|
|
|
if self.reverse:
|
|
|
|
|
value = (1 << 31) - 1
|
|
|
|
|
else:
|
|
|
|
|
value = -1 << 31
|
2010-01-03 02:24:53 +00:00
|
|
|
|
2015-12-18 16:17:13 +11:00
|
|
|
exec("context.%s = value" % data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_cycle_enum(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Toggle a context value"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.context_cycle_enum"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Enum Cycle"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
reverse: rna_reverse_prop
|
|
|
|
|
wrap: rna_wrap_prop
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix=iface_("Cycle"))
|
|
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
|
value = context_path_validate(context, data_path)
|
2009-12-14 01:58:08 +00:00
|
|
|
if value is Ellipsis:
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'PASS_THROUGH'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
orig_value = value
|
|
|
|
|
|
2021-03-31 15:07:44 +11:00
|
|
|
rna_prop = context_path_to_rna_property(context, data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
if type(rna_prop) != bpy.types.EnumProperty:
|
|
|
|
|
raise Exception("expected an enum property")
|
|
|
|
|
|
2021-03-31 15:07:44 +11:00
|
|
|
enums = rna_prop.enum_items.keys()
|
2009-11-04 20:21:08 +00:00
|
|
|
orig_index = enums.index(orig_value)
|
|
|
|
|
|
2015-12-18 16:17:13 +11:00
|
|
|
# Have the info we need, advance to the next item.
|
|
|
|
|
#
|
|
|
|
|
# When wrap's disabled we may set the value to its self,
|
|
|
|
|
# this is done to ensure update callbacks run.
|
2010-09-09 18:03:57 +00:00
|
|
|
if self.reverse:
|
2009-11-04 20:21:08 +00:00
|
|
|
if orig_index == 0:
|
2015-12-18 16:17:13 +11:00
|
|
|
advance_enum = enums[-1] if self.wrap else enums[0]
|
2009-11-04 20:21:08 +00:00
|
|
|
else:
|
2010-09-07 15:17:42 +00:00
|
|
|
advance_enum = enums[orig_index - 1]
|
2009-11-04 20:21:08 +00:00
|
|
|
else:
|
|
|
|
|
if orig_index == len(enums) - 1:
|
2015-12-18 16:17:13 +11:00
|
|
|
advance_enum = enums[0] if self.wrap else enums[-1]
|
2009-11-04 20:21:08 +00:00
|
|
|
else:
|
|
|
|
|
advance_enum = enums[orig_index + 1]
|
|
|
|
|
|
|
|
|
|
# set the new value
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = advance_enum" % data_path)
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2010-08-04 12:18:07 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_cycle_array(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set a context array value """ \
|
2018-06-26 19:41:37 +02:00
|
|
|
"""(useful for cycling the active mesh edit mode)"""
|
2010-09-06 22:43:09 +00:00
|
|
|
bl_idname = "wm.context_cycle_array"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Array Cycle"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2010-09-06 22:43:09 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
reverse: rna_reverse_prop
|
2010-09-06 22:43:09 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix=iface_("Cycle"))
|
|
|
|
|
|
2010-09-06 22:43:09 +00:00
|
|
|
def execute(self, context):
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path = self.data_path
|
2010-09-06 22:43:09 +00:00
|
|
|
value = context_path_validate(context, data_path)
|
|
|
|
|
if value is Ellipsis:
|
|
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
|
|
|
|
|
|
def cycle(array):
|
2010-09-09 18:03:57 +00:00
|
|
|
if self.reverse:
|
2010-09-06 22:43:09 +00:00
|
|
|
array.insert(0, array.pop())
|
|
|
|
|
else:
|
|
|
|
|
array.append(array.pop(0))
|
|
|
|
|
return array
|
|
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = cycle(context.%s[:])" % (data_path, data_path))
|
2010-09-06 22:43:09 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2010-09-06 22:43:09 +00:00
|
|
|
|
|
|
|
|
|
2013-07-06 03:04:48 +00:00
|
|
|
class WM_OT_context_menu_enum(Operator):
|
|
|
|
|
bl_idname = "wm.context_menu_enum"
|
|
|
|
|
bl_label = "Context Enum Menu"
|
|
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2018-07-11 22:18:09 +02:00
|
|
|
|
|
|
|
|
data_path: rna_path_prop
|
2011-02-16 02:25:03 +00:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix=iface_("Menu"))
|
|
|
|
|
|
2013-07-06 03:04:48 +00:00
|
|
|
def execute(self, context):
|
2011-02-15 18:12:41 +00:00
|
|
|
data_path = self.data_path
|
2013-01-13 16:25:46 +00:00
|
|
|
value = context_path_validate(context, data_path)
|
2013-07-06 03:04:48 +00:00
|
|
|
|
2011-02-15 18:12:41 +00:00
|
|
|
if value is Ellipsis:
|
|
|
|
|
return {'PASS_THROUGH'}
|
2013-07-06 03:04:48 +00:00
|
|
|
|
2021-03-31 15:07:44 +11:00
|
|
|
base_path, prop_attr, _ = context_path_decompose(data_path)
|
2011-02-15 18:12:41 +00:00
|
|
|
value_base = context_path_validate(context, base_path)
|
2021-03-31 15:07:44 +11:00
|
|
|
rna_prop = context_path_to_rna_property(context, data_path)
|
2013-01-17 00:29:39 +00:00
|
|
|
|
2013-07-06 03:04:48 +00:00
|
|
|
def draw_cb(self, context):
|
|
|
|
|
layout = self.layout
|
2021-03-31 15:07:44 +11:00
|
|
|
layout.prop(value_base, prop_attr, expand=True)
|
2011-02-15 18:12:41 +00:00
|
|
|
|
2021-03-31 15:07:44 +11:00
|
|
|
context.window_manager.popup_menu(draw_func=draw_cb, title=rna_prop.name, icon=rna_prop.icon)
|
2011-02-15 18:12:41 +00:00
|
|
|
|
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
2014-08-11 10:39:59 +02:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_context_pie_enum(Operator):
|
|
|
|
|
bl_idname = "wm.context_pie_enum"
|
|
|
|
|
bl_label = "Context Enum Pie"
|
|
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2018-07-11 22:18:09 +02:00
|
|
|
|
|
|
|
|
data_path: rna_path_prop
|
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
2014-08-11 10:39:59 +02:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix=iface_("Pie Menu"))
|
|
|
|
|
|
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
2014-08-11 10:39:59 +02:00
|
|
|
def invoke(self, context, event):
|
2014-08-14 07:29:38 +10:00
|
|
|
wm = context.window_manager
|
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
2014-08-11 10:39:59 +02:00
|
|
|
data_path = self.data_path
|
|
|
|
|
value = context_path_validate(context, data_path)
|
|
|
|
|
|
|
|
|
|
if value is Ellipsis:
|
|
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
|
|
2021-03-31 15:07:44 +11:00
|
|
|
base_path, prop_attr, _ = context_path_decompose(data_path)
|
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
2014-08-11 10:39:59 +02:00
|
|
|
value_base = context_path_validate(context, base_path)
|
2021-03-31 15:07:44 +11:00
|
|
|
rna_prop = context_path_to_rna_property(context, data_path)
|
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
2014-08-11 10:39:59 +02:00
|
|
|
|
|
|
|
|
def draw_cb(self, context):
|
|
|
|
|
layout = self.layout
|
2021-03-31 15:07:44 +11:00
|
|
|
layout.prop(value_base, prop_attr, expand=True)
|
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
2014-08-11 10:39:59 +02:00
|
|
|
|
2021-03-31 15:07:44 +11:00
|
|
|
wm.popup_menu_pie(draw_func=draw_cb, title=rna_prop.name, icon=rna_prop.icon, event=event)
|
Pie Menus C code backend.
This commit merges the code in the pie-menu branch.
As per decisions taken the last few days, there are no pie menus
included and there will be an official add-on including overrides of
some keys with pie menus. However, people will now be able to use the
new code in python.
Full Documentation is in http://wiki.blender.org/index.php/Dev:Ref/
Thanks:
Campbell Barton, Dalai Felinto and Ton Roosendaal for the code review
and design comments
Jonathan Williamson, Pawel Lyczkowski, Pablo Vazquez among others for
suggestions during the development.
Special Thanks to Sean Olson, for his support, suggestions, testing and
merciless bugging so that I would finish the pie menu code. Without him
we wouldn't be here. Also to the rest of the developers of the original
python add-on, Patrick Moore and Dan Eicher and finally to Matt Ebb, who
did the research and first implementation and whose code I used to get
started.
2014-08-11 10:39:59 +02:00
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
2011-02-15 18:12:41 +00:00
|
|
|
|
|
|
|
|
|
2014-08-13 14:04:37 +02:00
|
|
|
class WM_OT_operator_pie_enum(Operator):
|
|
|
|
|
bl_idname = "wm.operator_pie_enum"
|
|
|
|
|
bl_label = "Operator Enum Pie"
|
|
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2018-07-11 22:18:09 +02:00
|
|
|
|
|
|
|
|
data_path: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Operator",
|
|
|
|
|
description="Operator name (in python as string)",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
prop_string: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Property",
|
|
|
|
|
description="Property name (as a string)",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
)
|
2014-08-13 14:04:37 +02:00
|
|
|
|
2021-03-31 00:44:29 +11:00
|
|
|
@classmethod
|
|
|
|
|
def description(cls, context, props):
|
|
|
|
|
return description_from_data_path(context, props.data_path, prefix=iface_("Pie Menu"))
|
|
|
|
|
|
2014-08-13 14:04:37 +02:00
|
|
|
def invoke(self, context, event):
|
2014-08-14 07:29:38 +10:00
|
|
|
wm = context.window_manager
|
2014-08-13 14:04:37 +02:00
|
|
|
|
2014-08-14 07:29:38 +10:00
|
|
|
data_path = self.data_path
|
2021-03-31 15:07:44 +11:00
|
|
|
prop_attr = self.prop_string
|
2014-08-13 14:04:37 +02:00
|
|
|
|
2014-08-14 07:29:38 +10:00
|
|
|
# same as eval("bpy.ops." + data_path)
|
|
|
|
|
op_mod_str, ob_id_str = data_path.split(".", 1)
|
|
|
|
|
op = getattr(getattr(bpy.ops, op_mod_str), ob_id_str)
|
|
|
|
|
del op_mod_str, ob_id_str
|
|
|
|
|
|
|
|
|
|
try:
|
2018-09-13 18:16:06 +10:00
|
|
|
op_rna = op.get_rna_type()
|
2014-08-14 07:29:38 +10:00
|
|
|
except KeyError:
|
|
|
|
|
self.report({'ERROR'}, "Operator not found: bpy.ops.%s" % data_path)
|
|
|
|
|
return {'CANCELLED'}
|
2014-08-13 14:04:37 +02:00
|
|
|
|
|
|
|
|
def draw_cb(self, context):
|
|
|
|
|
layout = self.layout
|
|
|
|
|
pie = layout.menu_pie()
|
2021-03-31 15:07:44 +11:00
|
|
|
pie.operator_enum(data_path, prop_attr)
|
2014-08-13 14:04:37 +02:00
|
|
|
|
2018-09-13 18:16:06 +10:00
|
|
|
wm.popup_menu_pie(draw_func=draw_cb, title=op_rna.name, event=event)
|
2014-08-13 14:04:37 +02:00
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_set_id(Operator):
|
2013-09-30 05:50:41 +00:00
|
|
|
"""Set a context value to an ID data-block"""
|
2010-08-04 12:18:07 +00:00
|
|
|
bl_idname = "wm.context_set_id"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Set Library ID"
|
2011-06-06 11:56:54 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2010-08-04 12:18:07 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path_prop
|
|
|
|
|
value: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Value",
|
|
|
|
|
description="Assign value",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
)
|
2010-08-04 12:18:07 +00:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2010-09-09 18:03:57 +00:00
|
|
|
value = self.value
|
|
|
|
|
data_path = self.data_path
|
2010-08-04 13:59:25 +00:00
|
|
|
|
2021-03-31 15:07:44 +11:00
|
|
|
# Match the pointer type from the target property to `bpy.data.*`
|
2010-08-04 13:59:25 +00:00
|
|
|
# so we lookup the correct list.
|
2021-03-31 15:07:44 +11:00
|
|
|
|
|
|
|
|
rna_prop = context_path_to_rna_property(context, data_path)
|
|
|
|
|
rna_prop_fixed_type = rna_prop.fixed_type
|
2010-08-04 13:59:25 +00:00
|
|
|
|
|
|
|
|
id_iter = None
|
|
|
|
|
|
|
|
|
|
for prop in bpy.data.rna_type.properties:
|
|
|
|
|
if prop.rna_type.identifier == "CollectionProperty":
|
2021-03-31 15:07:44 +11:00
|
|
|
if prop.fixed_type == rna_prop_fixed_type:
|
2010-08-04 13:59:25 +00:00
|
|
|
id_iter = prop.identifier
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
if id_iter:
|
|
|
|
|
value_id = getattr(bpy.data, id_iter).get(value)
|
2011-08-18 15:25:18 +00:00
|
|
|
exec("context.%s = value_id" % data_path)
|
2010-08-04 12:18:07 +00:00
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_path_undo_return(context, data_path)
|
2010-08-04 12:18:07 +00:00
|
|
|
|
|
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
doc_id = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Doc ID",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
options={'HIDDEN'},
|
|
|
|
|
)
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-06-27 07:51:52 +00:00
|
|
|
data_path_iter = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
description="The data path relative to the context, must point to an iterable")
|
2011-06-27 07:51:52 +00:00
|
|
|
|
|
|
|
|
data_path_item = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
description="The data path from each iterable to the value (int or float)")
|
2011-06-27 07:51:52 +00:00
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_collection_boolean_set(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Set boolean values for a collection of items"""
|
2011-06-27 07:51:52 +00:00
|
|
|
bl_idname = "wm.context_collection_boolean_set"
|
|
|
|
|
bl_label = "Context Collection Boolean Set"
|
|
|
|
|
bl_options = {'UNDO', 'REGISTER', 'INTERNAL'}
|
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path_iter: data_path_iter
|
|
|
|
|
data_path_item: data_path_item
|
2011-06-27 07:51:52 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
type: EnumProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Type",
|
2019-03-14 11:08:48 +11:00
|
|
|
items=(
|
|
|
|
|
('TOGGLE', "Toggle", ""),
|
|
|
|
|
('ENABLE', "Enable", ""),
|
|
|
|
|
('DISABLE', "Disable", ""),
|
|
|
|
|
),
|
2018-06-26 19:41:37 +02:00
|
|
|
)
|
2011-06-27 07:51:52 +00:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
data_path_iter = self.data_path_iter
|
|
|
|
|
data_path_item = self.data_path_item
|
|
|
|
|
|
|
|
|
|
items = list(getattr(context, data_path_iter))
|
|
|
|
|
items_ok = []
|
|
|
|
|
is_set = False
|
|
|
|
|
for item in items:
|
|
|
|
|
try:
|
|
|
|
|
value_orig = eval("item." + data_path_item)
|
|
|
|
|
except:
|
|
|
|
|
continue
|
|
|
|
|
|
2012-10-08 08:28:05 +00:00
|
|
|
if value_orig is True:
|
2011-06-27 07:51:52 +00:00
|
|
|
is_set = True
|
2012-10-08 08:28:05 +00:00
|
|
|
elif value_orig is False:
|
2011-06-27 07:51:52 +00:00
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
self.report({'WARNING'}, "Non boolean value found: %s[ ].%s" %
|
|
|
|
|
(data_path_iter, data_path_item))
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
|
|
items_ok.append(item)
|
|
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
# avoid undo push when nothing to do
|
|
|
|
|
if not items_ok:
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
2011-06-27 07:51:52 +00:00
|
|
|
if self.type == 'ENABLE':
|
|
|
|
|
is_set = True
|
|
|
|
|
elif self.type == 'DISABLE':
|
|
|
|
|
is_set = False
|
|
|
|
|
else:
|
|
|
|
|
is_set = not is_set
|
|
|
|
|
|
|
|
|
|
exec_str = "item.%s = %s" % (data_path_item, is_set)
|
|
|
|
|
for item in items_ok:
|
|
|
|
|
exec(exec_str)
|
|
|
|
|
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_value_undo_return(item)
|
2011-06-27 07:51:52 +00:00
|
|
|
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_context_modal_mouse(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Adjust arbitrary values with mouse input"""
|
2010-02-21 14:05:02 +00:00
|
|
|
bl_idname = "wm.context_modal_mouse"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Context Modal Mouse"
|
2015-04-27 18:44:27 +10:00
|
|
|
bl_options = {'GRAB_CURSOR', 'BLOCKING', 'UNDO', 'INTERNAL'}
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path_iter: data_path_iter
|
|
|
|
|
data_path_item: data_path_item
|
|
|
|
|
header_text: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Header Text",
|
|
|
|
|
description="Text to display in header during scale",
|
|
|
|
|
)
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
input_scale: FloatProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
description="Scale the mouse movement by this value before applying the delta",
|
|
|
|
|
default=0.01,
|
2021-01-26 08:04:25 +11:00
|
|
|
options={'SKIP_SAVE'},
|
2018-06-26 19:41:37 +02:00
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
invert: BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
description="Invert the mouse input",
|
|
|
|
|
default=False,
|
2021-01-26 08:04:25 +11:00
|
|
|
options={'SKIP_SAVE'},
|
2018-06-26 19:41:37 +02:00
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
initial_x: IntProperty(options={'HIDDEN'})
|
2010-02-21 14:05:02 +00:00
|
|
|
|
|
|
|
|
def _values_store(self, context):
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path_iter = self.data_path_iter
|
|
|
|
|
data_path_item = self.data_path_item
|
2010-02-21 14:05:02 +00:00
|
|
|
|
2010-02-27 14:54:45 +00:00
|
|
|
self._values = values = {}
|
2010-02-21 14:05:02 +00:00
|
|
|
|
2010-06-14 03:52:10 +00:00
|
|
|
for item in getattr(context, data_path_iter):
|
2010-02-21 14:05:02 +00:00
|
|
|
try:
|
2010-06-14 03:52:10 +00:00
|
|
|
value_orig = eval("item." + data_path_item)
|
2010-02-21 14:05:02 +00:00
|
|
|
except:
|
|
|
|
|
continue
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2010-02-21 14:05:02 +00:00
|
|
|
# check this can be set, maybe this is library data.
|
|
|
|
|
try:
|
2010-06-14 03:52:10 +00:00
|
|
|
exec("item.%s = %s" % (data_path_item, value_orig))
|
2010-02-21 14:05:02 +00:00
|
|
|
except:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
values[item] = value_orig
|
|
|
|
|
|
|
|
|
|
def _values_delta(self, delta):
|
2010-09-09 18:03:57 +00:00
|
|
|
delta *= self.input_scale
|
|
|
|
|
if self.invert:
|
2010-02-22 23:32:58 +00:00
|
|
|
delta = - delta
|
2010-02-21 14:05:02 +00:00
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path_item = self.data_path_item
|
2010-02-21 14:05:02 +00:00
|
|
|
for item, value_orig in self._values.items():
|
2010-02-23 15:34:02 +00:00
|
|
|
if type(value_orig) == int:
|
2010-06-14 03:52:10 +00:00
|
|
|
exec("item.%s = int(%d)" % (data_path_item, round(value_orig + delta)))
|
2010-02-23 15:34:02 +00:00
|
|
|
else:
|
2010-06-14 03:52:10 +00:00
|
|
|
exec("item.%s = %f" % (data_path_item, value_orig + delta))
|
2010-02-21 14:05:02 +00:00
|
|
|
|
|
|
|
|
def _values_restore(self):
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path_item = self.data_path_item
|
2010-02-21 14:05:02 +00:00
|
|
|
for item, value_orig in self._values.items():
|
2010-06-14 03:52:10 +00:00
|
|
|
exec("item.%s = %s" % (data_path_item, value_orig))
|
2010-02-21 14:05:02 +00:00
|
|
|
|
|
|
|
|
self._values.clear()
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2010-02-21 14:05:02 +00:00
|
|
|
def _values_clear(self):
|
|
|
|
|
self._values.clear()
|
|
|
|
|
|
|
|
|
|
def modal(self, context, event):
|
|
|
|
|
event_type = event.type
|
|
|
|
|
|
|
|
|
|
if event_type == 'MOUSEMOVE':
|
2010-09-09 18:03:57 +00:00
|
|
|
delta = event.mouse_x - self.initial_x
|
2010-02-21 14:05:02 +00:00
|
|
|
self._values_delta(delta)
|
2012-02-25 21:15:02 +00:00
|
|
|
header_text = self.header_text
|
|
|
|
|
if header_text:
|
|
|
|
|
if len(self._values) == 1:
|
|
|
|
|
(item, ) = self._values.keys()
|
|
|
|
|
header_text = header_text % eval("item.%s" % self.data_path_item)
|
|
|
|
|
else:
|
|
|
|
|
header_text = (self.header_text % delta) + " (delta)"
|
2018-09-08 06:23:25 +10:00
|
|
|
context.area.header_text_set(header_text)
|
2010-02-21 14:05:02 +00:00
|
|
|
|
|
|
|
|
elif 'LEFTMOUSE' == event_type:
|
2011-08-18 16:01:11 +00:00
|
|
|
item = next(iter(self._values.keys()))
|
2010-02-21 14:05:02 +00:00
|
|
|
self._values_clear()
|
2018-10-30 16:16:41 +11:00
|
|
|
context.area.header_text_set(None)
|
2011-08-18 16:01:11 +00:00
|
|
|
return operator_value_undo_return(item)
|
2010-02-21 14:05:02 +00:00
|
|
|
|
2011-08-08 05:21:37 +00:00
|
|
|
elif event_type in {'RIGHTMOUSE', 'ESC'}:
|
2010-02-21 14:05:02 +00:00
|
|
|
self._values_restore()
|
2018-10-30 16:16:41 +11:00
|
|
|
context.area.header_text_set(None)
|
2011-08-18 16:01:11 +00:00
|
|
|
return {'CANCELLED'}
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2010-02-21 14:05:02 +00:00
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
self._values_store(context)
|
|
|
|
|
|
|
|
|
|
if not self._values:
|
2010-03-06 01:40:29 +00:00
|
|
|
self.report({'WARNING'}, "Nothing to operate on: %s[ ].%s" %
|
2012-09-05 00:11:39 +00:00
|
|
|
(self.data_path_iter, self.data_path_item))
|
2010-03-06 01:40:29 +00:00
|
|
|
|
2010-02-21 14:05:02 +00:00
|
|
|
return {'CANCELLED'}
|
|
|
|
|
else:
|
2010-09-09 18:03:57 +00:00
|
|
|
self.initial_x = event.mouse_x
|
2010-02-21 14:05:02 +00:00
|
|
|
|
2010-12-08 11:42:11 +00:00
|
|
|
context.window_manager.modal_handler_add(self)
|
2010-02-21 14:05:02 +00:00
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_url_open(Operator):
|
2020-12-24 13:11:22 -06:00
|
|
|
"""Open a website in the web browser"""
|
2010-03-14 20:07:15 +00:00
|
|
|
bl_idname = "wm.url_open"
|
|
|
|
|
bl_label = ""
|
2014-02-26 13:48:41 +11:00
|
|
|
bl_options = {'INTERNAL'}
|
2010-03-14 20:07:15 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
url: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="URL",
|
|
|
|
|
description="URL to open",
|
|
|
|
|
)
|
2010-03-14 20:07:15 +00:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def execute(self, _context):
|
2010-03-14 20:07:15 +00:00
|
|
|
import webbrowser
|
2010-09-09 18:03:57 +00:00
|
|
|
webbrowser.open(self.url)
|
2010-03-14 20:07:15 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
2019-08-20 22:00:01 +10:00
|
|
|
class WM_OT_url_open_preset(Operator):
|
2020-12-24 13:11:22 -06:00
|
|
|
"""Open a preset website in the web browser"""
|
2019-08-20 22:00:01 +10:00
|
|
|
bl_idname = "wm.url_open_preset"
|
|
|
|
|
bl_label = "Open Preset Website"
|
|
|
|
|
bl_options = {'INTERNAL'}
|
|
|
|
|
|
|
|
|
|
type: EnumProperty(
|
|
|
|
|
name="Site",
|
2021-03-16 12:19:13 +11:00
|
|
|
items=lambda self, _context: (
|
|
|
|
|
item for (item, _) in WM_OT_url_open_preset.preset_items
|
|
|
|
|
),
|
2019-08-20 22:00:01 +10:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
id: StringProperty(
|
|
|
|
|
name="Identifier",
|
|
|
|
|
description="Optional identifier",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def _url_from_bug(self, _context):
|
|
|
|
|
from bl_ui_utils.bug_report_url import url_prefill_from_blender
|
|
|
|
|
return url_prefill_from_blender()
|
|
|
|
|
|
|
|
|
|
def _url_from_bug_addon(self, _context):
|
|
|
|
|
from bl_ui_utils.bug_report_url import url_prefill_from_blender
|
|
|
|
|
return url_prefill_from_blender(addon_info=self.id)
|
|
|
|
|
|
|
|
|
|
def _url_from_release_notes(self, _context):
|
|
|
|
|
return "https://www.blender.org/download/releases/%d-%d/" % bpy.app.version[:2]
|
|
|
|
|
|
|
|
|
|
def _url_from_manual(self, _context):
|
|
|
|
|
if bpy.app.version_cycle in {"rc", "release"}:
|
|
|
|
|
manual_version = "%d.%d" % bpy.app.version[:2]
|
|
|
|
|
else:
|
|
|
|
|
manual_version = "dev"
|
|
|
|
|
return "https://docs.blender.org/manual/en/" + manual_version + "/"
|
|
|
|
|
|
|
|
|
|
# This list is: (enum_item, url) pairs.
|
|
|
|
|
# Allow dynamically extending.
|
|
|
|
|
preset_items = [
|
|
|
|
|
# Dynamic URL's.
|
|
|
|
|
(('BUG', "Bug",
|
|
|
|
|
"Report a bug with pre-filled version information"),
|
|
|
|
|
_url_from_bug),
|
2020-12-24 11:07:32 -06:00
|
|
|
(('BUG_ADDON', "Add-on Bug",
|
2019-08-20 22:00:01 +10:00
|
|
|
"Report a bug in an add-on"),
|
|
|
|
|
_url_from_bug_addon),
|
|
|
|
|
(('RELEASE_NOTES', "Release Notes",
|
|
|
|
|
"Read about whats new in this version of Blender"),
|
|
|
|
|
_url_from_release_notes),
|
|
|
|
|
(('MANUAL', "Manual",
|
|
|
|
|
"The reference manual for this version of Blender"),
|
|
|
|
|
_url_from_manual),
|
|
|
|
|
|
|
|
|
|
# Static URL's.
|
|
|
|
|
(('FUND', "Development Fund",
|
|
|
|
|
"The donation program to support maintenance and improvements"),
|
|
|
|
|
"https://fund.blender.org"),
|
|
|
|
|
(('BLENDER', "blender.org",
|
|
|
|
|
"Blender's official web-site"),
|
|
|
|
|
"https://www.blender.org"),
|
|
|
|
|
(('CREDITS', "Credits",
|
|
|
|
|
"Lists committers to Blender's source code"),
|
|
|
|
|
"https://www.blender.org/about/credits/"),
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
url = None
|
|
|
|
|
type = self.type
|
|
|
|
|
for (item_id, _, _), url in self.preset_items:
|
|
|
|
|
if item_id == type:
|
|
|
|
|
if callable(url):
|
|
|
|
|
url = url(self, context)
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
import webbrowser
|
|
|
|
|
webbrowser.open(url)
|
|
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_path_open(Operator):
|
2017-04-14 20:01:43 +10:00
|
|
|
"""Open a path in a file browser"""
|
2010-07-04 10:02:30 +00:00
|
|
|
bl_idname = "wm.path_open"
|
|
|
|
|
bl_label = ""
|
2014-02-26 13:48:41 +11:00
|
|
|
bl_options = {'INTERNAL'}
|
2010-07-04 10:02:30 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
filepath: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
subtype='FILE_PATH',
|
|
|
|
|
options={'SKIP_SAVE'},
|
|
|
|
|
)
|
2010-07-04 10:02:30 +00:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def execute(self, _context):
|
2010-07-04 10:02:30 +00:00
|
|
|
import sys
|
2011-09-23 13:47:29 +00:00
|
|
|
import os
|
2010-07-04 10:02:30 +00:00
|
|
|
import subprocess
|
|
|
|
|
|
2012-05-12 21:11:48 +00:00
|
|
|
filepath = self.filepath
|
|
|
|
|
|
|
|
|
|
if not filepath:
|
|
|
|
|
self.report({'ERROR'}, "File path was not set")
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
|
|
filepath = bpy.path.abspath(filepath)
|
2010-07-04 10:02:30 +00:00
|
|
|
filepath = os.path.normpath(filepath)
|
2010-07-05 22:22:22 +00:00
|
|
|
|
2010-07-04 10:02:30 +00:00
|
|
|
if not os.path.exists(filepath):
|
|
|
|
|
self.report({'ERROR'}, "File '%s' not found" % filepath)
|
|
|
|
|
return {'CANCELLED'}
|
2010-07-05 22:22:22 +00:00
|
|
|
|
2011-03-11 01:24:16 +00:00
|
|
|
if sys.platform[:3] == "win":
|
2012-08-23 11:18:31 +00:00
|
|
|
os.startfile(filepath)
|
2011-12-22 03:56:21 +00:00
|
|
|
elif sys.platform == "darwin":
|
2014-06-20 01:57:06 +10:00
|
|
|
subprocess.check_call(["open", filepath])
|
2010-07-04 10:02:30 +00:00
|
|
|
else:
|
|
|
|
|
try:
|
2014-06-20 01:57:06 +10:00
|
|
|
subprocess.check_call(["xdg-open", filepath])
|
|
|
|
|
except:
|
2010-07-04 10:02:30 +00:00
|
|
|
# xdg-open *should* be supported by recent Gnome, KDE, Xfce
|
2014-06-20 01:57:06 +10:00
|
|
|
import traceback
|
|
|
|
|
traceback.print_exc()
|
2010-07-04 10:02:30 +00:00
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
PyAPI: use keyword only arguments
Use keyword only arguments for the following functions.
- addon_utils.module_bl_info 2nd arg `info_basis`.
- addon_utils.modules 1st `module_cache`, 2nd arg `refresh`.
- addon_utils.modules_refresh 1st arg `module_cache`.
- bl_app_template_utils.activate 1nd arg `template_id`.
- bl_app_template_utils.import_from_id 2nd arg `ignore_not_found`.
- bl_app_template_utils.import_from_path 2nd arg `ignore_not_found`.
- bl_keymap_utils.keymap_from_toolbar.generate 2nd & 3rd args `use_fallback_keys` & `use_reset`.
- bl_keymap_utils.platform_helpers.keyconfig_data_oskey_from_ctrl 2nd arg `filter_fn`.
- bl_ui_utils.bug_report_url.url_prefill_from_blender 1st arg `addon_info`.
- bmesh.types.BMFace.copy 1st & 2nd args `verts`, `edges`.
- bmesh.types.BMesh.calc_volume 1st arg `signed`.
- bmesh.types.BMesh.from_mesh 2nd..4th args `face_normals`, `use_shape_key`, `shape_key_index`.
- bmesh.types.BMesh.from_object 3rd & 4th args `cage`, `face_normals`.
- bmesh.types.BMesh.transform 2nd arg `filter`.
- bmesh.types.BMesh.update_edit_mesh 2nd & 3rd args `loop_triangles`, `destructive`.
- bmesh.types.{BMVertSeq,BMEdgeSeq,BMFaceSeq}.sort 1st & 2nd arg `key`, `reverse`.
- bmesh.utils.face_split 4th..6th args `coords`, `use_exist`, `example`.
- bpy.data.libraries.load 2nd..4th args `link`, `relative`, `assets_only`.
- bpy.data.user_map 1st..3rd args `subset`, `key_types, `value_types`.
- bpy.msgbus.subscribe_rna 5th arg `options`.
- bpy.path.abspath 2nd & 3rd args `start` & `library`.
- bpy.path.clean_name 2nd arg `replace`.
- bpy.path.ensure_ext 3rd arg `case_sensitive`.
- bpy.path.module_names 2nd arg `recursive`.
- bpy.path.relpath 2nd arg `start`.
- bpy.types.EditBone.transform 2nd & 3rd arg `scale`, `roll`.
- bpy.types.Operator.as_keywords 1st arg `ignore`.
- bpy.types.Struct.{keyframe_insert,keyframe_delete} 2nd..5th args `index`, `frame`, `group`, `options`.
- bpy.types.WindowManager.popup_menu 2nd & 3rd arg `title`, `icon`.
- bpy.types.WindowManager.popup_menu_pie 3rd & 4th arg `title`, `icon`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.blend_paths 1st..3rd args `absolute`, `packed`, `local`.
- bpy.utils.execfile 2nd arg `mod`.
- bpy.utils.keyconfig_set 2nd arg `report`.
- bpy.utils.load_scripts 1st & 2nd `reload_scripts` & `refresh_scripts`.
- bpy.utils.preset_find 3rd & 4th args `display_name`, `ext`.
- bpy.utils.resource_path 2nd & 3rd arg `major`, `minor`.
- bpy.utils.script_paths 1st..4th args `subdir`, `user_pref`, `check_all`, `use_user`.
- bpy.utils.smpte_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.smpte_from_seconds 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.system_resource 2nd arg `subdir`.
- bpy.utils.time_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.time_to_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.units.to_string 4th..6th `precision`, `split_unit`, `compatible_unit`.
- bpy.utils.units.to_value 4th arg `str_ref_unit`.
- bpy.utils.user_resource 2nd & 3rd args `subdir`, `create`
- bpy_extras.view3d_utils.location_3d_to_region_2d 4th arg `default`.
- bpy_extras.view3d_utils.region_2d_to_origin_3d 4th arg `clamp`.
- gpu.offscreen.unbind 1st arg `restore`.
- gpu_extras.batch.batch_for_shader 4th arg `indices`.
- gpu_extras.batch.presets.draw_circle_2d 4th arg `segments`.
- gpu_extras.presets.draw_circle_2d 4th arg `segments`.
- imbuf.types.ImBuf.resize 2nd arg `resize`.
- imbuf.write 2nd arg `filepath`.
- mathutils.kdtree.KDTree.find 2nd arg `filter`.
- nodeitems_utils.NodeCategory 3rd & 4th arg `descriptions`, `items`.
- nodeitems_utils.NodeItem 2nd..4th args `label`, `settings`, `poll`.
- nodeitems_utils.NodeItemCustom 1st & 2nd arg `poll`, `draw`.
- rna_prop_ui.draw 5th arg `use_edit`.
- rna_prop_ui.rna_idprop_ui_get 2nd arg `create`.
- rna_prop_ui.rna_idprop_ui_prop_clear 3rd arg `remove`.
- rna_prop_ui.rna_idprop_ui_prop_get 3rd arg `create`.
- rna_xml.xml2rna 2nd arg `root_rna`.
- rna_xml.xml_file_write 4th arg `skip_typemap`.
2021-06-08 18:03:14 +10:00
|
|
|
def _wm_doc_get_id(doc_id, *, do_url=True, url_prefix="", report=None):
|
2018-01-22 09:45:51 +11:00
|
|
|
|
|
|
|
|
def operator_exists_pair(a, b):
|
|
|
|
|
# Not fast, this is only for docs.
|
|
|
|
|
return b in dir(getattr(bpy.ops, a))
|
|
|
|
|
|
|
|
|
|
def operator_exists_single(a):
|
|
|
|
|
a, b = a.partition("_OT_")[::2]
|
|
|
|
|
return operator_exists_pair(a.lower(), b)
|
|
|
|
|
|
2012-06-01 20:38:40 +00:00
|
|
|
id_split = doc_id.split(".")
|
|
|
|
|
url = rna = None
|
|
|
|
|
|
|
|
|
|
if len(id_split) == 1: # rna, class
|
|
|
|
|
if do_url:
|
|
|
|
|
url = "%s/bpy.types.%s.html" % (url_prefix, id_split[0])
|
|
|
|
|
else:
|
|
|
|
|
rna = "bpy.types.%s" % id_split[0]
|
|
|
|
|
|
|
|
|
|
elif len(id_split) == 2: # rna, class.prop
|
|
|
|
|
class_name, class_prop = id_split
|
|
|
|
|
|
2013-02-25 04:19:28 +00:00
|
|
|
# an operator (common case - just button referencing an op)
|
2018-01-22 09:45:51 +11:00
|
|
|
if operator_exists_pair(class_name, class_prop):
|
|
|
|
|
if do_url:
|
|
|
|
|
url = (
|
|
|
|
|
"%s/bpy.ops.%s.html#bpy.ops.%s.%s" %
|
|
|
|
|
(url_prefix, class_name, class_name, class_prop)
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
rna = "bpy.ops.%s.%s" % (class_name, class_prop)
|
|
|
|
|
elif operator_exists_single(class_name):
|
|
|
|
|
# note: ignore the prop name since we don't have a way to link into it
|
|
|
|
|
class_name, class_prop = class_name.split("_OT_", 1)
|
|
|
|
|
class_name = class_name.lower()
|
2012-06-01 20:38:40 +00:00
|
|
|
if do_url:
|
2017-03-25 11:07:05 +11:00
|
|
|
url = (
|
|
|
|
|
"%s/bpy.ops.%s.html#bpy.ops.%s.%s" %
|
|
|
|
|
(url_prefix, class_name, class_name, class_prop)
|
|
|
|
|
)
|
2012-06-01 20:38:40 +00:00
|
|
|
else:
|
|
|
|
|
rna = "bpy.ops.%s.%s" % (class_name, class_prop)
|
|
|
|
|
else:
|
2020-06-05 18:06:44 +10:00
|
|
|
# An RNA setting, common case.
|
2013-03-28 19:33:14 +00:00
|
|
|
|
2020-06-05 18:06:44 +10:00
|
|
|
# Check the built-in RNA types.
|
|
|
|
|
rna_class = getattr(bpy.types, class_name, None)
|
|
|
|
|
if rna_class is None:
|
|
|
|
|
# Check class for dynamically registered types.
|
|
|
|
|
rna_class = bpy.types.PropertyGroup.bl_rna_get_subclass_py(class_name)
|
|
|
|
|
|
2021-01-07 23:24:23 +11:00
|
|
|
if rna_class is None:
|
|
|
|
|
if report is not None:
|
|
|
|
|
report({'ERROR'}, iface_("Type \"%s\" can not be found") % class_name)
|
|
|
|
|
return None
|
|
|
|
|
|
2020-06-05 18:06:44 +10:00
|
|
|
# Detect if this is a inherited member and use that name instead.
|
2018-01-22 09:45:51 +11:00
|
|
|
rna_parent = rna_class.bl_rna
|
|
|
|
|
rna_prop = rna_parent.properties.get(class_prop)
|
|
|
|
|
if rna_prop:
|
|
|
|
|
rna_parent = rna_parent.base
|
|
|
|
|
while rna_parent and rna_prop == rna_parent.properties.get(class_prop):
|
|
|
|
|
class_name = rna_parent.identifier
|
|
|
|
|
rna_parent = rna_parent.base
|
|
|
|
|
|
2013-02-25 04:19:28 +00:00
|
|
|
if do_url:
|
2017-03-25 11:07:05 +11:00
|
|
|
url = (
|
2018-01-22 09:45:51 +11:00
|
|
|
"%s/bpy.types.%s.html#bpy.types.%s.%s" %
|
2017-03-25 11:07:05 +11:00
|
|
|
(url_prefix, class_name, class_name, class_prop)
|
|
|
|
|
)
|
2013-02-25 04:19:28 +00:00
|
|
|
else:
|
2018-01-22 09:45:51 +11:00
|
|
|
rna = "bpy.types.%s.%s" % (class_name, class_prop)
|
2013-02-25 04:19:28 +00:00
|
|
|
else:
|
2018-01-22 09:45:51 +11:00
|
|
|
# We assume this is custom property, only try to generate generic url/rna_id...
|
|
|
|
|
if do_url:
|
|
|
|
|
url = ("%s/bpy.types.bpy_struct.html#bpy.types.bpy_struct.items" % (url_prefix,))
|
2013-02-25 04:19:28 +00:00
|
|
|
else:
|
2018-01-22 09:45:51 +11:00
|
|
|
rna = "bpy.types.bpy_struct"
|
2012-06-19 22:17:19 +00:00
|
|
|
|
2012-06-01 20:38:40 +00:00
|
|
|
return url if do_url else rna
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_doc_view_manual(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Load online manual"""
|
2012-06-01 20:38:40 +00:00
|
|
|
bl_idname = "wm.doc_view_manual"
|
|
|
|
|
bl_label = "View Manual"
|
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
doc_id: doc_id
|
2012-06-01 20:38:40 +00:00
|
|
|
|
|
|
|
|
@staticmethod
|
PyAPI: use keyword only arguments
Use keyword only arguments for the following functions.
- addon_utils.module_bl_info 2nd arg `info_basis`.
- addon_utils.modules 1st `module_cache`, 2nd arg `refresh`.
- addon_utils.modules_refresh 1st arg `module_cache`.
- bl_app_template_utils.activate 1nd arg `template_id`.
- bl_app_template_utils.import_from_id 2nd arg `ignore_not_found`.
- bl_app_template_utils.import_from_path 2nd arg `ignore_not_found`.
- bl_keymap_utils.keymap_from_toolbar.generate 2nd & 3rd args `use_fallback_keys` & `use_reset`.
- bl_keymap_utils.platform_helpers.keyconfig_data_oskey_from_ctrl 2nd arg `filter_fn`.
- bl_ui_utils.bug_report_url.url_prefill_from_blender 1st arg `addon_info`.
- bmesh.types.BMFace.copy 1st & 2nd args `verts`, `edges`.
- bmesh.types.BMesh.calc_volume 1st arg `signed`.
- bmesh.types.BMesh.from_mesh 2nd..4th args `face_normals`, `use_shape_key`, `shape_key_index`.
- bmesh.types.BMesh.from_object 3rd & 4th args `cage`, `face_normals`.
- bmesh.types.BMesh.transform 2nd arg `filter`.
- bmesh.types.BMesh.update_edit_mesh 2nd & 3rd args `loop_triangles`, `destructive`.
- bmesh.types.{BMVertSeq,BMEdgeSeq,BMFaceSeq}.sort 1st & 2nd arg `key`, `reverse`.
- bmesh.utils.face_split 4th..6th args `coords`, `use_exist`, `example`.
- bpy.data.libraries.load 2nd..4th args `link`, `relative`, `assets_only`.
- bpy.data.user_map 1st..3rd args `subset`, `key_types, `value_types`.
- bpy.msgbus.subscribe_rna 5th arg `options`.
- bpy.path.abspath 2nd & 3rd args `start` & `library`.
- bpy.path.clean_name 2nd arg `replace`.
- bpy.path.ensure_ext 3rd arg `case_sensitive`.
- bpy.path.module_names 2nd arg `recursive`.
- bpy.path.relpath 2nd arg `start`.
- bpy.types.EditBone.transform 2nd & 3rd arg `scale`, `roll`.
- bpy.types.Operator.as_keywords 1st arg `ignore`.
- bpy.types.Struct.{keyframe_insert,keyframe_delete} 2nd..5th args `index`, `frame`, `group`, `options`.
- bpy.types.WindowManager.popup_menu 2nd & 3rd arg `title`, `icon`.
- bpy.types.WindowManager.popup_menu_pie 3rd & 4th arg `title`, `icon`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.blend_paths 1st..3rd args `absolute`, `packed`, `local`.
- bpy.utils.execfile 2nd arg `mod`.
- bpy.utils.keyconfig_set 2nd arg `report`.
- bpy.utils.load_scripts 1st & 2nd `reload_scripts` & `refresh_scripts`.
- bpy.utils.preset_find 3rd & 4th args `display_name`, `ext`.
- bpy.utils.resource_path 2nd & 3rd arg `major`, `minor`.
- bpy.utils.script_paths 1st..4th args `subdir`, `user_pref`, `check_all`, `use_user`.
- bpy.utils.smpte_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.smpte_from_seconds 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.system_resource 2nd arg `subdir`.
- bpy.utils.time_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.time_to_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.units.to_string 4th..6th `precision`, `split_unit`, `compatible_unit`.
- bpy.utils.units.to_value 4th arg `str_ref_unit`.
- bpy.utils.user_resource 2nd & 3rd args `subdir`, `create`
- bpy_extras.view3d_utils.location_3d_to_region_2d 4th arg `default`.
- bpy_extras.view3d_utils.region_2d_to_origin_3d 4th arg `clamp`.
- gpu.offscreen.unbind 1st arg `restore`.
- gpu_extras.batch.batch_for_shader 4th arg `indices`.
- gpu_extras.batch.presets.draw_circle_2d 4th arg `segments`.
- gpu_extras.presets.draw_circle_2d 4th arg `segments`.
- imbuf.types.ImBuf.resize 2nd arg `resize`.
- imbuf.write 2nd arg `filepath`.
- mathutils.kdtree.KDTree.find 2nd arg `filter`.
- nodeitems_utils.NodeCategory 3rd & 4th arg `descriptions`, `items`.
- nodeitems_utils.NodeItem 2nd..4th args `label`, `settings`, `poll`.
- nodeitems_utils.NodeItemCustom 1st & 2nd arg `poll`, `draw`.
- rna_prop_ui.draw 5th arg `use_edit`.
- rna_prop_ui.rna_idprop_ui_get 2nd arg `create`.
- rna_prop_ui.rna_idprop_ui_prop_clear 3rd arg `remove`.
- rna_prop_ui.rna_idprop_ui_prop_get 3rd arg `create`.
- rna_xml.xml2rna 2nd arg `root_rna`.
- rna_xml.xml_file_write 4th arg `skip_typemap`.
2021-06-08 18:03:14 +10:00
|
|
|
def _find_reference(rna_id, url_mapping, *, verbose=True):
|
2012-09-13 00:26:01 +00:00
|
|
|
if verbose:
|
|
|
|
|
print("online manual check for: '%s'... " % rna_id)
|
2018-01-22 09:50:41 +11:00
|
|
|
from fnmatch import fnmatchcase
|
|
|
|
|
# XXX, for some reason all RNA ID's are stored lowercase
|
|
|
|
|
# Adding case into all ID's isn't worth the hassle so force lowercase.
|
|
|
|
|
rna_id = rna_id.lower()
|
2012-06-01 20:38:40 +00:00
|
|
|
for pattern, url_suffix in url_mapping:
|
2018-01-22 09:50:41 +11:00
|
|
|
if fnmatchcase(rna_id, pattern):
|
2012-09-13 00:26:01 +00:00
|
|
|
if verbose:
|
|
|
|
|
print(" match found: '%s' --> '%s'" % (pattern, url_suffix))
|
2012-06-01 20:38:40 +00:00
|
|
|
return url_suffix
|
2012-09-13 00:26:01 +00:00
|
|
|
if verbose:
|
|
|
|
|
print("match not found")
|
2012-06-01 20:38:40 +00:00
|
|
|
return None
|
|
|
|
|
|
2012-09-13 00:26:01 +00:00
|
|
|
@staticmethod
|
|
|
|
|
def _lookup_rna_url(rna_id, verbose=True):
|
2012-08-25 14:07:51 +00:00
|
|
|
for prefix, url_manual_mapping in bpy.utils.manual_map():
|
2012-09-13 00:26:01 +00:00
|
|
|
rna_ref = WM_OT_doc_view_manual._find_reference(rna_id, url_manual_mapping, verbose=verbose)
|
2012-08-25 14:07:51 +00:00
|
|
|
if rna_ref is not None:
|
|
|
|
|
url = prefix + rna_ref
|
2013-04-20 13:23:53 +00:00
|
|
|
return url
|
2012-09-13 00:26:01 +00:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def execute(self, _context):
|
2021-01-07 23:24:23 +11:00
|
|
|
rna_id = _wm_doc_get_id(self.doc_id, do_url=False, report=self.report)
|
2012-09-13 00:26:01 +00:00
|
|
|
if rna_id is None:
|
2021-01-07 23:24:23 +11:00
|
|
|
return {'CANCELLED'}
|
2012-09-13 00:26:01 +00:00
|
|
|
|
|
|
|
|
url = self._lookup_rna_url(rna_id)
|
2012-08-25 14:07:51 +00:00
|
|
|
|
|
|
|
|
if url is None:
|
2015-06-08 11:33:03 +10:00
|
|
|
self.report(
|
2018-02-01 13:58:44 +11:00
|
|
|
{'WARNING'},
|
|
|
|
|
"No reference available %r, "
|
|
|
|
|
"Update info in 'rna_manual_reference.py' "
|
|
|
|
|
"or callback to bpy.utils.manual_map()" %
|
|
|
|
|
self.doc_id
|
|
|
|
|
)
|
2012-06-01 20:38:40 +00:00
|
|
|
return {'CANCELLED'}
|
|
|
|
|
else:
|
2012-08-25 14:07:51 +00:00
|
|
|
import webbrowser
|
|
|
|
|
webbrowser.open(url)
|
|
|
|
|
return {'FINISHED'}
|
2012-06-01 20:38:40 +00:00
|
|
|
|
|
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_doc_view(Operator):
|
2019-05-07 10:01:25 +10:00
|
|
|
"""Open online reference docs in a web browser"""
|
2009-11-04 20:21:08 +00:00
|
|
|
bl_idname = "wm.doc_view"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "View Documentation"
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
doc_id: doc_id
|
2019-10-14 17:35:21 +02:00
|
|
|
if bpy.app.version_cycle in {"release", "rc", "beta"}:
|
Blender: change bugfix release versioning from a/b/c to .1/.2/.3
The file subversion is no longer used in the Python API or user interface,
and is now internal to Blender.
User interface, Python API and file I/O metadata now use more consistent
formatting for version numbers. Official releases use "2.83.0", "2.83.1",
and releases under development use "2.90.0 Alpha", "2.90.0 Beta".
Some Python add-ons may need to lower the Blender version in bl_info to
(2, 83, 0) or (2, 90, 0) if they used a subversion number higher than 0.
https://wiki.blender.org/wiki/Reference/Release_Notes/2.83/Python_API#Compatibility
This change is in preparation of LTS releases, and also brings us more
in line with semantic versioning.
Fixes T76058.
Differential Revision: https://developer.blender.org/D7748
2020-05-25 10:49:04 +02:00
|
|
|
_prefix = ("https://docs.blender.org/api/%d.%d" %
|
|
|
|
|
(bpy.app.version[0], bpy.app.version[1]))
|
2011-04-01 15:13:58 +00:00
|
|
|
else:
|
2019-03-29 17:34:16 +01:00
|
|
|
_prefix = ("https://docs.blender.org/api/master")
|
2009-11-04 20:21:08 +00:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def execute(self, _context):
|
2021-01-07 23:24:23 +11:00
|
|
|
url = _wm_doc_get_id(self.doc_id, do_url=True, url_prefix=self._prefix, report=self.report)
|
2012-06-01 20:38:40 +00:00
|
|
|
if url is None:
|
2021-01-07 23:24:23 +11:00
|
|
|
return {'CANCELLED'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
import webbrowser
|
|
|
|
|
webbrowser.open(url)
|
|
|
|
|
|
2009-12-24 19:50:43 +00:00
|
|
|
return {'FINISHED'}
|
2009-11-04 20:21:08 +00:00
|
|
|
|
|
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_path = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Property Edit",
|
|
|
|
|
description="Property data_path edit",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
options={'HIDDEN'},
|
|
|
|
|
)
|
2011-08-18 12:20:10 +00:00
|
|
|
|
|
|
|
|
rna_value = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Property Value",
|
|
|
|
|
description="Property value edit",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2018-12-15 22:37:12 +03:00
|
|
|
rna_default = StringProperty(
|
|
|
|
|
name="Default Value",
|
|
|
|
|
description="Default value of the property. Important for NLA mixing",
|
|
|
|
|
maxlen=1024,
|
|
|
|
|
)
|
|
|
|
|
|
2020-12-09 17:17:27 +11:00
|
|
|
rna_custom_property = StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Property Name",
|
|
|
|
|
description="Property name edit",
|
2020-12-09 17:17:27 +11:00
|
|
|
# Match `MAX_IDPROP_NAME - 1` in Blender's source.
|
|
|
|
|
maxlen=63,
|
2018-06-26 19:41:37 +02:00
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_min = FloatProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Min",
|
2020-03-28 16:40:09 -04:00
|
|
|
description="Minimum value of the property",
|
2018-06-26 19:41:37 +02:00
|
|
|
default=-10000.0,
|
|
|
|
|
precision=3,
|
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2011-08-18 12:20:10 +00:00
|
|
|
rna_max = FloatProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Max",
|
2020-03-28 16:40:09 -04:00
|
|
|
description="Maximum value of the property",
|
2018-06-26 19:41:37 +02:00
|
|
|
default=10000.0,
|
|
|
|
|
precision=3,
|
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2016-02-24 02:53:11 +11:00
|
|
|
rna_use_soft_limits = BoolProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Use Soft Limits",
|
2020-04-20 15:26:37 +02:00
|
|
|
description="Limits the Property Value slider to a range, values outside the range must be inputted numerically",
|
2018-06-26 19:41:37 +02:00
|
|
|
)
|
2016-02-24 02:53:11 +11:00
|
|
|
|
2019-06-14 23:16:04 +02:00
|
|
|
rna_is_overridable_library = BoolProperty(
|
|
|
|
|
name="Is Library Overridable",
|
2020-12-24 11:07:32 -06:00
|
|
|
description="Allow the property to be overridden when the data-block is linked",
|
2018-06-26 22:56:39 +02:00
|
|
|
default=False,
|
|
|
|
|
)
|
2018-04-17 18:06:41 +02:00
|
|
|
|
2019-08-13 19:45:20 +03:00
|
|
|
# Most useful entries of rna_enum_property_subtype_items for number arrays:
|
|
|
|
|
rna_vector_subtype_items = (
|
|
|
|
|
('NONE', "Plain Data", "Data values without special behavior"),
|
|
|
|
|
('COLOR', "Linear Color", "Color in the linear space"),
|
|
|
|
|
('COLOR_GAMMA', "Gamma-Corrected Color", "Color in the gamma corrected space"),
|
|
|
|
|
('EULER', "Euler Angles", "Euler rotation angles in radians"),
|
|
|
|
|
('QUATERNION', "Quaternion Rotation", "Quaternion rotation (affects NLA blending)"),
|
|
|
|
|
)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2019-09-01 03:16:58 +10:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_properties_edit(Operator):
|
2020-03-28 16:40:09 -04:00
|
|
|
"""Edit the attributes of the property"""
|
2010-08-02 02:55:12 +00:00
|
|
|
bl_idname = "wm.properties_edit"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Edit Property"
|
2013-04-20 13:23:53 +00:00
|
|
|
# register only because invoke_props_popup requires.
|
|
|
|
|
bl_options = {'REGISTER', 'INTERNAL'}
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path
|
2020-12-09 17:17:27 +11:00
|
|
|
property: rna_custom_property
|
2018-07-11 22:18:09 +02:00
|
|
|
value: rna_value
|
2018-12-15 22:37:12 +03:00
|
|
|
default: rna_default
|
2018-07-11 22:18:09 +02:00
|
|
|
min: rna_min
|
|
|
|
|
max: rna_max
|
|
|
|
|
use_soft_limits: rna_use_soft_limits
|
2019-06-14 23:16:04 +02:00
|
|
|
is_overridable_library: rna_is_overridable_library
|
2018-07-11 22:18:09 +02:00
|
|
|
soft_min: rna_min
|
|
|
|
|
soft_max: rna_max
|
|
|
|
|
description: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Tooltip",
|
|
|
|
|
)
|
2019-08-13 19:45:20 +03:00
|
|
|
subtype: EnumProperty(
|
|
|
|
|
name="Subtype",
|
2021-03-16 12:19:13 +11:00
|
|
|
items=lambda self, _context: WM_OT_properties_edit.subtype_items,
|
2019-08-13 19:45:20 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
subtype_items = rna_vector_subtype_items
|
|
|
|
|
|
2019-08-14 23:25:30 +10:00
|
|
|
def _init_subtype(self, prop_type, is_array, subtype):
|
2019-08-13 19:45:20 +03:00
|
|
|
subtype = subtype or 'NONE'
|
|
|
|
|
subtype_items = rna_vector_subtype_items
|
|
|
|
|
|
|
|
|
|
# Add a temporary enum entry to preserve unknown subtypes
|
|
|
|
|
if not any(subtype == item[0] for item in subtype_items):
|
|
|
|
|
subtype_items += ((subtype, subtype, ""),)
|
|
|
|
|
|
|
|
|
|
WM_OT_properties_edit.subtype_items = subtype_items
|
|
|
|
|
self.subtype = subtype
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2016-02-24 02:53:11 +11:00
|
|
|
def _cmp_props_get(self):
|
|
|
|
|
# Changing these properties will refresh the UI
|
|
|
|
|
return {
|
|
|
|
|
"use_soft_limits": self.use_soft_limits,
|
|
|
|
|
"soft_range": (self.soft_min, self.soft_max),
|
|
|
|
|
"hard_range": (self.min, self.max),
|
2018-02-01 13:58:44 +11:00
|
|
|
}
|
2016-02-24 02:53:11 +11:00
|
|
|
|
2018-12-15 22:37:12 +03:00
|
|
|
def get_value_eval(self):
|
2021-09-01 11:47:59 -05:00
|
|
|
failed = False
|
2018-12-15 22:37:12 +03:00
|
|
|
try:
|
|
|
|
|
value_eval = eval(self.value)
|
2020-09-30 20:09:02 +10:00
|
|
|
# assert else None -> None, not "None", see T33431.
|
2018-12-15 22:37:12 +03:00
|
|
|
assert(type(value_eval) in {str, float, int, bool, tuple, list})
|
|
|
|
|
except:
|
2021-09-01 11:47:59 -05:00
|
|
|
failed = True
|
2018-12-15 22:37:12 +03:00
|
|
|
value_eval = self.value
|
|
|
|
|
|
2021-09-01 11:47:59 -05:00
|
|
|
return value_eval, failed
|
2018-12-15 22:37:12 +03:00
|
|
|
|
|
|
|
|
def get_default_eval(self):
|
2021-09-01 11:47:59 -05:00
|
|
|
failed = False
|
2018-12-15 22:37:12 +03:00
|
|
|
try:
|
|
|
|
|
default_eval = eval(self.default)
|
2020-09-30 20:09:02 +10:00
|
|
|
# assert else None -> None, not "None", see T33431.
|
2018-12-15 22:37:12 +03:00
|
|
|
assert(type(default_eval) in {str, float, int, bool, tuple, list})
|
|
|
|
|
except:
|
2021-09-01 11:47:59 -05:00
|
|
|
failed = True
|
2018-12-15 22:37:12 +03:00
|
|
|
default_eval = self.default
|
|
|
|
|
|
2021-09-01 11:47:59 -05:00
|
|
|
return default_eval, failed
|
2018-12-15 22:37:12 +03:00
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
def execute(self, context):
|
2015-09-11 04:26:30 +10:00
|
|
|
from rna_prop_ui import (
|
2017-03-25 11:07:05 +11:00
|
|
|
rna_idprop_ui_prop_update,
|
2019-08-10 14:42:14 +03:00
|
|
|
rna_idprop_value_item_type,
|
2017-03-25 11:07:05 +11:00
|
|
|
)
|
2013-07-03 01:20:32 +00:00
|
|
|
|
2010-09-09 18:03:57 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
|
prop = self.property
|
2020-12-09 17:01:03 +11:00
|
|
|
prop_escape = bpy.utils.escape_identifier(prop)
|
2011-03-21 23:53:19 +00:00
|
|
|
|
|
|
|
|
prop_old = getattr(self, "_last_prop", [None])[0]
|
|
|
|
|
|
|
|
|
|
if prop_old is None:
|
|
|
|
|
self.report({'ERROR'}, "Direct execution not supported")
|
|
|
|
|
return {'CANCELLED'}
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2021-09-01 11:47:59 -05:00
|
|
|
value_eval, value_failed = self.get_value_eval()
|
|
|
|
|
default_eval, default_failed = self.get_default_eval()
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
|
# First remove
|
|
|
|
|
item = eval("context.%s" % data_path)
|
2020-08-18 12:43:42 +02:00
|
|
|
|
|
|
|
|
if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference):
|
|
|
|
|
self.report({'ERROR'}, "Cannot edit properties from override data")
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
2013-12-17 09:40:52 +01:00
|
|
|
prop_type_old = type(item[prop_old])
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2021-09-01 11:47:59 -05:00
|
|
|
# Deleting the property will also remove the UI data.
|
2020-08-21 12:34:41 +02:00
|
|
|
del item[prop_old]
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
|
# Reassign
|
2020-08-21 12:34:41 +02:00
|
|
|
item[prop] = value_eval
|
2020-12-09 17:01:03 +11:00
|
|
|
item.property_overridable_library_set('["%s"]' % prop_escape, self.is_overridable_library)
|
2015-09-11 04:26:30 +10:00
|
|
|
rna_idprop_ui_prop_update(item, prop)
|
|
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
self._last_prop[:] = [prop]
|
|
|
|
|
|
2019-08-10 14:42:14 +03:00
|
|
|
prop_value = item[prop]
|
|
|
|
|
prop_type_new = type(prop_value)
|
|
|
|
|
prop_type, is_array = rna_idprop_value_item_type(prop_value)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
Refactor IDProperty UI data storage
The storage of IDProperty UI data (min, max, default value, etc) is
quite complicated. For every property, retrieving a single one of these
values involves three string lookups. First for the "_RNA_UI" group
property, then another for a group with the property's name, then for
the data value name. Not only is this inefficient, it's hard to reason
about, unintuitive, and not at all self-explanatory.
This commit replaces that system with a UI data struct directly in the
IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond
storing the description, name, and RNA subtype, derived structs are used
to store type specific UI data like min and max.
Note that this means that addons using (abusing) the `_RNA_UI` custom
property will have to be changed. A few places in the addons repository
will be changed after this commit with D9919.
**Before**
Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group,
then the subgroup for the original property, then specific UI data
is accessed like any other IDProperty.
```
prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True)
prop["min"] = 1.0
```
**After**
After, the `id_properties_ui` function for RNA structs returns a python
object specifically for managing an IDProperty's UI data.
```
ui_data = idproperties_owner.id_properties_ui("prop_name")
ui_data.update(min=1.0)
```
In addition to `update`, there are now other functions:
- `as_dict`: Returns a dictionary of the property's UI data.
- `clear`: Removes the property's UI data.
- `update_from`: Copy UI data between properties,
even if they have different owners.
Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
|
|
|
if prop_type == int:
|
2021-08-30 22:21:11 -05:00
|
|
|
ui_data = item.id_properties_ui(prop)
|
Refactor IDProperty UI data storage
The storage of IDProperty UI data (min, max, default value, etc) is
quite complicated. For every property, retrieving a single one of these
values involves three string lookups. First for the "_RNA_UI" group
property, then another for a group with the property's name, then for
the data value name. Not only is this inefficient, it's hard to reason
about, unintuitive, and not at all self-explanatory.
This commit replaces that system with a UI data struct directly in the
IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond
storing the description, name, and RNA subtype, derived structs are used
to store type specific UI data like min and max.
Note that this means that addons using (abusing) the `_RNA_UI` custom
property will have to be changed. A few places in the addons repository
will be changed after this commit with D9919.
**Before**
Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group,
then the subgroup for the original property, then specific UI data
is accessed like any other IDProperty.
```
prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True)
prop["min"] = 1.0
```
**After**
After, the `id_properties_ui` function for RNA structs returns a python
object specifically for managing an IDProperty's UI data.
```
ui_data = idproperties_owner.id_properties_ui("prop_name")
ui_data.update(min=1.0)
```
In addition to `update`, there are now other functions:
- `as_dict`: Returns a dictionary of the property's UI data.
- `clear`: Removes the property's UI data.
- `update_from`: Copy UI data between properties,
even if they have different owners.
Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
|
|
|
if type(default_eval) == str:
|
|
|
|
|
self.report({'WARNING'}, "Could not evaluate number from default value")
|
|
|
|
|
default_eval = None
|
|
|
|
|
elif hasattr(default_eval, "__len__"):
|
|
|
|
|
default_eval = [int(round(value)) for value in default_eval]
|
|
|
|
|
ui_data.update(
|
|
|
|
|
min=int(round(self.min)),
|
|
|
|
|
max=int(round(self.max)),
|
|
|
|
|
soft_min=int(round(self.soft_min)),
|
|
|
|
|
soft_max=int(round(self.soft_max)),
|
|
|
|
|
default=default_eval,
|
2021-08-30 22:21:11 -05:00
|
|
|
subtype=self.subtype,
|
|
|
|
|
description=self.description
|
Refactor IDProperty UI data storage
The storage of IDProperty UI data (min, max, default value, etc) is
quite complicated. For every property, retrieving a single one of these
values involves three string lookups. First for the "_RNA_UI" group
property, then another for a group with the property's name, then for
the data value name. Not only is this inefficient, it's hard to reason
about, unintuitive, and not at all self-explanatory.
This commit replaces that system with a UI data struct directly in the
IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond
storing the description, name, and RNA subtype, derived structs are used
to store type specific UI data like min and max.
Note that this means that addons using (abusing) the `_RNA_UI` custom
property will have to be changed. A few places in the addons repository
will be changed after this commit with D9919.
**Before**
Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group,
then the subgroup for the original property, then specific UI data
is accessed like any other IDProperty.
```
prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True)
prop["min"] = 1.0
```
**After**
After, the `id_properties_ui` function for RNA structs returns a python
object specifically for managing an IDProperty's UI data.
```
ui_data = idproperties_owner.id_properties_ui("prop_name")
ui_data.update(min=1.0)
```
In addition to `update`, there are now other functions:
- `as_dict`: Returns a dictionary of the property's UI data.
- `clear`: Removes the property's UI data.
- `update_from`: Copy UI data between properties,
even if they have different owners.
Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
|
|
|
)
|
|
|
|
|
elif prop_type == float:
|
2021-08-30 22:21:11 -05:00
|
|
|
ui_data = item.id_properties_ui(prop)
|
Refactor IDProperty UI data storage
The storage of IDProperty UI data (min, max, default value, etc) is
quite complicated. For every property, retrieving a single one of these
values involves three string lookups. First for the "_RNA_UI" group
property, then another for a group with the property's name, then for
the data value name. Not only is this inefficient, it's hard to reason
about, unintuitive, and not at all self-explanatory.
This commit replaces that system with a UI data struct directly in the
IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond
storing the description, name, and RNA subtype, derived structs are used
to store type specific UI data like min and max.
Note that this means that addons using (abusing) the `_RNA_UI` custom
property will have to be changed. A few places in the addons repository
will be changed after this commit with D9919.
**Before**
Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group,
then the subgroup for the original property, then specific UI data
is accessed like any other IDProperty.
```
prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True)
prop["min"] = 1.0
```
**After**
After, the `id_properties_ui` function for RNA structs returns a python
object specifically for managing an IDProperty's UI data.
```
ui_data = idproperties_owner.id_properties_ui("prop_name")
ui_data.update(min=1.0)
```
In addition to `update`, there are now other functions:
- `as_dict`: Returns a dictionary of the property's UI data.
- `clear`: Removes the property's UI data.
- `update_from`: Copy UI data between properties,
even if they have different owners.
Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
|
|
|
if type(default_eval) == str:
|
|
|
|
|
self.report({'WARNING'}, "Could not evaluate number from default value")
|
|
|
|
|
default_eval = None
|
|
|
|
|
ui_data.update(
|
|
|
|
|
min=self.min,
|
|
|
|
|
max=self.max,
|
|
|
|
|
soft_min=self.soft_min,
|
|
|
|
|
soft_max=self.soft_max,
|
|
|
|
|
default=default_eval,
|
2021-08-30 22:21:11 -05:00
|
|
|
subtype=self.subtype,
|
|
|
|
|
description=self.description
|
|
|
|
|
)
|
2021-09-01 11:47:59 -05:00
|
|
|
elif prop_type == str and not is_array and not default_failed: # String arrays do not support UI data.
|
2021-08-30 22:21:11 -05:00
|
|
|
ui_data = item.id_properties_ui(prop)
|
|
|
|
|
ui_data.update(
|
|
|
|
|
default=self.default,
|
|
|
|
|
subtype=self.subtype,
|
|
|
|
|
description=self.description
|
Refactor IDProperty UI data storage
The storage of IDProperty UI data (min, max, default value, etc) is
quite complicated. For every property, retrieving a single one of these
values involves three string lookups. First for the "_RNA_UI" group
property, then another for a group with the property's name, then for
the data value name. Not only is this inefficient, it's hard to reason
about, unintuitive, and not at all self-explanatory.
This commit replaces that system with a UI data struct directly in the
IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond
storing the description, name, and RNA subtype, derived structs are used
to store type specific UI data like min and max.
Note that this means that addons using (abusing) the `_RNA_UI` custom
property will have to be changed. A few places in the addons repository
will be changed after this commit with D9919.
**Before**
Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group,
then the subgroup for the original property, then specific UI data
is accessed like any other IDProperty.
```
prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True)
prop["min"] = 1.0
```
**After**
After, the `id_properties_ui` function for RNA structs returns a python
object specifically for managing an IDProperty's UI data.
```
ui_data = idproperties_owner.id_properties_ui("prop_name")
ui_data.update(min=1.0)
```
In addition to `update`, there are now other functions:
- `as_dict`: Returns a dictionary of the property's UI data.
- `clear`: Removes the property's UI data.
- `update_from`: Copy UI data between properties,
even if they have different owners.
Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
|
|
|
)
|
2019-08-10 14:42:14 +03:00
|
|
|
|
2013-12-17 09:40:52 +01:00
|
|
|
# If we have changed the type of the property, update its potential anim curves!
|
2019-08-10 14:42:14 +03:00
|
|
|
if prop_type_old != prop_type_new:
|
2020-12-09 17:01:03 +11:00
|
|
|
data_path = '["%s"]' % prop_escape
|
2013-12-17 09:40:52 +01:00
|
|
|
done = set()
|
2014-02-19 08:02:59 +11:00
|
|
|
|
2013-12-17 09:40:52 +01:00
|
|
|
def _update(fcurves):
|
|
|
|
|
for fcu in fcurves:
|
|
|
|
|
if fcu not in done and fcu.data_path == data_path:
|
|
|
|
|
fcu.update_autoflags(item)
|
|
|
|
|
done.add(fcu)
|
|
|
|
|
|
|
|
|
|
def _update_strips(strips):
|
|
|
|
|
for st in strips:
|
2014-02-25 16:49:14 +11:00
|
|
|
if st.type == 'CLIP' and st.action:
|
2013-12-17 09:40:52 +01:00
|
|
|
_update(st.action.fcurves)
|
2014-02-25 16:49:14 +11:00
|
|
|
elif st.type == 'META':
|
2013-12-17 09:40:52 +01:00
|
|
|
_update_strips(st.strips)
|
|
|
|
|
|
|
|
|
|
adt = getattr(item, "animation_data", None)
|
|
|
|
|
if adt is not None:
|
|
|
|
|
if adt.action:
|
|
|
|
|
_update(adt.action.fcurves)
|
|
|
|
|
if adt.drivers:
|
|
|
|
|
_update(adt.drivers)
|
|
|
|
|
if adt.nla_tracks:
|
|
|
|
|
for nt in adt.nla_tracks:
|
|
|
|
|
_update_strips(nt.strips)
|
|
|
|
|
|
2020-09-30 20:09:02 +10:00
|
|
|
# Otherwise existing buttons which reference freed
|
|
|
|
|
# memory may crash Blender T26510.
|
2011-03-24 11:38:20 +00:00
|
|
|
# context.area.tag_redraw()
|
|
|
|
|
for win in context.window_manager.windows:
|
|
|
|
|
for area in win.screen.areas:
|
|
|
|
|
area.tag_redraw()
|
2011-03-18 06:36:57 +00:00
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def invoke(self, context, _event):
|
2019-08-13 19:45:20 +03:00
|
|
|
from rna_prop_ui import (
|
|
|
|
|
rna_idprop_value_to_python,
|
|
|
|
|
rna_idprop_value_item_type
|
|
|
|
|
)
|
2013-07-03 01:20:32 +00:00
|
|
|
|
2020-12-09 17:01:03 +11:00
|
|
|
prop = self.property
|
|
|
|
|
prop_escape = bpy.utils.escape_identifier(prop)
|
|
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
if not data_path:
|
2011-03-21 23:53:19 +00:00
|
|
|
self.report({'ERROR'}, "Data path not set")
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
2020-12-09 17:01:03 +11:00
|
|
|
self._last_prop = [prop]
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
item = eval("context.%s" % data_path)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2020-08-18 12:43:42 +02:00
|
|
|
if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference):
|
|
|
|
|
self.report({'ERROR'}, "Cannot edit properties from override data")
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
2018-12-15 18:50:44 +03:00
|
|
|
# retrieve overridable static
|
2020-12-09 17:01:03 +11:00
|
|
|
is_overridable = item.is_property_overridable_library('["%s"]' % prop_escape)
|
2020-08-21 12:34:41 +02:00
|
|
|
self.is_overridable_library = bool(is_overridable)
|
|
|
|
|
|
2018-12-15 22:37:12 +03:00
|
|
|
# default default value
|
2021-09-01 11:47:59 -05:00
|
|
|
value, value_failed = self.get_value_eval()
|
|
|
|
|
prop_type, is_array = rna_idprop_value_item_type(value)
|
2018-12-20 13:01:40 +11:00
|
|
|
if prop_type in {int, float}:
|
2018-12-15 22:37:12 +03:00
|
|
|
self.default = str(prop_type(0))
|
|
|
|
|
else:
|
|
|
|
|
self.default = ""
|
|
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
# setup defaults
|
Refactor IDProperty UI data storage
The storage of IDProperty UI data (min, max, default value, etc) is
quite complicated. For every property, retrieving a single one of these
values involves three string lookups. First for the "_RNA_UI" group
property, then another for a group with the property's name, then for
the data value name. Not only is this inefficient, it's hard to reason
about, unintuitive, and not at all self-explanatory.
This commit replaces that system with a UI data struct directly in the
IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond
storing the description, name, and RNA subtype, derived structs are used
to store type specific UI data like min and max.
Note that this means that addons using (abusing) the `_RNA_UI` custom
property will have to be changed. A few places in the addons repository
will be changed after this commit with D9919.
**Before**
Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group,
then the subgroup for the original property, then specific UI data
is accessed like any other IDProperty.
```
prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True)
prop["min"] = 1.0
```
**After**
After, the `id_properties_ui` function for RNA structs returns a python
object specifically for managing an IDProperty's UI data.
```
ui_data = idproperties_owner.id_properties_ui("prop_name")
ui_data.update(min=1.0)
```
In addition to `update`, there are now other functions:
- `as_dict`: Returns a dictionary of the property's UI data.
- `clear`: Removes the property's UI data.
- `update_from`: Copy UI data between properties,
even if they have different owners.
Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
|
|
|
if prop_type in {int, float}:
|
2021-08-30 22:21:11 -05:00
|
|
|
ui_data = item.id_properties_ui(prop)
|
|
|
|
|
rna_data = ui_data.as_dict()
|
|
|
|
|
self.subtype = rna_data["subtype"]
|
Refactor IDProperty UI data storage
The storage of IDProperty UI data (min, max, default value, etc) is
quite complicated. For every property, retrieving a single one of these
values involves three string lookups. First for the "_RNA_UI" group
property, then another for a group with the property's name, then for
the data value name. Not only is this inefficient, it's hard to reason
about, unintuitive, and not at all self-explanatory.
This commit replaces that system with a UI data struct directly in the
IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond
storing the description, name, and RNA subtype, derived structs are used
to store type specific UI data like min and max.
Note that this means that addons using (abusing) the `_RNA_UI` custom
property will have to be changed. A few places in the addons repository
will be changed after this commit with D9919.
**Before**
Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group,
then the subgroup for the original property, then specific UI data
is accessed like any other IDProperty.
```
prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True)
prop["min"] = 1.0
```
**After**
After, the `id_properties_ui` function for RNA structs returns a python
object specifically for managing an IDProperty's UI data.
```
ui_data = idproperties_owner.id_properties_ui("prop_name")
ui_data.update(min=1.0)
```
In addition to `update`, there are now other functions:
- `as_dict`: Returns a dictionary of the property's UI data.
- `clear`: Removes the property's UI data.
- `update_from`: Copy UI data between properties,
even if they have different owners.
Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
|
|
|
self.min = rna_data["min"]
|
|
|
|
|
self.max = rna_data["max"]
|
|
|
|
|
self.soft_min = rna_data["soft_min"]
|
|
|
|
|
self.soft_max = rna_data["soft_max"]
|
2016-02-24 02:53:11 +11:00
|
|
|
self.use_soft_limits = (
|
2018-02-01 13:58:44 +11:00
|
|
|
self.min != self.soft_min or
|
|
|
|
|
self.max != self.soft_max
|
|
|
|
|
)
|
2021-08-30 22:21:11 -05:00
|
|
|
self.default = str(rna_data["default"])
|
2021-09-22 13:12:48 +03:00
|
|
|
self.description = rna_data.get("description", "")
|
|
|
|
|
elif prop_type == str and not is_array and not value_failed: # String arrays do not support UI data.
|
2021-08-30 22:21:11 -05:00
|
|
|
ui_data = item.id_properties_ui(prop)
|
|
|
|
|
rna_data = ui_data.as_dict()
|
|
|
|
|
self.subtype = rna_data["subtype"]
|
Refactor IDProperty UI data storage
The storage of IDProperty UI data (min, max, default value, etc) is
quite complicated. For every property, retrieving a single one of these
values involves three string lookups. First for the "_RNA_UI" group
property, then another for a group with the property's name, then for
the data value name. Not only is this inefficient, it's hard to reason
about, unintuitive, and not at all self-explanatory.
This commit replaces that system with a UI data struct directly in the
IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond
storing the description, name, and RNA subtype, derived structs are used
to store type specific UI data like min and max.
Note that this means that addons using (abusing) the `_RNA_UI` custom
property will have to be changed. A few places in the addons repository
will be changed after this commit with D9919.
**Before**
Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group,
then the subgroup for the original property, then specific UI data
is accessed like any other IDProperty.
```
prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True)
prop["min"] = 1.0
```
**After**
After, the `id_properties_ui` function for RNA structs returns a python
object specifically for managing an IDProperty's UI data.
```
ui_data = idproperties_owner.id_properties_ui("prop_name")
ui_data.update(min=1.0)
```
In addition to `update`, there are now other functions:
- `as_dict`: Returns a dictionary of the property's UI data.
- `clear`: Removes the property's UI data.
- `update_from`: Copy UI data between properties,
even if they have different owners.
Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
|
|
|
self.default = str(rna_data["default"])
|
2021-09-22 13:12:48 +03:00
|
|
|
self.description = rna_data.get("description", "")
|
|
|
|
|
else:
|
|
|
|
|
self.min = self.soft_min = 0
|
|
|
|
|
self.max = self.soft_max = 1
|
|
|
|
|
self.use_soft_limits = False
|
|
|
|
|
self.description = ""
|
2016-02-24 02:53:11 +11:00
|
|
|
|
Refactor IDProperty UI data storage
The storage of IDProperty UI data (min, max, default value, etc) is
quite complicated. For every property, retrieving a single one of these
values involves three string lookups. First for the "_RNA_UI" group
property, then another for a group with the property's name, then for
the data value name. Not only is this inefficient, it's hard to reason
about, unintuitive, and not at all self-explanatory.
This commit replaces that system with a UI data struct directly in the
IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond
storing the description, name, and RNA subtype, derived structs are used
to store type specific UI data like min and max.
Note that this means that addons using (abusing) the `_RNA_UI` custom
property will have to be changed. A few places in the addons repository
will be changed after this commit with D9919.
**Before**
Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group,
then the subgroup for the original property, then specific UI data
is accessed like any other IDProperty.
```
prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True)
prop["min"] = 1.0
```
**After**
After, the `id_properties_ui` function for RNA structs returns a python
object specifically for managing an IDProperty's UI data.
```
ui_data = idproperties_owner.id_properties_ui("prop_name")
ui_data.update(min=1.0)
```
In addition to `update`, there are now other functions:
- `as_dict`: Returns a dictionary of the property's UI data.
- `clear`: Removes the property's UI data.
- `update_from`: Copy UI data between properties,
even if they have different owners.
Differential Revision: https://developer.blender.org/D9697
2021-08-27 08:27:24 -05:00
|
|
|
self._init_subtype(prop_type, is_array, self.subtype)
|
2019-08-13 19:45:20 +03:00
|
|
|
|
2016-02-24 02:53:11 +11:00
|
|
|
# store for comparison
|
|
|
|
|
self._cmp_props = self._cmp_props_get()
|
|
|
|
|
|
2010-09-02 04:53:05 +00:00
|
|
|
wm = context.window_manager
|
2011-01-15 19:15:35 +00:00
|
|
|
return wm.invoke_props_dialog(self)
|
2010-02-14 11:21:21 +00:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def check(self, _context):
|
2016-02-24 02:53:11 +11:00
|
|
|
cmp_props = self._cmp_props_get()
|
|
|
|
|
changed = False
|
|
|
|
|
if self._cmp_props != cmp_props:
|
|
|
|
|
if cmp_props["use_soft_limits"]:
|
|
|
|
|
if cmp_props["soft_range"] != self._cmp_props["soft_range"]:
|
|
|
|
|
self.min = min(self.min, self.soft_min)
|
|
|
|
|
self.max = max(self.max, self.soft_max)
|
|
|
|
|
changed = True
|
|
|
|
|
if cmp_props["hard_range"] != self._cmp_props["hard_range"]:
|
|
|
|
|
self.soft_min = max(self.min, self.soft_min)
|
|
|
|
|
self.soft_max = min(self.max, self.soft_max)
|
|
|
|
|
changed = True
|
|
|
|
|
else:
|
|
|
|
|
if cmp_props["soft_range"] != cmp_props["hard_range"]:
|
|
|
|
|
self.soft_min = self.min
|
|
|
|
|
self.soft_max = self.max
|
|
|
|
|
changed = True
|
|
|
|
|
|
|
|
|
|
changed |= (cmp_props["use_soft_limits"] != self._cmp_props["use_soft_limits"])
|
|
|
|
|
|
|
|
|
|
if changed:
|
|
|
|
|
cmp_props = self._cmp_props_get()
|
|
|
|
|
|
|
|
|
|
self._cmp_props = cmp_props
|
|
|
|
|
|
|
|
|
|
return changed
|
|
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def draw(self, _context):
|
2019-08-10 14:42:14 +03:00
|
|
|
from rna_prop_ui import (
|
|
|
|
|
rna_idprop_value_item_type,
|
|
|
|
|
)
|
|
|
|
|
|
2016-02-24 02:53:11 +11:00
|
|
|
layout = self.layout
|
2020-07-17 19:54:11 -04:00
|
|
|
|
|
|
|
|
layout.use_property_split = True
|
|
|
|
|
layout.use_property_decorate = False
|
|
|
|
|
|
2016-02-24 02:53:11 +11:00
|
|
|
layout.prop(self, "property")
|
|
|
|
|
layout.prop(self, "value")
|
2018-12-15 22:37:12 +03:00
|
|
|
|
2021-09-22 13:12:48 +03:00
|
|
|
value, value_failed = self.get_value_eval()
|
2019-08-10 14:42:14 +03:00
|
|
|
proptype, is_array = rna_idprop_value_item_type(value)
|
|
|
|
|
|
2018-12-15 22:37:12 +03:00
|
|
|
row = layout.row()
|
2020-06-30 15:01:56 +02:00
|
|
|
row.enabled = proptype in {int, float, str}
|
2018-12-15 22:37:12 +03:00
|
|
|
row.prop(self, "default")
|
|
|
|
|
|
2020-07-17 19:54:11 -04:00
|
|
|
col = layout.column(align=True)
|
|
|
|
|
col.prop(self, "min")
|
|
|
|
|
col.prop(self, "max")
|
2016-02-24 02:53:11 +11:00
|
|
|
|
2020-07-17 19:54:11 -04:00
|
|
|
col = layout.column()
|
|
|
|
|
col.prop(self, "is_overridable_library")
|
|
|
|
|
col.prop(self, "use_soft_limits")
|
2016-02-24 02:53:11 +11:00
|
|
|
|
2020-07-17 19:54:11 -04:00
|
|
|
col = layout.column(align=True)
|
|
|
|
|
col.enabled = self.use_soft_limits
|
|
|
|
|
col.prop(self, "soft_min", text="Soft Min")
|
|
|
|
|
col.prop(self, "soft_max", text="Max")
|
2016-02-24 02:53:11 +11:00
|
|
|
layout.prop(self, "description")
|
|
|
|
|
|
2019-08-13 19:45:20 +03:00
|
|
|
if is_array and proptype == float:
|
|
|
|
|
layout.prop(self, "subtype")
|
|
|
|
|
|
2010-03-14 23:26:17 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_properties_add(Operator):
|
2020-03-28 16:40:09 -04:00
|
|
|
"""Add your own property to the data-block"""
|
2010-08-02 02:55:12 +00:00
|
|
|
bl_idname = "wm.properties_add"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Add Property"
|
2013-04-20 13:23:53 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2010-02-14 11:21:21 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path
|
2010-02-14 11:21:21 +00:00
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
def execute(self, context):
|
2015-09-11 04:26:30 +10:00
|
|
|
from rna_prop_ui import (
|
2019-03-30 12:53:39 +03:00
|
|
|
rna_idprop_ui_create,
|
2017-03-25 11:07:05 +11:00
|
|
|
)
|
2013-07-03 01:20:32 +00:00
|
|
|
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
|
item = eval("context.%s" % data_path)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2020-08-18 12:43:42 +02:00
|
|
|
if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference):
|
|
|
|
|
self.report({'ERROR'}, "Cannot add properties to override data")
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
def unique_name(names):
|
2011-12-22 03:56:21 +00:00
|
|
|
prop = "prop"
|
2010-08-02 02:55:12 +00:00
|
|
|
prop_new = prop
|
|
|
|
|
i = 1
|
|
|
|
|
while prop_new in names:
|
|
|
|
|
prop_new = prop + str(i)
|
|
|
|
|
i += 1
|
|
|
|
|
|
|
|
|
|
return prop_new
|
|
|
|
|
|
2017-03-25 11:07:05 +11:00
|
|
|
prop = unique_name({
|
|
|
|
|
*item.keys(),
|
|
|
|
|
*type(item).bl_rna.properties.keys(),
|
|
|
|
|
})
|
2012-10-26 10:54:02 +00:00
|
|
|
|
2019-04-16 16:00:24 +02:00
|
|
|
rna_idprop_ui_create(item, prop, default=1.0)
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
2010-02-14 11:21:21 +00:00
|
|
|
|
2011-05-02 17:29:30 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_properties_context_change(Operator):
|
2017-04-14 20:01:43 +10:00
|
|
|
"""Jump to a different tab inside the properties editor"""
|
2011-04-25 13:47:15 +00:00
|
|
|
bl_idname = "wm.properties_context_change"
|
|
|
|
|
bl_label = ""
|
2013-04-20 13:23:53 +00:00
|
|
|
bl_options = {'INTERNAL'}
|
2011-04-25 13:47:15 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
context: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
name="Context",
|
|
|
|
|
maxlen=64,
|
|
|
|
|
)
|
2011-04-25 13:47:15 +00:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2011-08-18 15:25:18 +00:00
|
|
|
context.space_data.context = self.context
|
2011-04-25 13:47:15 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
2010-02-22 23:32:58 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_properties_remove(Operator):
|
2012-07-03 09:02:41 +00:00
|
|
|
"""Internal use (edit a property data_path)"""
|
2010-08-02 02:55:12 +00:00
|
|
|
bl_idname = "wm.properties_remove"
|
2011-09-15 13:20:18 +00:00
|
|
|
bl_label = "Remove Property"
|
2013-04-20 13:23:53 +00:00
|
|
|
bl_options = {'UNDO', 'INTERNAL'}
|
2010-08-02 02:55:12 +00:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
data_path: rna_path
|
2020-12-09 17:17:27 +11:00
|
|
|
property: rna_custom_property
|
2010-08-02 02:55:12 +00:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
2015-09-11 04:26:30 +10:00
|
|
|
from rna_prop_ui import (
|
2017-03-25 11:07:05 +11:00
|
|
|
rna_idprop_ui_prop_update,
|
|
|
|
|
)
|
2011-08-18 15:25:18 +00:00
|
|
|
data_path = self.data_path
|
|
|
|
|
item = eval("context.%s" % data_path)
|
2020-08-18 12:43:42 +02:00
|
|
|
|
|
|
|
|
if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference):
|
|
|
|
|
self.report({'ERROR'}, "Cannot remove properties from override data")
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
2015-03-26 00:10:39 +11:00
|
|
|
prop = self.property
|
2015-09-11 04:26:30 +10:00
|
|
|
rna_idprop_ui_prop_update(item, prop)
|
2015-03-26 00:10:39 +11:00
|
|
|
del item[prop]
|
|
|
|
|
|
2010-08-02 02:55:12 +00:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
2010-09-07 15:17:42 +00:00
|
|
|
|
2011-08-12 06:57:00 +00:00
|
|
|
class WM_OT_sysinfo(Operator):
|
2016-01-07 00:18:40 +11:00
|
|
|
"""Generate system information, saved into a text file"""
|
2016-01-03 23:21:44 +11:00
|
|
|
|
2010-10-16 17:26:40 +00:00
|
|
|
bl_idname = "wm.sysinfo"
|
2016-01-07 00:18:40 +11:00
|
|
|
bl_label = "Save System Info"
|
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
filepath: StringProperty(
|
2018-06-26 19:41:37 +02:00
|
|
|
subtype='FILE_PATH',
|
|
|
|
|
options={'SKIP_SAVE'},
|
|
|
|
|
)
|
2010-10-16 17:26:40 +00:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def execute(self, _context):
|
2010-10-16 17:26:40 +00:00
|
|
|
import sys_info
|
2016-01-07 00:18:40 +11:00
|
|
|
sys_info.write_sysinfo(self.filepath)
|
2010-10-16 17:26:40 +00:00
|
|
|
return {'FINISHED'}
|
2011-03-23 13:04:35 +00:00
|
|
|
|
2019-05-10 17:15:05 +02:00
|
|
|
def invoke(self, context, _event):
|
2016-01-07 00:18:40 +11:00
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
if not self.filepath:
|
|
|
|
|
self.filepath = os.path.join(
|
2018-06-26 19:41:37 +02:00
|
|
|
os.path.expanduser("~"), "system-info.txt")
|
2016-01-07 00:18:40 +11:00
|
|
|
|
|
|
|
|
wm = context.window_manager
|
|
|
|
|
wm.fileselect_add(self)
|
|
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
|
2011-03-23 13:04:35 +00:00
|
|
|
|
2011-09-22 19:50:41 +00:00
|
|
|
class WM_OT_operator_cheat_sheet(Operator):
|
2020-12-24 11:07:32 -06:00
|
|
|
"""List all the operators in a text-block, useful for scripting"""
|
2011-09-22 19:50:41 +00:00
|
|
|
bl_idname = "wm.operator_cheat_sheet"
|
|
|
|
|
bl_label = "Operator Cheat Sheet"
|
|
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def execute(self, _context):
|
2011-09-22 19:50:41 +00:00
|
|
|
op_strings = []
|
|
|
|
|
tot = 0
|
|
|
|
|
for op_module_name in dir(bpy.ops):
|
|
|
|
|
op_module = getattr(bpy.ops, op_module_name)
|
|
|
|
|
for op_submodule_name in dir(op_module):
|
|
|
|
|
op = getattr(op_module, op_submodule_name)
|
|
|
|
|
text = repr(op)
|
2011-12-22 03:56:21 +00:00
|
|
|
if text.split("\n")[-1].startswith("bpy.ops."):
|
2011-09-22 19:50:41 +00:00
|
|
|
op_strings.append(text)
|
|
|
|
|
tot += 1
|
|
|
|
|
|
|
|
|
|
op_strings.append('')
|
|
|
|
|
|
|
|
|
|
textblock = bpy.data.texts.new("OperatorList.txt")
|
|
|
|
|
textblock.write('# %d Operators\n\n' % tot)
|
|
|
|
|
textblock.write('\n'.join(op_strings))
|
2020-12-21 11:35:02 +01:00
|
|
|
self.report({'INFO'}, "See OperatorList.txt text block")
|
2011-09-22 19:50:41 +00:00
|
|
|
return {'FINISHED'}
|
2011-09-26 15:39:15 +00:00
|
|
|
|
2012-01-01 08:52:54 +00:00
|
|
|
|
2012-01-01 08:12:51 +00:00
|
|
|
# -----------------------------------------------------------------------------
|
2016-04-13 00:29:01 +10:00
|
|
|
# Add-on Operators
|
2011-09-26 15:39:15 +00:00
|
|
|
|
2018-03-01 01:26:02 +11:00
|
|
|
class WM_OT_owner_enable(Operator):
|
|
|
|
|
"""Enable workspace owner ID"""
|
|
|
|
|
bl_idname = "wm.owner_enable"
|
|
|
|
|
bl_label = "Enable Add-on"
|
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
owner_id: StringProperty(
|
2018-06-26 22:56:39 +02:00
|
|
|
name="UI Tag",
|
|
|
|
|
)
|
2018-03-01 01:26:02 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
workspace = context.workspace
|
|
|
|
|
workspace.owner_ids.new(self.owner_id)
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_owner_disable(Operator):
|
|
|
|
|
"""Enable workspace owner ID"""
|
|
|
|
|
bl_idname = "wm.owner_disable"
|
|
|
|
|
bl_label = "Disable UI Tag"
|
|
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
owner_id: StringProperty(
|
2018-06-26 22:56:39 +02:00
|
|
|
name="UI Tag",
|
|
|
|
|
)
|
2018-03-01 01:26:02 +11:00
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
workspace = context.workspace
|
|
|
|
|
owner_id = workspace.owner_ids[self.owner_id]
|
|
|
|
|
workspace.owner_ids.remove(owner_id)
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
2019-03-15 12:45:41 +11:00
|
|
|
class WM_OT_tool_set_by_id(Operator):
|
2018-05-13 08:59:50 +02:00
|
|
|
"""Set the tool by name (for keymaps)"""
|
2019-03-15 12:45:41 +11:00
|
|
|
bl_idname = "wm.tool_set_by_id"
|
2020-10-24 11:42:17 -07:00
|
|
|
bl_label = "Set Tool by Name"
|
2018-05-13 08:59:50 +02:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
name: StringProperty(
|
2019-03-15 12:45:41 +11:00
|
|
|
name="Identifier",
|
|
|
|
|
description="Identifier of the tool",
|
2018-06-26 22:56:39 +02:00
|
|
|
)
|
2018-07-11 22:18:09 +02:00
|
|
|
cycle: BoolProperty(
|
2018-06-26 22:56:39 +02:00
|
|
|
name="Cycle",
|
|
|
|
|
description="Cycle through tools in this group",
|
|
|
|
|
default=False,
|
|
|
|
|
options={'SKIP_SAVE'},
|
|
|
|
|
)
|
2019-12-07 03:45:50 +11:00
|
|
|
as_fallback: BoolProperty(
|
|
|
|
|
name="Set Fallback",
|
|
|
|
|
description="Set the fallback tool instead of the primary tool",
|
|
|
|
|
default=False,
|
|
|
|
|
options={'SKIP_SAVE', 'HIDDEN'},
|
|
|
|
|
)
|
2018-05-13 08:59:50 +02:00
|
|
|
|
2018-07-11 22:18:09 +02:00
|
|
|
space_type: rna_space_type_prop
|
2018-05-30 22:15:10 +02:00
|
|
|
|
2018-05-13 08:59:50 +02:00
|
|
|
def execute(self, context):
|
2018-05-30 22:15:10 +02:00
|
|
|
from bl_ui.space_toolsystem_common import (
|
2019-03-15 12:45:41 +11:00
|
|
|
activate_by_id,
|
|
|
|
|
activate_by_id_or_cycle,
|
2018-05-30 22:15:10 +02:00
|
|
|
)
|
2018-05-31 12:54:44 +02:00
|
|
|
|
|
|
|
|
if self.properties.is_property_set("space_type"):
|
|
|
|
|
space_type = self.space_type
|
|
|
|
|
else:
|
2018-05-16 18:41:11 +02:00
|
|
|
space_type = context.space_data.type
|
2018-05-30 22:15:10 +02:00
|
|
|
|
2019-03-15 12:45:41 +11:00
|
|
|
fn = activate_by_id_or_cycle if self.cycle else activate_by_id
|
2019-12-07 03:45:50 +11:00
|
|
|
if fn(context, space_type, self.name, as_fallback=self.as_fallback):
|
|
|
|
|
if self.as_fallback:
|
|
|
|
|
tool_settings = context.tool_settings
|
|
|
|
|
tool_settings.workspace_tool_type = 'FALLBACK'
|
2018-05-13 08:59:50 +02:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
else:
|
2020-12-21 11:35:02 +01:00
|
|
|
self.report({'WARNING'}, "Tool %r not found for space %r" % (self.name, space_type))
|
2018-05-13 08:59:50 +02:00
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
|
|
|
2019-06-25 01:06:09 +10:00
|
|
|
class WM_OT_tool_set_by_index(Operator):
|
|
|
|
|
"""Set the tool by index (for keymaps)"""
|
|
|
|
|
bl_idname = "wm.tool_set_by_index"
|
2020-10-24 11:42:17 -07:00
|
|
|
bl_label = "Set Tool by Index"
|
2019-06-25 01:06:09 +10:00
|
|
|
index: IntProperty(
|
2020-12-24 11:07:32 -06:00
|
|
|
name="Index in Toolbar",
|
2019-06-25 01:06:09 +10:00
|
|
|
default=0,
|
|
|
|
|
)
|
|
|
|
|
cycle: BoolProperty(
|
|
|
|
|
name="Cycle",
|
|
|
|
|
description="Cycle through tools in this group",
|
|
|
|
|
default=False,
|
|
|
|
|
options={'SKIP_SAVE'},
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expand: BoolProperty(
|
2020-12-24 13:11:22 -06:00
|
|
|
description="Include tool subgroups",
|
2019-06-25 01:06:17 +10:00
|
|
|
default=True,
|
2021-01-26 08:04:25 +11:00
|
|
|
options={'SKIP_SAVE'},
|
2019-06-25 01:06:09 +10:00
|
|
|
)
|
|
|
|
|
|
2019-12-07 03:45:50 +11:00
|
|
|
as_fallback: BoolProperty(
|
|
|
|
|
name="Set Fallback",
|
|
|
|
|
description="Set the fallback tool instead of the primary",
|
|
|
|
|
default=False,
|
|
|
|
|
options={'SKIP_SAVE', 'HIDDEN'},
|
|
|
|
|
)
|
|
|
|
|
|
2019-06-25 01:06:09 +10:00
|
|
|
space_type: rna_space_type_prop
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
from bl_ui.space_toolsystem_common import (
|
|
|
|
|
activate_by_id,
|
|
|
|
|
activate_by_id_or_cycle,
|
2019-12-07 03:45:50 +11:00
|
|
|
item_from_index_active,
|
2019-06-25 01:06:09 +10:00
|
|
|
item_from_flat_index,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if self.properties.is_property_set("space_type"):
|
|
|
|
|
space_type = self.space_type
|
|
|
|
|
else:
|
|
|
|
|
space_type = context.space_data.type
|
|
|
|
|
|
2019-12-07 03:45:50 +11:00
|
|
|
fn = item_from_flat_index if self.expand else item_from_index_active
|
2019-06-25 01:06:09 +10:00
|
|
|
item = fn(context, space_type, self.index)
|
|
|
|
|
if item is None:
|
|
|
|
|
# Don't report, since the number of tools may change.
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
|
|
# Same as: WM_OT_tool_set_by_id
|
|
|
|
|
fn = activate_by_id_or_cycle if self.cycle else activate_by_id
|
2019-12-07 03:45:50 +11:00
|
|
|
if fn(context, space_type, item.idname, as_fallback=self.as_fallback):
|
|
|
|
|
if self.as_fallback:
|
|
|
|
|
tool_settings = context.tool_settings
|
|
|
|
|
tool_settings.workspace_tool_type = 'FALLBACK'
|
2019-06-25 01:06:09 +10:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
else:
|
2019-06-25 01:16:51 +10:00
|
|
|
# Since we already have the tool, this can't happen.
|
|
|
|
|
raise Exception("Internal error setting tool")
|
2019-06-25 01:06:09 +10:00
|
|
|
|
|
|
|
|
|
2018-05-18 22:27:59 +02:00
|
|
|
class WM_OT_toolbar(Operator):
|
|
|
|
|
bl_idname = "wm.toolbar"
|
|
|
|
|
bl_label = "Toolbar"
|
|
|
|
|
|
2018-09-03 14:22:58 +10:00
|
|
|
@classmethod
|
|
|
|
|
def poll(cls, context):
|
|
|
|
|
return context.space_data is not None
|
|
|
|
|
|
2019-10-28 22:43:15 +11:00
|
|
|
@staticmethod
|
PyAPI: use keyword only arguments
Use keyword only arguments for the following functions.
- addon_utils.module_bl_info 2nd arg `info_basis`.
- addon_utils.modules 1st `module_cache`, 2nd arg `refresh`.
- addon_utils.modules_refresh 1st arg `module_cache`.
- bl_app_template_utils.activate 1nd arg `template_id`.
- bl_app_template_utils.import_from_id 2nd arg `ignore_not_found`.
- bl_app_template_utils.import_from_path 2nd arg `ignore_not_found`.
- bl_keymap_utils.keymap_from_toolbar.generate 2nd & 3rd args `use_fallback_keys` & `use_reset`.
- bl_keymap_utils.platform_helpers.keyconfig_data_oskey_from_ctrl 2nd arg `filter_fn`.
- bl_ui_utils.bug_report_url.url_prefill_from_blender 1st arg `addon_info`.
- bmesh.types.BMFace.copy 1st & 2nd args `verts`, `edges`.
- bmesh.types.BMesh.calc_volume 1st arg `signed`.
- bmesh.types.BMesh.from_mesh 2nd..4th args `face_normals`, `use_shape_key`, `shape_key_index`.
- bmesh.types.BMesh.from_object 3rd & 4th args `cage`, `face_normals`.
- bmesh.types.BMesh.transform 2nd arg `filter`.
- bmesh.types.BMesh.update_edit_mesh 2nd & 3rd args `loop_triangles`, `destructive`.
- bmesh.types.{BMVertSeq,BMEdgeSeq,BMFaceSeq}.sort 1st & 2nd arg `key`, `reverse`.
- bmesh.utils.face_split 4th..6th args `coords`, `use_exist`, `example`.
- bpy.data.libraries.load 2nd..4th args `link`, `relative`, `assets_only`.
- bpy.data.user_map 1st..3rd args `subset`, `key_types, `value_types`.
- bpy.msgbus.subscribe_rna 5th arg `options`.
- bpy.path.abspath 2nd & 3rd args `start` & `library`.
- bpy.path.clean_name 2nd arg `replace`.
- bpy.path.ensure_ext 3rd arg `case_sensitive`.
- bpy.path.module_names 2nd arg `recursive`.
- bpy.path.relpath 2nd arg `start`.
- bpy.types.EditBone.transform 2nd & 3rd arg `scale`, `roll`.
- bpy.types.Operator.as_keywords 1st arg `ignore`.
- bpy.types.Struct.{keyframe_insert,keyframe_delete} 2nd..5th args `index`, `frame`, `group`, `options`.
- bpy.types.WindowManager.popup_menu 2nd & 3rd arg `title`, `icon`.
- bpy.types.WindowManager.popup_menu_pie 3rd & 4th arg `title`, `icon`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.blend_paths 1st..3rd args `absolute`, `packed`, `local`.
- bpy.utils.execfile 2nd arg `mod`.
- bpy.utils.keyconfig_set 2nd arg `report`.
- bpy.utils.load_scripts 1st & 2nd `reload_scripts` & `refresh_scripts`.
- bpy.utils.preset_find 3rd & 4th args `display_name`, `ext`.
- bpy.utils.resource_path 2nd & 3rd arg `major`, `minor`.
- bpy.utils.script_paths 1st..4th args `subdir`, `user_pref`, `check_all`, `use_user`.
- bpy.utils.smpte_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.smpte_from_seconds 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.system_resource 2nd arg `subdir`.
- bpy.utils.time_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.time_to_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.units.to_string 4th..6th `precision`, `split_unit`, `compatible_unit`.
- bpy.utils.units.to_value 4th arg `str_ref_unit`.
- bpy.utils.user_resource 2nd & 3rd args `subdir`, `create`
- bpy_extras.view3d_utils.location_3d_to_region_2d 4th arg `default`.
- bpy_extras.view3d_utils.region_2d_to_origin_3d 4th arg `clamp`.
- gpu.offscreen.unbind 1st arg `restore`.
- gpu_extras.batch.batch_for_shader 4th arg `indices`.
- gpu_extras.batch.presets.draw_circle_2d 4th arg `segments`.
- gpu_extras.presets.draw_circle_2d 4th arg `segments`.
- imbuf.types.ImBuf.resize 2nd arg `resize`.
- imbuf.write 2nd arg `filepath`.
- mathutils.kdtree.KDTree.find 2nd arg `filter`.
- nodeitems_utils.NodeCategory 3rd & 4th arg `descriptions`, `items`.
- nodeitems_utils.NodeItem 2nd..4th args `label`, `settings`, `poll`.
- nodeitems_utils.NodeItemCustom 1st & 2nd arg `poll`, `draw`.
- rna_prop_ui.draw 5th arg `use_edit`.
- rna_prop_ui.rna_idprop_ui_get 2nd arg `create`.
- rna_prop_ui.rna_idprop_ui_prop_clear 3rd arg `remove`.
- rna_prop_ui.rna_idprop_ui_prop_get 3rd arg `create`.
- rna_xml.xml2rna 2nd arg `root_rna`.
- rna_xml.xml_file_write 4th arg `skip_typemap`.
2021-06-08 18:03:14 +10:00
|
|
|
def keymap_from_toolbar(context, space_type, *, use_fallback_keys=True, use_reset=True):
|
2018-11-20 11:15:37 +11:00
|
|
|
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
|
|
|
|
from bl_keymap_utils import keymap_from_toolbar
|
2018-05-21 10:43:15 +02:00
|
|
|
|
2018-05-18 23:14:56 +02:00
|
|
|
cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
|
|
|
|
|
if cls is None:
|
2019-10-28 22:43:15 +11:00
|
|
|
return None, None
|
2018-05-18 22:27:59 +02:00
|
|
|
|
2019-10-28 22:43:15 +11:00
|
|
|
return cls, keymap_from_toolbar.generate(
|
|
|
|
|
context,
|
|
|
|
|
space_type,
|
|
|
|
|
use_fallback_keys=use_fallback_keys,
|
|
|
|
|
use_reset=use_reset,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
|
|
|
|
space_type = context.space_data.type
|
|
|
|
|
cls, keymap = self.keymap_from_toolbar(context, space_type)
|
|
|
|
|
if keymap is None:
|
|
|
|
|
return {'CANCELLED'}
|
2018-05-20 22:37:35 +02:00
|
|
|
|
2018-05-18 22:27:59 +02:00
|
|
|
def draw_menu(popover, context):
|
2018-05-20 22:37:35 +02:00
|
|
|
layout = popover.layout
|
2018-11-12 11:56:13 +11:00
|
|
|
layout.operator_context = 'INVOKE_REGION_WIN'
|
2018-06-06 08:31:31 +02:00
|
|
|
cls.draw_cls(layout, context, detect_layout=False, scale_y=1.0)
|
2018-05-18 22:27:59 +02:00
|
|
|
|
2019-10-28 22:43:15 +11:00
|
|
|
wm = context.window_manager
|
2018-07-03 19:55:51 +02:00
|
|
|
wm.popover(draw_menu, ui_units_x=8, keymap=keymap)
|
2018-05-18 22:27:59 +02:00
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
2019-12-07 03:45:50 +11:00
|
|
|
class WM_OT_toolbar_fallback_pie(Operator):
|
|
|
|
|
bl_idname = "wm.toolbar_fallback_pie"
|
|
|
|
|
bl_label = "Fallback Tool Pie Menu"
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def poll(cls, context):
|
|
|
|
|
return context.space_data is not None
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
|
|
|
|
space_type = context.space_data.type
|
|
|
|
|
cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
|
|
|
|
|
if cls is None:
|
|
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
|
|
|
|
|
|
# It's possible we don't have the fallback tool available.
|
|
|
|
|
# This can happen in the image editor for example when there is no selection
|
|
|
|
|
# in painting modes.
|
2019-12-07 16:36:33 +11:00
|
|
|
item, _ = cls._tool_get_by_id(context, cls.tool_fallback_id)
|
2019-12-07 03:45:50 +11:00
|
|
|
if item is None:
|
|
|
|
|
print("Tool", cls.tool_fallback_id, "not active in", cls)
|
|
|
|
|
return {'PASS_THROUGH'}
|
|
|
|
|
|
|
|
|
|
def draw_cb(self, context):
|
|
|
|
|
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
|
|
|
|
ToolSelectPanelHelper.draw_fallback_tool_items_for_pie_menu(self.layout, context)
|
|
|
|
|
|
|
|
|
|
wm = context.window_manager
|
|
|
|
|
wm.popup_menu_pie(draw_func=draw_cb, title="Fallback Tool", event=event)
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
|
2019-10-28 22:43:15 +11:00
|
|
|
class WM_OT_toolbar_prompt(Operator):
|
|
|
|
|
"""Leader key like functionality for accessing tools"""
|
|
|
|
|
bl_idname = "wm.toolbar_prompt"
|
|
|
|
|
bl_label = "Toolbar Prompt"
|
|
|
|
|
|
2019-10-31 09:52:26 +11:00
|
|
|
@staticmethod
|
|
|
|
|
def _status_items_generate(cls, keymap, context):
|
|
|
|
|
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
|
|
|
|
|
|
|
|
|
# The keymap doesn't have the same order the tools are declared in,
|
|
|
|
|
# while we could support this, it's simpler to apply order here.
|
|
|
|
|
tool_map_id_to_order = {}
|
|
|
|
|
# Map the
|
|
|
|
|
tool_map_id_to_label = {}
|
|
|
|
|
for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context)):
|
|
|
|
|
if item is not None:
|
|
|
|
|
tool_map_id_to_label[item.idname] = item.label
|
|
|
|
|
tool_map_id_to_order[item.idname] = len(tool_map_id_to_order)
|
|
|
|
|
|
|
|
|
|
status_items = []
|
|
|
|
|
|
|
|
|
|
for item in keymap.keymap_items:
|
|
|
|
|
name = item.name
|
|
|
|
|
key_str = item.to_string()
|
|
|
|
|
# These are duplicated from regular numbers.
|
|
|
|
|
if key_str.startswith("Numpad "):
|
|
|
|
|
continue
|
|
|
|
|
properties = item.properties
|
|
|
|
|
idname = item.idname
|
|
|
|
|
if idname == "wm.tool_set_by_id":
|
|
|
|
|
tool_idname = properties["name"]
|
|
|
|
|
name = tool_map_id_to_label[tool_idname]
|
|
|
|
|
name = name.replace("Annotate ", "")
|
|
|
|
|
else:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
status_items.append((tool_idname, name, item))
|
|
|
|
|
|
|
|
|
|
status_items.sort(
|
|
|
|
|
key=lambda a: tool_map_id_to_order[a[0]]
|
|
|
|
|
)
|
|
|
|
|
return status_items
|
|
|
|
|
|
2019-10-28 22:43:15 +11:00
|
|
|
def modal(self, context, event):
|
|
|
|
|
event_type = event.type
|
|
|
|
|
event_value = event.value
|
|
|
|
|
|
2019-11-08 17:41:04 +11:00
|
|
|
if event_type in {
|
|
|
|
|
'LEFTMOUSE', 'RIGHTMOUSE', 'MIDDLEMOUSE',
|
|
|
|
|
'WHEELDOWNMOUSE', 'WHEELUPMOUSE', 'WHEELINMOUSE', 'WHEELOUTMOUSE',
|
|
|
|
|
'ESC',
|
|
|
|
|
}:
|
2019-10-28 22:43:15 +11:00
|
|
|
context.workspace.status_text_set(None)
|
|
|
|
|
return {'CANCELLED', 'PASS_THROUGH'}
|
|
|
|
|
|
2019-11-08 17:41:04 +11:00
|
|
|
keymap = self._keymap
|
2019-10-28 22:43:15 +11:00
|
|
|
item = keymap.keymap_items.match_event(event)
|
|
|
|
|
if item is not None:
|
|
|
|
|
idname = item.idname
|
|
|
|
|
properties = item.properties
|
|
|
|
|
if idname == "wm.tool_set_by_id":
|
|
|
|
|
tool_idname = properties["name"]
|
|
|
|
|
bpy.ops.wm.tool_set_by_id(name=tool_idname)
|
|
|
|
|
|
|
|
|
|
context.workspace.status_text_set(None)
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
2019-11-15 13:52:16 +11:00
|
|
|
# Pressing entry even again exists, as long as it's not mapped to a key (for convenience).
|
|
|
|
|
if event_type == self._init_event_type:
|
|
|
|
|
if event_value == 'RELEASE':
|
|
|
|
|
if not (event.ctrl or event.alt or event.shift or event.oskey):
|
|
|
|
|
context.workspace.status_text_set(None)
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
2019-10-28 22:43:15 +11:00
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
2019-10-31 09:52:26 +11:00
|
|
|
space_data = context.space_data
|
|
|
|
|
if space_data is None:
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
|
|
space_type = space_data.type
|
2019-10-28 22:43:15 +11:00
|
|
|
cls, keymap = WM_OT_toolbar.keymap_from_toolbar(
|
|
|
|
|
context,
|
|
|
|
|
space_type,
|
|
|
|
|
use_fallback_keys=False,
|
|
|
|
|
use_reset=False,
|
|
|
|
|
)
|
2019-10-31 09:52:26 +11:00
|
|
|
if (keymap is None) or (not keymap.keymap_items):
|
2019-10-28 22:43:15 +11:00
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
|
|
|
|
self._init_event_type = event.type
|
|
|
|
|
|
|
|
|
|
# Strip Left/Right, since "Left Alt" isn't especially useful.
|
|
|
|
|
init_event_type_as_text = self._init_event_type.title().split("_")
|
|
|
|
|
if init_event_type_as_text[0] in {"Left", "Right"}:
|
|
|
|
|
del init_event_type_as_text[0]
|
|
|
|
|
init_event_type_as_text = " ".join(init_event_type_as_text)
|
|
|
|
|
|
2019-10-31 09:52:26 +11:00
|
|
|
status_items = self._status_items_generate(cls, keymap, context)
|
|
|
|
|
|
2019-10-28 22:43:15 +11:00
|
|
|
def status_text_fn(self, context):
|
|
|
|
|
|
|
|
|
|
layout = self.layout
|
|
|
|
|
if True:
|
|
|
|
|
box = layout.row(align=True).box()
|
|
|
|
|
box.scale_x = 0.8
|
|
|
|
|
box.label(text=init_event_type_as_text)
|
|
|
|
|
|
|
|
|
|
flow = layout.grid_flow(columns=len(status_items), align=True, row_major=True)
|
|
|
|
|
for _, name, item in status_items:
|
|
|
|
|
row = flow.row(align=True)
|
|
|
|
|
row.template_event_from_keymap_item(item, text=name)
|
|
|
|
|
|
|
|
|
|
self._keymap = keymap
|
|
|
|
|
|
|
|
|
|
context.workspace.status_text_set(status_text_fn)
|
|
|
|
|
|
|
|
|
|
context.window_manager.modal_handler_add(self)
|
|
|
|
|
return {'RUNNING_MODAL'}
|
|
|
|
|
|
|
|
|
|
|
2019-09-01 03:16:58 +10:00
|
|
|
class BatchRenameAction(bpy.types.PropertyGroup):
|
|
|
|
|
# category: StringProperty()
|
|
|
|
|
type: EnumProperty(
|
|
|
|
|
name="Operation",
|
|
|
|
|
items=(
|
2019-09-02 01:57:39 +10:00
|
|
|
('REPLACE', "Find/Replace", "Replace text in the name"),
|
2019-09-01 03:16:58 +10:00
|
|
|
('SET', "Set Name", "Set a new name or prefix/suffix the existing one"),
|
|
|
|
|
('STRIP', "Strip Characters", "Strip leading/trailing text from the name"),
|
|
|
|
|
('CASE', "Change Case", "Change case of each name"),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# We could split these into sub-properties, however it's not so important.
|
|
|
|
|
|
|
|
|
|
# type: 'SET'.
|
|
|
|
|
set_name: StringProperty(name="Name")
|
|
|
|
|
set_method: EnumProperty(
|
|
|
|
|
name="Method",
|
|
|
|
|
items=(
|
|
|
|
|
('NEW', "New", ""),
|
|
|
|
|
('PREFIX', "Prefix", ""),
|
|
|
|
|
('SUFFIX', "Suffix", ""),
|
|
|
|
|
),
|
|
|
|
|
default='SUFFIX',
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# type: 'STRIP'.
|
|
|
|
|
strip_chars: EnumProperty(
|
|
|
|
|
name="Strip Characters",
|
|
|
|
|
options={'ENUM_FLAG'},
|
|
|
|
|
items=(
|
|
|
|
|
('SPACE', "Spaces", ""),
|
|
|
|
|
('DIGIT', "Digits", ""),
|
|
|
|
|
('PUNCT', "Punctuation", ""),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# type: 'STRIP'.
|
|
|
|
|
strip_part: EnumProperty(
|
|
|
|
|
name="Strip Part",
|
|
|
|
|
options={'ENUM_FLAG'},
|
|
|
|
|
items=(
|
|
|
|
|
('START', "Start", ""),
|
|
|
|
|
('END', "End", ""),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# type: 'REPLACE'.
|
|
|
|
|
replace_src: StringProperty(name="Find")
|
|
|
|
|
replace_dst: StringProperty(name="Replace")
|
2019-09-02 15:18:07 +10:00
|
|
|
replace_match_case: BoolProperty(name="Case Sensitive")
|
2019-09-06 23:21:26 +10:00
|
|
|
use_replace_regex_src: BoolProperty(
|
|
|
|
|
name="Regular Expression Find",
|
2019-09-02 15:18:07 +10:00
|
|
|
description="Use regular expressions to match text in the 'Find' field"
|
|
|
|
|
)
|
2019-09-06 23:21:26 +10:00
|
|
|
use_replace_regex_dst: BoolProperty(
|
|
|
|
|
name="Regular Expression Replace",
|
|
|
|
|
description="Use regular expression for the replacement text (supporting groups)"
|
|
|
|
|
)
|
2019-09-01 03:16:58 +10:00
|
|
|
|
|
|
|
|
# type: 'CASE'.
|
|
|
|
|
case_method: EnumProperty(
|
|
|
|
|
name="Case",
|
|
|
|
|
items=(
|
|
|
|
|
('UPPER', "Upper Case", ""),
|
|
|
|
|
('LOWER', "Lower Case", ""),
|
2019-09-08 17:40:26 -04:00
|
|
|
('TITLE', "Title Case", ""),
|
2019-09-01 03:16:58 +10:00
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Weak, add/remove as properties.
|
|
|
|
|
op_add: BoolProperty()
|
|
|
|
|
op_remove: BoolProperty()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class WM_OT_batch_rename(Operator):
|
|
|
|
|
bl_idname = "wm.batch_rename"
|
|
|
|
|
bl_label = "Batch Rename"
|
|
|
|
|
|
2019-09-14 02:39:58 +10:00
|
|
|
bl_options = {'UNDO'}
|
2019-09-01 03:16:58 +10:00
|
|
|
|
2019-09-02 00:51:26 +10:00
|
|
|
data_type: EnumProperty(
|
|
|
|
|
name="Type",
|
|
|
|
|
items=(
|
|
|
|
|
('OBJECT', "Objects", ""),
|
|
|
|
|
('MATERIAL', "Materials", ""),
|
|
|
|
|
None,
|
2019-09-02 15:18:07 +10:00
|
|
|
# Enum identifiers are compared with 'object.type'.
|
|
|
|
|
('MESH', "Meshes", ""),
|
2019-09-02 00:51:26 +10:00
|
|
|
('CURVE', "Curves", ""),
|
2021-02-24 13:44:24 -06:00
|
|
|
('META', "Metaballs", ""),
|
2019-09-02 00:51:26 +10:00
|
|
|
('ARMATURE', "Armatures", ""),
|
|
|
|
|
('LATTICE', "Lattices", ""),
|
|
|
|
|
('GPENCIL', "Grease Pencils", ""),
|
|
|
|
|
('CAMERA', "Cameras", ""),
|
|
|
|
|
('SPEAKER', "Speakers", ""),
|
2019-09-02 15:18:07 +10:00
|
|
|
('LIGHT_PROBE', "Light Probes", ""),
|
2019-09-02 00:51:26 +10:00
|
|
|
None,
|
|
|
|
|
('BONE', "Bones", ""),
|
|
|
|
|
('NODE', "Nodes", ""),
|
|
|
|
|
('SEQUENCE_STRIP', "Sequence Strips", ""),
|
|
|
|
|
),
|
|
|
|
|
description="Type of data to rename",
|
|
|
|
|
)
|
|
|
|
|
|
2019-09-01 03:16:58 +10:00
|
|
|
data_source: EnumProperty(
|
|
|
|
|
name="Source",
|
|
|
|
|
items=(
|
|
|
|
|
('SELECT', "Selected", ""),
|
|
|
|
|
('ALL', "All", ""),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
actions: CollectionProperty(type=BatchRenameAction)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
PyAPI: use keyword only arguments
Use keyword only arguments for the following functions.
- addon_utils.module_bl_info 2nd arg `info_basis`.
- addon_utils.modules 1st `module_cache`, 2nd arg `refresh`.
- addon_utils.modules_refresh 1st arg `module_cache`.
- bl_app_template_utils.activate 1nd arg `template_id`.
- bl_app_template_utils.import_from_id 2nd arg `ignore_not_found`.
- bl_app_template_utils.import_from_path 2nd arg `ignore_not_found`.
- bl_keymap_utils.keymap_from_toolbar.generate 2nd & 3rd args `use_fallback_keys` & `use_reset`.
- bl_keymap_utils.platform_helpers.keyconfig_data_oskey_from_ctrl 2nd arg `filter_fn`.
- bl_ui_utils.bug_report_url.url_prefill_from_blender 1st arg `addon_info`.
- bmesh.types.BMFace.copy 1st & 2nd args `verts`, `edges`.
- bmesh.types.BMesh.calc_volume 1st arg `signed`.
- bmesh.types.BMesh.from_mesh 2nd..4th args `face_normals`, `use_shape_key`, `shape_key_index`.
- bmesh.types.BMesh.from_object 3rd & 4th args `cage`, `face_normals`.
- bmesh.types.BMesh.transform 2nd arg `filter`.
- bmesh.types.BMesh.update_edit_mesh 2nd & 3rd args `loop_triangles`, `destructive`.
- bmesh.types.{BMVertSeq,BMEdgeSeq,BMFaceSeq}.sort 1st & 2nd arg `key`, `reverse`.
- bmesh.utils.face_split 4th..6th args `coords`, `use_exist`, `example`.
- bpy.data.libraries.load 2nd..4th args `link`, `relative`, `assets_only`.
- bpy.data.user_map 1st..3rd args `subset`, `key_types, `value_types`.
- bpy.msgbus.subscribe_rna 5th arg `options`.
- bpy.path.abspath 2nd & 3rd args `start` & `library`.
- bpy.path.clean_name 2nd arg `replace`.
- bpy.path.ensure_ext 3rd arg `case_sensitive`.
- bpy.path.module_names 2nd arg `recursive`.
- bpy.path.relpath 2nd arg `start`.
- bpy.types.EditBone.transform 2nd & 3rd arg `scale`, `roll`.
- bpy.types.Operator.as_keywords 1st arg `ignore`.
- bpy.types.Struct.{keyframe_insert,keyframe_delete} 2nd..5th args `index`, `frame`, `group`, `options`.
- bpy.types.WindowManager.popup_menu 2nd & 3rd arg `title`, `icon`.
- bpy.types.WindowManager.popup_menu_pie 3rd & 4th arg `title`, `icon`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.app_template_paths 1st arg `subdir`.
- bpy.utils.blend_paths 1st..3rd args `absolute`, `packed`, `local`.
- bpy.utils.execfile 2nd arg `mod`.
- bpy.utils.keyconfig_set 2nd arg `report`.
- bpy.utils.load_scripts 1st & 2nd `reload_scripts` & `refresh_scripts`.
- bpy.utils.preset_find 3rd & 4th args `display_name`, `ext`.
- bpy.utils.resource_path 2nd & 3rd arg `major`, `minor`.
- bpy.utils.script_paths 1st..4th args `subdir`, `user_pref`, `check_all`, `use_user`.
- bpy.utils.smpte_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.smpte_from_seconds 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.system_resource 2nd arg `subdir`.
- bpy.utils.time_from_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.time_to_frame 2nd & 3rd args `fps`, `fps_base`.
- bpy.utils.units.to_string 4th..6th `precision`, `split_unit`, `compatible_unit`.
- bpy.utils.units.to_value 4th arg `str_ref_unit`.
- bpy.utils.user_resource 2nd & 3rd args `subdir`, `create`
- bpy_extras.view3d_utils.location_3d_to_region_2d 4th arg `default`.
- bpy_extras.view3d_utils.region_2d_to_origin_3d 4th arg `clamp`.
- gpu.offscreen.unbind 1st arg `restore`.
- gpu_extras.batch.batch_for_shader 4th arg `indices`.
- gpu_extras.batch.presets.draw_circle_2d 4th arg `segments`.
- gpu_extras.presets.draw_circle_2d 4th arg `segments`.
- imbuf.types.ImBuf.resize 2nd arg `resize`.
- imbuf.write 2nd arg `filepath`.
- mathutils.kdtree.KDTree.find 2nd arg `filter`.
- nodeitems_utils.NodeCategory 3rd & 4th arg `descriptions`, `items`.
- nodeitems_utils.NodeItem 2nd..4th args `label`, `settings`, `poll`.
- nodeitems_utils.NodeItemCustom 1st & 2nd arg `poll`, `draw`.
- rna_prop_ui.draw 5th arg `use_edit`.
- rna_prop_ui.rna_idprop_ui_get 2nd arg `create`.
- rna_prop_ui.rna_idprop_ui_prop_clear 3rd arg `remove`.
- rna_prop_ui.rna_idprop_ui_prop_get 3rd arg `create`.
- rna_xml.xml2rna 2nd arg `root_rna`.
- rna_xml.xml_file_write 4th arg `skip_typemap`.
2021-06-08 18:03:14 +10:00
|
|
|
def _data_from_context(context, data_type, only_selected, *, check_context=False):
|
2019-09-01 03:16:58 +10:00
|
|
|
|
|
|
|
|
mode = context.mode
|
|
|
|
|
scene = context.scene
|
|
|
|
|
space = context.space_data
|
|
|
|
|
space_type = None if (space is None) else space.type
|
|
|
|
|
|
|
|
|
|
data = None
|
|
|
|
|
if space_type == 'SEQUENCE_EDITOR':
|
2019-09-02 00:51:26 +10:00
|
|
|
data_type_test = 'SEQUENCE_STRIP'
|
|
|
|
|
if check_context:
|
|
|
|
|
return data_type_test
|
|
|
|
|
if data_type == data_type_test:
|
|
|
|
|
data = (
|
|
|
|
|
# TODO, we don't have access to seqbasep, this won't work when inside metas.
|
|
|
|
|
[seq for seq in context.scene.sequence_editor.sequences_all if seq.select]
|
|
|
|
|
if only_selected else
|
|
|
|
|
context.scene.sequence_editor.sequences_all,
|
|
|
|
|
"name",
|
|
|
|
|
"Strip(s)",
|
|
|
|
|
)
|
2019-09-01 03:16:58 +10:00
|
|
|
elif space_type == 'NODE_EDITOR':
|
2019-09-02 00:51:26 +10:00
|
|
|
data_type_test = 'NODE'
|
|
|
|
|
if check_context:
|
|
|
|
|
return data_type_test
|
|
|
|
|
if data_type == data_type_test:
|
|
|
|
|
data = (
|
|
|
|
|
context.selected_nodes
|
|
|
|
|
if only_selected else
|
|
|
|
|
list(space.node_tree.nodes),
|
|
|
|
|
"name",
|
|
|
|
|
"Node(s)",
|
|
|
|
|
)
|
2019-09-01 03:16:58 +10:00
|
|
|
else:
|
|
|
|
|
if mode == 'POSE' or (mode == 'WEIGHT_PAINT' and context.pose_object):
|
2019-09-02 00:51:26 +10:00
|
|
|
data_type_test = 'BONE'
|
|
|
|
|
if check_context:
|
|
|
|
|
return data_type_test
|
|
|
|
|
if data_type == data_type_test:
|
|
|
|
|
data = (
|
|
|
|
|
[pchan.bone for pchan in context.selected_pose_bones]
|
|
|
|
|
if only_selected else
|
2019-10-23 13:45:05 +02:00
|
|
|
[pbone.bone for ob in context.objects_in_mode_unique_data for pbone in ob.pose.bones],
|
2019-09-02 00:51:26 +10:00
|
|
|
"name",
|
|
|
|
|
"Bone(s)",
|
|
|
|
|
)
|
|
|
|
|
elif mode == 'EDIT_ARMATURE':
|
|
|
|
|
data_type_test = 'BONE'
|
|
|
|
|
if check_context:
|
|
|
|
|
return data_type_test
|
|
|
|
|
if data_type == data_type_test:
|
|
|
|
|
data = (
|
|
|
|
|
context.selected_editable_bones
|
|
|
|
|
if only_selected else
|
|
|
|
|
[ebone for ob in context.objects_in_mode_unique_data for ebone in ob.data.edit_bones],
|
|
|
|
|
"name",
|
|
|
|
|
"Edit Bone(s)",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if check_context:
|
|
|
|
|
return 'OBJECT'
|
|
|
|
|
|
|
|
|
|
object_data_type_attrs_map = {
|
|
|
|
|
'MESH': ("meshes", "Mesh(es)"),
|
|
|
|
|
'CURVE': ("curves", "Curve(s)"),
|
2020-12-24 11:07:32 -06:00
|
|
|
'META': ("metaballs", "Metaball(s)"),
|
2019-09-02 00:51:26 +10:00
|
|
|
'ARMATURE': ("armatures", "Armature(s)"),
|
|
|
|
|
'LATTICE': ("lattices", "Lattice(s)"),
|
|
|
|
|
'GPENCIL': ("grease_pencils", "Grease Pencil(s)"),
|
|
|
|
|
'CAMERA': ("cameras", "Camera(s)"),
|
|
|
|
|
'SPEAKER': ("speakers", "Speaker(s)"),
|
2020-12-24 13:11:22 -06:00
|
|
|
'LIGHT_PROBE': ("light_probes", "Light Probe(s)"),
|
2019-09-02 00:51:26 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# Finish with space types.
|
|
|
|
|
if data is None:
|
|
|
|
|
|
|
|
|
|
if data_type == 'OBJECT':
|
2019-09-01 03:16:58 +10:00
|
|
|
data = (
|
2019-09-02 00:51:26 +10:00
|
|
|
context.selected_editable_objects
|
2019-09-01 03:16:58 +10:00
|
|
|
if only_selected else
|
2019-09-02 00:51:26 +10:00
|
|
|
[id for id in bpy.data.objects if id.library is None],
|
2019-09-01 03:16:58 +10:00
|
|
|
"name",
|
2019-09-02 00:51:26 +10:00
|
|
|
"Object(s)",
|
2019-09-01 03:16:58 +10:00
|
|
|
)
|
2019-09-02 00:51:26 +10:00
|
|
|
elif data_type == 'MATERIAL':
|
2019-09-01 03:16:58 +10:00
|
|
|
data = (
|
2019-09-02 00:51:26 +10:00
|
|
|
tuple(set(
|
|
|
|
|
slot.material
|
|
|
|
|
for ob in context.selected_objects
|
|
|
|
|
for slot in ob.material_slots
|
|
|
|
|
if slot.material is not None
|
|
|
|
|
))
|
2019-09-01 03:16:58 +10:00
|
|
|
if only_selected else
|
2019-09-02 00:51:26 +10:00
|
|
|
[id for id in bpy.data.materials if id.library is None],
|
2019-09-01 03:16:58 +10:00
|
|
|
"name",
|
2019-09-02 00:51:26 +10:00
|
|
|
"Material(s)",
|
2019-09-01 03:16:58 +10:00
|
|
|
)
|
2019-09-02 00:51:26 +10:00
|
|
|
elif data_type in object_data_type_attrs_map.keys():
|
|
|
|
|
attr, descr = object_data_type_attrs_map[data_type]
|
2019-09-01 03:16:58 +10:00
|
|
|
data = (
|
2019-09-02 00:51:26 +10:00
|
|
|
tuple(set(
|
|
|
|
|
id
|
|
|
|
|
for ob in context.selected_objects
|
|
|
|
|
if ob.type == data_type
|
2021-02-12 15:26:32 +11:00
|
|
|
if (id := ob.data) is not None and id.library is None
|
2019-09-02 00:51:26 +10:00
|
|
|
))
|
2019-09-01 03:16:58 +10:00
|
|
|
if only_selected else
|
2019-09-02 00:51:26 +10:00
|
|
|
[id for id in getattr(bpy.data, attr) if id.library is None],
|
2019-09-01 03:16:58 +10:00
|
|
|
"name",
|
2019-09-02 00:51:26 +10:00
|
|
|
descr,
|
2019-09-01 03:16:58 +10:00
|
|
|
)
|
2019-09-02 00:51:26 +10:00
|
|
|
|
2019-09-01 03:16:58 +10:00
|
|
|
return data
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _apply_actions(actions, name):
|
|
|
|
|
import string
|
|
|
|
|
import re
|
|
|
|
|
|
|
|
|
|
for action in actions:
|
|
|
|
|
ty = action.type
|
|
|
|
|
if ty == 'SET':
|
|
|
|
|
text = action.set_name
|
|
|
|
|
method = action.set_method
|
|
|
|
|
if method == 'NEW':
|
|
|
|
|
name = text
|
|
|
|
|
elif method == 'PREFIX':
|
|
|
|
|
name = text + name
|
|
|
|
|
elif method == 'SUFFIX':
|
|
|
|
|
name = name + text
|
|
|
|
|
else:
|
|
|
|
|
assert(0)
|
|
|
|
|
|
|
|
|
|
elif ty == 'STRIP':
|
|
|
|
|
chars = action.strip_chars
|
|
|
|
|
chars_strip = (
|
2020-06-26 12:30:03 +10:00
|
|
|
"%s%s%s"
|
|
|
|
|
) % (
|
2019-09-01 03:16:58 +10:00
|
|
|
string.punctuation if 'PUNCT' in chars else "",
|
|
|
|
|
string.digits if 'DIGIT' in chars else "",
|
|
|
|
|
" " if 'SPACE' in chars else "",
|
|
|
|
|
)
|
|
|
|
|
part = action.strip_part
|
|
|
|
|
if 'START' in part:
|
|
|
|
|
name = name.lstrip(chars_strip)
|
|
|
|
|
if 'END' in part:
|
|
|
|
|
name = name.rstrip(chars_strip)
|
|
|
|
|
|
|
|
|
|
elif ty == 'REPLACE':
|
2019-09-06 23:21:26 +10:00
|
|
|
if action.use_replace_regex_src:
|
2019-09-02 15:18:07 +10:00
|
|
|
replace_src = action.replace_src
|
2019-09-06 23:21:26 +10:00
|
|
|
if action.use_replace_regex_dst:
|
|
|
|
|
replace_dst = action.replace_dst
|
|
|
|
|
else:
|
2019-10-09 15:12:15 +11:00
|
|
|
replace_dst = action.replace_dst.replace("\\", "\\\\")
|
2019-09-01 03:16:58 +10:00
|
|
|
else:
|
2019-09-02 15:18:07 +10:00
|
|
|
replace_src = re.escape(action.replace_src)
|
2019-11-03 11:27:03 +11:00
|
|
|
replace_dst = action.replace_dst.replace("\\", "\\\\")
|
2019-09-02 15:18:07 +10:00
|
|
|
name = re.sub(
|
|
|
|
|
replace_src,
|
2019-09-06 23:21:26 +10:00
|
|
|
replace_dst,
|
2019-09-02 15:18:07 +10:00
|
|
|
name,
|
|
|
|
|
flags=(
|
|
|
|
|
0 if action.replace_match_case else
|
|
|
|
|
re.IGNORECASE
|
|
|
|
|
),
|
|
|
|
|
)
|
2019-09-01 03:16:58 +10:00
|
|
|
elif ty == 'CASE':
|
|
|
|
|
method = action.case_method
|
|
|
|
|
if method == 'UPPER':
|
|
|
|
|
name = name.upper()
|
|
|
|
|
elif method == 'LOWER':
|
|
|
|
|
name = name.lower()
|
|
|
|
|
elif method == 'TITLE':
|
|
|
|
|
name = name.title()
|
|
|
|
|
else:
|
|
|
|
|
assert(0)
|
|
|
|
|
else:
|
|
|
|
|
assert(0)
|
|
|
|
|
return name
|
|
|
|
|
|
2019-09-02 00:51:26 +10:00
|
|
|
def _data_update(self, context):
|
|
|
|
|
only_selected = self.data_source == 'SELECT'
|
|
|
|
|
|
|
|
|
|
self._data = self._data_from_context(context, self.data_type, only_selected)
|
|
|
|
|
if self._data is None:
|
|
|
|
|
self.data_type = self._data_from_context(context, None, False, check_context=True)
|
|
|
|
|
self._data = self._data_from_context(context, self.data_type, only_selected)
|
|
|
|
|
|
|
|
|
|
self._data_source_prev = self.data_source
|
|
|
|
|
self._data_type_prev = self.data_type
|
|
|
|
|
|
2019-09-01 03:16:58 +10:00
|
|
|
def draw(self, context):
|
2019-09-02 15:18:07 +10:00
|
|
|
import re
|
|
|
|
|
|
2019-09-01 03:16:58 +10:00
|
|
|
layout = self.layout
|
|
|
|
|
|
2020-11-23 17:27:01 -08:00
|
|
|
split = layout.split(align=True)
|
|
|
|
|
split.row(align=True).prop(self, "data_source", expand=True)
|
2019-09-02 15:18:07 +10:00
|
|
|
split.prop(self, "data_type", text="")
|
2019-09-02 00:51:26 +10:00
|
|
|
|
2019-09-01 03:16:58 +10:00
|
|
|
for action in self.actions:
|
|
|
|
|
box = layout.box()
|
2020-11-23 17:27:01 -08:00
|
|
|
split = box.split(factor=0.87)
|
|
|
|
|
|
|
|
|
|
# Column 1: main content.
|
|
|
|
|
col = split.column()
|
2019-09-01 03:16:58 +10:00
|
|
|
|
2020-11-23 17:27:01 -08:00
|
|
|
# Label's width.
|
|
|
|
|
fac = 0.25
|
|
|
|
|
|
|
|
|
|
# Row 1: type.
|
|
|
|
|
row = col.split(factor=fac)
|
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Type")
|
2019-09-01 03:16:58 +10:00
|
|
|
row.prop(action, "type", text="")
|
|
|
|
|
|
|
|
|
|
ty = action.type
|
|
|
|
|
if ty == 'SET':
|
2020-11-23 17:27:01 -08:00
|
|
|
# Row 2: method.
|
|
|
|
|
row = col.split(factor=fac)
|
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Method")
|
|
|
|
|
row.row().prop(action, "set_method", expand=True)
|
|
|
|
|
|
|
|
|
|
# Row 3: name.
|
|
|
|
|
row = col.split(factor=fac)
|
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Name")
|
|
|
|
|
row.prop(action, "set_name", text="")
|
|
|
|
|
|
2019-09-01 03:16:58 +10:00
|
|
|
elif ty == 'STRIP':
|
2020-11-23 17:27:01 -08:00
|
|
|
# Row 2: chars.
|
|
|
|
|
row = col.split(factor=fac)
|
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Characters")
|
|
|
|
|
row.row().prop(action, "strip_chars")
|
|
|
|
|
|
|
|
|
|
# Row 3: part.
|
|
|
|
|
row = col.split(factor=fac)
|
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Strip From")
|
|
|
|
|
row.row().prop(action, "strip_part")
|
|
|
|
|
|
2019-09-01 03:16:58 +10:00
|
|
|
elif ty == 'REPLACE':
|
2020-11-23 17:27:01 -08:00
|
|
|
# Row 2: find.
|
|
|
|
|
row = col.split(factor=fac)
|
2019-09-02 15:18:07 +10:00
|
|
|
|
2019-09-06 23:21:26 +10:00
|
|
|
re_error_src = None
|
|
|
|
|
if action.use_replace_regex_src:
|
2019-09-02 15:18:07 +10:00
|
|
|
try:
|
|
|
|
|
re.compile(action.replace_src)
|
|
|
|
|
except Exception as ex:
|
2019-09-06 23:21:26 +10:00
|
|
|
re_error_src = str(ex)
|
2019-09-02 15:18:07 +10:00
|
|
|
row.alert = True
|
2020-11-23 17:27:01 -08:00
|
|
|
|
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Find")
|
|
|
|
|
sub = row.row(align=True)
|
|
|
|
|
sub.prop(action, "replace_src", text="")
|
|
|
|
|
sub.prop(action, "use_replace_regex_src", text="", icon='SORTBYEXT')
|
|
|
|
|
|
|
|
|
|
# Row.
|
2019-09-06 23:21:26 +10:00
|
|
|
if re_error_src is not None:
|
2020-11-23 17:27:01 -08:00
|
|
|
row = col.split(factor=fac)
|
|
|
|
|
row.label(text="")
|
|
|
|
|
row.alert = True
|
|
|
|
|
row.label(text=re_error_src)
|
|
|
|
|
|
|
|
|
|
# Row 3: replace.
|
|
|
|
|
row = col.split(factor=fac)
|
2019-09-06 23:21:26 +10:00
|
|
|
|
|
|
|
|
re_error_dst = None
|
|
|
|
|
if action.use_replace_regex_src:
|
|
|
|
|
if action.use_replace_regex_dst:
|
|
|
|
|
if re_error_src is None:
|
|
|
|
|
try:
|
|
|
|
|
re.sub(action.replace_src, action.replace_dst, "")
|
|
|
|
|
except Exception as ex:
|
|
|
|
|
re_error_dst = str(ex)
|
|
|
|
|
row.alert = True
|
|
|
|
|
|
2020-11-23 17:27:01 -08:00
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Replace")
|
|
|
|
|
sub = row.row(align=True)
|
|
|
|
|
sub.prop(action, "replace_dst", text="")
|
|
|
|
|
subsub = sub.row(align=True)
|
|
|
|
|
subsub.active = action.use_replace_regex_src
|
|
|
|
|
subsub.prop(action, "use_replace_regex_dst", text="", icon='SORTBYEXT')
|
2019-09-02 15:18:07 +10:00
|
|
|
|
2020-11-23 17:27:01 -08:00
|
|
|
# Row.
|
|
|
|
|
if re_error_dst is not None:
|
|
|
|
|
row = col.split(factor=fac)
|
|
|
|
|
row.label(text="")
|
|
|
|
|
row.alert = True
|
|
|
|
|
row.label(text=re_error_dst)
|
|
|
|
|
|
|
|
|
|
# Row 4: case.
|
|
|
|
|
row = col.split(factor=fac)
|
|
|
|
|
row.label(text="")
|
2019-09-02 15:18:07 +10:00
|
|
|
row.prop(action, "replace_match_case")
|
2020-11-23 17:27:01 -08:00
|
|
|
|
2019-09-01 03:16:58 +10:00
|
|
|
elif ty == 'CASE':
|
2020-11-23 17:27:01 -08:00
|
|
|
# Row 2: method.
|
|
|
|
|
row = col.split(factor=fac)
|
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Convert To")
|
|
|
|
|
row.row().prop(action, "case_method", expand=True)
|
|
|
|
|
|
|
|
|
|
# Column 2: add-remove.
|
|
|
|
|
row = split.split(align=True)
|
|
|
|
|
row.prop(action, "op_remove", text="", icon='REMOVE')
|
|
|
|
|
row.prop(action, "op_add", text="", icon='ADD')
|
|
|
|
|
|
|
|
|
|
layout.label(text="Rename %d %s" % (len(self._data[0]), self._data[2]))
|
2019-09-01 03:16:58 +10:00
|
|
|
|
|
|
|
|
def check(self, context):
|
|
|
|
|
changed = False
|
|
|
|
|
for i, action in enumerate(self.actions):
|
|
|
|
|
if action.op_add:
|
|
|
|
|
action.op_add = False
|
|
|
|
|
self.actions.add()
|
|
|
|
|
if i + 2 != len(self.actions):
|
|
|
|
|
self.actions.move(len(self.actions) - 1, i + 1)
|
|
|
|
|
changed = True
|
|
|
|
|
break
|
|
|
|
|
if action.op_remove:
|
|
|
|
|
action.op_remove = False
|
|
|
|
|
if len(self.actions) > 1:
|
|
|
|
|
self.actions.remove(i)
|
|
|
|
|
changed = True
|
|
|
|
|
break
|
|
|
|
|
|
2019-09-02 00:51:26 +10:00
|
|
|
if (
|
|
|
|
|
(self._data_source_prev != self.data_source) or
|
|
|
|
|
(self._data_type_prev != self.data_type)
|
|
|
|
|
):
|
|
|
|
|
self._data_update(context)
|
2019-09-01 03:16:58 +10:00
|
|
|
changed = True
|
|
|
|
|
|
|
|
|
|
return changed
|
|
|
|
|
|
|
|
|
|
def execute(self, context):
|
2019-09-02 15:18:07 +10:00
|
|
|
import re
|
|
|
|
|
|
2019-09-01 03:16:58 +10:00
|
|
|
seq, attr, descr = self._data
|
|
|
|
|
|
|
|
|
|
actions = self.actions
|
|
|
|
|
|
2019-09-02 15:18:07 +10:00
|
|
|
# Sanitize actions.
|
|
|
|
|
for action in actions:
|
2019-09-06 23:21:26 +10:00
|
|
|
if action.use_replace_regex_src:
|
2019-09-02 15:18:07 +10:00
|
|
|
try:
|
|
|
|
|
re.compile(action.replace_src)
|
|
|
|
|
except Exception as ex:
|
2019-09-06 23:21:26 +10:00
|
|
|
self.report({'ERROR'}, "Invalid regular expression (find): " + str(ex))
|
2019-09-02 15:18:07 +10:00
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
2019-09-06 23:21:26 +10:00
|
|
|
if action.use_replace_regex_dst:
|
|
|
|
|
try:
|
|
|
|
|
re.sub(action.replace_src, action.replace_dst, "")
|
|
|
|
|
except Exception as ex:
|
|
|
|
|
self.report({'ERROR'}, "Invalid regular expression (replace): " + str(ex))
|
|
|
|
|
return {'CANCELLED'}
|
|
|
|
|
|
2019-09-01 03:16:58 +10:00
|
|
|
total_len = 0
|
|
|
|
|
change_len = 0
|
|
|
|
|
for item in seq:
|
|
|
|
|
name_src = getattr(item, attr)
|
|
|
|
|
name_dst = self._apply_actions(actions, name_src)
|
|
|
|
|
if name_src != name_dst:
|
|
|
|
|
setattr(item, attr, name_dst)
|
|
|
|
|
change_len += 1
|
|
|
|
|
total_len += 1
|
|
|
|
|
|
2020-06-26 12:30:03 +10:00
|
|
|
self.report({'INFO'}, "Renamed %d of %d %s" % (change_len, total_len, descr))
|
2019-09-01 03:16:58 +10:00
|
|
|
|
|
|
|
|
return {'FINISHED'}
|
|
|
|
|
|
|
|
|
|
def invoke(self, context, event):
|
|
|
|
|
|
2019-09-02 00:51:26 +10:00
|
|
|
self._data_update(context)
|
2019-09-01 03:16:58 +10:00
|
|
|
|
|
|
|
|
if not self.actions:
|
|
|
|
|
self.actions.add()
|
|
|
|
|
wm = context.window_manager
|
|
|
|
|
return wm.invoke_props_dialog(self, width=400)
|
|
|
|
|
|
|
|
|
|
|
2021-02-21 17:26:55 +01:00
|
|
|
class WM_MT_splash_quick_setup(Menu):
|
|
|
|
|
bl_label = "Quick Setup"
|
2018-09-18 17:44:14 +02:00
|
|
|
|
2021-02-21 17:26:55 +01:00
|
|
|
def draw(self, context):
|
2018-11-15 15:04:23 +01:00
|
|
|
wm = context.window_manager
|
2018-12-21 12:47:44 +11:00
|
|
|
# prefs = context.preferences
|
2018-11-15 15:04:23 +01:00
|
|
|
|
2018-09-18 17:44:14 +02:00
|
|
|
layout = self.layout
|
2018-11-15 15:04:23 +01:00
|
|
|
|
2018-09-18 17:44:14 +02:00
|
|
|
layout.operator_context = 'EXEC_DEFAULT'
|
|
|
|
|
|
|
|
|
|
layout.label(text="Quick Setup")
|
|
|
|
|
|
|
|
|
|
split = layout.split(factor=0.25)
|
|
|
|
|
split.label()
|
2018-09-20 07:11:01 +10:00
|
|
|
split = split.split(factor=2.0 / 3.0)
|
2018-09-18 17:44:14 +02:00
|
|
|
|
|
|
|
|
col = split.column()
|
|
|
|
|
|
2020-04-07 16:32:42 -07:00
|
|
|
sub = col.split(factor=0.35)
|
|
|
|
|
row = sub.row()
|
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Language")
|
|
|
|
|
prefs = context.preferences
|
|
|
|
|
sub.prop(prefs.view, "language", text="")
|
|
|
|
|
|
|
|
|
|
col.separator()
|
2018-11-15 15:04:23 +01:00
|
|
|
|
|
|
|
|
sub = col.split(factor=0.35)
|
|
|
|
|
row = sub.row()
|
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Shortcuts")
|
2018-11-15 15:04:23 +01:00
|
|
|
text = bpy.path.display_name(wm.keyconfigs.active.name)
|
2018-09-18 17:44:14 +02:00
|
|
|
if not text:
|
2018-11-15 15:04:23 +01:00
|
|
|
text = "Blender"
|
|
|
|
|
sub.menu("USERPREF_MT_keyconfigs", text=text)
|
|
|
|
|
|
2018-11-19 06:14:20 +11:00
|
|
|
kc = wm.keyconfigs.active
|
|
|
|
|
kc_prefs = kc.preferences
|
|
|
|
|
has_select_mouse = hasattr(kc_prefs, "select_mouse")
|
|
|
|
|
if has_select_mouse:
|
2018-11-16 08:28:58 +11:00
|
|
|
sub = col.split(factor=0.35)
|
|
|
|
|
row = sub.row()
|
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Select With")
|
2018-11-28 06:57:25 +11:00
|
|
|
sub.row().prop(kc_prefs, "select_mouse", expand=True)
|
2018-11-19 06:14:20 +11:00
|
|
|
has_select_mouse = True
|
|
|
|
|
|
2018-11-28 06:57:25 +11:00
|
|
|
has_spacebar_action = hasattr(kc_prefs, "spacebar_action")
|
|
|
|
|
if has_spacebar_action:
|
|
|
|
|
sub = col.split(factor=0.35)
|
|
|
|
|
row = sub.row()
|
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Spacebar")
|
|
|
|
|
sub.row().prop(kc_prefs, "spacebar_action", expand=True)
|
|
|
|
|
has_select_mouse = True
|
2018-09-18 17:44:14 +02:00
|
|
|
|
|
|
|
|
col.separator()
|
|
|
|
|
|
2018-11-15 15:04:23 +01:00
|
|
|
sub = col.split(factor=0.35)
|
|
|
|
|
row = sub.row()
|
|
|
|
|
row.alignment = 'RIGHT'
|
|
|
|
|
row.label(text="Theme")
|
2018-10-08 19:19:05 +02:00
|
|
|
label = bpy.types.USERPREF_MT_interface_theme_presets.bl_label
|
|
|
|
|
if label == "Presets":
|
|
|
|
|
label = "Blender Dark"
|
|
|
|
|
sub.menu("USERPREF_MT_interface_theme_presets", text=label)
|
|
|
|
|
|
2018-11-16 08:28:58 +11:00
|
|
|
# Keep height constant
|
2018-11-19 06:14:20 +11:00
|
|
|
if not has_select_mouse:
|
2018-11-16 08:28:58 +11:00
|
|
|
col.label()
|
2018-11-28 06:57:25 +11:00
|
|
|
if not has_spacebar_action:
|
|
|
|
|
col.label()
|
2018-11-15 15:04:23 +01:00
|
|
|
|
2018-09-18 17:44:14 +02:00
|
|
|
layout.label()
|
|
|
|
|
|
|
|
|
|
row = layout.row()
|
|
|
|
|
|
|
|
|
|
sub = row.row()
|
2020-04-15 15:51:08 +02:00
|
|
|
old_version = bpy.types.PREFERENCES_OT_copy_prev.previous_version()
|
|
|
|
|
if bpy.types.PREFERENCES_OT_copy_prev.poll(context) and old_version:
|
2019-03-02 00:21:05 +11:00
|
|
|
sub.operator("preferences.copy_prev", text="Load %d.%d Settings" % old_version)
|
2018-09-18 17:44:14 +02:00
|
|
|
sub.operator("wm.save_userpref", text="Save New Settings")
|
|
|
|
|
else:
|
|
|
|
|
sub.label()
|
|
|
|
|
sub.label()
|
|
|
|
|
sub.operator("wm.save_userpref", text="Next")
|
|
|
|
|
|
|
|
|
|
layout.separator()
|
2018-11-28 16:36:48 +01:00
|
|
|
layout.separator()
|
2018-09-18 17:44:14 +02:00
|
|
|
|
|
|
|
|
|
2021-02-21 17:26:55 +01:00
|
|
|
class WM_MT_splash(Menu):
|
|
|
|
|
bl_label = "Splash"
|
2018-09-18 17:44:14 +02:00
|
|
|
|
2021-02-21 17:26:55 +01:00
|
|
|
def draw(self, context):
|
2018-09-18 17:44:14 +02:00
|
|
|
layout = self.layout
|
|
|
|
|
layout.operator_context = 'EXEC_DEFAULT'
|
|
|
|
|
layout.emboss = 'PULLDOWN_MENU'
|
|
|
|
|
|
|
|
|
|
split = layout.split()
|
|
|
|
|
|
|
|
|
|
# Templates
|
|
|
|
|
col1 = split.column()
|
|
|
|
|
col1.label(text="New File")
|
|
|
|
|
|
|
|
|
|
bpy.types.TOPBAR_MT_file_new.draw_ex(col1, context, use_splash=True)
|
|
|
|
|
|
|
|
|
|
# Recent
|
|
|
|
|
col2 = split.column()
|
|
|
|
|
col2_title = col2.row()
|
|
|
|
|
|
|
|
|
|
found_recent = col2.template_recent_files()
|
|
|
|
|
|
|
|
|
|
if found_recent:
|
|
|
|
|
col2_title.label(text="Recent Files")
|
|
|
|
|
else:
|
2019-07-04 18:24:13 -04:00
|
|
|
|
2018-09-18 17:44:14 +02:00
|
|
|
# Links if no recent files
|
|
|
|
|
col2_title.label(text="Getting Started")
|
|
|
|
|
|
2019-08-20 22:00:01 +10:00
|
|
|
col2.operator("wm.url_open_preset", text="Manual", icon='URL').type = 'MANUAL'
|
|
|
|
|
col2.operator("wm.url_open_preset", text="Blender Website", icon='URL').type = 'BLENDER'
|
|
|
|
|
col2.operator("wm.url_open_preset", text="Credits", icon='URL').type = 'CREDITS'
|
2018-09-18 17:44:14 +02:00
|
|
|
|
|
|
|
|
layout.separator()
|
|
|
|
|
|
|
|
|
|
split = layout.split()
|
|
|
|
|
|
|
|
|
|
col1 = split.column()
|
|
|
|
|
sub = col1.row()
|
|
|
|
|
sub.operator_context = 'INVOKE_DEFAULT'
|
|
|
|
|
sub.operator("wm.open_mainfile", text="Open...", icon='FILE_FOLDER')
|
|
|
|
|
col1.operator("wm.recover_last_session", icon='RECOVER_LAST')
|
|
|
|
|
|
|
|
|
|
col2 = split.column()
|
2019-08-20 22:00:01 +10:00
|
|
|
|
|
|
|
|
col2.operator("wm.url_open_preset", text="Release Notes", icon='URL').type = 'RELEASE_NOTES'
|
|
|
|
|
col2.operator("wm.url_open_preset", text="Development Fund", icon='FUND').type = 'FUND'
|
2018-09-18 17:44:14 +02:00
|
|
|
|
|
|
|
|
layout.separator()
|
2018-11-28 16:36:48 +01:00
|
|
|
layout.separator()
|
2018-09-18 17:44:14 +02:00
|
|
|
|
|
|
|
|
|
2020-05-12 14:28:32 -07:00
|
|
|
class WM_MT_splash_about(Menu):
|
|
|
|
|
bl_label = "About"
|
|
|
|
|
|
|
|
|
|
def draw(self, context):
|
|
|
|
|
|
|
|
|
|
layout = self.layout
|
|
|
|
|
layout.operator_context = 'EXEC_DEFAULT'
|
|
|
|
|
|
2020-12-06 10:29:26 -08:00
|
|
|
split = layout.split(factor=0.65)
|
|
|
|
|
|
|
|
|
|
col = split.column(align=True)
|
|
|
|
|
col.scale_y = 0.8
|
|
|
|
|
col.label(text=bpy.app.version_string, translate=False)
|
|
|
|
|
col.separator(factor=2.5)
|
|
|
|
|
col.label(text=iface_("Date: %s %s") % (bpy.app.build_commit_date.decode('utf-8', 'replace'),
|
|
|
|
|
bpy.app.build_commit_time.decode('utf-8', 'replace')), translate=False)
|
|
|
|
|
col.label(text=iface_("Hash: %s") % bpy.app.build_hash.decode('ascii'), translate=False)
|
|
|
|
|
col.label(text=iface_("Branch: %s") % bpy.app.build_branch.decode('utf-8', 'replace'), translate=False)
|
|
|
|
|
col.separator(factor=2.0)
|
|
|
|
|
col.label(text="Blender is free software")
|
|
|
|
|
col.label(text="Licensed under the GNU General Public License")
|
|
|
|
|
|
|
|
|
|
col = split.column(align=True)
|
|
|
|
|
col.emboss = 'PULLDOWN_MENU'
|
|
|
|
|
col.operator("wm.url_open_preset", text="Release Notes", icon='URL').type = 'RELEASE_NOTES'
|
|
|
|
|
col.operator("wm.url_open_preset", text="Credits", icon='URL').type = 'CREDITS'
|
|
|
|
|
col.operator("wm.url_open", text="License", icon='URL').url = "https://www.blender.org/about/license/"
|
|
|
|
|
col.operator("wm.url_open_preset", text="Blender Website", icon='URL').type = 'BLENDER'
|
|
|
|
|
col.operator("wm.url_open", text="Blender Store", icon='URL').url = "https://store.blender.org"
|
|
|
|
|
col.operator("wm.url_open_preset", text="Development Fund", icon='FUND').type = 'FUND'
|
2020-05-12 14:28:32 -07:00
|
|
|
|
|
|
|
|
|
2018-10-22 17:18:42 +02:00
|
|
|
class WM_OT_drop_blend_file(Operator):
|
|
|
|
|
bl_idname = "wm.drop_blend_file"
|
|
|
|
|
bl_label = "Handle dropped .blend file"
|
|
|
|
|
bl_options = {'INTERNAL'}
|
|
|
|
|
|
|
|
|
|
filepath: StringProperty()
|
|
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def invoke(self, context, _event):
|
2018-10-22 17:18:42 +02:00
|
|
|
context.window_manager.popup_menu(self.draw_menu, title=bpy.path.basename(self.filepath), icon='QUESTION')
|
2019-03-04 22:06:23 +11:00
|
|
|
return {'FINISHED'}
|
2018-10-22 17:18:42 +02:00
|
|
|
|
2019-05-09 09:15:01 +10:00
|
|
|
def draw_menu(self, menu, _context):
|
2018-10-22 17:18:42 +02:00
|
|
|
layout = menu.layout
|
|
|
|
|
|
|
|
|
|
col = layout.column()
|
2019-05-14 14:13:02 +02:00
|
|
|
col.operator_context = 'INVOKE_DEFAULT'
|
|
|
|
|
props = col.operator("wm.open_mainfile", text="Open", icon='FILE_FOLDER')
|
|
|
|
|
props.filepath = self.filepath
|
|
|
|
|
props.display_file_selector = False
|
2018-10-22 17:18:42 +02:00
|
|
|
|
|
|
|
|
layout.separator()
|
|
|
|
|
col = layout.column()
|
|
|
|
|
col.operator_context = 'INVOKE_DEFAULT'
|
|
|
|
|
col.operator("wm.link", text="Link...", icon='LINK_BLEND').filepath = self.filepath
|
|
|
|
|
col.operator("wm.append", text="Append...", icon='APPEND_BLEND').filepath = self.filepath
|
|
|
|
|
|
2018-12-20 13:01:40 +11:00
|
|
|
|
2017-03-18 20:03:24 +11:00
|
|
|
classes = (
|
|
|
|
|
WM_OT_context_collection_boolean_set,
|
|
|
|
|
WM_OT_context_cycle_array,
|
|
|
|
|
WM_OT_context_cycle_enum,
|
|
|
|
|
WM_OT_context_cycle_int,
|
|
|
|
|
WM_OT_context_menu_enum,
|
|
|
|
|
WM_OT_context_modal_mouse,
|
|
|
|
|
WM_OT_context_pie_enum,
|
|
|
|
|
WM_OT_context_scale_float,
|
|
|
|
|
WM_OT_context_scale_int,
|
|
|
|
|
WM_OT_context_set_boolean,
|
|
|
|
|
WM_OT_context_set_enum,
|
|
|
|
|
WM_OT_context_set_float,
|
|
|
|
|
WM_OT_context_set_id,
|
|
|
|
|
WM_OT_context_set_int,
|
|
|
|
|
WM_OT_context_set_string,
|
|
|
|
|
WM_OT_context_set_value,
|
|
|
|
|
WM_OT_context_toggle,
|
|
|
|
|
WM_OT_context_toggle_enum,
|
|
|
|
|
WM_OT_doc_view,
|
|
|
|
|
WM_OT_doc_view_manual,
|
2018-10-22 17:18:42 +02:00
|
|
|
WM_OT_drop_blend_file,
|
2017-03-18 20:03:24 +11:00
|
|
|
WM_OT_operator_cheat_sheet,
|
|
|
|
|
WM_OT_operator_pie_enum,
|
|
|
|
|
WM_OT_path_open,
|
|
|
|
|
WM_OT_properties_add,
|
|
|
|
|
WM_OT_properties_context_change,
|
|
|
|
|
WM_OT_properties_edit,
|
|
|
|
|
WM_OT_properties_remove,
|
|
|
|
|
WM_OT_sysinfo,
|
2018-03-01 01:26:02 +11:00
|
|
|
WM_OT_owner_disable,
|
|
|
|
|
WM_OT_owner_enable,
|
2017-03-18 20:03:24 +11:00
|
|
|
WM_OT_url_open,
|
2019-08-20 22:00:01 +10:00
|
|
|
WM_OT_url_open_preset,
|
2019-03-15 12:45:41 +11:00
|
|
|
WM_OT_tool_set_by_id,
|
2019-06-25 01:06:09 +10:00
|
|
|
WM_OT_tool_set_by_index,
|
2018-05-18 22:27:59 +02:00
|
|
|
WM_OT_toolbar,
|
2019-12-07 03:45:50 +11:00
|
|
|
WM_OT_toolbar_fallback_pie,
|
2019-10-28 22:43:15 +11:00
|
|
|
WM_OT_toolbar_prompt,
|
2019-09-01 03:16:58 +10:00
|
|
|
BatchRenameAction,
|
|
|
|
|
WM_OT_batch_rename,
|
2021-02-21 17:26:55 +01:00
|
|
|
WM_MT_splash_quick_setup,
|
2018-09-18 17:44:14 +02:00
|
|
|
WM_MT_splash,
|
2020-05-12 14:28:32 -07:00
|
|
|
WM_MT_splash_about,
|
2017-03-25 09:29:51 +11:00
|
|
|
)
|