123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853 |
- # ##### BEGIN GPL LICENSE BLOCK #####
- #
- # This program is free software; you can redistribute it and/or
- # modify it under the terms of the GNU General Public License
- # as published by the Free Software Foundation; either version 2
- # of the License, or (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program; if not, write to the Free Software Foundation,
- # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- #
- # ##### END GPL LICENSE BLOCK #####
- # <pep8 compliant>
- import bpy
- from bpy.types import (
- Menu,
- )
- __all__ = (
- "ToolDef",
- "ToolSelectPanelHelper",
- "activate_by_id",
- "activate_by_id_or_cycle",
- "description_from_id",
- "keymap_from_id",
- )
- # Support reloading icons.
- if "_icon_cache" in locals():
- release = bpy.app.icons.release
- for icon_value in _icon_cache.values():
- if icon_value != 0:
- release(icon_value)
- del release
- # (filename -> icon_value) map
- _icon_cache = {}
- def _keymap_fn_from_seq(keymap_data):
- def keymap_fn(km):
- if keymap_fn.keymap_data:
- from bl_keymap_utils.io import keymap_init_from_data
- keymap_init_from_data(km, keymap_fn.keymap_data)
- keymap_fn.keymap_data = keymap_data
- return keymap_fn
- def _item_is_fn(item):
- return (not (type(item) is ToolDef) and callable(item))
- from collections import namedtuple
- ToolDef = namedtuple(
- "ToolDef",
- (
- # Unique tool name (withing space & mode context).
- "idname",
- # The name to display in the interface.
- "label",
- # Description (for tooltip), when not set, use the description of 'operator',
- # may be a string or a 'function(context, item, keymap) -> string'.
- "description",
- # The name of the icon to use (found in ``release/datafiles/icons``) or None for no icon.
- "icon",
- # An optional cursor to use when this tool is active.
- "cursor",
- # An optional gizmo group to activate when the tool is set or None for no gizmo.
- "widget",
- # Optional keymap for tool, either:
- # - A function that populates a keymaps passed in as an argument.
- # - A tuple filled with triple's of:
- # ``(operator_id, operator_properties, keymap_item_args)``.
- #
- # Warning: currently 'from_dict' this is a list of one item,
- # so internally we can swap the keymap function for the keymap it's self.
- # This isn't very nice and may change, tool definitions shouldn't care about this.
- "keymap",
- # Optional data-block assosiated with this tool.
- # (Typically brush name, usage depends on mode, we could use for non-brush ID's in other modes).
- "data_block",
- # Optional primary operator (for introspection only).
- "operator",
- # Optional draw settings (operator options, tool_settings).
- "draw_settings",
- # Optional draw cursor.
- "draw_cursor",
- )
- )
- del namedtuple
- def from_dict(kw_args):
- """
- Use so each tool can avoid defining all members of the named tuple.
- Also convert the keymap from a tuple into a function
- (since keymap is a callback).
- """
- kw = {
- "description": None,
- "icon": None,
- "cursor": None,
- "widget": None,
- "keymap": None,
- "data_block": None,
- "operator": None,
- "draw_settings": None,
- "draw_cursor": None,
- }
- kw.update(kw_args)
- keymap = kw["keymap"]
- if keymap is None:
- pass
- elif type(keymap) is tuple:
- keymap = [_keymap_fn_from_seq(keymap)]
- else:
- keymap = [keymap]
- kw["keymap"] = keymap
- return ToolDef(**kw)
- def from_fn(fn):
- """
- Use as decorator so we can define functions.
- """
- return ToolDef.from_dict(fn())
- def with_args(**kw):
- def from_fn(fn):
- return ToolDef.from_dict(fn(**kw))
- return from_fn
- from_fn.with_args = with_args
- ToolDef.from_dict = from_dict
- ToolDef.from_fn = from_fn
- del from_dict, from_fn, with_args
- class ToolActivePanelHelper:
- # Sub-class must define.
- # bl_space_type = 'VIEW_3D'
- # bl_region_type = 'UI'
- bl_label = "Active Tool"
- # bl_category = "Tool"
- def draw(self, context):
- layout = self.layout
- layout.use_property_split = True
- layout.use_property_decorate = False
- ToolSelectPanelHelper.draw_active_tool_header(
- context,
- layout,
- show_tool_name=True,
- tool_key=ToolSelectPanelHelper._tool_key_from_context(context, space_type=self.bl_space_type),
- )
- class ToolSelectPanelHelper:
- """
- Generic Class, can be used for any toolbar.
- - keymap_prefix:
- The text prefix for each key-map for this spaces tools.
- - tools_all():
- Returns (context_mode, tools) tuple pair for all tools defined.
- - tools_from_context(context, mode=None):
- Returns tools available in this context.
- Each tool is a 'ToolDef' or None for a separator in the toolbar, use ``None``.
- """
- @staticmethod
- def _tool_class_from_space_type(space_type):
- return next(
- (cls for cls in ToolSelectPanelHelper.__subclasses__()
- if cls.bl_space_type == space_type),
- None
- )
- @staticmethod
- def _icon_value_from_icon_handle(icon_name):
- import os
- if icon_name is not None:
- assert(type(icon_name) is str)
- icon_value = _icon_cache.get(icon_name)
- if icon_value is None:
- dirname = bpy.utils.resource_path('LOCAL')
- if not os.path.exists(dirname):
- # TODO(campbell): use a better way of finding datafiles.
- dirname = bpy.utils.resource_path('SYSTEM')
- filename = os.path.join(dirname, "datafiles", "icons", icon_name + ".dat")
- try:
- icon_value = bpy.app.icons.new_triangles_from_file(filename)
- except Exception as ex:
- if not os.path.exists(filename):
- print("Missing icons:", filename, ex)
- else:
- print("Corrupt icon:", filename, ex)
- # Use none as a fallback (avoids layout issues).
- if icon_name != "none":
- icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle("none")
- else:
- icon_value = 0
- _icon_cache[icon_name] = icon_value
- return icon_value
- else:
- return 0
- @staticmethod
- def _tools_flatten(tools):
- """
- Flattens, skips None and calls generators.
- """
- for item in tools:
- if item is None:
- yield None
- elif type(item) is tuple:
- for sub_item in item:
- if sub_item is None:
- yield None
- elif _item_is_fn(sub_item):
- yield from sub_item(context)
- else:
- yield sub_item
- else:
- if _item_is_fn(item):
- yield from item(context)
- else:
- yield item
- @staticmethod
- def _tools_flatten_with_tool_index(tools):
- for item in tools:
- if item is None:
- yield None, -1
- elif type(item) is tuple:
- i = 0
- for sub_item in item:
- if sub_item is None:
- yield None, -1
- elif _item_is_fn(sub_item):
- for item_dyn in sub_item(context):
- yield item_dyn, i
- i += 1
- else:
- yield sub_item, i
- i += 1
- else:
- if _item_is_fn(item):
- for item_dyn in item(context):
- yield item_dyn, -1
- else:
- yield item, -1
- @staticmethod
- def _tool_get_active(context, space_type, mode, with_icon=False):
- """
- Return the active Python tool definition and icon name.
- """
- cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
- if cls is not None:
- tool_active = ToolSelectPanelHelper._tool_active_from_context(context, space_type, mode)
- tool_active_id = getattr(tool_active, "idname", None)
- for item in ToolSelectPanelHelper._tools_flatten(cls.tools_from_context(context, mode)):
- if item is not None:
- if item.idname == tool_active_id:
- if with_icon:
- icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon)
- else:
- icon_value = 0
- return (item, tool_active, icon_value)
- return None, None, 0
- @staticmethod
- def _tool_get_by_id(context, space_type, idname):
- """
- Return the active Python tool definition and index (if in sub-group, else -1).
- """
- cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
- if cls is not None:
- for item, index in ToolSelectPanelHelper._tools_flatten_with_tool_index(cls.tools_from_context(context)):
- if item is not None:
- if item.idname == idname:
- return (cls, item, index)
- return None, None, -1
- @staticmethod
- def _tool_get_by_flat_index(context, space_type, tool_index):
- """
- Return the active Python tool definition and index (if in sub-group, else -1).
- Return the index of the expanded list.
- """
- cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
- if cls is not None:
- i = 0
- for item, index in ToolSelectPanelHelper._tools_flatten_with_tool_index(cls.tools_from_context(context)):
- if item is not None:
- if i == tool_index:
- return (cls, item, index)
- i += 1
- return None, None, -1
- @staticmethod
- def _tool_get_by_index(context, space_type, tool_index):
- """
- Return the active Python tool definition and index (if in sub-group, else -1).
- Return the index of the list without expanding.
- """
- cls = ToolSelectPanelHelper._tool_class_from_space_type(space_type)
- if cls is not None:
- i = 0
- for item in cls.tools_from_context(context):
- if item is not None:
- if i == tool_index:
- if type(item) is tuple:
- index = cls._tool_group_active.get(item[0].idname, 0)
- item = item[index]
- else:
- index = -1
- return (cls, item, index)
- i += 1
- return None, None, -1
- @staticmethod
- def _tool_active_from_context(context, space_type, mode=None, create=False):
- if space_type == 'VIEW_3D':
- if mode is None:
- mode = context.mode
- tool = context.workspace.tools.from_space_view3d_mode(mode, create=create)
- if tool is not None:
- tool.refresh_from_context()
- return tool
- elif space_type == 'IMAGE_EDITOR':
- space_data = context.space_data
- if mode is None:
- if space_data is None:
- mode = 'VIEW'
- else:
- mode = space_data.mode
- tool = context.workspace.tools.from_space_image_mode(mode, create=create)
- if tool is not None:
- tool.refresh_from_context()
- return tool
- elif space_type == 'NODE_EDITOR':
- space_data = context.space_data
- tool = context.workspace.tools.from_space_node(create=create)
- if tool is not None:
- tool.refresh_from_context()
- return tool
- return None
- @staticmethod
- def _tool_identifier_from_button(context):
- return context.button_operator.name
- @classmethod
- def _km_action_simple(cls, kc, context_descr, label, keymap_fn):
- km_idname = f"{cls.keymap_prefix:s} {context_descr:s}, {label:s}"
- km = kc.keymaps.get(km_idname)
- if km is None:
- km = kc.keymaps.new(km_idname, space_type=cls.bl_space_type, region_type='WINDOW', tool=True)
- keymap_fn[0](km)
- keymap_fn[0] = km.name
- # Special internal function, gives use items that contain keymaps.
- @staticmethod
- def _tools_flatten_with_keymap(tools):
- for item_parent in tools:
- if item_parent is None:
- continue
- for item in item_parent if (type(item_parent) is tuple) else (item_parent,):
- # skip None or generator function
- if item is None or _item_is_fn(item):
- continue
- if item.keymap is not None:
- yield item
- @classmethod
- def register(cls):
- wm = bpy.context.window_manager
- # Write into defaults, users may modify in preferences.
- kc = wm.keyconfigs.default
- # Track which tool-group was last used for non-active groups.
- # Blender stores the active tool-group index.
- #
- # {tool_name_first: index_in_group, ...}
- cls._tool_group_active = {}
- # ignore in background mode
- if kc is None:
- return
- for context_mode, tools in cls.tools_all():
- if context_mode is None:
- context_descr = "All"
- else:
- context_descr = context_mode.replace("_", " ").title()
- for item in cls._tools_flatten_with_keymap(tools):
- keymap_data = item.keymap
- if callable(keymap_data[0]):
- cls._km_action_simple(kc, context_descr, item.label, keymap_data)
- @classmethod
- def keymap_ui_hierarchy(cls, context_mode):
- # See: bpy_extras.keyconfig_utils
- # Keymaps may be shared, don't show them twice.
- visited = set()
- for context_mode_test, tools in cls.tools_all():
- if context_mode_test == context_mode:
- for item in cls._tools_flatten_with_keymap(tools):
- km_name = item.keymap[0]
- # print((km.name, cls.bl_space_type, 'WINDOW', []))
- if km_name in visited:
- continue
- visited.add(km_name)
- yield (km_name, cls.bl_space_type, 'WINDOW', [])
- # -------------------------------------------------------------------------
- # Layout Generators
- #
- # Meaning of recieved values:
- # - Bool: True for a separator, otherwise False for regular tools.
- # - None: Signal to finish (complete any final operations, e.g. add padding).
- @staticmethod
- def _layout_generator_single_column(layout, scale_y):
- col = layout.column(align=True)
- col.scale_y = scale_y
- is_sep = False
- while True:
- if is_sep is True:
- col = layout.column(align=True)
- col.scale_y = scale_y
- elif is_sep is None:
- yield None
- return
- is_sep = yield col
- @staticmethod
- def _layout_generator_multi_columns(layout, column_count, scale_y):
- scale_x = scale_y * 1.1
- column_last = column_count - 1
- col = layout.column(align=True)
- row = col.row(align=True)
- row.scale_x = scale_x
- row.scale_y = scale_y
- is_sep = False
- column_index = 0
- while True:
- if is_sep is True:
- if column_index != column_last:
- row.label(text="")
- col = layout.column(align=True)
- row = col.row(align=True)
- row.scale_x = scale_x
- row.scale_y = scale_y
- column_index = 0
- is_sep = yield row
- if is_sep is None:
- if column_index == column_last:
- row.label(text="")
- yield None
- return
- if column_index == column_count:
- column_index = 0
- row = col.row(align=True)
- row.scale_x = scale_x
- row.scale_y = scale_y
- column_index += 1
- @staticmethod
- def _layout_generator_detect_from_region(layout, region, scale_y):
- """
- Choose an appropriate layout for the toolbar.
- """
- # Currently this just checks the width,
- # we could have different layouts as preferences too.
- system = bpy.context.preferences.system
- view2d = region.view2d
- view2d_scale = (
- view2d.region_to_view(1.0, 0.0)[0] -
- view2d.region_to_view(0.0, 0.0)[0]
- )
- width_scale = region.width * view2d_scale / system.ui_scale
- if width_scale > 120.0:
- show_text = True
- column_count = 1
- else:
- show_text = False
- # 2 column layout, disabled
- if width_scale > 80.0:
- column_count = 2
- else:
- column_count = 1
- if column_count == 1:
- ui_gen = ToolSelectPanelHelper._layout_generator_single_column(
- layout, scale_y=scale_y,
- )
- else:
- ui_gen = ToolSelectPanelHelper._layout_generator_multi_columns(
- layout, column_count=column_count, scale_y=scale_y,
- )
- return ui_gen, show_text
- @classmethod
- def draw_cls(cls, layout, context, detect_layout=True, scale_y=1.75):
- # Use a classmethod so it can be called outside of a panel context.
- # XXX, this UI isn't very nice.
- # We might need to create new button types for this.
- # Since we probably want:
- # - tool-tips that include multiple key shortcuts.
- # - ability to click and hold to expose sub-tools.
- space_type = context.space_data.type
- tool_active_id = getattr(
- ToolSelectPanelHelper._tool_active_from_context(context, space_type),
- "idname", None,
- )
- if detect_layout:
- ui_gen, show_text = cls._layout_generator_detect_from_region(layout, context.region, scale_y)
- else:
- ui_gen = ToolSelectPanelHelper._layout_generator_single_column(layout, scale_y)
- show_text = True
- # Start iteration
- ui_gen.send(None)
- for item in cls.tools_from_context(context):
- if item is None:
- ui_gen.send(True)
- continue
- if type(item) is tuple:
- is_active = False
- i = 0
- for i, sub_item in enumerate(item):
- if sub_item is None:
- continue
- is_active = (sub_item.idname == tool_active_id)
- if is_active:
- index = i
- break
- del i, sub_item
- if is_active:
- # not ideal, write this every time :S
- cls._tool_group_active[item[0].idname] = index
- else:
- index = cls._tool_group_active.get(item[0].idname, 0)
- item = item[index]
- use_menu = True
- else:
- index = -1
- use_menu = False
- is_active = (item.idname == tool_active_id)
- icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon)
- sub = ui_gen.send(False)
- if use_menu:
- sub.operator_menu_hold(
- "wm.tool_set_by_id",
- text=item.label if show_text else "",
- depress=is_active,
- menu="WM_MT_toolsystem_submenu",
- icon_value=icon_value,
- ).name = item.idname
- else:
- sub.operator(
- "wm.tool_set_by_id",
- text=item.label if show_text else "",
- depress=is_active,
- icon_value=icon_value,
- ).name = item.idname
- # Signal to finish any remaining layout edits.
- ui_gen.send(None)
- def draw(self, context):
- self.draw_cls(self.layout, context)
- @staticmethod
- def _tool_key_from_context(context, *, space_type=None):
- if space_type is None:
- space_data = context.space_data
- space_type = space_data.type
- else:
- space_data = None
- if space_type == 'VIEW_3D':
- return space_type, context.mode
- elif space_type == 'IMAGE_EDITOR':
- if space_data is None:
- space_data = context.space_data
- return space_type, space_data.mode
- elif space_type == 'NODE_EDITOR':
- return space_type, None
- else:
- return None, None
- @staticmethod
- def tool_active_from_context(context):
- space_type = context.space_data.type
- return ToolSelectPanelHelper._tool_active_from_context(context, space_type)
- @staticmethod
- def draw_active_tool_header(
- context, layout,
- *,
- show_tool_name=False,
- tool_key=None,
- ):
- if tool_key is None:
- space_type, mode = ToolSelectPanelHelper._tool_key_from_context(context)
- else:
- space_type, mode = tool_key
- if space_type is None:
- return None
- item, tool, icon_value = ToolSelectPanelHelper._tool_get_active(context, space_type, mode, with_icon=True)
- if item is None:
- return None
- # Note: we could show 'item.text' here but it makes the layout jitter when switching tools.
- # Add some spacing since the icon is currently assuming regular small icon size.
- layout.label(text=" " + item.label if show_tool_name else " ", icon_value=icon_value)
- if show_tool_name:
- layout.separator()
- draw_settings = item.draw_settings
- if draw_settings is not None:
- draw_settings(context, layout, tool)
- return tool
- # The purpose of this menu is to be a generic popup to select between tools
- # in cases when a single tool allows to select alternative tools.
- class WM_MT_toolsystem_submenu(Menu):
- bl_label = ""
- @staticmethod
- def _tool_group_from_button(context):
- # Lookup the tool definitions based on the space-type.
- cls = ToolSelectPanelHelper._tool_class_from_space_type(context.space_data.type)
- if cls is not None:
- button_identifier = ToolSelectPanelHelper._tool_identifier_from_button(context)
- for item_group in cls.tools_from_context(context):
- if type(item_group) is tuple:
- for sub_item in item_group:
- if (sub_item is not None) and (sub_item.idname == button_identifier):
- return cls, item_group
- return None, None
- def draw(self, context):
- layout = self.layout
- layout.scale_y = 2.0
- _cls, item_group = self._tool_group_from_button(context)
- if item_group is None:
- # Should never happen, just in case
- layout.label(text="Unable to find toolbar group")
- return
- for item in item_group:
- if item is None:
- layout.separator()
- continue
- icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon)
- layout.operator(
- "wm.tool_set_by_id",
- text=item.label,
- icon_value=icon_value,
- ).name = item.idname
- def _activate_by_item(context, space_type, item, index):
- tool = ToolSelectPanelHelper._tool_active_from_context(context, space_type, create=True)
- tool.setup(
- idname=item.idname,
- keymap=item.keymap[0] if item.keymap is not None else "",
- cursor=item.cursor or 'DEFAULT',
- gizmo_group=item.widget or "",
- data_block=item.data_block or "",
- operator=item.operator or "",
- index=index,
- )
- WindowManager = bpy.types.WindowManager
- handle_map = _activate_by_item._cursor_draw_handle
- handle = handle_map.pop(space_type, None)
- if (handle is not None):
- WindowManager.draw_cursor_remove(handle)
- if item.draw_cursor is not None:
- def handle_fn(context, item, tool, xy):
- item.draw_cursor(context, tool, xy)
- handle = WindowManager.draw_cursor_add(handle_fn, (context, item, tool), space_type)
- handle_map[space_type] = handle
- _activate_by_item._cursor_draw_handle = {}
- def activate_by_id(context, space_type, text):
- _cls, item, index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, text)
- if item is None:
- return False
- _activate_by_item(context, space_type, item, index)
- return True
- def activate_by_id_or_cycle(context, space_type, idname, offset=1):
- # Only cycle when the active tool is activated again.
- cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, idname)
- if item is None:
- return False
- tool_active = ToolSelectPanelHelper._tool_active_from_context(context, space_type)
- id_active = getattr(tool_active, "idname", None)
- id_current = ""
- for item_group in cls.tools_from_context(context):
- if type(item_group) is tuple:
- index_current = cls._tool_group_active.get(item_group[0].idname, 0)
- for sub_item in item_group:
- if sub_item.idname == idname:
- id_current = item_group[index_current].idname
- break
- if id_current:
- break
- if id_current == "":
- return activate_by_id(context, space_type, idname)
- if id_active != id_current:
- return activate_by_id(context, space_type, id_current)
- index_found = (tool_active.index + offset) % len(item_group)
- cls._tool_group_active[item_group[0].idname] = index_found
- item_found = item_group[index_found]
- _activate_by_item(context, space_type, item_found, index_found)
- return True
- def description_from_id(context, space_type, idname, *, use_operator=True):
- # Used directly for tooltips.
- _cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, idname)
- if item is None:
- return False
- # Custom description.
- description = item.description
- if description is not None:
- if callable(description):
- km = _keymap_from_item(context, item)
- return description(context, item, km)
- return description
- # Extract from the operator.
- if use_operator:
- operator = item.operator
- if operator is None:
- if item.keymap is not None:
- km = _keymap_from_item(context, item)
- if km is not None:
- for kmi in km.keymap_items:
- if kmi.active:
- operator = kmi.idname
- break
- if operator is not None:
- import _bpy
- return _bpy.ops.get_rna_type(operator).description
- return ""
- def item_from_id(context, space_type, idname):
- # Used directly for tooltips.
- _cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, idname)
- return item
- def item_from_flat_index(context, space_type, index):
- _cls, item, _index = ToolSelectPanelHelper._tool_get_by_flat_index(context, space_type, index)
- return item
- def item_from_index(context, space_type, index):
- _cls, item, _index = ToolSelectPanelHelper._tool_get_by_index(context, space_type, index)
- return item
- def keymap_from_id(context, space_type, idname):
- # Used directly for tooltips.
- _cls, item, _index = ToolSelectPanelHelper._tool_get_by_id(context, space_type, idname)
- if item is None:
- return False
- keymap = item.keymap
- # List container of one.
- if keymap:
- return keymap[0]
- return ""
- def _keymap_from_item(context, item):
- if item.keymap is not None:
- wm = context.window_manager
- keyconf = wm.keyconfigs.active
- return keyconf.keymaps.get(item.keymap[0])
- return None
- classes = (
- WM_MT_toolsystem_submenu,
- )
- if __name__ == "__main__": # only for live edit.
- from bpy.utils import register_class
- for cls in classes:
- register_class(cls)
|