Houdini has a good python model and I discovered a really useful feature in most of the nodes which is a method called .asCode(). What this does it returns as a string the code used to create the current node.
This is really good as you can get output and inspect for you own code. I decided to write a simple shelf tool for automating this process and dumping the data out to a file.
GetAbsoluteFileName
The first issue I had was with the Houdini file manager shown below
If a file is chosen from the normal file system we get an absolute file name, however Houdini has the ability to set up certain local environment variables which it uses internally. These however are not translated into the python scripts. These variables are $HIP, $JOP, $HOME which will be returned with any filename selected for example $HIP/myGeo.bgeo.
This needs to be translated into an absolute filename before python can access it, so a simple function was written to pop up the dialog and then strip any of the environment variables and replace them with the actual values. This is shown in the function below (which I use in a lot of other code)
######################################################################################################################## ## @brief a basic function to return a file name / absolute path stripping off $HIP etc ## @param[in] _title the title to be displayed in the file box ## @param[in] _wildCard the file selection wildcard i.e. *.obj etc ## @param[in] _fileType the houdini file type option e.g. hou.fileType.Any ## @returns a fully qualified file path or None ######################################################################################################################## def GetAbsoluteFileName(_title,_wildCard,_fileType) : # launch a file select and get the data file=hou.ui.selectFile(None,"Select File To Save",False,_fileType,"*.py","",False,False,hou.fileChooserMode.Write) # if it was empty bomb out and return none if file =="" : return None else : # so we got some data we need to split it as we could have $JOB $HIP or $HOME prepended # to it if we partition based on the / we get a tuple with "", "/","/....." where the # first element is going to be an environment var etc. file=file.partition("/") # we have $HOME so extract the full $HOME path and use it if file[0]=="$HOME" : prefix=str(hou.getenv("HOME")) elif file[0] == "$HIP" : #we have $HIP so extract the full $HIP path prefix=str(hou.getenv("HIP")) # we have a $JOB so extract the full $JOB path elif file[0] == "$JOB" : prefix=str(hou.getenv("JOB")) # nothing so just blank the string else : prefix=str("") #now construct our new file name from the elements we've found return "%s/%s" %(prefix,file[2])Now this has been written we can use it to popup a dialog and return the filename
HouAsCode.py
The rest of the function is simple. We see which nodes are selected in houdini iterate through all of them calling the asCode() method
file=GetAbsoluteFileName("Enter File to Save ","*.py",hou.fileType.Any) if file != None : fileOut=open(file,'w') fileOut.write("#############################################\n") fileOut.write("# generated by Hou as Code script \n") fileOut.write("#############################################\n") for objects in hou.selectedNodes() : fileOut.write("#############################################\n") fileOut.write("# from object %s \n" %(objects.name())) fileOut.write("#############################################\n") fileOut.write("%s \n" %(objects.asCode())) fileOut.write("#############################################\n\n") fileOut.close()
You can download the full script here
Installation
The easiest way to run this script is to place a shelf button into Houdini with the script embedded into it. First right click onto the shelf you wish to place the button on as shown
Next we edit the tool and add the name etc as shown
Finally we can paste the script into the tool as shown in the next dialog
Output
The following is a sample output from the tool
############################################# # generated by Hou as Code script ############################################# ############################################# # from object helixRename ############################################# # Initialize parent node variable. if locals().get("hou_parent") is None: hou_parent = hou.node("/obj/helixObjectImport/helixBakeChannel") # Code for /obj/helixObjectImport/helixBakeChannel/helixRename hou_node = hou_parent.createNode("rename", "helixRename", run_init_scripts=False, load_contents=True) hou_node.move(hou.Vector2(0, 0)) hou_node.setAudioFlag(False) hou_node.setExportFlag(False) hou_node.bypass(False) hou_node.setDisplayFlag(False) hou_node.setSelected(True) hou_node.setUnloadFlag(False) # Code for /obj/helixObjectImport/helixBakeChannel/helixRename/stdswitcher1 parm if locals().get("hou_node") is None: hou_node = hou.node("/obj/helixObjectImport/helixBakeChannel/helixRename") hou_parm = hou_node.parm("stdswitcher1") hou_parm.set(0) hou_parm.setAutoscope(False) hou_parm.lock(False) # Code for /obj/helixObjectImport/helixBakeChannel/helixRename/renamefrom parm if locals().get("hou_node") is None: hou_node = hou.node("/obj/helixObjectImport/helixBakeChannel/helixRename") hou_parm = hou_node.parm("renamefrom") hou_parm.set("?[xyz]") hou_parm.setAutoscope(False) hou_parm.lock(False) # Code for /obj/helixObjectImport/helixBakeChannel/helixRename/renameto parm if locals().get("hou_node") is None: hou_node = hou.node("/obj/helixObjectImport/helixBakeChannel/helixRename") hou_parm = hou_node.parm("renameto") hou_parm.set("t[xyz]0") hou_parm.setAutoscope(False) hou_parm.lock(False) # Code for /obj/helixObjectImport/helixBakeChannel/helixRename/scope parm if locals().get("hou_node") is None: hou_node = hou.node("/obj/helixObjectImport/helixBakeChannel/helixRename") hou_parm = hou_node.parm("scope") hou_parm.set("*") hou_parm.setAutoscope(False) hou_parm.lock(False) # Code for /obj/helixObjectImport/helixBakeChannel/helixRename/srselect parm if locals().get("hou_node") is None: hou_node = hou.node("/obj/helixObjectImport/helixBakeChannel/helixRename") hou_parm = hou_node.parm("srselect") hou_parm.set("max") hou_parm.setAutoscope(False) hou_parm.lock(False) # Code for /obj/helixObjectImport/helixBakeChannel/helixRename/units parm if locals().get("hou_node") is None: hou_node = hou.node("/obj/helixObjectImport/helixBakeChannel/helixRename") hou_parm = hou_node.parm("units") hou_parm.set("seconds") hou_parm.setAutoscope(False) hou_parm.lock(False) # Code for /obj/helixObjectImport/helixBakeChannel/helixRename/timeslice parm if locals().get("hou_node") is None: hou_node = hou.node("/obj/helixObjectImport/helixBakeChannel/helixRename") hou_parm = hou_node.parm("timeslice") hou_parm.set(0) hou_parm.setAutoscope(False) hou_parm.lock(False) # Code for /obj/helixObjectImport/helixBakeChannel/helixRename/unload parm if locals().get("hou_node") is None: hou_node = hou.node("/obj/helixObjectImport/helixBakeChannel/helixRename") hou_parm = hou_node.parm("unload") hou_parm.set(0) hou_parm.setAutoscope(False) hou_parm.lock(False) # Code for /obj/helixObjectImport/helixBakeChannel/helixRename/export parm if locals().get("hou_node") is None: hou_node = hou.node("/obj/helixObjectImport/helixBakeChannel/helixRename") hou_parm = hou_node.parm("export") hou_parm.set("/obj") hou_parm.setAutoscope(False) hou_parm.lock(False) # Code for /obj/helixObjectImport/helixBakeChannel/helixRename/gcolor parm tuple if locals().get("hou_node") is None: hou_node = hou.node("/obj/helixObjectImport/helixBakeChannel/helixRename") hou_parm_tuple = hou_node.parmTuple("gcolor") hou_parm_tuple.set((0.9, 0.9, 0)) hou_parm_tuple.setAutoscope((False, False, False)) hou_parm_tuple.lock((False, False, False)) # Code for /obj/helixObjectImport/helixBakeChannel/helixRename/gcolorstep parm if locals().get("hou_node") is None: hou_node = hou.node("/obj/helixObjectImport/helixBakeChannel/helixRename") hou_parm = hou_node.parm("gcolorstep") hou_parm.set(0.05) hou_parm.setAutoscope(False) hou_parm.lock(False) hou_node.setColor(hou.Color([0.8, 0.8, 0.8])) hou_node.setExpressionLanguage(hou.exprLanguage.Python) # Code to establish connections for /obj/helixObjectImport/helixBakeChannel/helixRename hou_node = hou_parent.node("helixRename") if hou_parent.node("helixImportData") is not None: hou_node.setInput(0, hou_parent.node("helixImportData"), 0) hou_node.setUserData("___toolcount___", "1") hou_node.setUserData("___toolid___", "NCCAPointBakeImport") #############################################