.. _pme-scripting:
=========
Scripting
=========
PME allows for advanced customization and automation using Blender's `Python API `_.
This article provides an overview of PME's scripting capabilities and explains the built-in global variables and functions.
.. NOTE: Not necessary for furo or book themes.
.. .. contents::
.. :local:
.. :depth: 2
.. :class: this-will-duplicate-information-and-it-is-still-useful-here
---------
Tutorials
---------
- **Video**: `Introduction to Scripting with Python in Blender (vimeo.com) `_
- **Video**: `Task Automation with Python Scripting in Blender (youtube.com) `_
- `Python for Non-Programmers (python.org) `_
- `Blender Python API `_
- `Blender/Python Quickstart `_
----------------
Global Variables
----------------
Variables available within each PME slot editor.
.. list-table::
:header-rows: 1
:widths: 25 75
* - **Variable**
- **Description**
* - ``menu``
- Name of the active menu
* - ``slot``
- Name of the active slot
* - ``C``
- `bpy.context `_
* - ``D``
- `bpy.data `_
* - ``O``
- `bpy.ops `_
* - ``T``
- `bpy.types `_
* - ``P``
- `bpy.props `_
* - ``L``
- Current `UILayout `_ object
.. code-block:: python
L.box().label(text="My Label")
* - ``E``
- Current `Event `_ object
.. code-block:: python
E.ctrl and E.shift and message_box("Ctrl+Shift Pressed")
* - ``U``
- `pme.UserData <#pme.UserData>`_ instance for user data storage
.. code-block:: python
U.foo = "value"
U.update(foo="value1", bar="value2")
U.foo
U.get("foo", "default_value")
----------------
Global Functions
----------------
Functions available within PME slot editors. Different functions are available in Command tab and Custom tab.
.. _pme-common-functions:
Common Functions
****************
.. py:function:: execute_script(path, **kwargs)
Execute an external Python script.
:param str path: Script file path. Relative path (from ``pie_menu_editor`` folder, recommended) or absolute path.
:param kwargs: Additional keyword arguments passed to the script.
:return: ``return_value`` from the script, or ``True`` by default.
.. warning::
- Only place and execute scripts from trusted sources
- Review contents before execution and verify in a backup or test environment if necessary
- Scripts may contain operations that affect your environment, such as file operations or settings changes
**Variables available in script**: ``kwargs``, ``__file__``, ``return_value``, all PME global variables
**Examples**::
# Basic execution and return value
execute_script("scripts/hello_world.py", msg="Hello World!")
message_box(execute_script("scripts/get_message.py"))
# scripts/hello_world.py
message_box(kwargs["msg"])
# scripts/get_message.py
return_value = "Hi!"
# Processing with parameters
# scripts/process_data.py
kwargs = locals().get("kwargs", {})
result = my_function(kwargs.get("param1"), kwargs.get("param2", "default"))
return_value = result
# Call
result = execute_script("scripts/process_data.py", param1=200, param2="Hello")
# UI drawing in Custom tab
# scripts/custom_ui.py
msg = kwargs.get("msg", pme.context.text or "Default Message")
box = L.box()
box.label(text=msg, icon=pme.context.icon, icon_value=pme.context.icon_value)
# Call
execute_script("scripts/custom_ui.py", msg="Custom message")
.. py:function:: props(name=None, value=None)
Get or set the value of a PME Property.
:param str name: Name of the property.
:param value: New value of the property.
:return: PME property container if ``name`` is ``None``, property value if only ``name`` is given, ``True`` if setting a value.
**Example**::
# Get property value using string notation
value = props("MyProperty")
# Alternative: get property using attribute notation
value = props().MyProperty # props() returns property container
# Set property value using string notation
props("MyProperty", value)
# Alternative: set property using attribute notation
props().MyProperty = value # props() returns property container
.. py:function:: paint_settings()
Retrieve the context-sensitive paint settings.
:return: The current paint settings or ``None`` if not in a paint mode.
**Example**::
ps = paint_settings(); ps and L.template_ID_preview(ps, 'brush')
.. py:function:: find_by(collection, key, value)
Find the first item in ``collection`` where ``key`` equals ``value``.
:return: Collection item if found, otherwise ``None``.
**Example**::
m = find_by(C.active_object.modifiers, "type", 'SUBSURF')
.. py:function:: setattr(object, name, value)
Same as Python's built-in :func:`setattr`, but returns ``True`` after setting.
:return: ``True``
.. _pme-command-tab-functions:
Command Tab Functions
*********************
.. py:function:: open_menu(name, slot=None, **kwargs)
Open menu, pie menu, popup dialog or execute a stack key, sticky key, modal operator, or macro operator by name.
:param str name: Name of the menu.
:param slot: Index or name of the slot for Stack Key execution.
:param kwargs: Arguments for Modal / Macro Operators used as local variables.
:return: ``True`` if the menu exists and is currently available. Returns ``False`` when the target is missing, disabled, poll-blocked, or the requested slot is not found.
**Example**::
# Open the menu depending on the active object's type:
open_menu("Lamp Pie Menu" if C.active_object.type == 'LAMP' else "Object Pie Menu")
# Call "My Stack Key" slot depending on Ctrl modifier:
open_menu("My Stack Key", "Ctrl slot" if E.ctrl else "Shift slot")
.. py:function:: toggle_menu(name, value=None)
Enable or disable a menu.
:param str name: Name of the menu.
:param bool value: ``True`` to enable, ``False`` to disable, ``None`` to toggle.
:return: ``True`` if the menu exists, ``False`` otherwise.
.. py:function:: tag_redraw(area=None, region=None)
Redraw UI areas or regions.
:param str area: The :attr:`Area.type ` to redraw. Redraw all areas if ``None``.
:param str region: The :attr:`Region.type ` to redraw. Redraw all regions if ``None``.
:return: ``True``
.. py:function:: close_popups()
Close all popup dialogs.
:return: ``True``
.. py:function:: overlay(text, **kwargs)
Draw an overlay message.
:param str text: Message to display.
:param kwargs:
- ``alignment``: One of ``['TOP', 'TOP_LEFT', 'TOP_RIGHT', 'BOTTOM', 'BOTTOM_LEFT', 'BOTTOM_RIGHT']``. Default is ``'TOP'`` .
- ``duration``: Duration in seconds. Default is ``2.0`` .
- ``offset_x``: Horizontal offset. Default is ``10`` px.
- ``offset_y``: Vertical offset. Default is ``10`` px.
:return: ``True``
**Example**::
overlay('Hello PME!', offset_y=100, duration=1.0)
.. py:function:: message_box(text, icon='INFO', title="Pie Menu Editor")
Show a message box.
:param str text: Message to display.
:param str icon: Icon name (e.g. 'INFO', 'ERROR', 'QUESTION', etc.).
:param str title: Window title.
:return: ``True``
.. py:function:: input_box(func=None, prop=None)
Show an input box.
:param func: Function to call with the input value.
:param str prop: Path to the property to edit.
:return: ``True``
**Example**::
# Rename object:
input_box(prop="C.active_object.name")
# Display input value:
input_box(func=lambda value: overlay(value))
.. _pme-custom-tab-functions:
Custom Tab Functions
********************
.. py:function:: draw_menu(name, frame=True, dx=0, dy=0)
Draw a popup dialog inside another popup dialog or a pie menu.
:param str name: Name of the menu (popup dialog).
:param bool frame: Whether to draw a frame.
:param int dx: Horizontal offset.
:param int dy: Vertical offset.
:return: ``True`` if the menu exists and is currently available. Returns ``False`` without drawing when the target is missing, disabled, or poll-blocked.
.. py:function:: operator(layout, idname, text="", icon='NONE', emboss=True, icon_value=0, **kwargs)
Similar to :meth:`UILayout.operator() `, but allows filling operator properties.
:param layout: A :class:`UILayout ` instance.
:param str idname: Identifier of the operator.
:return: :class:`OperatorProperties ` object.
**Example**::
operator(L, "wm.context_set_int", "Material Slot 1",
data_path="active_object.active_material_index", value=0)
# Same as:
# op = L.operator("wm.context_set_int", text="Material Slot 1")
# op.data_path = "active_object.active_material_index"
# op.value = 0
.. py:function:: custom_icon(filename)
Get the integer value associated with a custom icon.
:param str filename: Icon filename without extension, located in ``pie_menu_editor/icons/``.
:return: The integer value of the custom icon.
**Example**::
L.label(text="My Custom Icon", icon_value=custom_icon("p1"))
.. py:function:: panel(pt, frame=True, header=True, expand=None, area=None, root=False, poll=True, layout=None)
Draws a panel by its ID.
:param pt: Panel class or panel class name string. If string, the corresponding class is searched from ``bpy.types``.
:type pt: Union[str, Type]
:param bool frame: Controls whether to frame the panel. If ``True``, uses ``layout.box()``. If ``False``, uses ``layout.column()``.
:param bool header: Controls panel header display style.
:param expand: Controls initial expansion state of panel. ``True``: start expanded, ``False``: start collapsed, ``None``: retain previous state.
:type expand: Optional[bool]
:param area: :attr:`Area.type ` the panel should be drawn against (e.g. ``'VIEW_3D'``, ``'PROPERTIES'``).
Useful when drawing an editor-specific panel (such as ``VIEW3D_PT_*``) from a popup dialog or a different editor,
so the panel's ``poll`` / ``draw`` can resolve the expected ``space_data``.
Use ``None`` or ``'CURRENT'`` to keep the current context.
:type area: Optional[str]
:param bool root: If ``True``, draws the panel directly on the current ``pme.context.layout`` without wrapping it in
an extra ``box()`` / ``column()``. When ``True``, the ``frame`` and ``layout`` parameters are ignored.
Use this to avoid an extra layer of nesting in tightly controlled layouts.
:param bool poll: Controls whether to execute the panel's ``poll`` method. If ``True``, checks the panel's display conditions.
:param layout: Specify a custom layout.
:type layout: Optional[Any]
:return: True
:rtype: bool
**Example**::
panel("MATERIAL_PT_context_material", True, True, True)
# Change panel size
L.scale_x = 0.8; panel("USERPREF_PT_interface", layout=L.box())
# Draw a 3D View panel from a popup dialog
panel("VIEW3D_PT_tools_meshedit_options", area='VIEW_3D')
# Draw without an extra wrapping box/column
panel("MATERIAL_PT_context_material", root=True)
----
---------------------
Auto-run Scripts
---------------------
PME allows you to create Python scripts that automatically execute when Blender starts.
To use this feature, place files in the ``pie_menu_editor/scripts/autorun`` folder using any of these methods:
- Direct ``.py`` files
- Folders containing scripts
- Symbolic links
.. warning::
- Only place and execute scripts from trusted sources
- Review contents before execution and verify in a backup or test environment if necessary
- Scripts may contain operations that affect your environment, such as file operations or settings changes
----------------------------
Add Custom Global Functions
----------------------------
To use custom functions in PME:
1. Place your script in ``pie_menu_editor/scripts/autorun`` folder
2. Register functions using ``pme.context.add_global()``
Example:
.. code-block:: python
def hello_world():
print("Hello World")
pme.context.add_global("hello", hello_world)
The registered function ``hello()`` becomes available in:
- Command tab
- Custom tab
- External scripts
-------------------
PME Components
-------------------
PME maintains a global context that provides access to commonly used functions, variables, and user-defined additions.
This context is accessible through two main interfaces:
.. py:class:: pme.context
.. py:attribute:: globals
:type: dict
Access PME's global context dictionary. Contains:
- Built-in shortcuts (``C``, ``D``, ``O``, ``L``, etc.)
- Registered custom functions and values
- User data storage (``U``)
.. code-block:: python
from pie_menu_editor import pme
# Access globals from external scripts
g = pme.context.globals
props = g.get('props')
user_data = g.get('U')
.. py:method:: add_global(key, value)
Register a custom function or value in the global context.
:param str key: Name for accessing the item
:param value: Function or value to register
:rtype: None
.. code-block:: python
# Register a function
def my_tool():
bpy.ops.mesh.select_all(action='TOGGLE')
pme.context.add_global("toggle_select", my_tool)
# Register a constant
pme.context.add_global("MAX_ITEMS", 10)
# Access from PME menus via Command tab:
# toggle_select()
# MAX_ITEMS
.. py:class:: pme.UserData
Flexible storage for user-defined data that persists during the Blender session.
.. py:method:: get(name, default=None)
Get a stored value.
:param str name: Data key
:param default: Value to return if key doesn't exist
:return: Stored value or default
.. py:method:: update(**kwargs)
Update multiple values at once.
.. code-block:: python
U = pme.context.globals['U'] # Get UserData instance
U.update(tool_state="active", count=5)
print(U.tool_state) # "active"