Houdini 20.5 Executing tasks with PDG/TOPs

Event handling

You can register a Python function to handle events from a PDG node or graph

On this page

Overview

PDG provides an event handling mechanism that can be used to run custom Python code when things happen during the cook. Event hooks are available for fine-grained changes in work items such as a state changes or dependency modifications, as well as more granular graph events like cook completion. The full list of events is documented in the pdg.EventType API reference.

To register an event handler you first need to have a reference to either a pdg.Node, pdg.WorkItem or a pdg.GraphContext. The Node and Graph Context can be accessed from a hou.TopNode once the TOP graph has been cooked. A work item can be found by looking it up using the workItemId on the pdg.Event with pdg.GraphContext.workItemById.

Prior to Houdini 18, an event emitted by a work item would also be emitted by the node holding it and by the graph context. That means that adding event handlers to the graph context would be enough to catch all work item, node and graph events, this is no longer the case, in order to catch a work item event, you need to add a handler to the work item. Some events will still be emitted by multiple objects, but that behavior is now specific to some events.

import hou
import pdg

topnode_path = '/tasks/topnet1/genericgenerator1'
top_node = hou.node(topnode_path)

# Ensure the TOP graph has been cooked so that we can
# access the underlying PDG graph
top_node.cookWorkItems(tops_only=True)

pdg_node = top_node.getPDGNode()
pdg_context = top_node.getPDGGraphContext()

The API for registering an event handler is the same on node and graph context objects. Each handler is create by defining a function that takes a pdg.Event as its first argument:

# Define a function to handle a work item event
def print_add(event):
    work_item = pdg_context.graph.workItemById(event.workItemId)
    print ("Work item '{}' was added to node {}".format(work_item.name, event.node.name))
    # Add a listener for state changes on this new work item

    def print_workitem_change(event):
        work_item = pdg_context.graph.workItemById(event.workItemId)
        print ('State Change for {}: {} -> {}'.format(work_item.name, event.lastState, event.currentState))
    work_item.addEventHandler(print_workitem_change, pdg.EventType.WorkItemStateChange)

# Define a function to handle a graph level event
def print_done(event):
    print ("Graph cook complete!")

# Register the handlers
node_handler = pdg_node.addEventHandler(print_add, pdg.EventType.WorkItemAdd)
context_handler = pdg_context.addEventHandler(print_done, pdg.EventType.CookComplete)

The best place to put event registration code depends on your set up. One option is to place the code in a shelf tool that registers the handlers when the tool is clicked.

Removing Handlers

Sometimes you may wish to remove event handlers after they have finished executing. One way to do this is to call the removeEventHandler method on the object that was initially used to add the handler:

pdg_node.removeEventHandler(node_handler)

It is also possible to remove a handler from within the event function itself. To do so you’ll need to include the extra pass_handler=True argument when registering the handler function. This is so that the event function knows which handler it is associated with it. Inside the event function itself you can then call the pdg.EventHandler.removeFromAllEmitters method on the handler:

def print_done_and_remove(handler, event):
    print ("Graph cook complete!")

    # Deregister the handler from all events its listening to
    handler.removeFromAllEmitters()

pdg_context.addEventHandler(print_done_and_remove, pdg.EventType.CookComplete, True)

Events that Trigger UI Changes

PDG events are processed in the background on the same cook thread that emitted them. This means that generally speaking it isn’t safe to start another cook, change parameters or open UI dialogs from an event handler function. In some cases you can perform these actions by the using the hdefereval module to ensure that your code runs on the main thread instead in the background:

import hdefereval

# Define a function for displaying our message
def display_message(message):
    import hou
    hou.ui.displayMessage(message, title="Example Message from PDG")

# Define our event handler
def display_event(event):
    hdefereval.executeDeferred(display_message, "An example message created in"
        " a PDG Event but displayed from the main thread!")

pdg_context.addEventHandler(display_event, pdg.EventType.CookComplete)

Note

The only event that can safely recook the current PDG/TOP graph is the CookComplete event. Event handler functions are able to cook other TOP networks, but must use the hdefereval module to do so.

Lists of Events Emitted by PDG Objects

All event sources are subclasses of the pdg.EventEmitter base class. You can check the list of event types supported by an instance of that class using the pdg.EventEmitter.supportedEventTypes property. The list of supported events for each type of event emitter is also listed below:

Graph Context Events (pdg.GraphContext):

  • pdg.EventType.CookComplete

  • pdg.EventType.CookError

  • pdg.EventType.CookStart

  • pdg.EventType.CookWarning

  • pdg.EventType.DirtyAll

  • pdg.EventType.NodeCooked

  • pdg.EventType.NodeConnect

  • pdg.EventType.NodeCreate

  • pdg.EventType.NodeDisconnect

  • pdg.EventType.NodeFirstCook

  • pdg.EventType.NodeGenerated

  • pdg.EventType.NodeRemove

  • pdg.EventType.NodeRename

  • pdg.EventType.NodeSetScheduler (When setting the default scheduler)

  • pdg.EventType.SchedulerAdded

  • pdg.EventType.SchedulerRemoved

  • pdg.EventType.UISelect

  • pdg.EventType.WorkItemAdd

  • pdg.EventType.WorkItemRemove

Node Events (pdg.Node):

  • pdg.EventType.CookComplete

  • pdg.EventType.CookError

  • pdg.EventType.CookStart

  • pdg.EventType.CookWarning

  • pdg.EventType.DirtyStart

  • pdg.EventType.DirtyStop

  • pdg.EventType.NodeClear

  • pdg.EventType.NodeCooked

  • pdg.EventType.NodeGenerated

  • pdg.EventType.NodeProgressUpdate

  • pdg.EventType.NodeRename

  • pdg.EventType.NodeSetScheduler

  • pdg.EventType.WorkItemAdd

  • pdg.EventType.WorkItemMerge

  • pdg.EventType.WorkItemRemove

  • pdg.EventType.WorkItemStateChange

Scheduler Events (pdg.Scheduler):

  • pdg.EventType.CookError

  • pdg.EventType.CookWarning

Service Manager Events (pdg.ServiceManager):

  • pdg.EventType.ServiceClientStarted

  • pdg.EventType.ServiceClientChanged

  • pdg.EventType.ServiceManagerAll

  • pdg.EventType.ServiceStartBegin

  • pdg.EventType.ServiceStartEnd

Work Item Events (pdg.WorkItem):

  • pdg.EventType.WorkItemAddDep

  • pdg.EVentType.WorkItemCookPercentUpdate

  • pdg.EventType.WorkItemMerge

  • pdg.EventType.WorkItemOutputFiles

  • pdg.EventType.WorkItemPriority

  • pdg.EventType.WorkItemRemoveDep

  • pdg.EventType.WorkItemSetFile

  • pdg.EventType.WorkItemSetFloat

  • pdg.EventType.WorkItemSetGeometry

  • pdg.EventType.WorkItemSetInt

  • pdg.EventType.WorkItemSetPyObject

  • pdg.EventType.WorkItemSetString

  • pdg.EventType.WorkItemStateChange

Executing tasks with PDG/TOPs

Basics

Beginner Tutorials

Next steps

  • Running external programs

    How to wrap external functionality in a TOP node.

  • File tags

    Work items track the results created by their work. Each result is tagged with a type.

  • PDG Path Map

    The PDG Path Map manages the mapping of paths between file systems.

  • Feedback loops

    You can use for-each blocks to process looping, sequential chains of operations on work items.

  • Service Blocks

    Services blocks let you define a section of work items that should run using a shared Service process

  • PDG Services

    PDG services manages pools of persistent Houdini sessions that can be used to reduce work item cooking time.

  • Integrating PDG with render farm schedulers

    How to use different schedulers to schedule and execute work.

  • Visualizing work item performance

    How to visualize the relative cook times (or file output sizes) of work items in the network.

  • Event handling

    You can register a Python function to handle events from a PDG node or graph

  • Tips and tricks

    Useful general information and best practices for working with TOPs.

  • Troubleshooting PDG scheduler issues on the farm

    Useful information to help you troubleshoot scheduling PDG work items on the farm.

  • PilotPDG

    Standalone application or limited license for working with PDG-specific workflows.

Reference

  • All TOPs nodes

    TOP nodes define a workflow where data is fed into the network, turned into work items and manipulated by different nodes. Many nodes represent external processes that can be run on the local machine or a server farm.

  • Processor Node Callbacks

    Processor nodes generate work items that can be executed by a scheduler

  • Partitioner Node Callbacks

    Partitioner nodes group multiple upstream work items into single partitions.

  • Scheduler Node Callbacks

    Scheduler nodes execute work items

  • Custom File Tags and Handlers

    PDG uses file tags to determine the type of an output file.

  • Python API

    The classes and functions in the Python pdg package for working with dependency graphs.

  • Job API

    Python API used by job scripts.

  • Utility API

    The classes and functions in the Python pdgutils package are intended for use both in PDG nodes and scripts as well as out-of-process job scripts.