Node networks to hscript / Python

   1605   7   4
User Avatar
Member
9 posts
Joined: Dec. 2018
Offline
Hi everyone,

lately I have been testing solutions to build a python tool that allows me to quickly save node networks to a python file and being able to choose presets on disk and execute them, similar to dragging node constellations into the Shelf.

So far I it worked pretty good by using the hou.Node.asCode() method, but this quickly breaks down when using Attribute Vops
As far as I understand, by dragging nodes into the shelf, it generates some hscript code (Shown in the picture below).

My question: Is there a way to generate this hscript code with python, so kind of what dragging nodes into the shelf does, but with code myself, so I can use it to save snippets to disk myself?

Thanks in advance!
Edited by JoshRizzo - Feb. 15, 2023 04:56:01

Attachments:
Screenshot 2023-02-15 105512.jpg (274.7 KB)

User Avatar
Member
731 posts
Joined: Dec. 2006
Offline
Do you mean
hou.hscript("opscript -b -r -s /path/to/node")
?
Sean Lewkiw
CG Supervisor
Machine FX - Cinesite MTL
User Avatar
Member
9 posts
Joined: Dec. 2018
Offline
mrCatfish
hou.hscript("opscript -b -r -s /path/to/node")
Yes, exactly! Thanks
Does that also keep intact node connections like dragging into the shelf does?? (If I run over multiple nodes) Or can I do something like this
hs_code = hou.hscript("opscript -b -r -s /path/to/node /path/to/node2 /path/to/node3")
?
User Avatar
Member
117 posts
Joined: Aug. 2017
Offline
Maybe can be useful for someone , endless possibility for speed and creativity .
#Save_Nodes.py


import hou

# cpio
dirPath = "DIRECTORY"

def SaveTemplate(name):

    sel = hou.selectedNodes()
    path = sel[0].path()
    nodeCategoryName = sel[0].type().category().name()
    pathSplit = path.split("/")
    pathSplit.pop(-1)
    
    getRootPath = "/".join(pathSplit)
    rootName = getRootPath[1:].replace("/","_")
    
    contextnode = hou.node(getRootPath)
    
    filename = "{}/{}_{}".format(dirPath, nodeCategoryName, name + ".cpio")
    
    contextnode.saveItemsToFile(sel, filename, save_hda_fallbacks = False)
    hou.ui.displayMessage("Success!\n" + "Save File: "+filename)
    print("Save File: "+filename)


Dialog = hou.ui.readInput(message ="Save Select Nodes?\n",title = "Save Template",severity=hou.severityType.Message,buttons=["Save","Cancel"])

if Dialog[0]==0:
    SaveTemplate(Dialog[1])
# Import_Nodes.py


import hou
import os

# cpio
dirPath = "DIRECTORY"

files = os.listdir(dirPath)
fileList = [f for f in files if os.path.isfile(os.path.join(dirPath, f))]

def ImportTemplate(filename):
    desktop = hou.ui.curDesktop()
    pane =  desktop.paneTabOfType(hou.paneTabType.NetworkEditor)
    current_context = pane.pwd().path()
    
    hou.clearAllSelected()
    
    contextnode = hou.node(current_context)
    nodes = contextnode.loadItemsFromFile(filename, ignore_load_warnings=False)
    sel = hou.selectedNodes()
    
    firstNodePos = sel[0].position()
    for node in sel:
        node.setPosition(
        node.position() + hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor).visibleBounds().center() - firstNodePos
        )

Dialog = hou.ui.selectFromList(fileList, message='Select Import File')
 
if len(Dialog)!=0:
    for i in Dialog:
        ImportTemplate("{}/{}".format(dirPath, fileList[i]))
Conservation of Momentum
User Avatar
Member
9 posts
Joined: Dec. 2018
Offline
Thanks both!

I found a way for myself in case anyone is interested.
Of course I need to work on creating an UI for this, custom file naming and loading etc.
I'm saving a dictionary at the start of the text file for safe renaming like this:
[{'temp_name': 'CPPS_NODE0', 'name': 'attribvop1'}, {'temp_name': 'CPPS_NODE1', 'name': 'attribvop2'}, {'temp_name': 'CPPS_NODE2', 'name': 'attribvop4'}, {'temp_name': 'CPPS_NODE3', 'name': 'attribvop3'}]
Preset Saving:
import hou

nodes = hou.selectedNodes()
names = []
for n in nodes:
    names.append(n.name())
curP = nodes[0].parent()

# Create duplicates
new_nodes = hou.copyNodesTo(nodes, curP)

definition = []

# Make dictionary and rename nodes to temporary names
for num, new in enumerate(new_nodes):
    new_def = {}
    old_name = names[num]
    new.setName("CPPS_NODE{}".format(num))
    new_def["temp_name"] = "CPPS_NODE{}".format(num)
    new_def["name"] = old_name
    definition.append(new_def)


definition_str = str(definition) 
print(definition_str)   


# Create multiuple node string for hscript command
node_string = ""
for node in new_nodes:
    path = node.path()
    node_string += " " + path

hs = hou.hscript("opscript -b -r -s{}".format(node_string))

# Delete temp nodes
for d in new_nodes:
    d.destroy()

# Add dictionary to top line
final_output = definition_str + "\n \n"
for h in hs:   
    final_output += h
    final_output += " "

file = open("C:/Users/Joshi/Desktop/Preset-Save/test.txt","w")
file.write(final_output)
file.close()

Preset Loading:
import sys
import toolutils
import hou
import json

h_extra_args = ''
pane = toolutils.activePane(kwargs)
if not isinstance(pane, hou.NetworkEditor):
    pane = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)
    if pane is None:
       hou.ui.displayMessage(
               'Cannot create node: cannot find any network pane')
       sys.exit(0)

pane_node = pane.pwd()
child_type = pane_node.childTypeCategory().nodeTypes()

if 'attribvop' not in child_type:
   hou.ui.displayMessage(
           'Cannot create node: incompatible pane network type')
   sys.exit(0)


pane_node.setSelected(False, True)

h_path = pane_node.path()
h_preamble = 'set arg1 = "' + h_path + '"\n'

h_cmd = ""
with open("C:/Users/Joshi/Desktop/Preset-Save/test.txt","r") as file:
    temp = file.read()

# Filter first line to be json dictionary
dict_str = ""
for num, l in enumerate(temp.splitlines()):
    if num == 0:
        dict_str = l.replace("'",'"')
    else:
        if "opcf " in l:
            l = "opcf $arg1"
        h_cmd += l + "\n"

d = json.loads(dict_str)

hou.hscript(h_preamble + h_extra_args + h_cmd)

# Rename nodes to fit name from dictionary
for item in d:
    temp_name = item["temp_name"]
    name = item["name"]
    node = hou.node("{}/{}".format(pane_node.path(),temp_name))
    node.setName(name,True)
    node.setSelected(True)
User Avatar
Member
52 posts
Joined: June 2017
Offline
This doesn't happen to also save out parameters does it? I've made similar scripts but this is a good bit more elegant.
Houdini TD, I focus on tools for procedural asset creation.

www.jaworenko.design
User Avatar
Member
9 posts
Joined: Dec. 2018
Offline
EJaworenko
This doesn't happen to also save out parameters does it? I've made similar scripts but this is a good bit more elegant.

I discovered a much cleaner way to generate these files, without any hscript

For saving:
contextnode = hou.node(getRootPath)
contextnode.saveItemsToFile(nodes, filename, save_hda_fallbacks = False)

And for loading:
contextnode = hou.node(getRootPath)
contextnode.loadItemsFromFile(file_load, ignore_load_warnings=False)

This is muuuch cleaner and retains all node connections, subnets, parameters and works in any context.
User Avatar
Member
52 posts
Joined: June 2017
Offline
JoshRizzo
I discovered a much cleaner way to generate these files, without any hscript

For saving:
contextnode = hou.node(getRootPath)
contextnode.saveItemsToFile(nodes, filename, save_hda_fallbacks = False)

And for loading:
contextnode = hou.node(getRootPath)
contextnode.loadItemsFromFile(file_load, ignore_load_warnings=False)

This is muuuch cleaner and retains all node connections, subnets, parameters and works in any context.

Damn! Its almost like they designed a couple functions for this specifically. I'll have to test it out, thanks!
Houdini TD, I focus on tools for procedural asset creation.

www.jaworenko.design
  • Quick Links