Client API
The Client API provides an object-oriented approach to remote Python execution on Designer. Define a class that inherits from D3PluginClient, and all your methods automatically execute remotely. Unlike the Functional API which decorates individual functions, the Client API wraps entire classes for transparent remote execution.
When to Use Client API
- Building plugins with multiple related methods
- Object-oriented design with instance state
- Function chaining (calling one method from another)
- Cleaner syntax without explicit payload generation
Examples
For introductory walkthroughs, see the Overview.
Retrieving Data from Designer
from designer_plugin.d3sdk import D3PluginClient
from designer_plugin.pystub import *
class MyPlugin(D3PluginClient):
def get_surface_uid(self, 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)
# Create plugin instance
my_plugin = MyPlugin()
# Execute methods remotely within a session
with my_plugin.session('localhost'):
uid = my_plugin.get_surface_uid("surface 1")
print(f"Surface UID: {uid}")
You cannot retrieve the object from designer_plugin.pystub. See Limitation.
Adding a Layer
from designer_plugin.d3sdk import D3PluginClient
from designer_plugin.pystub import *
class MyPlugin(D3PluginClient):
def hello_world(self):
local_state: LocalState = state.localOrDirectorState()
layer = local_state.track.addNewLayer(
TextModule, 0, 60, 'Text - Hello World'
)
layer.findSequence('text').sequence.setString(0, "Hello world")
arial_font: TextFont = resourceManager.load(
Path("objects/text/arial.apx"), TextFont
)
layer.findSequence('font').sequence.setResource(0, arial_font)
plugin = MyPlugin()
# localhost can be replaced with desired ipaddress
with plugin.session('localhost'):
plugin.hello_world()
Function Chaining
from designer_plugin.d3sdk import D3PluginClient
from designer_plugin.pystub import *
class MyPlugin(D3PluginClient):
def get_surface(self, name: str) -> Screen2:
return resourceManager.load(
Path(f'objects/screen2/{name}.apx'),
Screen2)
def rename_surface(self, name: str, new_name: str) -> None:
surface: Screen2 = self.get_surface(name)
surface.rename(surface.path.replaceFilename(new_name))
plugin = MyPlugin()
with plugin.session('localhost'):
plugin.rename_surface("surface 1", "surface 2")
Async
import asyncio
from designer_plugin.d3sdk import D3PluginClient
class MyAsyncPlugin(D3PluginClient):
async def get_current_time(self) -> str:
import datetime
return str(datetime.datetime.now())
async def main():
plugin = MyAsyncPlugin()
async with plugin.async_session('localhost'):
time = await plugin.get_current_time()
print(f"Designer time: {time}")
asyncio.run(main())
Execution Flow
- Class Definition: Your class inherits from
D3PluginClient - Code Extraction: Metaclass extracts and converts source to Python 2.7
- Session Start: Module is registered with Designer on session enter
- Method Call: Arguments are serialised and sent via HTTP
- Remote Execution: Code runs on Designer, result is returned
Details
When you subclass D3PluginClient, the metaclass automatically:
- Extracts your class source code
- Converts it to Python 2.7 compatible code
- Wraps all methods to execute remotely via HTTP
- Manages module registration with Designer
This enables remote Python 2.7 execution in Designer whilst looking like native Python 3 running locally.
Code Transformation
Your Python 3 code is automatically converted to Python 2.7:
- F-strings →
.format()calls - Type hints → Removed
async/await→ Converted to sync
The same conversion rules apply to the Functional API.
Method Wrapping
All public methods are wrapped to:
- Validate arguments against the original signature
- Build a payload with serialised arguments
- Send to Designer via HTTP
- Return the result
Session Requirements
Methods can only be called within a session context. Calling outside a session raises RuntimeError:
plugin = MyPlugin()
plugin.get_surface("test") # RuntimeError!
with plugin.session('localhost'):
plugin.get_surface("test") # OK
Skipping Registration
If the module was already registered in a previous session (for example, when reconnecting to the same Designer instance), pass register_module=False to skip re-registration:
with plugin.session('localhost', register_module=False):
result = plugin.get_surface("test")
Next Steps
- Functional API Reference - Decorator-based remote execution
- API Reference - Data models and low-level API
- Overview - designer-plugin overview