Jump to content
Shalinar

Python Node Placement

Recommended Posts

Hi everyone,

I'm trying to have my Python Module in my HDA place another node right below my asset on creation. So my OnCreation script runs my python module from disk, which has the following code:

pos = first_node.position()
second_pos = (pos[0], pos[1] - 2)
second_node.setPosition(second_pos)

This does nothing. The second node is always created near (0, 0) in the network editor. But if i run those lines in the Python shell in Houdini, it works just fine. So that tells me that the OnCreated scripts don't wait for the HDA to actually be placed into the scene before running, despite what the docs say (a problem I've run into before).

My solution to this was to use hdefereval to wait to run the code until the event loop was idle. This shows promises of working, but I'm running into issues now. The code I'm trying is:

pos = first_node.position()
second_pos = (pos[0], pos[1] - 2)

hdefereval.executeDeferred("second_node.setPosition(second_pos)")

but the problem here is that when it goes to execute the code, the variables no longer exist, and the code fails because "Name 'second_node' is not defined". But I use a custom 123.py script every day that has the exact line

hdefereval.executeDeferred("hou.hscript(code)")

where "code" is a pre-defined variable, and hdefereval has no problem using that variable when it goes to execute. So I'm not really sure what the problem is here.

What is the best way to move a node using Python? (and if you know how to get my hdefereval call to work, that'd be even better!)

Thanks,

 

~Chris

Share this post


Link to post
Share on other sites

I think this stuff is done with tools. Check how the Sky Light tool was implemented. You don't need external tool to be installed with HDA and added to the user's shelf tab. Instead, go to the "Tool" tab (next to the "Scripts" where you are editing OnCreated) and here on "Script" tab you will see built-in creation tool. For example:

import objecttoolutils

objecttoolutils.genericTool(kwargs, '$HDA_NAME')

The last line returns an actual node with position already set:

node = objecttoolutils.genericTool(kwargs, '$HDA_NAME')
print(node.position())

Here is a test script, without using position explicitly:

import objecttoolutils

node = objecttoolutils.genericTool(kwargs, '$HDA_NAME')
null = node.createOutputNode('null')

You can add this to make the output node selected and active, if you want:

null.setCurrent(True, clear_all_selected=True)
Edited by f1480187

Share this post


Link to post
Share on other sites

Thanks F1! Experimenting with that now. I always wondered how the Tools tab was used. The documentation is pretty basic and doesn't address a lot of the advanced options for power users. Do you have a reference for this tab? My google-fu is weak here, since searching for the "tools" tab doesn't help much considering a lot of people use "tool" interchangeably with "node/asset/hda/otl".

Share this post


Link to post
Share on other sites

I guess, the tool is a script invoked right after click on tool's icon on shelf or tab-menu. There is bunch of useful data in local kwargs dictionary, and *toolutils modules creating stuff using it.

Tool script precedes OnCreated event, which is called during node creation inside tool's script. You can add new tools to the list, they will be available for adding to the shelves. Like usual .xml ones.

All *toolutils modules available in sources in $HH/python2.7libs/. They documented, you can read docstrings to get the basic idea. Variables used inside tool panel are not documented. Text search in $HH does not locate any places where they were defined, so I collected all different occurrences and printed their contents from the tool body:
 

Custom Python OBJ test.

name     test_op
label    Test OP
network  Object

Variable              Prints as:
------------------------------------
$HDA_TABLE            Object
$HDA_NAME             test_op
$HDA_TABLE_AND_NAME   Object/test_op
$HDA_LABEL            Test OP
$HDA_ICON             MISC_python
$HDA_DEFAULT_TOOL     object_test_op

They are quite easy to understand, but I'm not sure if there is more of them.

Edited by f1480187

Share this post


Link to post
Share on other sites

Thank you, this worked perfectly. Time to go explore the *toolutils modules etc...!

Share this post


Link to post
Share on other sites

Hi!
I have the same question.
How can I get HDA position or position where it will be created in OnCreated Event?
kwargs['node'].position() returns [0, 0] as expected.

Share this post


Link to post
Share on other sites
On 4/27/2017 at 10:00 AM, elecstorm said:

Hi!
I have the same question.
How can I get HDA position or position where it will be created in OnCreated Event?
kwargs['node'].position() returns [0, 0] as expected.

I know this is more than a year later, but if anyone is still wondering how to do this, I don't believe that it is directly possible to get a node's position OnCreated. However, you can get your cursor's position at the time of creation, which is usually close enough.

# python code inside an OnCreated script

# get current network pane
net_editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)

# create your node
myNode = hou.node(".").createNode("null", "my_node", 0, 0)

# get cursor position
position = net_editor.cursorPosition()

# set position of node to cursor position
myNode.setPosition(position)

I use this to create nodes and connect outputs as desired. (Note that the position vector can be modified to set the node location to a position relative to your cursor location instead of directly on it.)

import numpy as np

# reference the created node
createdNode = kwargs["node"]

# get current network pane
net_editor = hou.ui.paneTabOfType(hou.paneTabType.NetworkEditor)

# create a child node of the created node

# can just use createOutputNode if you only have 1 output and 1 input
#childNode = createdNode.createOutputNode("null", "child_node")
childNode = hou.node(".").createNode("null", "child_node", 0, 0)

# for nodes with multiple inputs/outputs use setInputs
# assign the 2nd output of the created node to the first input of the childNode
childNode.setInput(0, createdNode, 1)

# set position equal to cursor position minus a small amount
# offset position down and to the right of cursor position
position = np.subtract(net_editor.cursorPosition(), [-0.5,1])

# set childNode position
childNode.setPosition(position)

 

Edited by bhouse
  • Like 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×