Houdini UI Customization

From bernie's
Jump to: navigation, search

Various things I do to my Houdini

Shelf Tools

I find it odd that Sidefx makes you go through shelf items for various functions but that's the way to cookie crumbles.

Toggle Viewport Background Color

Bound it to F6

import sys
import toolutils

bg = None

try:
    # cycle next bg
    if kwargs['ctrlclick']: raise
    bgs = hou.session.bg[:]
    bgs = bgs[1:]+bgs[:1]
    if kwargs['shiftclick']: bgs = ['bw', 'light', 'wb']
    bg = bgs[0]
    hou.session.bg = bgs
except:
    # set up default bg vars
    hou.session.bg = ['wb', 'bw', 'light']
    bg = hou.session.bg[0]

bgs = { 'wb':'dark', 'bw':'grey', 'light':'light' }

hou.hscript("viewdisplay -B %s *" % bg)
hou.ui.setStatusMessage("Cycled background to %s" % bgs[bg].upper() )

Toggle Auto / Manual scene update

Bound to F11, I use it every day.

import hou
mode = hou.updateModeSetting().name()
if mode == 'AutoUpdate':
    hou.setUpdateMode(hou.updateMode.Manual)
if mode == 'Manual':
    hou.setUpdateMode(hou.updateMode.AutoUpdate)

Copy auto Paste-Merge

Copy from https://berniebernie.fr/wiki/Houdini_Python#Paste_clipboard_nodes_to_object_merges

Allow to copy nodes elsewhere with an object merge. Bound it to alt-v (so ctrl-c, alt-v in another place)

M7N0Stq.gif

# this snippet will paste nodes in clipboard to object merges
# use it with a shortcut (I overrode 'alt-v' in network pane context)

import hou

network = hou.ui.curDesktop().paneTabUnderCursor()
networkpath = network.pwd().path()
pos = network.cursorPosition()

clipboard = hou.ui.getTextFromClipboard()

n = 0

if clipboard:
    list = clipboard.split()
    for item in list:
        if hou.node(item) != None:
            merge = hou.node(networkpath).createNode('object_merge','merge_'+item.split('/')[-1])
            merge.parm('objpath1').set(str(item))
            merge.setPosition(pos)
            merge.move([n*2,0])
            if n == 0:
                merge.setSelected(True,True)
            else:
                merge.setSelected(True,False)
            n = n + 1

Other UI

Previews

Detach parameter window à la maya

3txLohO.gif

Auto add frame offset parm

ZXNrwvC.gif

Userdocs XMLs

This is for right click context menus. Use the hscript 'menurefresh' when developping so as not to relaunch H each time (like a reload(module) in python).

PARMmenu.xml

<?xml version="1.0" encoding="UTF-8"?>
<menuDocument>
	<menu>
		<subMenu id="bernie_rclick">
		<label>Bernie's</label>
			<modifyItem><insertAfter>motion_effects_menu</insertAfter></modifyItem>
			<scriptItem id="bernie_set_frameoffset_parm">
				<label>Add a frame offset parm to $F#</label>
				<context>
					<expression>
						import bernie_tools
						return bernie_tools.validate_sequence_parm(kwargs)
					</expression>
				</context>
				<scriptCode><![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.add_sequence_offset_spareparm(kwargs)]]></scriptCode>
			</scriptItem>
			<scriptItem id="bernie_set_frameoffset_parm">
				<label>Browse to...</label>
				<context>
					<expression>
						import bernie_tools
						reload(bernie_tools)
						return bernie_tools.validate_uri(kwargs)
					</expression>
				</context>
				<scriptCode><![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.browse_to(kwargs)]]></scriptCode>
			</scriptItem>
			<scriptItem id="bernie_open_parm_spreadsheet">
				<label>Open/Add in Parameter Spreadsheet</label>
				<scriptCode><![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.open_parm_spreadsheet(kwargs)]]></scriptCode>
			</scriptItem>
		</subMenu>
	</menu>
</menuDocument>

ParmGearMenu.xml

<?xml version="1.0" encoding="UTF-8"?>

<!--
 (long sidefx text)
-->

<menuDocument>
    <!-- menuDocument can only contain 1 menu element, whose id is 
         implicitly "root_menu"
      -->
    <menu>
        <scriptItem id="detach_parameter_window">
            <label>Detach Parameter Window...</label>
            <scriptCode><![CDATA[import bernie_tools;reload(bernie_tools);bernie_tools.detach_parameter_window(kwargs)]]></scriptCode>
        </scriptItem>
    </menu>
</menuDocument>

bernie_tools.py

In my userdocs\houdini18.5\python2.7libs folder

import hou
import traceback
import re
import os

def validate_sequence_parm(kwargs):
	'''Checks if the first parm contains a value with $F'''
	returnvalue = False
	try:
		returnvalue = '$F' in kwargs['parms'][0].rawValue() 
	except:
		print("ERROR: %s" % traceback.format_exc())
	return returnvalue
	
def validate_uri(kwargs):
	'''Checks if the given argument, or its parent or grandparent is either a file or folder'''
	parm = kwargs['parms'][0].eval()
	parm = str(parm)
	returnvalue = False
	if os.path.isdir(parm) or os.path.isdir(os.path.dirname(parm)):
		returnvalue = True
	return returnvalue

def browse_to(kwargs):
	'''opens a browser to this given path (if a file), or its parent if it doesn't exist'''
	path = os.path.dirname(kwargs['parms'][0].eval())
	dir = ''
	if os.path.isdir(path):
		dir = path
	elif os.path.isdir(os.path.dirname(path)):
		dir = os.path.dirname(path)
	else:
		print('No directory found')
	if dir != '':
		os.startfile(os.path.realpath(dir))

def add_sequence_offset_spareparm(kwargs):
	'''Adds an int spare parm below a parm that has a file sequence expression of type '$F#' using hscript padzero'''
	
	parm = kwargs['parms'][0]
	parmval = parm.rawValue()
	#check if there is a $F(plus digit)
	matchObj = re.match( r'^(.*)\$F(\d*)(.*)', parmval, re.M|re.I)

	if matchObj:
		n = parm.node()
		parmGrp = n.parmTemplateGroup()
		existing_parm = parmGrp.find(parm.name())
		#create an offset parm with a similar name. No error checking!
		plabel = existing_parm.label() + " Offset"
		pname = parm.name()+"_offset"
		offsetParmTemplate = hou.IntParmTemplate( pname, plabel, 1, default_expression=(["$F + 0"]))
		parmGrp.insertAfter(existing_parm, offsetParmTemplate)
		n.setParmTemplateGroup(parmGrp)
		#display the expression as it's most like we want to mmb through the offset
		n.parm(pname).showExpression(1)

		#figure out if there is any padding in the original parm using the regex result, otherwise padding at 1 (no padding)
		padding = 1
		if matchObj.groups()[1]:
			padding = matchObj.groups()[1]
		expression = matchObj.groups()[0]+"`padzero("+str(padding)+",ch('" + pname + "'))`"+matchObj.groups()[2]
		parm.set(expression)

def detach_parameter_window(kwargs):
	'''Open a floating parameter pane for a particular node.'''
	node = kwargs['node']
	pane_tab = hou.ui.curDesktop().createFloatingPaneTab(hou.paneTabType.Parm)
	pane_tab.setCurrentNode(node)
	pane_tab.setPin(True)
	return pane_tab

def open_parm_spreadsheet(kwargs):
	'''Opens the parameter spreadsheet with the current node selection and the right-clicked parm, appends the parm if it is already opened'''
	#import pprint
	#pprint.pprint(kwargs)
	selectedParm = kwargs['parms'][0]
	nodepaths = " ".join([node.path() for node in hou.selectedNodes()])
	if not hou.selectedNodes():
		nodepaths = selectedParm.node().path()
	#print(nodepaths)
	ps = hou.ui.findPaneTab('parmsheet')
	if not ps:
		desktop = hou.ui.curDesktop()
		ps = desktop.createFloatingPaneTab(hou.paneTabType.ParmSpreadsheet)
	cmd = 'parmsheet -w 1 -p "'+selectedParm.name()+'" -o "'+nodepaths+'" '+ps.name()
	hou.hscript(cmd)