Triggering a python script from a houdini engine interface button.

   5689   5   2
User Avatar
Member
21 posts
Joined: April 2006
Offline
I wanted to ask the community if anyone has been doing this successfully.

My experience lately has been baffling and frustrating. The basic idea is to select some geo in Maya, then the engine asset, press a button that runs a python script on disk. The script works flawlessly when run from command line. When I activate the button, the behavior is quite different.

First of all, I had to abandon using the cmds library in favor of the maya.mel library. As a result, it forced me to add a few extra lines of code each instance, as I needed to build a mel string, then run it with mel.eval( melString ). This works for my basic tests, like a print statement or a simple setAttr. However, when I started to get tricky with for loops, it all fell apart. The strangest thing is when I put print statements to test things, it looked like everything is returned in reverse order as expected. For example

list =
for num in list:
print num

this isn't the exact code I am using, but the idea is the same. The result I get from executing this via Houdini Engine button is:

5
4
3
2
1

I have never seen this happen in a loop, where the results are returned in reverse order. Is there some voodoo happening with Engine that would cause this? Any workarounds (code backwards LOL)?

Any help on this topic would be greatly appreciated

Thanks
Josh
User Avatar
Member
818 posts
Joined: Sept. 2013
Offline
Printing messages in the reverse order is actually a bug on the Maya side. The issue is that Maya catches Python's print statements, so that they can be printed in the script editor. When the print statements happen on the main thread, the prints happen in the correct order. However, if the print is from a separate thread, the prints is printed in the reversed order. The following script can be used to reproduce this issue on the Maya side:

def foo():
    for i in range(10):
        print i
# call from main thread
foo()
# call from separate thread
import threading
mythread = threading.Thread(target=foo)
mythread.start()

I have already submitted a bug to Autodesk. I'm not sure if it'll get fixed though.

Instead of calling separate prints, you could try appending the messages to a string variable, and print that variable once in the end of the execution. This would at least ensure your messages in that one string variable would be in the correct order.
Andrew / アンドリュー
User Avatar
Member
21 posts
Joined: April 2006
Offline
Thanks for the reply, Andrew.

I guess that explains it a little, and I remember seeing another post on here about threads and how they are problematic with Maya and python.

The real issue I am having isn't even these print statements, which were really just for debugging. I am performing getAttr and setAttr commands in the for loops, but it doesn't seemt to respect the contents and manipulation of those contents while in (or possibly once outside) the loop. The print statements were an attempt to troubleshoot what happens to the data while inside the loop.

Again, the script I am calling from Engine in Maya works perfectly when run from command line. The loops just don't seem to act properly when launched from a button on the Engine UI in Maya.
User Avatar
Member
818 posts
Joined: Sept. 2013
Offline
Can you explain what you mean by getAttr and setAttr not respecting the contents and manipulation? It might help if you can provide a simple example of what you're trying to do.
Andrew / アンドリュー
User Avatar
Member
21 posts
Joined: April 2006
Offline
Sure thing Andrew. I have actually added a relevant portion of my script.

import maya.cmds as cmds
import maya.mel as mel
import time
import os

# cmds lines commented out due to incompatability. Using mel.eval instead

def fromSelectedGuides():
#selection = cmds.ls( selection=True )
selection = mel.eval( 'ls -sl' )
guides_sel = selection[ 0:len( selection ) - 1 ]
fursim = selection[ len( selection ) - 1 ]

# make sets for no duplicates
base = set()
guides = set()
#print procs

# get the relevant guide files from each selected fur groom
for patch in guides_sel:
ref_mesh = cmds.getAttr( patch + '.parmrefCache' )
#command1 = ( "getAttr " + patch + "Locator.parmrefCache" )
#print command1
#pcmd = ( "print( \"" + command1 + "\\n\" )" )
#ref_mesh = mel.eval( command1 )
base.add( ref_mesh )
guide_file = cmds.getAttr( patch + '.parmguideCache' )
#command2 = ( "getAttr " + patch + "Locator.parmguideCache" )
#print command2
#pcmd = ( "print( \"" + command2 + "\\n\" )" )
#guide_file = mel.eval( command2 )
guides.add( guide_file )

# clear the fields current contents
c = 1
while c < 21:
command3 = ( "setAttr -type \"string\" " + fursim + ".houdiniAssetParm.houdiniAssetParm_stdswitcher3_2_1__folder.houdiniAssetParm_groom_files_" + str( c ) + " \"\"" )
#cmds.setAttr( fursim + ".houdiniAssetParm.houdiniAssetParm_stdswitcher3_2_1__folder.houdiniAssetParm_groom_files_" + str( c ), "", type='string' )
mel.eval( command3 )
c += 1

# fill in the fursim engine with relevant paths
i = 1
base_list = list( base )
#if len( guides_sel ) > 0:
#if len( base_list ) > 1:
#print 'There are inconsistent base meshes found on the procedurals.'
#else:
#print ( "Ref Mesh: " + base_list[ 0 ] )

guide_list = list( guides )
#print guide_list
#i = 0
for guide in guide_list:
#i += 1
print ( "Guide Curve: " + guide )
command4 = ( "setAttr -type \"string\" " + fursim + ".houdiniAssetParm.houdiniAssetParm_stdswitcher3_2_1__folder.houdiniAssetParm_groom_files_" + str( i ) + " \"" + guide + "\"" )
time.sleep( .2 )
print command4
#cmds.setAttr( fursim + ".houdiniAssetParm.houdiniAssetParm_stdswitcher3_2_1__folder.houdiniAssetParm_groom_files_" + str( i ), guide, type='string' )
mel.eval( command4 )
i += 1

command5 = ( "setAttr " + fursim + ".houdiniAssetParm_num_guide_files " + str(i-1) )
#cmds.setAttr( fursim + ".houdiniAssetParm_num_guide_files", i-1 )
mel.eval( command5 )

There are several lines commented out as I was still in the process of testing things.

So what I am trying to do is get values from selected objects and apply them to fields in the engine asset when pressing a button.

Another approach I tried was to re-write this script in mel, however, I couldn't get it to work. I had problems triggering the script, it simply wouldn't run using the callback script command set in asset type properties in Houdini. The mel script works as expected when run from the MEL command line in Maya.

strange
User Avatar
Member
818 posts
Joined: Sept. 2013
Offline
Ah, I see what the issue is.
The callback script is called when the Maya node cooks the Houdini asset.
After the callback script is executed, the value is indeed changed on the Maya side, but it's changed on the Maya side only.
After the Houdini asset is cooked, the Maya node will actually get the asset parameters from the Houdini asset, and set it onto the Maya node. This means any values that were originally on the Maya node is overwritten by the values from Houdini after the cook. This is the correct behavior, but this is also why you are losing the setAttr changes.

Instead of using setAttr, try setting the parameter on the Houdini asset node directly. For example:
node.parm(“test_float”).set(1.0)

However, a catch is that the Maya AE doesn't update immediately, even though the value on the Maya side changes. You could click the “Load Attributes” at the bottom of the AE to force a UI refresh though.

You could also get the asset parameters from the asset node directly this way, instead of using getAttr.

Take a look at the attached asset. The button on the asset simply sets the asset parameter to the frame number.

Attachments:
test_callback.hda (3.8 KB)

Andrew / アンドリュー
  • Quick Links