Peter
Peter

Reputation: 3495

Using callbacks to run function using the current values in a class

I struggled to think of a good title so I'll just explain it here. I'm using Python in Maya, which has some event callback options, so you can do something like on save: run function. I have a user interface class, which I'd like it to update when certain events are triggered, which I can do, but I'm looking for a cleaner way of doing it.

Here is a basic example similar to what I have:

class test(object):
    def __init__(self, x=0):
        self.x = x
    def run_this(self):
        print self.x
    def display(self):
        print 'load user interface'

#Here's the main stuff that used to be just 'test().display()'
try:
    callbacks = [callback1, callback2, ...]
except NameError:
    pass
else:
    for i in callbacks:
        try:
            OpenMaya.MEventMessage.removeCallback(i)
        except RuntimeError:
            pass

ui = test(5)
callback1 = OpenMaya.MEventMessage.addEventCallback('SomeEvent', ui.run_this)
callback2 = OpenMaya.MEventMessage.addEventCallback('SomeOtherEvent', ui.run_this)
callback3 = ......
ui.display()

The callback persists until Maya is restarted, but you can remove it using removeCallback if you pass it the value that is returned from addEventCallback. The way I have currently is just check if the variable is set before you set it, which is a lot more messy than the previous one line of test().display()

Would there be a way that I can neatly do it in the function? Something where it'd delete the old one if I ran the test class again or something similar?

Upvotes: 2

Views: 2761

Answers (1)

theodox
theodox

Reputation: 12218

There are two ways you might want to try this.

You can an have a persistent object which represents your callback manager, and allow it to hook and unhook itself.

import maya.api.OpenMaya as om
import maya.cmds as cmds
om.MEventMessage.getEventNames()

class CallbackHandler(object):

    def __init__(self, cb, fn):
        self.callback = cb
        self.function  = fn
        self.id = None

    def install(self):
        if self.id:
            print "callback is currently installed"
            return False
        self.id = om.MEventMessage.addEventCallback(self.callback, self.function)
        return True

    def uninstall(self):
        if self.id:
            om.MEventMessage.removeCallback(self.id)
            self.id = None
            return True
        else:
            print "callback not currently installed"
            return False

    def __del__(self):
        self.uninstall()

def test_fn(arg):
    print "callback fired 2", arg

cb = CallbackHandler('NameChanged', test_fn)
cb.install()
# callback is active
cb.uninstall()
# callback not active
cb.install()
# callback on again
del(cb) # or cb = None
# callback gone again

In this version you'd store the CallbackHandlers you create for as long as you want the callback to persist and then manually uninstall them or let them fall out of scope when you don't need them any more.

Another option would be to create your own object to represent the callbacks and then add or remove any functions you want it to trigger in your own code. This keeps the management entirely on your side instead of relying on the api, which could be good or bad depending on your needs. You'd have an Event() class which was callable (using __call__() and it would have a list of functions to fire then its' __call__() was invoked by Maya. There's an example of the kind of event handler object you'd want here

Upvotes: 1

Related Questions