Difference between revisions of "Scripting Guide"
|Line 206:||Line 206:|
print findPrevKeyTime(path, player.frame)
print findPrevKeyTime(path, player.frame)
Revision as of 15:15, 23 June 2014
- 1 Introduction
- 2 User Interface Scripting
- 3 Hooks
- 4 Scripting Reference
- 5 Scripting Examples
Silhouette has an embedded Python interpreter that can be used to control key bindings and invoke Actions. When Silhouette starts up, it executes the startup.py script, which in turn runs the keybinds.py script. There is a search path when looking for these scripts, so users can modify their scripts without modifying the default scripts.
The Silhouette scripting functionality is implemented in the fx module.
Script Search Path
Silhouette has multiple search paths where it looks for scripts. First, it checks in $(SFX_SCRIPT_PATH), then in $(SFX_USER_PATH)/scripts. These can easily be overridden on a per-user basis by setting the environment variables. Finally Silhouette looks in $(SFX_RESOURCE_PATH)/scripts, where SFX_RESOURCE_PATH is the path to the Silhouette resources directory on Windows and Linux, and the Resources bundle folder on Macintosh OSX.
In an environment where the stock Silhouette install lives on a server or cannot be written to by a normal user, the easiest way to support user-specific scripts is to set SFX_SCRIPT_PATH to some writable location in the user's home directory, such as $HOME/Silhouette/scripts. When adding user-defined Actions, you should copy the entire actions directory to the new location, and add your custom actions to that.
The Silhouette Console can be used to enter interactive scripts. This is useful for running quick code snippets or testing scripting commands. The fx module is imported automatically in the interactive console.
For example, load a Project and then open the Console tab. Press Enter until you see a >> prompt, and type the following command:
The current project name will be printed to the console.
User Interface Scripting
Most of the UI functions in Silhouette can be controlled by custom keybinds, which are normally defined in keybinds.py. Keys can be configured to perform simple, global operations such as activating a control, or complex operations such as toggling states or cycling through modes each time a key is pressed. Binds can also examine the current node and tool to perform different operations depending on the current state.
The easiest way to get familiar with key binding is to examine the default keybinds file. More information on the bind functions can be found in the Scripting Reference.
Silhouette Actions are commands, written as scripts, that show up in the Actions menu. Actions can operate on the Silhouette object model with proper undo/redo behavior, execute rendering loops, import/export data using custom formats, and other powerful operations.
An Action is implemented as a Python class that is subclassed from fx.Action. It must implement two functions, available() and execute().
available() is called to determine if the Action can be executed with the current Silhouette state. If an Action requires one or more selected objects, for example, it should check the selection to make sure there are enough selected objects, and return True if everything is order or False otherwise. An assertion can also be used to return a message that is displayed in the status bar when the action is hovered over, in the event the action is not available.
execute() is called to actually perform the command. The action should use an undo block if it manipulates the object model, or it can clone the project and work from the copy if it needs to manipulate some state for rendering.
Actions can create their own sub-menus by prefixing their label with the sub-menu name followed by the '|' character.
The easiest way to get familiar with actions is to examine the bundled actions, found in the scripts/actions directory. Many actions also make use of some helper functions and classes found in the scripts/tools directory.
More information on the various Action functions can be found in the Scripting Reference.
Hooks can be used to perform various operations when certain events happen. The available hooks are:
|startupComplete||called after startup initialization finishes|
|quit||called before shutting down||5.1|
|project_selected||called when then active project changes|
|node_selected||called when the active node changes|
|post_load||called after a Project is loaded|
|pre_save||called just before a Project is saved|
|post_saved||called just after a Project is saved|
|pre_render||called just before rendering begins|
|post_render||called just after rendering finishes|
|post_save_frame(frame, path, type)||called just after a frame is saved during rendering|
|frameChanged||called when the frame changes or playback stops||5.2|
|selectionChanged||called when the object selection changes||5.2|
Hooks are assigned by editing the fx.hooks dictionary, usually in startup.py.
As of 5.2, a hook value can be a callable (ie. function) or List of callables. To assist in adding multiple hook functions to a specific hook, the add() function from the hook.py utility module should be used:
import hook hook.add("post_render", my_post_render_function)
Hook Example: node_selected
# set the view mode to 'Foreground' each time a 'Roto' node is selected def nodeSelected(): node = activeNode() if node != None and node.type == "RotoNode": fx.viewer.setViewMode(1) fx.hooks["node_selected"] = nodeSelected
Hook Example: post_load
# parse the project name for a version field, and set an environment # variable with the version number. Later, the variable can be used # in the output format specification def post_load(): p = fx.activeProject() if p: name = p.path name = os.path.basename(name) name = os.path.splitext(name) version = name.split('_')[-1] if version.startswith('v'): os.environ["PROJECT_VERSION"] = version fx.hooks["post_load"] = post_load
Toggle Opacity Keybind
Here is an example of how to bind a key to toggle the opacity of selected shapes on/off:
def toggleOpacity(): s = fx.selection() fx.beginUndo("Toggle Opacity") frame = fx.player.frame for shape in s: opacity = shape.property("opacity") if opacity: value = opacity.getValue(frame) value = 100.0 - value if opacity.constant: opacity.constant = False opacity.setValue(value, frame) fx.endUndo() fx.bind("Alt+o", toggleOpacity)
Rename Layer based on first child snippet
This snippet is designed to be pasted into the Script Editor. It can easily be turned into an Action.
from tools.objectIterator import ObjectIterator class LayerIterator(ObjectIterator): def visit(self, object): children = object.children if children: object.label = object.children.label ObjectIterator.visit(self, object) node = activeNode() children = node.children li = LayerIterator() beginUndo("Relabel Layers") li.iterate(children) endUndo()
Count up all of the shapes and print the result
This snippet will iterate all of the objects in the current node and add up all the Shapes.
from fx import * from tools.objectIterator import ObjectIterator class ShapeCounter(ObjectIterator): def __init__(self): self.count = 0 def visit(self, object): if isinstance(object, Shape): self.count = self.count + 1 ObjectIterator.visit(self, object) node = activeNode() c = ShapeCounter() c.iterate(node.children) print c.count
Find the previous key to the current player frame
from fx import * def findPrevKeyTime(prop, time): keys = prop.keys kt = 0.0 for k in keys: if k >= time: break kt = k return kt # example here shape = selection() path = shape.property("path") print findPrevKeyTime(path, player.frame)
Note that as of 5.2, new property key-frame functions are included: getPrevKeyTime(), getNextKeyTime(), getKeyIndex(), isKey().