Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/modules/bpy/utils/__init__.py206
-rw-r--r--release/scripts/templates_py/ui_tool_simple.py65
2 files changed, 271 insertions, 0 deletions
diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py
index ce7781517b8..81b62b59fa6 100644
--- a/release/scripts/modules/bpy/utils/__init__.py
+++ b/release/scripts/modules/bpy/utils/__init__.py
@@ -39,6 +39,7 @@ __all__ = (
"unregister_manual_map",
"register_classes_factory",
"register_submodule_factory",
+ "register_tool",
"make_rna_paths",
"manual_map",
"previews",
@@ -50,6 +51,7 @@ __all__ = (
"smpte_from_seconds",
"units",
"unregister_class",
+ "unregister_tool",
"user_resource",
"execfile",
)
@@ -716,6 +718,210 @@ def register_submodule_factory(module_name, submodule_names):
# -----------------------------------------------------------------------------
+# Tool Registration
+
+
+def register_tool(tool_cls, *, after=None, separator=False, group=False):
+ """
+ Register a tool in the toolbar.
+
+ :arg tool: A tool subclass.
+ :type tool: :class:`bpy.types.WorkSpaceTool` subclass.
+ :arg space_type: Space type identifier.
+ :type space_type: string
+ :arg after: Optional identifiers this tool will be added after.
+ :type after: collection of strings or None.
+ :arg separator: When true, add a separator before this tool.
+ :type separator: bool
+ :arg group: When true, add a new nested group of tools.
+ :type group: bool
+ """
+ space_type = tool_cls.bl_space_type
+ context_mode = tool_cls.bl_context_mode
+
+ from bl_ui.space_toolsystem_common import (
+ ToolSelectPanelHelper,
+ ToolDef,
+ )
+
+ cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
+ if cls is None:
+ raise Exception(f"Space type {space_type!r} has no toolbar")
+ tools = cls._tools[context_mode]
+
+ # First sanity check
+ from bpy.types import WorkSpaceTool
+ tools_id = {
+ item.idname for item in ToolSelectPanelHelper._tools_flatten(tools)
+ if item is not None
+ }
+ if not issubclass(tool_cls, WorkSpaceTool):
+ raise Exception(f"Expected WorkSpaceTool subclass, not {type(tool_cls)!r}")
+ if tool_cls.bl_idname in tools_id:
+ raise Exception(f"Tool {tool_cls.bl_idname!r} already exists!")
+ del tools_id, WorkSpaceTool
+
+ # Convert the class into a ToolDef.
+ def tool_from_class(tool_cls):
+ # Convert class to tuple, store in the class for removal.
+ tool_def = ToolDef.from_dict({
+ "idname": tool_cls.bl_idname,
+ "label": tool_cls.bl_label,
+ "description": getattr(tool_cls, "bl_description", tool_cls.__doc__),
+ "icon": getattr(tool_cls, "bl_icon", None),
+ "cursor": getattr(tool_cls, "bl_cursor", None),
+ "widget": getattr(tool_cls, "bl_widget", None),
+ "keymap": getattr(tool_cls, "bl_keymap", None),
+ "data_block": getattr(tool_cls, "bl_data_block", None),
+ "operator": getattr(tool_cls, "bl_operator", None),
+ "draw_settings": getattr(tool_cls, "draw_settings", None),
+ "draw_cursor": getattr(tool_cls, "draw_cursor", None),
+ })
+ tool_cls._bl_tool = tool_def
+
+ keymap_data = tool_def.keymap
+ if keymap_data is not None:
+ if context_mode is None:
+ context_descr = "All"
+ else:
+ context_descr = context_mode.replace("_", " ").title()
+ from bpy import context
+ wm = context.window_manager
+ kc = wm.keyconfigs.default
+ if callable(keymap_data[0]):
+ cls._km_action_simple(kc, context_descr, tool_def.label, keymap_data)
+ return tool_def
+
+ tool_converted = tool_from_class(tool_cls)
+
+ if group:
+ # Create a new group
+ tool_converted = (tool_converted,)
+
+
+ tool_def_insert = (
+ (None, tool_converted) if separator else
+ (tool_converted,)
+ )
+
+ def skip_to_end_of_group(seq, i):
+ i_prev = i
+ while i < len(seq) and seq[i] is not None:
+ i_prev = i
+ i += 1
+ return i_prev
+
+ changed = False
+ if after is not None:
+ for i, item in enumerate(tools):
+ if item is None:
+ pass
+ elif isinstance(item, ToolDef):
+ if item.idname in after:
+ i = skip_to_end_of_group(item, i)
+ tools[i + 1:i + 1] = tool_def_insert
+ changed = True
+ break
+ elif isinstance(item, tuple):
+ for j, sub_item in enumerate(item, 1):
+ if isinstance(sub_item, ToolDef):
+ if sub_item.idname in after:
+ if group:
+ # Can't add a group within a group,
+ # add a new group after this group.
+ i = skip_to_end_of_group(tools, i)
+ tools[i + 1:i + 1] = tool_def_insert
+ else:
+ j = skip_to_end_of_group(item, j)
+ item = item[:j + 1] + tool_def_insert + item[j + 1:]
+ tools[i] = item
+ changed = True
+ break
+ if changed:
+ break
+
+ if not changed:
+ print("bpy.utils.register_tool: could not find 'after'", after)
+ if not changed:
+ tools.extend(tool_def_insert)
+
+
+def unregister_tool(tool_cls):
+ space_type = tool_cls.bl_space_type
+ context_mode = tool_cls.bl_context_mode
+
+ from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
+ cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
+ if cls is None:
+ raise Exception(f"Space type {space_type!r} has no toolbar")
+ tools = cls._tools[context_mode]
+
+ tool_def = tool_cls._bl_tool
+ try:
+ i = tools.index(tool_def)
+ except ValueError:
+ i = -1
+
+ def tool_list_clean(tool_list):
+ # Trim separators.
+ while tool_list and tool_list[-1] is None:
+ del tool_list[-1]
+ while tool_list and tool_list[0] is None:
+ del tool_list[0]
+ is_none_prev = False
+ # Remove duplicate separators.
+ for i in range(len(tool_list) - 1, -1, -1):
+ is_none = tool_list[i] is None
+ if is_none and prev_is_none:
+ del tool_list[i]
+ prev_is_none = is_none
+
+ changed = False
+ if i != -1:
+ del tools[i]
+ tool_list_clean(tools)
+ changed = True
+
+ if not changed:
+ for i, item in enumerate(tools):
+ if isinstance(item, tuple):
+ try:
+ j = item.index(tool_def)
+ except ValueError:
+ j = -1
+
+ if j != -1:
+ item_clean = list(item)
+ del item_clean[j]
+ tool_list_clean(item_clean)
+ if item_clean:
+ tools[i] = tuple(item_clean)
+ else:
+ del tools[i]
+ tool_list_clean(tools)
+ del item_clean
+
+ # tuple(sub_item for sub_item in items if sub_item is not tool_def)
+ changed = True
+ break
+
+ if not changed:
+ raise Exception(f"Unable to remove {tool_cls!r}")
+ del tool_cls._bl_tool
+
+ keymap_data = tool_def.keymap
+ if keymap_data is not None:
+ from bpy import context
+ wm = context.window_manager
+ kc = wm.keyconfigs.default
+ km = kc.keymaps.get(keymap_data[0])
+ if km is None:
+ print("Warning keymap {keymap_data[0]!r} not found!")
+ else:
+ kc.keymaps.remove(km)
+
+
+# -----------------------------------------------------------------------------
# Manual lookups, each function has to return a basepath and a sequence
# of...
diff --git a/release/scripts/templates_py/ui_tool_simple.py b/release/scripts/templates_py/ui_tool_simple.py
new file mode 100644
index 00000000000..9cd71d0160c
--- /dev/null
+++ b/release/scripts/templates_py/ui_tool_simple.py
@@ -0,0 +1,65 @@
+# This example adds an object mode tool to the toolbar.
+# This is just the circle-select and lasso tools tool.
+import bpy
+from bpy.utils.toolsystem import ToolDef
+from bpy.types import WorkSpaceTool
+
+class MyTool(WorkSpaceTool):
+ bl_space_type='VIEW_3D'
+ bl_context_mode='OBJECT'
+
+ # The prefix of the idname should be your add-on name.
+ bl_idname = "my_template.my_circle_select"
+ bl_label = "My Circle Select"
+ bl_description = (
+ "This is a tooltip\n"
+ "with multiple lines"
+ )
+ bl_icon = "ops.generic.select_circle"
+ bl_widget = None
+ bl_keymap = (
+ ("view3d.select_circle", {"type": 'LEFTMOUSE', "value": 'PRESS'},
+ {"properties": [("wait_for_input", False)]}),
+ ("view3d.select_circle", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'SUB'), ("wait_for_input", False)]}),
+ )
+
+ def draw_settings(context, layout, tool):
+ props = tool.operator_properties("view3d.select_circle")
+ layout.prop(props, "mode")
+ layout.prop(props, "radius")
+
+
+class MyOtherTool(WorkSpaceTool):
+ bl_space_type='VIEW_3D'
+ bl_context_mode='OBJECT'
+
+ bl_idname = "my_template.my_other_select"
+ bl_label = "My Lasso Tool Select"
+ bl_description = (
+ "This is a tooltip\n"
+ "with multiple lines"
+ )
+ bl_icon = "ops.generic.select_lasso"
+ bl_widget = None
+ bl_keymap = (
+ ("view3d.select_lasso", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
+ ("view3d.select_lasso", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
+ {"properties": [("mode", 'SUB')]}),
+ )
+
+ def draw_settings(context, layout, tool):
+ props = tool.operator_properties("view3d.select_lasso")
+ layout.prop(props, "mode")
+
+
+def register():
+ bpy.utils.register_tool(MyTool, after={"builtin.scale_cage"}, separator=True, group=True)
+ bpy.utils.register_tool(MyOtherTool, after={MyTool.bl_idname})
+
+def unregister():
+ bpy.utils.unregister_tool(MyTool)
+ bpy.utils.unregister_tool(MyOtherTool)
+
+if __name__ == "__main__":
+ register()