disguise developers

Designer Plugins

Plugins for Disguise Designer software.

Functional API

The Functional API provides fine-grained control over remote Python execution using decorators. Unlike the Client API which wraps entire classes, the Functional API lets you decorate individual functions and explicitly manage payloads and sessions.

When to Use Functional API

Supported Decorators

The Functional API offers two decorators:

When to Use @d3pythonscript

When to Use @d3function

Feature@d3pythonscript@d3function
RegistrationNot neededRequired before execution
Payload contentInlined function bodyFunction call
Function chainingNot supportedFunctions can call each other
PerformanceSends full code each timeCode registered once, calls are small
Best forOne-off scriptsReusable utility functions

Examples

Simple Execution with @d3pythonscript

Use @d3pythonscript for one-off scripts that don’t need registration:

from designer_plugin.d3sdk import d3pythonscript, D3Session
from designer_plugin.pystub import *

@d3pythonscript
def rename_surface(surface_name: str, new_name: str):
    """Rename a surface in Designer."""
    surface: Screen2 = resourceManager.load(
        Path(f'objects/screen2/{surface_name}.apx'),
        Screen2)
    surface.rename(surface.path.replaceFilename(new_name))

# Execute with a session
with D3Session('localhost') as session:
    session.rpc(rename_surface.payload("surface 1", "new_name"))

Retrieving Data from Designer with @d3pythonscript

from designer_plugin.d3sdk import d3pythonscript, D3Session
from designer_plugin.pystub import *

@d3pythonscript
def get_surface_uid(surface_name: str) -> str:
    """Get the UID of a surface by name."""
    surface: Screen2 = resourceManager.load(
        Path(f'objects/screen2/{surface_name}.apx'),
        Screen2)
    return str(surface.uid)

# Execute methods remotely within a session
with D3Session('localhost') as session:
    uid = session.rpc(get_surface_uid.payload("surface 1"))
    print(f"Surface UID: {uid}")

You cannot retrieve the object from designer_plugin.pystub. See Limitation.

Adding a Layer with @d3pythonscript

from designer_plugin.d3sdk import d3pythonscript, D3Session
from designer_plugin.pystub import *

@d3pythonscript
def hello_world(text: str):
    local_state: LocalState = state.localOrDirectorState()
    layer = local_state.track.addNewLayer(
        TextModule, 0, 60, f"Text - {text}"
    )
    layer.findSequence("text").sequence.setString(0, text)
    arial_font: TextFont = resourceManager.load(
        Path("objects/text/arial.apx"), TextFont
    )
    layer.findSequence('font').sequence.setResource(0, arial_font)

# Execute methods remotely within a session
with D3Session('localhost') as session:
    session.rpc(hello_world.payload("Hello world"))

Function Chaining with @d3function

Use @d3function for reusable functions that can call each other:

from designer_plugin.d3sdk import d3function, D3Session
from designer_plugin.pystub import *

@d3function("mymodule")
def get_time() -> str:
    import datetime
    return str(datetime.datetime.now())

@d3function("mymodule")
def get_surface_with_time(surface_name: str) -> dict:
    """Get surface info with timestamp."""
    surface: Screen2 = resourceManager.load(
        Path(f'objects/screen2/{surface_name}.apx'),
        Screen2)
    return {
        "uid": str(surface.uid),
        "time": get_time()  # Call another function in the same module
    }

# Execute with a session (module auto-registered)
with D3Session('localhost') as session:
    result = session.rpc(get_surface_with_time.payload("surface 1"))
    print(result)

Async Execution with D3AsyncSession

import asyncio
from designer_plugin.d3sdk import D3AsyncSession

async def main():
    async with D3AsyncSession('localhost') as session:
        result = await session.rpc(get_surface_with_time.payload("surface 1"))
        print(result)

asyncio.run(main())

Execution Flow

  1. Code Preparation: Your Python 3 code is converted to Python 2.7 (f-strings, type hints removed)
  2. Payload Generation: Arguments are serialised into a JSON payload
  3. Module Registration (for @d3function): Function code is lazily registered on Designer on the first rpc() call
  4. Remote Execution: Payload is sent to Designer via HTTP
  5. Response Handling: Return value is parsed and returned to your code

Session Methods

Both D3Session and D3AsyncSession provide rpc() and execute() methods.

MethodReturnsUse Case
rpc(payload)Return value onlySimple execution
execute(payload)Full PluginResponseAccess logs, status, debugging

Details

Module Registration

Modules are registered lazily. The session auto-registers a module the first time you execute a function from it. No manual setup is required in most cases.

For eager registration (e.g. to catch registration errors upfront), use register_module or register_all_modules explicitly:

with D3Session('localhost') as session:
    session.register_module("mymodule")  # Register eagerly

Import Handling

For @d3pythonscript, imports must be inside the function body:

@d3pythonscript
def use_numpy():
    import numpy as np  # Import inside function
    return np.zeros(10).tolist()

Code Transformation

Both decorators automatically convert Python 3 code to Python 2.7:

The same conversion rules apply to the Client API.


Helper Functions

get_register_payload()

Get the registration payload for a specific module.

Signature:

def get_register_payload(module_name: str) -> RegisterPayload | None

Returns: RegisterPayload for the module, or None if not found.

get_all_d3functions()

List all registered d3functions.

Signature:

def get_all_d3functions() -> list[tuple[str, str]]

Returns: List of (module_name, function_name) tuples.

Example:

from designer_plugin.d3sdk import get_all_d3functions

functions = get_all_d3functions()
# [("mymodule", "get_time"), ("mymodule", "get_surface")]

get_all_modules()

List all module names with registered d3functions.

Signature:

def get_all_modules() -> list[str]

Returns: List of module names.

Example:

from designer_plugin.d3sdk import get_all_modules

modules = get_all_modules()
# ["mymodule", "utilities"]

Next Steps