Create a ticket

Q100291: Collecting CEL statements in an OpScript


This article explains how to use cached results of evaluating a CEL statement (a collect operation) inside an OpScript node's Lua script.

This is achieved by executing a script button's Python script on the OpScript node, and caching the resulting scene graph location paths in a user parameter, so that they are available for use in the OpScript node's Lua script itself.

This allows users to perform the collect operation when it is convenient to them, and have the OpScript Lua script work on the cached results, rather than having to match the CEL statement against possibly the entire scene graph whenever the OpScript node is cooked.

Please read on for information on how and why to set up such a script.



Users may want to use the results of a collect operation within the Lua script on an OpScript node, for example to store the resulting location paths under a scene graph attribute or to have them available to operate on in the Lua script.


Collecting the results of evaluating CEL statements within an OpScript is undesirable, because it requires a portion of the scene, if not all of it, to be cooked each time the upstream recipe is modified, in order to determine whether locations match the given CEL statement.

This may be a very computationally expensive and lengthy operation when working on a scene with a large scene graph. This is highly undesirable in an interactive context, and goes against Katana’s model of deferred evaluation.


In order to collect the results of evaluating CEL statements, a better option is to use a user-triggered script that is executed once. This script traverses the scene in a one-off cook and collects the information that can then be used to populate node parameters.

This needs to be repeated to keep the project up to date should any relevant locations change.

To retrieve a set of existing locations that match a CEL statement, use a CEL collect operation. In the script that is executed via the Script Button, you can use the CollectAndSelectInScenegraph() function that is part of the Widgets module:

collector = Widgets.CollectAndSelectInScenegraph(celStatement, traversalRootLocationPath)
matchedLocationPaths = collector.collectAndSelect(select=False, node=myNode)

- celStatement  is the expression to be evaluated.
- traversalRootLocationPath is the scene graph location path at which the evaluation should start. This can be /root or any other specified scene graph location, for example /root/world/geo if only child locations in this branch should be matched against the CEL statement.



In the attached example scene you can see an implementation of this strategy. The scene is using the L-system from the 'Generating Geometry in OpScript' example project. You can inspect the script in the CollectAndSet OpScript node by clicking on the wrench icon at the top of the Parameters tab > ‘Edit User Parameters’, then clicking on the wrench icon above the CollectGeometryLocations button > ‘Widget Options…’.




The user parameters on the CollectAndSelect OpScript node were set up following these steps:

  1. Create an OpScript node and press to open its parameters in the Parameters tab.
  2. Click on the wrench icon at the top of the Parameters tab > ‘Edit User Parameters’. This adds a new group parameter to the node, named user.


  3. Click ‘Add’ to add parameters to the user group parameter and choose ‘String Array’ as the parameter type. This will be the parameter where we will store the matched location paths.


  4. Click the wrench icon directly above the new parameter and click ‘Rename Parameter’. This will open a dialog where you can enter a new name. Rename the parameter to ‘matchedLocationPaths’


  5. Click ‘Add’ again and this time choose ‘Button’ as the parameter type.
  6. Click the wrench icon directly above the newly created Button, and choose ‘Widget Options…’. This will open a dialog where you can change the button text to something descriptive, for example ‘CollectGeometryLocations’. The dialog also provides a Script field where you can add the Python commands that should be executed when the button is clicked.
  7. Copy and paste the following Python commands into the Script field:

    collector = Widgets.CollectAndSelectInScenegraph('//*{hasattr("geometry")}', '/root/world/geo')
    # This command will match all scene graph locations underneath /root/world/geo that have a ‘geometry’ attribute
    paths = collector.collectAndSelect(select=False, node=node)
    # The 'node' variable is this parameter's node
    To store the matched location paths in the node’s matchedLocationPaths user parameter, add the following commands to the script:

    paramName = 'user.matchedLocationPaths'
    param = node.getParameter(paramName)
    element = param.buildXmlIO(True)
    for path in paths:
       child = element.addChild(PyXmlIO.Element("string_parameter"))
       child.setAttr("name", "i%i" % (len(element) - 1))
       child.setAttr("value", path)
    element.setAttr("size", len(element))

Running the script by clicking the button will open a modal dialog while the scene is being traversed and locations are cooked, giving you the option to cancel the operation.

The collectAndSelect() function will return a list of location paths that can then be set as a user parameter on an OpScript node so that it is accessible for use in the Lua script as a user Op Arg, for example.



For more information on the CEL syntax,  please see the CEL Reference section of the Katana Developer Guide.

For more details on accessing user op args, like the one set in the Python script, in an OpScript node’s Lua script, please see  the Katana Developer Guide on the Cook Interface (OpScript).

If you are encountering any issues with this, then please open a Support ticket and let us know about the problem and which troubleshooting steps you have taken so far.

For more information on how to open a Support ticket, please refer to Q100064: How to raise a support ticket.

Was this article helpful?

We're sorry to hear that

Please tell us why
8 out of 8 found this helpful