Designer Plugins
Plugins for Disguise Designer software.
Plugins for Disguise Designer software.
This guide demonstrates how to use the Disguise Python API to manage resources via the resourceManager and interact with individual Resource objects. Resources are the core building blocks in Disguise projects, representing assets such as media files, screens, props, fixtures, and more.
Any class which derives from Resource can use these patterns.
All Resource classes have a __username__ property, which is the user-facing name of the class. For example, Screen2 has a __username__ of “Surface”.
Note: Not all Resources have populated path properties. If a Resource does not have a path (i.e. the path property == ""), it is referred to as unnamed. The Resource transfers its lifecycle to a parent Resource when it is unnamed.
If an unnamed Resource is altered, the parent Resource must be saved (namedParent.saveOnDelete()) in order to persist changes. Note that namedParent is not always the immediate parent - it is the nearest parent Resource which has a name. “Parent” here means the Resource which contains the unnamed Resource, which must be kept track of in your code. There are no reliable automatic references to parent Resources.
saveOnDelete() is the standard method for saving all Resources. It defers saving until the next batched save operation, which is more correct as Resources are often modified in groups. This is always called AFTER making changes to a Resource.
markDirty(resource) is used to indicate a Resource is about to be changed - it marks the Resource before the change is made. This is important for undo/redo functionality to work correctly.
Important: Only pass individual Resource objects to markDirty(), not containers or lists.
For example:
sub_resource = parent_resource.find_subresource() # Finds the unnamed sub-resource
markDirty(sub_resource) # This prepares the resource for change
sub_resource.property = new_value # Make the change
parent_resource.saveOnDelete() # This saves the resource
If this involves making changes to a container in the parent resource, then both the sub-resource and the parent resource should be marked dirty:
sub_resource = parent_resource.find_subresource() # Finds the unnamed sub-resource
markDirty(sub_resource) # This prepares the sub-resource for change (potentially unnecessary)
markDirty(parent_resource) # This prepares the parent resource for change to `container`
parent_resource.container.remove(sub_resource) # Make the change
parent_resource.saveOnDelete() # This saves the resource
All Resource paths follow a common pattern:
<prefix>/<type>/[subfolder/]<name>.apx
All elements of the path are expressed in lower case - resources are never referred to in upper case. Some user facing names may be capitalised (such as layers). Referring to a resource in any way with a capital letter will result in mismatches and code errors.
Prefixes tell us whether the Resource is intended to be user-selectable in the UI.
objects/ - Public objects that users can interact with.internal/ - Internal objects not exposed to users.internal/thumbnails/ - Thumbnail representations of objects.The type in the path is always the internal type (i.e. never __username__) and always lowercase. A safe method to get the type part of a path for a known object is to use the type() function - type(my_obj).__name__.lower().
Sometimes, subfolders are used to organize resources within a specific category or type. This is optional and usually only done for Resources loaded from disk, or auto-created Resources which might conflict otherwise. Generally, unless there’s a specific reason to use subfolders, it’s best to keep resources at the top level.
The name part of a Resource path can use any normal filename-safe characters. When the name represents a file on disk, this will include the original extension, and the .apx suffix will be appended, not replacing the original extension. The name will always be lowercase.
path.filename is usually presented to the user via the description property, however some Resources override that. It’s therefore always best to use description when displaying a resource’s name or matching against what a user refers to a Resource as.
(All examples assume isinstance(my_obj, Resource) is True)
| Task | Python |
|---|---|
| List all resources of a specific type | all_led_screens = resourceManager.allResources(LedScreen)Returns a list of all loaded resources of the given type (e.g., LedScreen, Projector, VideoClip). |
| Create or load a resource by path and type | resource_name = "myobject"my_obj = resourceManager.loadOrCreate("objects/screen2/{}.apx".format(resource_name), Screen2)If a resource exists at the given path and type, it is loaded; otherwise, a new one is created. |
| Delete a resource | resourceManager.deleteResource(my_obj)Removes the resource from the project and deletes its file. |
| Duplicate an existing resource | new_obj = my_obj.duplicate(my_obj.path.replaceFilename('new_name'))Creates a copy of the resource at a new path. |
| Save changes made to a resource | my_obj.save()Persists any modifications to disk. |
| Check if a Resource matches a name | my_obj.description.lower() == search_name.lower() |
| Task | Python |
|---|---|
| Get a resource’s user-facing name | my_obj.description |
| Get the resource user-visible type name | type(my_obj).__username__Some classes are internally named differently - e.g. Screen2 is presented to the user as “Surface”. |
| Get the resource’s unique path | my_obj.pathReturns a Path object representing the resource’s location. |
| Get the resource’s filename | my_obj.path.filename |
| Task | Python |
|---|---|
Create a new Path object from a string | Path("objects/type/resource.apx") |
| Replace the filename in a path | my_obj.path.replaceFilename("new_name")Returns a new Path object with the filename changed. |