Maya UI, python and drop callbacks

Has anyone managed to get a drop callback working in python? I’m running 2009. I can get it working with mel, but I already have a tool written in python and it would be a pain to port it to mel just to get drag and drop functionality.

##-----------------------------------------------------
from maya.cmds import *
def onDrag( *a ):
print ‘dragging’, a

def onDrop( *a ):
print ‘DROPPIN!’, a

w=window()
f=formLayout()
b1=button( l=‘apples’, p=f, w=60, h=20, dgc=onDrag, dpc=onDrop )
b2=button( l=‘bananas’, p=f, w=60, h=20, dgc=onDrag, dpc=onDrop )

formLayout( f, e=True, ap=((b1, ‘top’, 10, 0), (b1, ‘left’, 10, 0),
(b2, ‘top’, 50, 0), (b2, ‘left’, 30, 0)) )
showWindow(w)
##-----------------------------------------------------

Thats a pretty minimal repro - I’m not quite sure where I could be
going wrong. The drag callback works fine, but nothing on the drop.
Same deal if I use a “string” callback.

Incidentally this DOES work in 2011 - I’m running 2009. Am I just screwed?
-h.

dragging (u’window1|formLayout47|button7’, 33, 4, 0)
DROPPIN! (u’window1|formLayout47|button7’, u’window1|formLayout47|button7’, [], 1859, 538, 2)

This is what I got back ONLY if I use the middle mouse button to click and drag.

are you on 2009? This is the behaviour you should expect - but I don’t get this running 2009. It works fine in 2011 (not sure about 2010 - but seriously, who used that version anyway).

thanks.

Have the same issue here on 2009!

There are a lot of dead UI hooks for python in 2009 that were re-implemented properly in 2011. My favorite is that half of the tree control accepts python callables and the other half doesn’t.

A similar problem has actually been posted here before.

I had a similar problem, and in purely academic interests, I’ll describe the solution I used here:

I ended up making a python module that would publish “anonymous” MEL procs that accepted the proper arguments and paired them with an ID, then called back into the python module from MEL with that ID to distribute the callback.

Major players:
“Client”: the code that wanted to have the UI callback
“Proxy”: the singleton that brokered the callback

General flow:
[ul]
[li]Client asks Proxy for something to give the UI callback and provides the callable it wants to be called. That looks like:
[/li]

melCall = Proxy.GetCallback( callable, [argType, argType, argType], retType )

[li]Proxy creates a UUID and publishes an “anonymous” MEL proc that loops back to the Proxy module. If you want to bullet-proof against the one-in-a-zillion UUID collision here, you do a whatIs on the potential proc first to make sure you’re clear.
[/li]

maya.mel.eval( 'global proc [retType] [namespace]_[UUID]_proc( [argType] $arg1, [argType] $arg2, [argType] $arg3 ){ return python( "Proxy.Call([UUID], " + $arg1 + "," + $arg2 + "," + $arg3 + ")" );  }' )

stringArrayToString is cleaner in practice, but + is simpler for the example.
[li]Proxy stores a dictionary with the UUID as the key, and a weakref to the callable then returns the generated proc name.
[/li][li]Client uses the MEL proc as the argument for the ui callback.
[/li]

b1=button( l='apples', p=f, w=60, h=20, dgc=melCall )

[li]When Maya hits the callback it goes to the “anonymous” proc, which calls the Proxy.
[/li][li]Proxy uses the UUID to grab the right callable from it’s internal dictionary, and pushes it to the proper callback if it’s still valid, otherwise it nukes the reference to keep the dictionary tidy.
[/li][/ul]

Scariest looking thing I’ve ever written, and it’s very important that you’re using the right singleton paradigm or else you’ll start missing callbacks. Luckily, by doing this, Proxy is the only piece of code that has to obey those very specific requirements. The exercise of actually constructing that horrific MEL procedure generating function is best left to the reader.

It’s terrible, but it works for every case of the UI being busted in python but not MEL in 2009 that I could find. Many of these were fixed in 2011, and 2011 even introduced a feature by which it is easier to register MEL functions for python calls such as this which mostly negates the need for the monstrosity I wrote.

A simpler solution, if you don’t need it to be general case, is to just publish a few specific MEL functions manually. Or if you’re going to be upgrading anytime soon, I’d just put this down as another tickmark for “should upgrade.”

I’ve been removed from this piece of code for a long time, and unfortunately cannot provide it verbatim, but if you have any other questions, I can do my best to answer them, and hopefully you strike upon an even better solution that makes me look like a fool!

… and I am, in fact, a fool. I misread your problem. You’re not getting the callback at all when you register it, even if you register it as a string =. Although I guess if it was trying to use the string as MEL instead of somehow related to python it may fail similarly. However, I would expect it to complain loudly in some unreasonable fashion in that case. (I’m sorry that I can’t verify futher on my end, I don’t have a copy of Maya anymore, so I parse a lot of these things blindly) If the method described above doesn’t work, and registering from MEL does… you could still use the method above to get back into where you want to be in python from MEL. It’s just twice as ugly. I certainly hope that isn’t the case.

Again, good luck, and I apologize if my original response turns out to be less than useful.

Hey there Lithium! Thanks for your response - thats actually very useful. I might be able to go down that route and instead of instantiating the widget from python, construct a mel string and it from eval - then do as you suggest with the callbacks and again, hook them up via evals. I did experiment with this a little bit, but didn’t fully implement the solution. The little experiment did work, but was inconsistent which is why I didn’t take the implementation all the way.

Anyway thanks, these ideas are helpful.

Yay!

So this is easier than I thought it would be - but there is a weird what seems to be bug with defining callbacks. You CAN define drag/drop callbacks in python just make sure the drag callback returns a list with at least one string.

Yes, you read that right. the following drag callback will stop the drop callback from being called:

def drag( *a ): return []

while this drag callback will work fine:

def drag( *a ): return ['']

Super dumb/frustrating, but at least its possible - especially since I don’t have to resort to dealing with mel.

Nice, thanks for posting the real solution. It figures it had to be something simple like that.

no worries! Hopefully it’ll save someone else the half day worth of pain I went through. :slight_smile: Although I imagine most people have moved beyond version 2009…