API Connect Node Attribute Help

1,874 views
Skip to first unread message

NPuetz

unread,
Apr 7, 2008, 1:42:15 PM4/7/08
to python_inside_maya
Hello, I'm new to the Maya API and im stuck trying to figure out how
to connect time1.outTime into one of my custom nodes attributes. Im
currently using MFnDependencyNode's findPlug function to make a
"pointer" to that nodes Attribute (.input) and than evaluating MEL's
connectAttr to make the connection. Im doin something wrong and i
can't quite figure it out. This piece of code is currently in my
initializer function where all my attributes are being
created...should it not be outside this function? Something
Syntactical??? Should I approach this differently??? Any help or
advice would be much appreciated!! Thanks in advance!!

----------------------------------------------------------------------------------
class nmpSineWave(OpenMayaMPx.MPxNode):
# establish INPUT and OUTPUT var's
INPUT = OpenMaya.MObject()
OUTPUT = OpenMaya.MObject()

def __init__(self):
OpenMayaMPx.MPxNode.__init__(self)

def compute(self, plug, dataBlock):
blah blah blah.....


def nodeInit():
# Create Input Attribute
floatAttrInput = OpenMaya.MFnNumericAttribute()
nmpSineWave.INPUT =
floatAttrInput.create("input","in",OpenMaya.MFnNumericData.kFloat,0.0)
# Create Output Attribute
floatAttrOutput = OpenMaya.MFnNumericAttribute()
nmpSineWave.OUTPUT =
floatAttrOutput.create("output","out",OpenMaya.MFnNumericData.kFloat,
0.0)

# Connect time into INPUT
timeConnect = OpenMaya.MFnDependencyNode( nmpSineWave() )
inputPlug = timeConnect.findPlug( "input" )
cmd = "connectAttr 'time1.outTime' %s" % ( inputPlug.name() )
OpenMaya.MGlobal.executeCommand(cmd)

Olivier Renouard

unread,
Apr 7, 2008, 4:46:45 PM4/7/08
to python_in...@googlegroups.com
Hi,

You need to have completed the node initialization before you can manipulate / connect plugs. The nodeInit() is more like a class initialization, you define what attributes will exist on all nodes of that class but there is not yet any actual node created.

I'd say best thing would be to use the postConstructor() method of MPxNode as it will be called each time a node of this type is created. That's how it's done in C++ plugins usually.

Olivier

NPuetz

unread,
Apr 9, 2008, 9:28:23 AM4/9/08
to python_inside_maya
Hey Oliver, Thanks for the fast reply. I created a postConstructor
function within my nodes class after the compute function, here's what
i have so far...


----------------------------------------------------------------------------------------------------------------------------------------------------------------
class nmpSineWave(OpenMayaMPx.MPxNode):
# establish INPUT and OUTPUT var's
INPUT = OpenMaya.MObject()
OUTPUT = OpenMaya.MObject()
AMPLITUDE = OpenMaya.MObject()
FREQUENCY = OpenMaya.MObject()
PHASE = OpenMaya.MObject()
RAND = OpenMaya.MObject()

def __init__(self):
OpenMayaMPx.MPxNode.__init__(self)

def compute(self, plug, dataBlock):
...computeShtuff...

def postConstructor(self):
## get current node instance as MObject
nodeObjHandle = self.thisMObject()
depNodeFn = OpenMaya.MFnDependencyNode()

# set function to operate on node MObject
depNodeFn.setObject(nodeObjHandle)

# test and print if node has a ".input" attribute
(prints TRUE)
hasAttr = depNodeFn.hasAttribute("input")
OpenMaya.MGlobal.displayInfo("node has .input Attr =
%s" % (hasAttr))

# find plug to the nodes input attribute
inputPlug = depNodeFn.findPlug("input", 0)

# print nodes name and name of inputs plug (Node =
"") (input = ".input")
OpenMaya.MGlobal.displayInfo("Node Name = %s" %
(depNodeFn.name()))
OpenMaya.MGlobal.displayInfo("inputPlug = %s" %
(inputPlug.name()))

# create string for maya to connect time into
node.input
cmd = "connectAttr \"time1.outTime\" %s" %
( inputPlug.name() )

# execute mel command
OpenMaya.MGlobal.executeCommand(cmd)

----------------------------------------------------------------------------------------------------------------------------------------------------------------
As of now i get an error saying the destination attribute ".input"
could not be found. I used the displayInfo commands there to then test
if the attribute existed on that node and to print out the nodes name
as well as the name of the plug for the "input" attribute. It prints
out True for having an actual ".input" attribute existing in the node
but prints blank for the node name and only prints ".input" for the
inputPlug instead of "nodeName.input".
I cant quite figure out why the node name isn't getting stored/used by
the inputPlug even though it says that depNodeFn does indeed have an
attribute ".input". Im pulling my hairs out in frustration over this :
( What am i missing/overlooking here?? Thanks, Nicklas Puetz

Ofer Koren

unread,
Apr 9, 2008, 11:37:41 AM4/9/08
to python_in...@googlegroups.com
I also came across that issue a bunch of times, where the node object is not really in a valid state when the postConstructor is called. Seems like a Maya bug...
--


- Ofer
www.mrbroken.com

Olivier Renouard

unread,
Apr 9, 2008, 11:53:47 AM4/9/08
to python_in...@googlegroups.com
Hi,

I should have precised, when a node just has been created, it's MObject is available, but not yet its name

It may seem strange but it comes from the way Maya handles undo, nodes are created using a MDGModifier (or MDagModifier for dag nodes), then the MObject of the node is valid as soon as creation is done, but the node name isn't known until the MDGModifier is executed (because something could cause it's actual name to change, like then a duplicate name in the scene)

Usually you do all scene modifying stuff using MDGModifiers and keep them for undoing. Here you don't need it, as deleting the node will destroy the connection anyway, but you can still use a MDGModifier and execute it right away, using the methods connect ( const MPlug & source, const MPlug & dest ) then doIt() on it.

So it would be :

def postConstructor(self) :
	depNodeFn = OpenMaya.MFnDependencyNode(self.thisMObject())
	inputPlug = depNodeFn.findPlug("input")
	it = OpenMaya.MItDependencyNodes ( OpenMaya.MFn.kTime)
	if not it.isDone() :
		time = it.thisNode()
		depNodeFn.setObject(time)
		outputPlug = depNodeFn.findPlug("outTime")
		dgMod = OpenMaya.MDGModifier()
		dgMod.connect(outputPlug, inputPlug)
		dgMod.doIt()
	else :
		print "No time node in the scene, no connection done"

Not using names at all, it will just take the first "time" node found in the scene and do the connection (under normal circonstance there is one and only one "time" node named "time1" in the scene but just in case)


Here you don't have to keep track of the MDGModifier for a simple connection, because you connect something TO you node. Would it be the other way round then deleting your node would maybe not restore the other attribute (destination) to it's original value. Didn't try but would guess you'd need to build a proper MPxCommand for your node then with the classic undo support (which of course uses the same stuff, MDGModifier or MDagModifier)

Olivier

NPuetz wrote:
Hey Oliver, Thanks for the fast reply. I created a postConstructor
function within my nodes class after the compute function, here's what
i have so far...


----------------------------------------------------------------------------------------------------------------------------------------------------------------
class nmpSineWave(OpenMayaMPx.MPxNode):
	    # establish INPUT and OUTPUT var's
	    INPUT = OpenMaya.MObject()
	    OUTPUT = OpenMaya.MObject()
	    AMPLITUDE = OpenMaya.MObject()
	    FREQUENCY = OpenMaya.MObject()
	    PHASE = OpenMaya.MObject()
	    RAND = OpenMaya.MObject()

	    def __init__(self):
		OpenMayaMPx.MPxNode.__init__(self)

	    def compute(self, plug, dataBlock):
                      ...computeShtuff...

            def postConstructor(self):
                ## get current node instance as MObject
                nodeObjHandle = self.thisMObject()
                depNodeFn = OpenMaya.MFnDependencyNode()

                # set function to operate on node MObject
                depNodeFn.setObject(nodeObjHandle)

                # test and print if node has a ".input" attribute
(prints TRUE)
                hasAttr = depNodeFn.hasAttribute("input")
                OpenMaya.MGlobal.displayInfo("node has .input Attr =
%s" % (hasAttr))

                # find plug to the nodes input attribute
                inputPlug = depNodeFn.findPlug("input", 0)

                # print nodes name and name of inputs plug  (Node =
"") (input = ".input")
                OpenMaya.MGlobal.displayInfo("Node Name = %s" %
(depNodeFn.name()))
                OpenMaya.MGlobal.displayInfo("inputPlug = %s" %
(inputPlug.name()))

                # create string for maya to connect time into
node.input
                cmd = "connectAttr \"time1.outTime\" %s" %
( inputPlug.name() )

                # execute mel command
	        OpenMaya.MGlobal.executeCommand(cmd)

----------------------------------------------------------------------------------------------------------------------------------------------------------------
As of now i get an error saying the destination attribute ".input"
could not be found. I used the displayInfo commands there to then test
if the attribute existed on that node and to print out the nodes name
as well as the name of the plug for the "input" attribute. It prints
out True for having an actual ".input" attribute existing in the node
but prints blank for the node name and only prints ".input" for the
inputPlug instead of "nodeName.input".
I cant quite figure out why the node name isn't getting stored/used by
the inputPlug even though it says that depNodeFn does indeed have an
attribute ".input". Im pulling my hairs out in frustration over this :
( What am i missing/overlooking here?? Thanks, Nicklas Puetz
--~--~---------~--~----~------------~-------~--~----~
Best Regards,
Maya-Python Club.
-~----------~----~----~----~------~----~------~--~---



  

Olivier Renouard

unread,
Apr 9, 2008, 12:00:10 PM4/9/08
to python_in...@googlegroups.com
Hey Ofer

The node object is in a valid state, it's the node name that doesn't yet exist :D

Maya commands create objects with MDGModifers/MDagModifers, then names them as the modifers are executed and the objects are "spawned" in the scene, given the name you'll get is not always the name you asked for (in case of duplicates).

Olivier

Ofer Koren

unread,
Apr 10, 2008, 1:04:57 AM4/10/08
to python_in...@googlegroups.com
I stand corrected then :)

Thanks Olivier!
--


- Ofer
www.mrbroken.com

NPuetz

unread,
Apr 10, 2008, 7:46:12 AM4/10/08
to python_inside_maya
Hey Olivier

Thanks for all the help so far! I ran the code you gave me, but as
before it still gives me an error concerning my plug to "node.input"
for some reason.
--------------------------------------------------------------------------------------------------------------------------------------------------------------
// Error: Connection not made: 'timeToUnitConversion1.output' ->
'.input'. Destination node is not in a dependency graph. //
// Error: (kFailure): Unexpected Internal Failure
# Traceback (most recent call last):
# File "C:/Documents and Settings/NPuetz/Desktop/
nmp_myFirstPlugin.py", line 64, in postConstructor
# dgMod.doIt()
# RuntimeError: (kFailure): Unexpected Internal Failure //
--------------------------------------------------------------------------------------------------------------------------------------------------------------
It seems like it's still trying to connect to ".input" as if
self.thisMObject wasn't storing the nodes MObject, but i know it is
storing it because depNodeFn.hasAttribute("input") returns True. So
even though the inputPlug's name is ".input" is it still referencing
the correct node.attr? Also, when you say that the node name isn't
known until the MDGModifier is executed, is this MDGModifier executed
by maya internally and exactly when?Just a little confused on the
situation, and thanks again for all the help.

On Apr 9, 11:53 am, Olivier Renouard <o.renou...@attitude-studio.com>
wrote:
> Hi,
>
> I should have precised, when a node just has been created, it's MObject
> is available, but not yet its name
>
> It may seem strange but it comes from the way Maya handles undo, nodes
> are created using a MDGModifier (or MDagModifier for dag nodes), then
> the MObject of the node is valid as soon as creation is done, but the
> node name isn't known until the MDGModifier is executed (because
> something could cause it's actual name to change, like then a duplicate
> name in the scene)
>
> Usually you do all scene modifying stuff using MDGModifiers and keep
> them for undoing. Here you don't need it, as deleting the node will
> destroy the connection anyway, but you can still use a MDGModifier and
> execute it right away, using the methods *connect*
> <cid:part1.09030108.01080...@attitude-studio.com> ( const MPlug
> <cid:part2.07030608.00010...@attitude-studio.com>& source, const MPlug
> <cid:part2.07030608.00010...@attitude-studio.com>& dest ) then doIt() on it.

Olivier Renouard

unread,
Apr 10, 2008, 9:58:48 AM4/10/08
to python_in...@googlegroups.com
Hi Nicklas

My bad, I have missed something, I ran the code and it seemed to work allright for me but not in the same context exactly.

The error message seems quite clear, "Destination node is not in a dependency graph", we can't execute the MDGModifier in the postConstructor because of the same reason we can't get its name, its not yet in the graph. We can't execute any more MDGModifiers on that node before the creation one has its doIt() method called I guess, and probably it only happens after we return from postConstructor.

So Ofer you were right, I say I own you a beer... Yet I don't think it's not a bug. The MObject is not "Valid", but if you use MObjectHandle you'll notice it's "Alive". Its been creating and you can query attributes  but not connect them to nodes that are in the scene. You can set internal variables on the node in the postConstructor (attributes of the instance, not in the Maya "attributes" sense) and call member functions (like it's often the way you use to set the MP safe flag with setMPSafe), but not do what I thought you could.

Follows another method, using callbacks that I tested to work. Still I'd love to hear news about the "swig/python detected a memory leak of type 'MCallbackId *', no destructor found." error message every time you de-register a callback. Annoying and can't understand what other destructor i could call here, looks to me like some SWIG class it's lacking it's destructor, MCallbackIdArray maybe? But well, MCallbackId being itself a pointer what kind of memory leak are we talking about here ? 64 bits every time you unload a plugin ? If there is nothing more severe behind I guess I could live with that!

Regards

Olivier


import math, sys
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
kPluginNodeTypeName = "spSineNode"
sineNodeId = OpenMaya.MTypeId(0x8700)
# Node definition
class sineNode(OpenMayaMPx.MPxNode):
    # class variables
    input = OpenMaya.MObject()
    output = OpenMaya.MObject()
    callbackId = None
    def __init__(self):
        OpenMayaMPx.MPxNode.__init__(self)
    def compute(self,plug,dataBlock):
        if ( plug == sineNode.output ):
            dataHandle = dataBlock.inputValue( sineNode.input )
             
            inputFloat = dataHandle.asFloat()
            result = math.sin( inputFloat ) * 10.0
            outputHandle = dataBlock.outputValue( sineNode.output )
            outputHandle.setFloat( result )
            dataBlock.setClean( plug )

    # post constructor, not working
    def postConstructor(self) :
        thisObj = self.thisMObject()
        objHandle = OpenMaya.MObjectHandle(thisObj)
        print "In postConstructor, self object is valid: %s, is alive: %s" % (objHandle.isValid(), objHandle.isAlive())
        depNodeFn = OpenMaya.MFnDependencyNode(thisObj)

        inputPlug = depNodeFn.findPlug("input")
        print "Plugs can be queried: %s" % inputPlug.name()
        try :
            inputPlug.setValue(42)
            print "and set"
        except :
            print "but not set or connected"


# creator
def nodeCreator():
    return OpenMayaMPx.asMPxPtr( sineNode() )
# initializer
def nodeInitializer():
    # input
    nAttr = OpenMaya.MFnNumericAttribute();
    sineNode.input = nAttr.create( "input", "in", OpenMaya.MFnNumericData.kFloat, 0.0 )
    nAttr.setStorable(1)
    # output
    nAttr = OpenMaya.MFnNumericAttribute();
    sineNode.output = nAttr.create( "output", "out", OpenMaya.MFnNumericData.kFloat, 0.0 )
    nAttr.setStorable(1)
    nAttr.setWritable(1)
    # add attributes
    sineNode.addAttribute( sineNode.input )
    sineNode.addAttribute( sineNode.output )
    sineNode.attributeAffects( sineNode.input, sineNode.output )

# node added callback
def sineNodeAdded(node, clientData):
    thisObj = node
    objHandle = OpenMaya.MObjectHandle(thisObj)
    print "In callback, added object is valid: %s, is alive: %s" % (objHandle.isValid(), objHandle.isAlive())
    depNodeFn = OpenMaya.MFnDependencyNode(thisObj)

    inputPlug = depNodeFn.findPlug("input")
    print "Plugs can be queried: %s" % inputPlug.name()

    it = OpenMaya.MItDependencyNodes ( OpenMaya.MFn.kTime)
    if not it.isDone() :
        time = it.thisNode()
        depNodeFn.setObject(time)
        outputPlug = depNodeFn.findPlug("outTime")
        dgMod = OpenMaya.MDGModifier()
        dgMod.connect(outputPlug, inputPlug)
        try :
            dgMod.doIt()
            print "and connected"
        except :
            print "and not connected"

    else :
        print "No time node in the scene, no connection done"   
     
# initialize the script plug-in
def initializePlugin(mobject):
    mplugin = OpenMayaMPx.MFnPlugin(mobject)
    try:
        mplugin.registerNode( kPluginNodeTypeName, sineNodeId, nodeCreator, nodeInitializer )
        try :
            sineNode.callbackId = OpenMaya.MDGMessage.addNodeAddedCallback ( sineNodeAdded, kPluginNodeTypeName )
            print "Added creation callback, callbackId: %s" % sineNode.callbackId
        except:
            print "Failed adding creation callback"
    except:
        sys.stderr.write( "Failed to register node: %s" % kPluginNodeTypeName )
        raise
# uninitialize the script plug-in
def uninitializePlugin(mobject):
    mplugin = OpenMayaMPx.MFnPlugin(mobject)
    try:
        print "callbackId: %s" % sineNode.callbackId
        try :
            OpenMaya.MMessage.removeCallback ( sineNode.callbackId )
            print "Successfully de-registered callback: %s" % sineNode.callbackId
            sineNode.callbackId = None
        except :
            print "Failed de-registering callback, callbackId: %s" % nodeAddedCallbackId
        mplugin.deregisterNode( sineNodeId )
    except:
        sys.stderr.write( "Failed to register node: %s" % kPluginNodeTypeName )
        raise

NPuetz

unread,
Apr 11, 2008, 8:17:32 AM4/11/08
to python_inside_maya
Awesome it works!! Thank you sooo much for clearing this up for me
Olivier!

Olivier Renouard

unread,
Apr 11, 2008, 8:55:59 AM4/11/08
to python_in...@googlegroups.com
You're welcome

You'll notice the node leaves a unitConversion node when it's deleted as
one is created when the callback connects time to it. If you check in
the MNodeMessage class you could set up a callback the same way to break
the connection / delete the unitConversion node in a cleaner way on a
delete.

Just remember when using callback in plugins that they must be added
after the plugin is initialized/loaded (after the node is registered if
it's a node related callback) and removed before plugin is unloaded
(when node is de-registered). Otherwise you'd crash the next time the
callback is called (in C++) or get duplicate calls of the same callback
when pluging is reloaded (in Python)

Olivier

Reply all
Reply to author
Forward
0 new messages