SlideShare a Scribd company logo
Python in Houdini for Technical Directors Luke Moore, Senior Software Developer, Side Effects Software http://www.sidefx.com/masterclasses/
Content Covered The places you can put Python code How to integrate Houdini into your Python-based pipeline How to learn Houdini's new Python API Some examples
Assumptions You know Python You know Houdini You have at least a rough knowledge of Hscript
A New Scripting Interface in Houdini 9 Python scripts will replace Hscript scripts, but Hscript is still supported for backwards compatibility Python has many advantages over Hscript.  It: is a well used and proven language comes with a large set of modules is popular in the industry
Overview of Houdini's Python Scripting Houdini embeds the Python interpreter Houdini will invoke Python scripts and snippets Your Python scripts have full access to Python's modules With the hou Python module, you can control Houdini
 
Learning Python As I mentioned, I'm assuming you already know Python If you're learning Python, I highly recommend the online Python tutorial at http://docs.python.org/tut The tutorial is all you need to know to start scripting Houdini After reading the tutorial, you can explore the standard library as you're looking for modules After you've started writing some Python, go back and read the tutorial again
Experimenting with Python in Houdini Python shell Can be in a floating window or in a pane The best place to explore the hou module Good for interactively prototyping parts of a script If you use Python for nothing else, use a Python shell as a calculator
Experimenting with Python in Houdini Python 2.5.1 (r251:54863, Jul 17 2007, 14:40:58) [GCC 4.1.3 20070629 (prerelease) (Debian 4.1.2-13) on linux2 Type “help”, “copyright”, “credits” or “license” for more info. >>> g = hou.node(“/obj/geo1”) >>> g <hou.ObjNode of type geo at /obj/geo1> >>> g.path() '/obj/geo1' >>> tx = g.parm('tx') >>> tx <hou.Parm tx in /obj/geo> >>> tx.eval() 0.0 >>> tx.set(3.5); tx.eval() 3.5
Accessing Houdini from Python: The hou Module The hou module is written in C++ and hooks directly into Houdini It provides a brand new API called the Houdini Object Model (HOM) Contains classes for the conceptual entities in Houdini (e.g. nodes, parameters, keyframes, panes, vectors, Houdini digital asset definitions, etc.) Houdini also ships with other modules written in Python that build on the hou module.
A Simple Example: Changing Path Prefixes Suppose we want to change all the paths in all the file parameters in all the nodes in the scene If the file starts with '/home/luke/project', we'll replace that part with '$HIP'.
A Simple Example: Changing Path Prefixes to
A Simple Example: Changing Path Prefixes Let's start by building up our script in the Python shell, one piece at a time.  First, let's write the part that changes the value of a file parameter.
A Simple Example: Changing Path Prefixes >>> p = hou.parm(“/obj/geo1/file1/file”) >>> p <hou.Parm file in /obj/geo1/file1> >>> p.unexpandedString() '/home/luke/project/image.jpg' >>> p.unexpandedString()[18:] '/image.jpg' >>> '$HIP' + p.unexpandedString()[18:] '$HIP/image.jpg'
A Simple Example: Changing Path Prefixes >>> t = p.parmTemplate() >>> t.type() parmTemplateType.String >>> t.stringType() stringParmType.FileReference >>> def isFileParm(parm): ...  t = parm.parmTemplate() ...  return (t.type() == hou.parmTemplateType.String and ...  t.stringType() == hou.stringParmType.FileReference) >>> if isFileParm(p) and p.unexpandedString().startswith( '/home/luke/project'): parm.set('$HIP' + parm.unexpandedString()[18:])
A Simple Example: Changing Path Prefixes Let's write a function to generalize this logic: >>> def fixFilePrefix(parm, from_prefix, to_prefix): ...  if isFileParm(parm) and parm.unexpandedString().startswith( ...  from_prefix): ...  parm.set(to_prefix + ...  parm.unexpandedString()[len(from_prefix):]) >>> fixFilePrefix( ...  hou.parm('/obj/geo1/file1/file'), ...  '/home/luke/project', '$HIP')
hou.session Module We can fix a bug in a function we've written by redefining a new definition in the Python shell. Redefining long functions gets tedious, though.  Luckily, we can write (and edit!) functions inside Houdini's Python Source Editor window and then test them out from the Python shell. The code we write in the source editor window will become part of a module named hou.session.
hou.session Module
A Simple Example: Changing Path Prefixes Let's continue with our example and modify the function to work with all the parameters of a node. >>> hou.node(“/obj/geo1”).parms() (<hou.Parm stdswitcher1 in /obj/geo1>, <hou.Parm stdswitcher2 in /obj/geo1>, ..., <hou.Parm vm_computeN in /obj/geo1>)
A Simple Example: Changing Path Prefixes def fixFilePrefix(node, from_prefix, to_prefix): for parm in node.parms(): if (isFileParm(parm) and parm.unexpandedString().startswith( from_prefix)): parm.set(to_prefix + parm.unexpandedString()[len(from_prefix):])
A Simple Example: Changing Path Prefixes Now all that's left is to call fixFilePrefix() for all the nodes in the scene >>> hou.node('/obj').children() (<hou.ObjNode of type geo at /obj/geo1>, <hou.ObjNode of type geo at /obj/geo2>, <hou.ObjNode of type cam at /obj/cam1>) >>> def printNodes(node): ...  print node.path(), ...  for child in node.children(): ...  printNodes(child) >>> printNodes(hou.node('/')) / /obj /obj/geo1 /obj/geo2 /obj/cam1 /out /part /ch /shop ...
A Simple Example: Changing Path Prefixes def fixFilePrefix(node, from_prefix, to_prefix): for parm in node.parms(): if (isFileParm(parm) and parm.unexpandedString().startswith( from_prefix)): parm.set(to_prefix + parm.unexpandedString()[len(from_prefix):]) for child in node.children(): fixFilePrefix(child, from_prefix, to_prefix) >>> hou.session.fixFilePrefix( hou.node('/'), '/home/luke/project', '$HIP')
Interpreting Tracebacks At some point, we're all bound to make a typo or have a bug in our code.  Hscript users will appreciate Python tracebacks.
 
Exploring the hou Module Houdini's Python shell popup help Attribute completion Function/ method help
Exploring the hou Module Function/ method- specific argument autocompletion
Exploring  the hou Module The Python shell also supports tab completion The traditional Python dir() and help() functions also help you explore the hou module >>> dir(hou.Node) ['__class__', ..., 'allowEditingOfContents', 'addSpareParmTuple', 'appendComment', 'changeNodeType', 'childTypeCategory', 'children', 'clearParmAliases', 'collapseIntoSubnet', 'color', 'comment', 'cook', 'copyNetworkBox', 'createDigitalAsset', 'createNetworkBox', 'createNode', 'creator', 'destroy', 'digitsInName', 'evalParm', 'evalParmTuple', 'expressionLanguage', 'extractAndDelete', 'findNetworkBox', 'findNetworkBoxes', 'hdaModule', 'indirectInputs', 'inputAncestors', 'inputConnections', 'inputConectors', 'inputs', 'insertInput', isCurrent', 'isInsideLockedHDA', ...]
Exploring the hou Module Houdini's help browser's help Contains an introduction covering the material covered here Contains reference help for all the module functions, classes, and methods Also lists functions and methods that are not implemented but are planned for implementation in future versions of Houdini
 
Python for the Hscripter Most Hscript commands and expression functions have a “replaced by” section listing the hou module functions/methods with the same functionality The hou module contains hou.hscript() and hou.hscriptExpression() functions that let you call arbitrary Hscript functions and expressions Houdini 9 contains a new Hscript “python” command, including “python -c”, so you can invoke Python from hscript
Python for the Hscripter Tips Use Python variables to store nodes where you would have “cd'd” in Hscript cd /obj/geo1 opadd box opadd sphere cd /obj Could  be done with: hou.cd('/obj/geo1') hou.pwd().createNode('box') hou.pwd().createNode('sphere') hou.cd('/obj')
Python for the Hscripter Tips (continued) But you could instead write: n = hou.node('/obj/geo1') n.createNode('box') n.createNode('sphere')
Python for the Hscripter Tips (continued) The names in the hou module are more descriptive, making Python scripts using hou much easier to read than Hscript scripts.  However, they are consequently more verbose. You can use Python tricks to make it less verbose.  Consider a module named 'simplehou': import hou n = hou.node p = hou.parm hou.Node.create = hou.Node.createNode >>> from simplehou import * >>> n('/obj/geo1').create('box')
Invoking Python from Houdini There are many places where Houdini will invoke the Python interpreter.  Here are nine of them: Houdini's Python shell Use the hou.session module to store and edit one-off functions A couple of Python statements can easily invoke scripts.  For example, import a module from disk and call a function in it, or use execfile() to run a script
Shelf/Tab Menu The shelf/tab menu A place to easily attach Python code to a button A number of supporting modules used by the shelves can be found in $HFS/houdini/scripts/python Most, if not all, of Houdini's builtin shelf scripts are written in Python, so they provide a wealth of examples
HDA Button Callback An HDA Button Callback The PythonModule HDA section. hou.Node.hdaModule and hou.NodeType.hdaModule
 
 
HDA Button Callback Not a good idea to call hou.session functions from button callbacks, since hou.session is intended to store hip-file specific functions.  Instead, you need to store the callback script with the HDA.
HDA Event Handler An HDA's Event Handler There currently aren't Python versions of HDA event handler scripts (OnCreated, OnUpdated, etc.) However, you can use the python hscript command to call functions in the PythonModule section def onCreated(node):   print “created HDA”, node.path() python -c “hou.node('$arg1').hdaModule().onCreated(hou.node('$arg1'))”
Parameters You can write Python expressions in a node's parameter Each node's expression language is set to either Hscript expressions or Python When you type an expressionless parameter, the expression will be in the node's language
Parameters Once a parameter has an expression, that expression's language will not change when you change the node's language If the expression's language is different from the node's language, it will appear in red You can change a parameter's language by right-clicking on it and choosing “Expression -> Change Language to ...”
Parameters “ from hou import *” is implicitly run before evaluating a Python expression You don't need the 'hou.' prefix This way, common expressions like cubic(), linear(), ch(), etc. work in both Python and Hscript expressions You can call functions in the hou.session module, and “from hou.session import *” is also implicitly run For example, you could write a hou.session function, say foo(), that returns a tuple of 3 values, and put 'foo()[0]', 'foo()[1]', 'foo()[2]' into each of tx, ty, and tz
Parameters Parameters in nodes inside an HDA can call functions in the PythonModule section For example, if foo() is defined in PythonModule, /obj/myhda1 is the HDA, and you could write the following Python expression in /obj/myhda1/geo1/tx:  node('..').hdaModule().foo()
Parameters You can also put the bodies of Python functions inside expressions Simple rule to remember: single lines are expressions and multiple lines are function bodies
When Houdini Starts Up When Houdini Starts Up There are Python versions of 123.cmd and 456.cmd: 123.py and 456.py Houdini will look through $HOUDINI_SCRIPT_PATH, looking for the first directory with 123.* or 456.*.  Once it finds a script, it will call it and stop looking. 123.{cmd,py} is called when Houdini starts up without a hip file 456.{cmd,py} is called every time a file is loaded or the session is cleared
When Houdini Starts Up There is a new file that's called only once, every time Houdini starts: pythonrc.py Houdini will search $HOUDINI_SCRIPT_PATH for houdini/pythonrc.py and run each of the files found Use pythonrc.py to store functions, aliases, etc.
Python-Based SOPs Python-based SOPs “ File -> New Operator Type”, choose “Python type” and “Geometry Operator”, and write code in the “Cook” tab
 
The Help Browser In addition to the RunHCommand(), there are new RunPythonStatements() and RunPythonExpression() javascript functions <script src=”resource:///res/RunHCommand.js” /> <script> alert(RunPythonExpression(“hou.node('/obj').children()”)); </script>
From Another Process From Another Process Send XML to openport socket The RunPythonStatements() javascript function is just sending XML to Houdini's openport socket <xml version=”1.0”><python_statements>print “hello”</python_statements> Use houxmlrpc module From Houdini: houxmlrpc.run(port=8888) From the other process: s = houxmlrpc.ServerProxy('http://localhost:8888') hou = s.hou # access the hou module like you normally would
Loading Python Scripts from External Files You can use the standard Python execfile function to run a script on disk The hou.findFile() function can help locate a file in   $HOUDINI_PATH It's usually better to import a module than to use execfile, though Houdini will go through all directories in   $HOUDINI_SCRIPT_PATH , adding python subdirectories to sys.path. For example, Houdini will look in the   $HOME/houdini9.0/scripts/python  directory when importing modules
Accessing Houdini from a Regular Python Shell You can import the hou module into a standard Python 2.5 shell You'll need to add  $HFS/houdini/scripts/python  to sys.path When hou is imported, it will initialize and create a Houdini session It's very easy to bring Houdini into an existing Python-based pipeline
hython hython is a program that ships with Houdini It's just a regular Python shell, but it automatically imports the hou module You can pass hip files on the hython command line before any .py files It also supports tab completion
Example: Loading a hip File and Running a ROP #!/usr/bin/python2.5 import sys sys.path.append(os.environ['HFS']+“/houdini/scripts/python”) import hou try: hou.hipFile.load(sys.argv[1]) except hou.LoadWarning, e: print e except hou.OperationFailed: sys.exit(“Could not load “ + sys.argv[1]) rop = hou.node(“/out/OUT”) rop.render()
Example: A Simple Python SOP geo = hou.pwd().geometry() bbox = geo.boundingBox() cd_attrib = geo.addAttrib(hou.attribType.Point, 'Cd', default_value=(1.0, 1.0, 1.0)) for point in geo.points(): dist_vec = hou.Vector3(point.position()) - bbox.center() color = [0.5 + dist_vec[i] / bbox.sizevec()[i] for i in range(3)] point.setAttribValue(cd_attrib, color)
Example: Node Layout import pygraphviz def layout(network, scale=1.0): graph = pygraphviz.AGraph(directed=True) graph.graph_attr['ordering'] = 'in' # Add graph nodes for each Houdini node in the network. gv_nodes, nodes = {}, {} for node in network.children(): graph.add_node(node.name()) gv_node = graph.nodes()[-1] gv_node.attr['width'] = str(0.5 + len(node.name()) * 0.05) gv_node.attr['height'] = '0.12' gv_node.attr['fixedsize'] = 'true' gv_nodes[node] = gv_node nodes[gv_node] = node
Example: Node Layout (continued) # Now add graph edges for all the wires in the network for node in network.children(): for input_node in node.inputs(): graph.add_edge(gv_nodes[input_node], gv_nodes[node]) edge = graph.edges()[-1] edge.attr['headport'] = 'n' edge.attr['tailport'] = 's' # Layout the graph and set the Houdini node positions graph.layout(prog='neato') scale *= 0.04 for gv_node in graph.nodes(): nodes[gv_node].setPosition( [float(v) * scale for v in gv_node.attr['pos'].split(',')])
Example: Node Layout (continued) # Call layout on the current network in the network editor network_editor = hou.ui.curDesktop().paneTabOfType( hou.paneTabType.NetworkEditor) layout(network_editor.pwd())
Houdini's Default Layout
Graphviz: 'dot' algorithm
Graphviz: 'neato-hier' algorithm
Graphviz: 'neato' algorithm

More Related Content

ODP
Embed--Basic PERL XS
ODP
Python 3000
PDF
Module Workshop NSC "Raspberry pi3 with Python" - Sanusi & Sasmitoh RR
PPTX
Boost.Python: C++ and Python Integration
PDF
C++の話(本当にあった怖い話)
PDF
Virtual Machine Constructions for Dummies
PDF
Boost.Python - domesticating the snake
PPTX
Groovy
Embed--Basic PERL XS
Python 3000
Module Workshop NSC "Raspberry pi3 with Python" - Sanusi & Sasmitoh RR
Boost.Python: C++ and Python Integration
C++の話(本当にあった怖い話)
Virtual Machine Constructions for Dummies
Boost.Python - domesticating the snake
Groovy

What's hot (18)

PPT
Python scripting kick off
PPTX
Pypy is-it-ready-for-production-the-sequel
PDF
PyPy's approach to construct domain-specific language runtime
PDF
Interpreter, Compiler, JIT from scratch
PDF
Threads and Callbacks for Embedded Python
PPTX
Go. Why it goes
PDF
MeCC: Memory Comparison-based Code Clone Detector
PDF
Coding in GO - GDG SL - NSBM
PPTX
Golang iran - tutorial go programming language - Preliminary
PDF
Про асинхронность / Максим Щепелин / Web Developer Wargaming
PDF
Lab Log Summer 2016 - Sheng Li
PPT
Inheritance compiler support
PDF
Pointless Pointers - How to make our interfaces efficient?
PDF
C++ How I learned to stop worrying and love metaprogramming
PDF
Golang concurrency design
PDF
Python Workshop
PPT
OpenMP
PDF
Take advantage of C++ from Python
Python scripting kick off
Pypy is-it-ready-for-production-the-sequel
PyPy's approach to construct domain-specific language runtime
Interpreter, Compiler, JIT from scratch
Threads and Callbacks for Embedded Python
Go. Why it goes
MeCC: Memory Comparison-based Code Clone Detector
Coding in GO - GDG SL - NSBM
Golang iran - tutorial go programming language - Preliminary
Про асинхронность / Максим Щепелин / Web Developer Wargaming
Lab Log Summer 2016 - Sheng Li
Inheritance compiler support
Pointless Pointers - How to make our interfaces efficient?
C++ How I learned to stop worrying and love metaprogramming
Golang concurrency design
Python Workshop
OpenMP
Take advantage of C++ from Python
Ad

Similar to Hom Class (20)

PPT
Python 3000
PPTX
Introducing PHP Latest Updates
ODP
Python Presentation
PPT
course slides -- powerpoint
PPTX
Python分享
PPTX
Golang basics for Java developers - Part 1
PPT
Python - Getting to the Essence - Points.com - Dave Park
PDF
Living With Legacy Code
ODP
What's new in Perl 5.10?
PDF
Introduction to Go language
PPT
Groovy Introduction - JAX Germany - 2008
PPTX
Php extensions
PDF
Using Flow-based programming to write tools and workflows for Scientific Comp...
PPTX
The GO Language : From Beginners to Gophers
KEY
Sbaw091006
ODP
Perl Moderno
PPT
Unit 6
PDF
Go 1.10 Release Party - PDX Go
ODP
Perl Dancer, FPW 2010
ODP
How Xslate Works
Python 3000
Introducing PHP Latest Updates
Python Presentation
course slides -- powerpoint
Python分享
Golang basics for Java developers - Part 1
Python - Getting to the Essence - Points.com - Dave Park
Living With Legacy Code
What's new in Perl 5.10?
Introduction to Go language
Groovy Introduction - JAX Germany - 2008
Php extensions
Using Flow-based programming to write tools and workflows for Scientific Comp...
The GO Language : From Beginners to Gophers
Sbaw091006
Perl Moderno
Unit 6
Go 1.10 Release Party - PDX Go
Perl Dancer, FPW 2010
How Xslate Works
Ad

Recently uploaded (20)

PPTX
Lecture (1)-Introduction.pptx business communication
PPTX
5 Stages of group development guide.pptx
PDF
Outsourced Audit & Assurance in USA Why Globus Finanza is Your Trusted Choice
PPTX
ICG2025_ICG 6th steering committee 30-8-24.pptx
DOCX
unit 2 cost accounting- Tender and Quotation & Reconciliation Statement
PDF
How to Get Funding for Your Trucking Business
PPTX
Amazon (Business Studies) management studies
PPTX
Dragon_Fruit_Cultivation_in Nepal ppt.pptx
PDF
Solara Labs: Empowering Health through Innovative Nutraceutical Solutions
PDF
Unit 1 Cost Accounting - Cost sheet
PDF
IFRS Notes in your pocket for study all the time
PDF
WRN_Investor_Presentation_August 2025.pdf
PPT
340036916-American-Literature-Literary-Period-Overview.ppt
PDF
Katrina Stoneking: Shaking Up the Alcohol Beverage Industry
PDF
Laughter Yoga Basic Learning Workshop Manual
PDF
Reconciliation AND MEMORANDUM RECONCILATION
PDF
How to Get Business Funding for Small Business Fast
PDF
pdfcoffee.com-opt-b1plus-sb-answers.pdfvi
DOCX
Business Management - unit 1 and 2
PPT
Chapter four Project-Preparation material
Lecture (1)-Introduction.pptx business communication
5 Stages of group development guide.pptx
Outsourced Audit & Assurance in USA Why Globus Finanza is Your Trusted Choice
ICG2025_ICG 6th steering committee 30-8-24.pptx
unit 2 cost accounting- Tender and Quotation & Reconciliation Statement
How to Get Funding for Your Trucking Business
Amazon (Business Studies) management studies
Dragon_Fruit_Cultivation_in Nepal ppt.pptx
Solara Labs: Empowering Health through Innovative Nutraceutical Solutions
Unit 1 Cost Accounting - Cost sheet
IFRS Notes in your pocket for study all the time
WRN_Investor_Presentation_August 2025.pdf
340036916-American-Literature-Literary-Period-Overview.ppt
Katrina Stoneking: Shaking Up the Alcohol Beverage Industry
Laughter Yoga Basic Learning Workshop Manual
Reconciliation AND MEMORANDUM RECONCILATION
How to Get Business Funding for Small Business Fast
pdfcoffee.com-opt-b1plus-sb-answers.pdfvi
Business Management - unit 1 and 2
Chapter four Project-Preparation material

Hom Class

  • 1. Python in Houdini for Technical Directors Luke Moore, Senior Software Developer, Side Effects Software http://www.sidefx.com/masterclasses/
  • 2. Content Covered The places you can put Python code How to integrate Houdini into your Python-based pipeline How to learn Houdini's new Python API Some examples
  • 3. Assumptions You know Python You know Houdini You have at least a rough knowledge of Hscript
  • 4. A New Scripting Interface in Houdini 9 Python scripts will replace Hscript scripts, but Hscript is still supported for backwards compatibility Python has many advantages over Hscript. It: is a well used and proven language comes with a large set of modules is popular in the industry
  • 5. Overview of Houdini's Python Scripting Houdini embeds the Python interpreter Houdini will invoke Python scripts and snippets Your Python scripts have full access to Python's modules With the hou Python module, you can control Houdini
  • 6.  
  • 7. Learning Python As I mentioned, I'm assuming you already know Python If you're learning Python, I highly recommend the online Python tutorial at http://docs.python.org/tut The tutorial is all you need to know to start scripting Houdini After reading the tutorial, you can explore the standard library as you're looking for modules After you've started writing some Python, go back and read the tutorial again
  • 8. Experimenting with Python in Houdini Python shell Can be in a floating window or in a pane The best place to explore the hou module Good for interactively prototyping parts of a script If you use Python for nothing else, use a Python shell as a calculator
  • 9. Experimenting with Python in Houdini Python 2.5.1 (r251:54863, Jul 17 2007, 14:40:58) [GCC 4.1.3 20070629 (prerelease) (Debian 4.1.2-13) on linux2 Type “help”, “copyright”, “credits” or “license” for more info. >>> g = hou.node(“/obj/geo1”) >>> g <hou.ObjNode of type geo at /obj/geo1> >>> g.path() '/obj/geo1' >>> tx = g.parm('tx') >>> tx <hou.Parm tx in /obj/geo> >>> tx.eval() 0.0 >>> tx.set(3.5); tx.eval() 3.5
  • 10. Accessing Houdini from Python: The hou Module The hou module is written in C++ and hooks directly into Houdini It provides a brand new API called the Houdini Object Model (HOM) Contains classes for the conceptual entities in Houdini (e.g. nodes, parameters, keyframes, panes, vectors, Houdini digital asset definitions, etc.) Houdini also ships with other modules written in Python that build on the hou module.
  • 11. A Simple Example: Changing Path Prefixes Suppose we want to change all the paths in all the file parameters in all the nodes in the scene If the file starts with '/home/luke/project', we'll replace that part with '$HIP'.
  • 12. A Simple Example: Changing Path Prefixes to
  • 13. A Simple Example: Changing Path Prefixes Let's start by building up our script in the Python shell, one piece at a time. First, let's write the part that changes the value of a file parameter.
  • 14. A Simple Example: Changing Path Prefixes >>> p = hou.parm(“/obj/geo1/file1/file”) >>> p <hou.Parm file in /obj/geo1/file1> >>> p.unexpandedString() '/home/luke/project/image.jpg' >>> p.unexpandedString()[18:] '/image.jpg' >>> '$HIP' + p.unexpandedString()[18:] '$HIP/image.jpg'
  • 15. A Simple Example: Changing Path Prefixes >>> t = p.parmTemplate() >>> t.type() parmTemplateType.String >>> t.stringType() stringParmType.FileReference >>> def isFileParm(parm): ... t = parm.parmTemplate() ... return (t.type() == hou.parmTemplateType.String and ... t.stringType() == hou.stringParmType.FileReference) >>> if isFileParm(p) and p.unexpandedString().startswith( '/home/luke/project'): parm.set('$HIP' + parm.unexpandedString()[18:])
  • 16. A Simple Example: Changing Path Prefixes Let's write a function to generalize this logic: >>> def fixFilePrefix(parm, from_prefix, to_prefix): ... if isFileParm(parm) and parm.unexpandedString().startswith( ... from_prefix): ... parm.set(to_prefix + ... parm.unexpandedString()[len(from_prefix):]) >>> fixFilePrefix( ... hou.parm('/obj/geo1/file1/file'), ... '/home/luke/project', '$HIP')
  • 17. hou.session Module We can fix a bug in a function we've written by redefining a new definition in the Python shell. Redefining long functions gets tedious, though. Luckily, we can write (and edit!) functions inside Houdini's Python Source Editor window and then test them out from the Python shell. The code we write in the source editor window will become part of a module named hou.session.
  • 19. A Simple Example: Changing Path Prefixes Let's continue with our example and modify the function to work with all the parameters of a node. >>> hou.node(“/obj/geo1”).parms() (<hou.Parm stdswitcher1 in /obj/geo1>, <hou.Parm stdswitcher2 in /obj/geo1>, ..., <hou.Parm vm_computeN in /obj/geo1>)
  • 20. A Simple Example: Changing Path Prefixes def fixFilePrefix(node, from_prefix, to_prefix): for parm in node.parms(): if (isFileParm(parm) and parm.unexpandedString().startswith( from_prefix)): parm.set(to_prefix + parm.unexpandedString()[len(from_prefix):])
  • 21. A Simple Example: Changing Path Prefixes Now all that's left is to call fixFilePrefix() for all the nodes in the scene >>> hou.node('/obj').children() (<hou.ObjNode of type geo at /obj/geo1>, <hou.ObjNode of type geo at /obj/geo2>, <hou.ObjNode of type cam at /obj/cam1>) >>> def printNodes(node): ... print node.path(), ... for child in node.children(): ... printNodes(child) >>> printNodes(hou.node('/')) / /obj /obj/geo1 /obj/geo2 /obj/cam1 /out /part /ch /shop ...
  • 22. A Simple Example: Changing Path Prefixes def fixFilePrefix(node, from_prefix, to_prefix): for parm in node.parms(): if (isFileParm(parm) and parm.unexpandedString().startswith( from_prefix)): parm.set(to_prefix + parm.unexpandedString()[len(from_prefix):]) for child in node.children(): fixFilePrefix(child, from_prefix, to_prefix) >>> hou.session.fixFilePrefix( hou.node('/'), '/home/luke/project', '$HIP')
  • 23. Interpreting Tracebacks At some point, we're all bound to make a typo or have a bug in our code. Hscript users will appreciate Python tracebacks.
  • 24.  
  • 25. Exploring the hou Module Houdini's Python shell popup help Attribute completion Function/ method help
  • 26. Exploring the hou Module Function/ method- specific argument autocompletion
  • 27. Exploring the hou Module The Python shell also supports tab completion The traditional Python dir() and help() functions also help you explore the hou module >>> dir(hou.Node) ['__class__', ..., 'allowEditingOfContents', 'addSpareParmTuple', 'appendComment', 'changeNodeType', 'childTypeCategory', 'children', 'clearParmAliases', 'collapseIntoSubnet', 'color', 'comment', 'cook', 'copyNetworkBox', 'createDigitalAsset', 'createNetworkBox', 'createNode', 'creator', 'destroy', 'digitsInName', 'evalParm', 'evalParmTuple', 'expressionLanguage', 'extractAndDelete', 'findNetworkBox', 'findNetworkBoxes', 'hdaModule', 'indirectInputs', 'inputAncestors', 'inputConnections', 'inputConectors', 'inputs', 'insertInput', isCurrent', 'isInsideLockedHDA', ...]
  • 28. Exploring the hou Module Houdini's help browser's help Contains an introduction covering the material covered here Contains reference help for all the module functions, classes, and methods Also lists functions and methods that are not implemented but are planned for implementation in future versions of Houdini
  • 29.  
  • 30. Python for the Hscripter Most Hscript commands and expression functions have a “replaced by” section listing the hou module functions/methods with the same functionality The hou module contains hou.hscript() and hou.hscriptExpression() functions that let you call arbitrary Hscript functions and expressions Houdini 9 contains a new Hscript “python” command, including “python -c”, so you can invoke Python from hscript
  • 31. Python for the Hscripter Tips Use Python variables to store nodes where you would have “cd'd” in Hscript cd /obj/geo1 opadd box opadd sphere cd /obj Could be done with: hou.cd('/obj/geo1') hou.pwd().createNode('box') hou.pwd().createNode('sphere') hou.cd('/obj')
  • 32. Python for the Hscripter Tips (continued) But you could instead write: n = hou.node('/obj/geo1') n.createNode('box') n.createNode('sphere')
  • 33. Python for the Hscripter Tips (continued) The names in the hou module are more descriptive, making Python scripts using hou much easier to read than Hscript scripts. However, they are consequently more verbose. You can use Python tricks to make it less verbose. Consider a module named 'simplehou': import hou n = hou.node p = hou.parm hou.Node.create = hou.Node.createNode >>> from simplehou import * >>> n('/obj/geo1').create('box')
  • 34. Invoking Python from Houdini There are many places where Houdini will invoke the Python interpreter. Here are nine of them: Houdini's Python shell Use the hou.session module to store and edit one-off functions A couple of Python statements can easily invoke scripts. For example, import a module from disk and call a function in it, or use execfile() to run a script
  • 35. Shelf/Tab Menu The shelf/tab menu A place to easily attach Python code to a button A number of supporting modules used by the shelves can be found in $HFS/houdini/scripts/python Most, if not all, of Houdini's builtin shelf scripts are written in Python, so they provide a wealth of examples
  • 36. HDA Button Callback An HDA Button Callback The PythonModule HDA section. hou.Node.hdaModule and hou.NodeType.hdaModule
  • 37.  
  • 38.  
  • 39. HDA Button Callback Not a good idea to call hou.session functions from button callbacks, since hou.session is intended to store hip-file specific functions. Instead, you need to store the callback script with the HDA.
  • 40. HDA Event Handler An HDA's Event Handler There currently aren't Python versions of HDA event handler scripts (OnCreated, OnUpdated, etc.) However, you can use the python hscript command to call functions in the PythonModule section def onCreated(node): print “created HDA”, node.path() python -c “hou.node('$arg1').hdaModule().onCreated(hou.node('$arg1'))”
  • 41. Parameters You can write Python expressions in a node's parameter Each node's expression language is set to either Hscript expressions or Python When you type an expressionless parameter, the expression will be in the node's language
  • 42. Parameters Once a parameter has an expression, that expression's language will not change when you change the node's language If the expression's language is different from the node's language, it will appear in red You can change a parameter's language by right-clicking on it and choosing “Expression -> Change Language to ...”
  • 43. Parameters “ from hou import *” is implicitly run before evaluating a Python expression You don't need the 'hou.' prefix This way, common expressions like cubic(), linear(), ch(), etc. work in both Python and Hscript expressions You can call functions in the hou.session module, and “from hou.session import *” is also implicitly run For example, you could write a hou.session function, say foo(), that returns a tuple of 3 values, and put 'foo()[0]', 'foo()[1]', 'foo()[2]' into each of tx, ty, and tz
  • 44. Parameters Parameters in nodes inside an HDA can call functions in the PythonModule section For example, if foo() is defined in PythonModule, /obj/myhda1 is the HDA, and you could write the following Python expression in /obj/myhda1/geo1/tx: node('..').hdaModule().foo()
  • 45. Parameters You can also put the bodies of Python functions inside expressions Simple rule to remember: single lines are expressions and multiple lines are function bodies
  • 46. When Houdini Starts Up When Houdini Starts Up There are Python versions of 123.cmd and 456.cmd: 123.py and 456.py Houdini will look through $HOUDINI_SCRIPT_PATH, looking for the first directory with 123.* or 456.*. Once it finds a script, it will call it and stop looking. 123.{cmd,py} is called when Houdini starts up without a hip file 456.{cmd,py} is called every time a file is loaded or the session is cleared
  • 47. When Houdini Starts Up There is a new file that's called only once, every time Houdini starts: pythonrc.py Houdini will search $HOUDINI_SCRIPT_PATH for houdini/pythonrc.py and run each of the files found Use pythonrc.py to store functions, aliases, etc.
  • 48. Python-Based SOPs Python-based SOPs “ File -> New Operator Type”, choose “Python type” and “Geometry Operator”, and write code in the “Cook” tab
  • 49.  
  • 50. The Help Browser In addition to the RunHCommand(), there are new RunPythonStatements() and RunPythonExpression() javascript functions <script src=”resource:///res/RunHCommand.js” /> <script> alert(RunPythonExpression(“hou.node('/obj').children()”)); </script>
  • 51. From Another Process From Another Process Send XML to openport socket The RunPythonStatements() javascript function is just sending XML to Houdini's openport socket <xml version=”1.0”><python_statements>print “hello”</python_statements> Use houxmlrpc module From Houdini: houxmlrpc.run(port=8888) From the other process: s = houxmlrpc.ServerProxy('http://localhost:8888') hou = s.hou # access the hou module like you normally would
  • 52. Loading Python Scripts from External Files You can use the standard Python execfile function to run a script on disk The hou.findFile() function can help locate a file in $HOUDINI_PATH It's usually better to import a module than to use execfile, though Houdini will go through all directories in $HOUDINI_SCRIPT_PATH , adding python subdirectories to sys.path. For example, Houdini will look in the $HOME/houdini9.0/scripts/python directory when importing modules
  • 53. Accessing Houdini from a Regular Python Shell You can import the hou module into a standard Python 2.5 shell You'll need to add $HFS/houdini/scripts/python to sys.path When hou is imported, it will initialize and create a Houdini session It's very easy to bring Houdini into an existing Python-based pipeline
  • 54. hython hython is a program that ships with Houdini It's just a regular Python shell, but it automatically imports the hou module You can pass hip files on the hython command line before any .py files It also supports tab completion
  • 55. Example: Loading a hip File and Running a ROP #!/usr/bin/python2.5 import sys sys.path.append(os.environ['HFS']+“/houdini/scripts/python”) import hou try: hou.hipFile.load(sys.argv[1]) except hou.LoadWarning, e: print e except hou.OperationFailed: sys.exit(“Could not load “ + sys.argv[1]) rop = hou.node(“/out/OUT”) rop.render()
  • 56. Example: A Simple Python SOP geo = hou.pwd().geometry() bbox = geo.boundingBox() cd_attrib = geo.addAttrib(hou.attribType.Point, 'Cd', default_value=(1.0, 1.0, 1.0)) for point in geo.points(): dist_vec = hou.Vector3(point.position()) - bbox.center() color = [0.5 + dist_vec[i] / bbox.sizevec()[i] for i in range(3)] point.setAttribValue(cd_attrib, color)
  • 57. Example: Node Layout import pygraphviz def layout(network, scale=1.0): graph = pygraphviz.AGraph(directed=True) graph.graph_attr['ordering'] = 'in' # Add graph nodes for each Houdini node in the network. gv_nodes, nodes = {}, {} for node in network.children(): graph.add_node(node.name()) gv_node = graph.nodes()[-1] gv_node.attr['width'] = str(0.5 + len(node.name()) * 0.05) gv_node.attr['height'] = '0.12' gv_node.attr['fixedsize'] = 'true' gv_nodes[node] = gv_node nodes[gv_node] = node
  • 58. Example: Node Layout (continued) # Now add graph edges for all the wires in the network for node in network.children(): for input_node in node.inputs(): graph.add_edge(gv_nodes[input_node], gv_nodes[node]) edge = graph.edges()[-1] edge.attr['headport'] = 'n' edge.attr['tailport'] = 's' # Layout the graph and set the Houdini node positions graph.layout(prog='neato') scale *= 0.04 for gv_node in graph.nodes(): nodes[gv_node].setPosition( [float(v) * scale for v in gv_node.attr['pos'].split(',')])
  • 59. Example: Node Layout (continued) # Call layout on the current network in the network editor network_editor = hou.ui.curDesktop().paneTabOfType( hou.paneTabType.NetworkEditor) layout(network_editor.pwd())