Introduction to Slim Scripting |
You can write scripts to perform all the actions in Slim that you might perform interactively. You can write mel scripts to control Slim by wrapping the Slim commands in MTOR's slimcmd/slimmsg mel commands. In this way you can treat Slim as an extension of Maya and develop mel scripts that couple manipulations of geometry and Slim objects. This document serves as an introduction to scripting with Slim.
Using the Slim console you can interactively develop, debug and invoke the scripts you write. You can install developed Tcl scripts (see http://tcl.sourceforge.net/) by adding a LoadExtension directive to one of several initialization files.
Slim supports an object oriented programming paradigm. Slim commands are generally targeted to a unique object. Objects are of a particular class and all objects of the same class accept the same commands, or methods. Object handles are the means by which we identify a particular object. These handles are actually transitory Slim commands. Because they are transitory, they should not be hard coded into your scripts. Each time a script is run, it must locate object handles via appropriate commands.
There is one object that you can hard code into your scripts. This object handle is slim and it represents Slim's application context. The slim object is the starting point for many of the scripts you will write. You can query the slim object for the active palettes or appearances. These queries will return object handles which you can use to perform further queries.
The following example code illustrates the typical workflow in a slim script. This snippet will find all parameters named "Ks" and set them to 0. It executes by querying slim for all active appearances, querying those appearances for parameters which match our criteria, and executing the SetValue method on the parameters that are found:
foreach app [slim GetAppearances] { foreach p [$app GetProperties -name "Ks" -access input] { $p SetValue 0 } }The GetAppearances method is probably the most useful method of the slim object. Here are some of the other most useful methods of the slim object:
The complete list of methods available for the slim object, along with their usage and arguments can be found in the Slim Scripting Glossary.
- slim BuildShaders ?arg arg ...?
Generate and build all shaders associated with attachable functions.
- slim CreatePalette ?arg arg ...?
Create or open a Slim palette.
- slim GetPalettes ?arg arg ...?
Get object handles for all top-level palettes.
- slim GetTemplates ?arg arg ...?
Get a list of all active templates.
In the above example, we are operating on three classes of objects: the slim object, appearance objects, and property objects. The slim object is unique. It is the only object of its class and no other classes inherit from it. The same is true of the workspace class (discussed below).
The rest of the classes you will work with, however, will have some things in common. All of the objects in these classes can be arranged in a tree-like structure. Palettes can be grouped within palettes (these are referred to as subpalettes). Appearances can be collected within other appearances. Properties can be grouped into a collection. Because these classes all have some common functionality, they all inherit from a common base class called TreeNode.
The TreeNode base class defines basic methods for organizing nodes. The names of these methods follow the metaphor of a family tree. Nodes are grouped together under a parent. Those nodes are their parents children. In the following procedure, we search for an appearance with a name specified by the name argument, and then group all similarly named appearances underneath it. TreeNode methods will be highlighted in bold.
proc findAndGroupSimilar {name} { # find the appearance matching "name" set parentApp [slim GetAppearances -name $name] # get the root of the appearance's tree (a palette) set plt [$parentApp GetRoot] # get the list of Chrome's current children set children [$parentApp GetChildren] # find all appearances that start with $name foreach app [$plt GetAppearances -name "${name}*"] { # skip the original if {$app == $parentApp} { continue } # check if this appearance is already a child of Chrome if {-1 == [lsearch $children $app]} { # app isn't in the list, make it a child of Chrome $parentApp AddChild $app } } }
In the code above, we used TreeNode methods GetRoot, GetChildren, and AddChild. These methods are defined for any class that inherits from TreeNode. The classes we discuss below all inherit from TreeNode. A complete list of methods defined for the TreeNode class can be found in the Slim Scripting Glossary. To visualize the whole tree of classes that inherit from TreeNode, refer to the Class Hierarchy Diagram.
Palettes are containers of appearances and other palettes. In Slim your can have any number of Palettes and these can be organized hierarchically. You can search palette objects for appearance objects that match various criteria. You can also encapsulate the state of a palette into an ASCII string.
The following procedure finds all of the palettes active in a scene, adds a new node specified by tmplt argument, and then saves any palette files that are external:
# call this template with a valid template ID, e.g. "pixar,RIBBox#0" proc addTemplateToAllPalettes {tmplt} { # loop through all top-level (non sub-) palettes foreach pal [slim GetPalettes] { # check if this template exists in the palette already # if it does, skip if {[$pal GetAppearances -template $tmplt] != ""} { continue } # create an appearance using the template $pal CreateAppearance -template $tmplt # if this is an external palette, save it if {[$pal GetStorageType] == "external" && \ [$pal GetAccessMode] == "rw"} { $pal Save [$pal GetFilename] } # force a redraw of the editor $pal UpdateEditor } }In the example above, we used the methods GetAppearances, CreateAppearance, GetStorageType, GetAccessMode, Save, GetFilename, and UpdateEditor. Here are some other useful methods defined for Palette objects:
- plth DeleteDisconnected
Delete any nodes in the graph that are not attachable and are not connected to any other attachable nodes.
- plth Edit
Raise the Palette Editor for this object.
- plth SelectAppearances applist
Select the appearances specified by applist.
A complete list of methods defined for palettes can be found in the Slim Scripting Glossary.
An Appearance is a container of properties and is the base class for the various appearance types in Slim. Functions are a type of appearance that can participate in custom shader generation. Every function object has an associated Template object which itself is a type of appearance. Instances are a type of appearance that can't participate in custom shader generation. All of these classes of objects share a lot of behavior including the ability to search for properties that match various criteria.
As an example, the following procedure finds the appearance with name matching the passed argument. It then creates a duplicate copy of the appearance, opens the appearance in the Appearance Editor, checks its shading rate, and renders a preview.
proc findCopyAndEdit {name} { # get appearances that match string set apps [slim GetAppearances -name $name] # loop through the appearances foreach app $apps { # create copy of appearance # the result may be a list if $app has connected nodes set newApps [$app Duplicate] set newRoot [lindex $newApps end] # make sure shading rate is set to 1 (or lower) set curRate [$newApp GetPreviewShadingRate] if {$curRate > 1} { $newApp SetPreviewShadingRate 1 } # raise in Appearance Editor $newApp Edit # render preview $newApp PreviewRender } }
The procedure above made use of the Duplicate, GetPreviewShadingRate, SetPreviewShadingRate, Edit, and PreviewRender methods. These are some other useful methods for appearances:
- apph Attach ?subtype : ""?
Attach the appearance to the current Maya selection. This is only meaningful if called when Slim is connected to MTOR.
- apph Detach ?subtype : ""?
Detach the appearance to the current Maya selection. This is only meaningful if called when Slim is connected to MTOR.
- apph GetProperties ?arg arg ...?
Get the list of an appearance's properties that match the search criteria. This will be covered further in the Property Objects section (below).
- apph RevertValues
Revert all properties in the appearance to their respective default values.
- apph UpdateEditor
Update the Appearance Editor for the appearance.
A complete list of Appearance methods, along with methods specific to Function objects, can be found in the Slim Scripting Glossary.
Properties are where most of the information associated with appearances are stored. As with appearances, the property class is a base class for other subclasses. Collections, Parameters, Attributes and TORAttributes are the subclasses of Property. Collections are used to organize properties hierarchically. Parameters are those properties that are actual appearance parameters. Attributes are those properties that have a RIB representation that isn't part of a shader instance. SlimAttributes can be used to control code generation. TCLAttributes contain tcl code that may be executed within a TCLBox. TORAttributes are properties that control how additional rendering passes are performed.
Properties exist inside of appearances, and are accessed
using the GetProperties appearance method. This
method can be used with a number of different switches to
filter the properties returned. Given a variable apph
set to the object handle of an appearance, you might use
GetProperties in the following ways:
# get all input properties with name "Ks" $apph GetProperties -access input -name Ks # get all float input parameters $apph GetProperties -type float -access input -class ::Slim::Parameter # get all external parameters $apph GetProperties -provider variable -access input -class ::Slim::Parameter
Because this method is defined for appearances, you will only be operating on the properties of one appearance at a time. If you want to operate on the properties across a palette, or an entire session, you must do so by nesting loops. The example given in our explanation of The Slim Object does just this.
The following procedure copies property settings from one node to another. Property correspondence is done by name, so the nodes do not have to be of the same template.
proc copySettings {fromName toName} { # get source appearance set fromApp [slim GetAppearances -name $fromName] # if this does not return exactly one appearance, # print an error and exit set numFrom [llength $fromApp] if {[llength $fromApp] != 1} { ::RAT::LogMsg ERROR "copySettings : fromName must matches $numFrom appearances" return } # loop through destination appearance(s) foreach toApp [slim GetAppearances -name $toName] { # loop through properties in fromApp foreach fromProp [$fromApp GetProperties -access input] { # find properties in toApp with the same name set name [$fromProp GetName] foreach toProp [$toApp GetProperties -access input -name $name] { if {![$toProp isa ::Slim::AlterEgo::coll]} { # copy value $toProp SetValue [$fromProp GetValue] } # copy value provider set provider [$fromProp GetValueProvider] $toProp SetValueProvider $provider # if it's a connection, set that if {$provider == "connection"} { $toProp SetConnection [$fromProp GetConnection] } } # log copy ::RAT::LogMsg INFO "Copied settings from [$fromApp GetName] to [$toApp GetName]" } } }The example above made use of the GetName, GetValue, SetValue, GetValueProvider, and SetValueProvider methods for properties. These are some other useful methods for properties:
- proph GetAppearance
Get the object handle for the appearance that uses this property.
- proph GetConnectedFunction
Get the object handle for the function to which this property connects.
- proph RevertValue
Revert the value for this property to its default.
A complete list of methods defined for Properties, especially those specific to AtomicProperties (those with a value), can be found in the Slim Scripting Glossary.
You might have noticed the isa
query in the example above.
This is a standard method available to all classes, and you can use
it to check the class of an object. If you ever see the name of
an object handle (e.g. coll153), you will find that the prefix
of the handle is its class. A fully qualified class for a scripting
object will be ::Slim::AlterEgo::class
, e.g.
::Slim::AlterEgo::coll. You can query the class of any
object using the info method, e.g.:
% coll161 info class ::Slim::AlterEgo::coll % coll161 isa ::Slim::AlterEgo::coll 1An complete index of classes can be found in the Slim Scripting Glossary.
This class is responsible for managing issues related to the current workspace. As with the application object, there should only be a single instance of this class and its handle is workspace. You can use the workspace object to convert path names from global to relative references using the current search paths. The workspace state is manipulated by the Workspace Editor and is managed across applications. For this reason it may not be sufficient to simply set the workspace state using the workspace object.
Methods for the workspace can be found in the Slim Scripting Glossary. For more details on working with workspaces, please refer to the workspace editor docs.
You may find it useful to output information while your script is running, either to communicate to the user, or to help with debugging information.
Because Slim is designed to run in conjunction with a client application like Maya, the stdout channel is not usable for output. Instead, you can direct information to Slim's message log.
The advantage of using Slim's message log is that each message is automatically timestamped and can be coded according to' severity. You can log it using the ::RAT::LogMsg procedure. Just send your message, preceded by its severity:
::RAT::LogMsg COPIOUS "Reading from testdata.tcl" ::RAT::LogMsg INFO "Creating a new appearance" ::RAT::LogMsg NOTICE "This appearance has been changed" ::RAT::LogMsg WARNING "This appearance contains a very low shading rate" ::RAT::LogMsg ERROR "Cannot generate shader" ::RAT::LogMsg SEVERE "Cannot communicate with Maya"
As you develop scripts, you may find it useful to associate extra information with an appearance or with a palette. The User Data mechanism allows you to associate arbitrary information with any node in Slim.
Every node in Slim has a user data dictionary, or data that is stored and retrieved using a key. Any string can be used as a key. Slim makes no assumptions about its contents. User Data is only accessible through the scripting environment. It will not be visible anywhere in the interface.
As an example of how to use User Data, this procedure tags every active appearance with the current time, host, and user.
proc storeCreationInformation {} { # loop through all appearances foreach app [slim GetAppearances] { # check for existing "createdBy" key set user [$app GetUserData createdBy] # if this has already been set, skip if {$user != ""} { continue } # store user name, time, and host $app SetUserData createdBy [GetUserName] $app SetUserData createdAt [clock seconds] $app SetUserData createdOn [GetHostName] } }
We can later retrieve that information using the procedure below:
proc retrieveCreationInformation {} { # loop through all appearances foreach app [slim GetAppearances] { # check for existing "createdBy" key set user [$app GetUserData createdBy] # if this hasn't been set, skip if {$user == ""} { ::RAT::LogMsg NOTICE \ "Could not find creation information for [$app GetName]" continue } ::RAT::LogMsg INFO "Creation information for [$app GetName]" ::RAT::LogMsg INFO " - created by $user" # get time and format it set time [$app GetUserData createdAt] if {$time != ""} { ::RAT::LogMsg INFO " - created at [clock format $time]" } # get host set host [$app GetUserData createdOn] if {$host != ""} { ::RAT::LogMsg INFO " - created on $host" } } }
Commands allow you to present your scripts as items in the Commands menus of the Appearance Editor and the Palette Editor. The following trivial example demonstrates how to create a Command:
Creating the .slim File
Commands, like templates, are defined in .slim files. A declaration of a custom command consists of an invocation — how the command should appear and the tcl procedure to call when it is invoked — and a body of TclTkSource — a container in which to define tcl procedures. This example custom command will be displayed in the Appearance Editor and, when invoked, will log all of the values of the current appearance to Slim's message log.
## ## Creating a Custom Command ## # Tell Slim this is an extension slim 1 extensions pixardb { extensions pixar pxsl { # Begin custom command cmdui printValues { # Place the command in the Command Menu of the Appearance Editor. # The first argument defines how the item will appear. # The second argument defines the procedure to call. # Note %c will automatically be substituted for the # handle of the current apperance or palette. invocation {AppearanceEditor/Resources/Print Param Values} {::Slim::printValues %c} TclTkSource { # Here's the tcl command # Because we used "%c" in our invocation above, # The current context (in this case, the current appearance) # will be passed as the first argument proc ::Slim::printValues app { ::RAT::LogMsg INFO "Printing parameter values for [$app GetName]" # loop through parameters, printing their values foreach p [$app GetProperties -access input -class ::Slim::Parameter] { ::RAT::LogMsg INFO " - [$p GetLabel] : [$p GetValue]" } } } } } }
Loading the .slim File
Like templates, custom commands must be loaded in an initialization file.
You use the LoadExtension procedure, followed by the customcmd class, as below:
LoadExtension slim [file join $templates custom_commands.slim] customcmd
Running the Command
After restarting Slim, our command should be positioned in the Commands menu
as specified by its Invocation:
Executing the "Print Param Values" command yields these results in the message log:
Customizing the Invocation
Optional arguments to the invocation command allow you to further
customize how your Command appears in the menu:
- -accelerator binding
- Establishes a hot-key to invoke your command. The binding should be declared like a Tk binding, e.g. <Control-m>.
- -enabled expression
- Controls whether the menu item is enabled. The argument is a TCL expression that will be evaluated when the menu is posted. As an example, you can disable commands that may depend on Maya/MTOR using:
-enabled {[string match [slim GetRunMode] "server"]}You can use %c in your expression, and it will be replaced by the context (that is, the appearance or palette) of the editor. Remember to enclose your expression in braces so it is evaluated when the menu is posted, and not before.
Pixar Animation Studios
|