In this tutorial i will assume you already know your way around Houdini and parameters Interface.
First let's create a Null node.
Go to the parameter's interface.
Add a string and a button parameter.
Name the button executeSnippet and the label Execute
Name the string pythonSnippet and the label Python Snippet.
On the string parm, tick the multi line option and change the language to python
Hit the apply button.
If everything is correct you should have something like this:
And This:
Now let's connect the button and the python snippet content. Select the button parm and in the callback script section put that following line:
exec(hou.parm("pythonSnippet").eval())
we eval the content of the string parm and pass it to the python exec command. Which just execute any string as python command. It should be like this:
Hit the accept button on the Parameters Option UI.
Let's test this! Open a python shell tab andpPut that command inside the string parm.
print "Hello World! Bitches"
Nice! Pretty cool way to embed some python code hey?
Now let's see what we can do to make this a bit better. Say We would to be able to save and reuse code created in the snippet to a pre-defined location!
Alright so first we going to modify a bit the UI.
First add a second button and name it "saveCurrentSnippet" and label "Save Current Snippet"
Then Select the Sting parm, go to the Menu tab on the right. Tick on Use Menu and select the second to last option replace (Field + Single Menu Selection)
Now go back to the button and in the callback script section put that line.
import save_python_snippet as ps;ps.saveFile(hou.node("."))
Don't worry about the module's name, we will create it shortly after this.
Alright, so let's do the first part. Saving the content to a desired location. We will create a module with a few different functions in it. The easiest place to put it, is in folder where Houdini already looks for modules. You could definitely change that location but for that tutorial we'll stay simple.
The path would be C:\Users\YOURUSERNAME\Documents\houdini16.5\python2.7libs
If the last folder does not exists just create it manually. In that folder, python2.7libs, create a new python file and name it save_python_snippet.py ( Make sure you have one python file named __init__.py, otherwise it won't work ).
Restart Houdini, open a python shell tab and type:
import save_python_snippet
If everything works properly you should get something like this:
Cool! now let's create the function to save the snippet, but first some imports and some Globals. Globals are not usually good to use. But for this example we will discard this!
Put the following lines in our newly created module
import os import hou mainPath = '/YOUR/WORKING/PATH/saved-snippets' suffix = '-snippet.py'
Make sure to change the path to something working. You could put it in the same directory as above.
C:\Users\YOURUSERNAME\Documents\houdini16.5\python2.7libs\saved-snippets
Ok now let's create our first function. Which is to list the content of that directory and find all the snippets python files already saved.
def getFileList(): # we check if the path is an actual directory and we can write to it if not os.path.isdir(mainPath) and not os.access(mainPath, os.W_OK): raise Exception("Please specify a proper working directory") # we list all files in that directory files = os.listdir(mainPath) # we clean out the files we don't need files = [ f for f in files if f.endswith(suffix)] return files
Ok, so now we know the list we can check if the user would like to override a file if the given name already exists.
Let's try it! In the python shell, reload the module first to update the file.
reload(save_python_snippet)
then call the new module's function getFileList(). If everything works fine you should get an empty list in return. Since we haven't saved anything yet.
Now we will do the actual saving function.
def saveFile(node): # We ask the user what would it like to name the new snippet r, fileName = hou.ui.readInput("Please provide a name for the snippet") if fileName != '': # we make sure the fileName is proper if not fileName.endswith(suffix): fileName = fileName.replace(".py", "") + suffix existingFiles = getFileList() # if the fileName matches a current existing one # we ask for override if fileName in existingFiles: text = "Snippet with this name already Exists, Overwrite the file?" if not hou.ui.displayConfirmation(text, title="Found Matching File", suppress=hou.confirmType.OverwriteFile): return # We get the current content of the string parm snippet = node.parm("pythonSnippet").eval() # We write it to the file f = mainPath +'/'+ fileName with open(f, 'w') as ff: ff.write(snippet)
Ok now if you save the file and reload the module. You can test the button.
You should get a small dialog and ask you the name of the snippet before saving it.
Yes good stuff. We are pretty much done with the add-ons. We just need to finish the menu.
Open the Null's Parameter UI Editor and select the string parm. In the String parm's Menu Script section put this code:
import save_python_snippet as ps l = ps.getFileList() keys = l.keys() keys.sort() newL = [] for i in keys: newL.extend([ l[i], i] ) return newL
As you can see the output of getFileList() has changed from list to dict. This is because the way menu works. You have to return a long list of pairs in order to work properly. For example if you have something like:
l = ['a', 'A']
You will only have one value display in the menu. That value is 'A', the value 'a' is what is going to be returned and will replace your string parm's value.
So basically what we want is to return the python file's content when the name is selected. But first we need to read each files and store it with it's current file name.
Here is the new function with the modifications:
def getFileList(): # we check if the path is an actual directory and we can write to it if not os.path.isdir(mainPath) and not os.access(mainPath, os.W_OK): raise Exception("Please specify a proper working directory") # we list all files in that directory files = os.listdir(mainPath) # we clean out the files we don't need files = [ f for f in files if f.endswith(suffix)] contents = {} for f in files: with open(mainPath +"/"+ f, 'r') as rf: # reading the file's content c = rf.read() # creating the according dict # we remove the suffix for a better UI display contents[f.replace(suffix, '')] = c return contents
Sub sequentially we need to adjust the save function:
def saveFile(node): # We ask the user what would it like to name the new snippet r, name = hou.ui.readInput("Please provide a name for the snippet") if name != '': fileName = name # we make sure the fileName is proper if not fileName.endswith(suffix): fileName = fileName.replace(".py", "") + suffix existingFiles = getFileList() # if the fileName matches a current existing one # we ask for override if name in existingFiles.keys(): text = "Snippet with this name already Exists, Overwrite the file?" if not hou.ui.displayConfirmation(text, title="Found Matching File", suppress=hou.confirmType.OverwriteFile): return # We get the current content of the string parm snippet = node.parm("pythonSnippet").eval() # We write it to the file f = mainPath +'/'+ fileName with open(f, 'w') as ff: ff.write(snippet)
The main changes are to make sure we don't override a pre existing file by mistake.
Full script here
Alright here is the final phase. It's good to be able to save and reuse python snippet from a null. But if you loose it or delete by mistake then you will to recreate those parms. I'll just show you how you can simply add those parms to any selected nodes.
In the same file save_python_snippet.py append the following code:
def addParmsToNode(node): # This is the code we put in the menu script to generate the menu menuScript="""import save_python_snippet as ps l = ps.getFileList() keys = l.keys() keys.sort() newL = [] for i in keys: newL.extend([ l[i], i] ) return newL """ # let's create the new Folder snippetFolder = hou.FolderParmTemplate("folder", "Snippet", folder_type=hou.folderType.Simple) # we add the button Execute snippetFolder.addParmTemplate(hou.ButtonParmTemplate( "execute", "Execute", script_callback = 'exec(hou.parm("pythonSnippet").eval())', script_callback_language = hou.scriptLanguage.Python )) # then the button Save Current Snippet snippetFolder.addParmTemplate(hou.ButtonParmTemplate( "saveCurrentSnippet", "Save Current Snippet", script_callback = 'import save_python_snippet as ps;ps.saveFile(hou.node("."))', script_callback_language = hou.scriptLanguage.Python )) # we add a separator between the buttons and the string parms snippetFolder.addParmTemplate(hou.SeparatorParmTemplate('sep')) # we add the string parm snippetFolder.addParmTemplate(hou.StringParmTemplate( "pythonSnippet", "Python Snippet", 1, tags = {"editor": "1", # the tags are here to specify "editorlang": "python"}, # the multi-line and python language menu_type = hou.menuType.StringReplace, item_generator_script = menuScript)) # here we specify the menuscript # we grab the current parm Template group gr = node.parmTemplateGroup() # we append the new folder to the current parm layout gr.append(snippetFolder) # we set it back on the node node.setParmTemplateGroup(gr)
Now we just need to put it in a shelf tool and call it from the tab menu.
Here is the code:
import save_python_snippet as ps nodes = hou.selectedNodes() for n in nodes: ps.addParmsToNode(n)
Woohoo! done!
Now we can add those parms on any nodes!