You can use HOM to store and retrieve arbitrary data on individual nodes.
Overview
Sometimes you need to store extra information on individual nodes. For example, you may want to...
Associate scene-level nodes with production database entries or asset tags.
Attach additional versioning information beyond what is already stored by Houdini.
Tag nodes as having been operated on by some tool to avoid unnecessary work.
The hou.Node.setUserData, hou.Node.userData,
hou.Node.userDataDict, and hou.Node.destroyUserData methods let you
set and get a name/string pair on a given node instance. The name/string pairs
are saved with the node in the .hip file and so persist between Houdini
sessions.
Similarly, the hou.Node.setCachedUserData, hou.Node.cachedUserData, hou.Node.cachedUserDataDict, and hou.Node.destroyCachedUserData methods let you set a name/object pair on a given node instance. This cached data is not saved with the hip file, and the objects may be any Python object (not just strings). Cached data is useful for nodes implemented using Python, since they can save temporary values between cooks in order to avoid recomputing them on subsequent cooks.
Persistent Data Usage
>>> import time >>> # Get a node instance >>> n = hou.node("/obj/sphere1") >>> # Put a name/value on the node >>> n.setUserData("last_indexed", str(time.time())) >>> # Get the named value back from the node >>> n.userData("last_indexed") 1260572254.21 >>> # Get a dictionary of all name/value pairs on the node >>> n.userDataDict() {'last_indexed': '1260572254.21'} >>> # Accessing a non-existent name returns None >>> print n.userData("foo") None >>> # Remove a value from the node >>> n.destroyUserData("last_indexed")
The contents of the string can be structured data such as XML, JSON, compressed binary data, or pickled Python objects, however you are responsible for encoding and decoding structured data to/from simple strings.
For example, here are two functions you could use to store compressed JSON representations of Python values:
import zlib import hutil.json def setCompressedJSON(node, data): node.setUserData( "acme_studio", zlib.compress(hutil.json.utf8Dumps(data))) def compressedJSON(node): str_data = node.userData("acme_studio") if str_data is None: return None return hutil.json.utf8Loads(zlib.decompress(str_data))
>>> n = hou.node("/obj/geo1") >>> setNodeData(n, {"version": 1, "database_id": 1723}) >>> print nodeData(n)["database_id"] 1723
Cached Data Usage
The following example code would exist in the Cook tab of a node type (e.g. a SOP) implemented using Python. It avoids unnecessarily reparsing a configuration file between subsequent cooks.
def cook(node): config_file_name = node.evalParm("configfile") if node.cachedUserData("config_file_name") != config_file_name: config = ConfigParser.RawConfigParser() config.read(config_file_name) node.setCachedUserData("config", config) node.setCachedUserData("config_file_name", config_file_name) else: config = node.cachedUserData("config") # Use the configuration values and perform the cook. #value = config.getfloat("foo") # ... cook(hou.pwd())
Tips
Trying to access a non-existent value returns
None. It does not raise aKeyErroror other exception. SinceNode.userDatareturns a string if the key is valid andNoneotherwise, you can simply check againstNoneto see if a key is valid. However,Node.cachedUserDatamay containNonefor a valid key, so usekey in node.cachedUserDataDict()to check if a key is valid.To remove a name/value from a node, use
Node.destroyUserDataorNode.destroyCachedUserData. Deleting the value from the dictionary returned byNode.userDataDictorNode.cachedUserDataDictwill not remove the value from the node. If you try to remove a non-existent key, the methods will raisehou.OperationFailed.To avoid namespace conflicts for persistent node data, you should consider storing all your data in a single name/value pair (e.g. using JSON), rather than storing multiple name/value pairs per node. Namespace conflicts should be less common for cached node data, since cached data is intended for use by the code implementing a Python node.
When storing structured data in persistent node data, you should add a
versionkey to identify the format in which the data is stored, as shown in the example above. This practice will come in handy when you need to change or extend the format and want your tools to deal gracefully with old data.