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

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?

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

Related

ExitStack within classes

I would like to understand why using the following snippet leads me to an error:
a) I want to use the following class to create a context manager, as outlined in the link attached below: for me it is very important to keep the "class PrintStop(ExitStack)" form, so please bear in mind when trying to solve this issue, that I already know there are other ways to use ExitStack(), but I am interested in this specific way of using it:
class PrintStop(ExitStack):
def __init__(self, verbose: bool = False):
super().__init__()
self.verbose = verbose
def __enter__(self):
super().__enter__()
if not self.verbose:
sys.stdout = self.enter_context(open(os.devnull, 'w'))
b) when trying to use the class in the more appropriate way, I get the desired effect to stop all the printing within the "with" block, but when trying to print again after that block I get an error:
with PrintStop(verbose=False):
print('this shouldn't be printed') <------ok till here
print('this should be printed again as it is outside the with block) <-----ERROR
c) the error I get is "ValueError: I/O operation on closed file": the reason I guess is the fact that exit method of ExitStack() is not automatically called once we exit the 'with' block, so, how may I change the class to fix this bug?
Here is a quick reference to a similar topic,
Pythonic way to compose context managers for objects owned by a class
ExitStack.__exit__ simply ensures that each context you enter has its __exit__ method called; it does not ensure that any changes you made (like assigning to sys.stdout) inside the corresponding __enter__ is undone.
Also, the purpose of an exit stack is to make it easy to enter contexts that require information not known when the with statement is introduced, or to create a variable number of contexts without having to enumerate them statically.
If you really want to use an exit stack, you'll need something like
class PrintStop(ExitStack):
def __init__(self, verbose: bool = False):
super().__init__()
self.verbose = verbose
def __enter__(self):
rv = super().__enter__()
if not self.verbose:
sys.stdout = self.enter_context(open(os.devnull, 'w'))
return rv
def __exit__(self):
sys.stdout = sys.__stdout__ # Restore the original
return super().__exit__()
Keep in mind that contextlib already provides a context manager for temporarily replacing standard output with a different file, appropriately named redirect_stdout.
with redirect_stdout(open(os.devnull, 'w')):
...
Using this as the basis for PrintStop makes use of composition, rather than inheritance.
from contextlib import redirect_stdout, nullcontext
class PrintStop:
def __init__(self, verbose: bool = False):
super().__init__()
if verbose:
self.cm = redirect_stdout(open(os.devnull, 'w'))
else:
self.cm = nullcontext()
def __enter__(self):
return self.cm.__enter__()
def __exit__(self):
return self.cm.__exit__()

How can I retrieve a signal parameter with New-Style Syntax?

Inside a custom button class I have a signal which emits when is dropped something into it. Here the relevant method:
class CustomButton
linked = QtCore.pyqtSignal()
...
def dropEvent(self, e):
print e.source().objectName()
print self.objectName()
# set the drop action as LinkAction
e.setDropAction(QtCore.Qt.LinkAction)
# tell the QDrag we accepted it
e.accept()
#Emit linked signal with the drag object's name as parameter
self.linked.emit( e.source().objectName() )
return QtGui.QPushButton.dropEvent(self, QtGui.QDropEvent(QtCore.QPoint(e.pos().x(), e.pos().y()), e.possibleActions(), e.mimeData(), e.buttons(), e.modifiers()))
In otherhand, outside the class, in the main application I'm creating a slot, and a way to connect it to the signal.
#The slot (actually is just a python callable)
def on_link(self):
input = self.sender().objectName()[4:]
print input
#I need to print the name of the other object emitted as str parameter in the signal....
#Instance of custom button
custom_button.linked.connect( lambda: on_link( custom_button ) )
At this point I already know that I can get the sender() of the signal, however, I don't know how to get the parameter of self.linked.emit( e.source().objectName() ). I just know that first I have to change first this: linked = QtCore.pyqtSignal(str), but don't know how to write the connection or the slot and retrieve the e.source().objectName() in the emit signal.
The current design of the slot looks very confusing. At first glance, it looks like an instance method, but it is actually just a module-level function with a fake self parameter.
I would suggest something simpler, and more explicit, like this:
class CustomButton(QtGui.QPushButton):
linked = QtCore.pyqtSignal(str, str)
def dropEvent(self, event):
...
self.linked.emit(self.objectName(), event.source().objectName())
return QtGui.QPushButton.dropEvent(self, event)
def on_link(btn_name, src_name):
print btn_name, src_name
custom_button.linked.connect(on_link)
An alternative design would be to send the objects, instead of their names:
linked = QtCore.pyqtSignal(object, object)
...
self.linked.emit(self, event.source())
def on_link(button, source):
print button.objectName(), source.objectName()

Override static member in DaemonRunner

I'm trying to override the DaemonRunner in the python standard daemon process library (found here https://pypi.python.org/pypi/python-daemon/)
The DaemonRunner responds to command line arguments for start, stop, and restart, but I want to add a fourth option for status.
The class I want to override looks something like this:
class DaemonRunner(object):
def _start(self):
...etc
action_funcs = {'start': _start}
I've tried to override it like this:
class StatusDaemonRunner(DaemonRunner):
def _status(self):
...
DaemonRunner.action_funcs['status'] = _status
This works to some extent, but the problem is that every instance of DaemonRunner now have the new behaviour. Is it possible to override it without modifying every instance of DaemonRunner?
I would override action_functs to make it a non-static member of class StatusDaemonRunner(DaemonRunner).
In terms of code I would do:
class StatusDaemonRunner(runner.DaemonRunner):
def __init__(self, app):
self.action_funcs = runner.DaemonRunner.action_funcs.copy()
self.action_funcs['status'] = StatusDaemonRunner._status
super(StatusDaemonRunner, self).__init__(app)
def _status(self):
pass # do your stuff
Indeed, if we look at the getter in the implementation of DaemonRunner (here) we can see that it acess the attribute using self
def _get_action_func(self):
""" Return the function for the specified action.
Raises ``DaemonRunnerInvalidActionError`` if the action is
unknown.
"""
try:
func = self.action_funcs[self.action]
except KeyError:
raise DaemonRunnerInvalidActionError(
u"Unknown action: %(action)r" % vars(self))
return func
Hence the previous code should do the trick.

Python duck-typing for MVC event handling in pygame

A friend and I have been playing around with pygame some and came across this tutorial for building games using pygame. We really liked how it broke out the game into a model-view-controller system with events as a go-between, but the code makes heavy use of isinstance checks for the event system.
Example:
class CPUSpinnerController:
...
def Notify(self, event):
if isinstance( event, QuitEvent ):
self.keepGoing = 0
This results in some extremely unpythonic code. Does anyone have any suggestions on how this could be improved? Or an alternative methodology for implementing MVC?
This is a bit of code I wrote based on #Mark-Hildreth answer (how do I link users?) Does anyone else have any good suggestions? I'm going to leave this open for another day or so before picking a solution.
class EventManager:
def __init__(self):
from weakref import WeakKeyDictionary
self.listeners = WeakKeyDictionary()
def add(self, listener):
self.listeners[ listener ] = 1
def remove(self, listener):
del self.listeners[ listener ]
def post(self, event):
print "post event %s" % event.name
for listener in self.listeners.keys():
listener.notify(event)
class Listener:
def __init__(self, event_mgr=None):
if event_mgr is not None:
event_mgr.add(self)
def notify(self, event):
event(self)
class Event:
def __init__(self, name="Generic Event"):
self.name = name
def __call__(self, controller):
pass
class QuitEvent(Event):
def __init__(self):
Event.__init__(self, "Quit")
def __call__(self, listener):
listener.exit(self)
class RunController(Listener):
def __init__(self, event_mgr):
Listener.__init__(self, event_mgr)
self.running = True
self.event_mgr = event_mgr
def exit(self, event):
print "exit called"
self.running = False
def run(self):
print "run called"
while self.running:
event = QuitEvent()
self.event_mgr.post(event)
em = EventManager()
run = RunController(em)
run.run()
This is another build using the examples from #Paul - impressively simple!
class WeakBoundMethod:
def __init__(self, meth):
import weakref
self._self = weakref.ref(meth.__self__)
self._func = meth.__func__
def __call__(self, *args, **kwargs):
self._func(self._self(), *args, **kwargs)
class EventManager:
def __init__(self):
# does this actually do anything?
self._listeners = { None : [ None ] }
def add(self, eventClass, listener):
print "add %s" % eventClass.__name__
key = eventClass.__name__
if (hasattr(listener, '__self__') and
hasattr(listener, '__func__')):
listener = WeakBoundMethod(listener)
try:
self._listeners[key].append(listener)
except KeyError:
# why did you not need this in your code?
self._listeners[key] = [listener]
print "add count %s" % len(self._listeners[key])
def remove(self, eventClass, listener):
key = eventClass.__name__
self._listeners[key].remove(listener)
def post(self, event):
eventClass = event.__class__
key = eventClass.__name__
print "post event %s (keys %s)" % (
key, len(self._listeners[key]))
for listener in self._listeners[key]:
listener(event)
class Event:
pass
class QuitEvent(Event):
pass
class RunController:
def __init__(self, event_mgr):
event_mgr.add(QuitEvent, self.exit)
self.running = True
self.event_mgr = event_mgr
def exit(self, event):
print "exit called"
self.running = False
def run(self):
print "run called"
while self.running:
event = QuitEvent()
self.event_mgr.post(event)
em = EventManager()
run = RunController(em)
run.run()
A cleaner way of handling events (and also a lot faster, but possibly consumes a bit more memory) is to have multiple event handler functions in your code. Something along these lines:
The Desired Interface
class KeyboardEvent:
pass
class MouseEvent:
pass
class NotifyThisClass:
def __init__(self, event_dispatcher):
self.ed = event_dispatcher
self.ed.add(KeyboardEvent, self.on_keyboard_event)
self.ed.add(MouseEvent, self.on_mouse_event)
def __del__(self):
self.ed.remove(KeyboardEvent, self.on_keyboard_event)
self.ed.remove(MouseEvent, self.on_mouse_event)
def on_keyboard_event(self, event):
pass
def on_mouse_event(self, event):
pass
Here, the __init__ method receives an EventDispatcher as an argument. The EventDispatcher.add function now takes the type of the event you are interested in, and the listener.
This has benefits for efficiency since the listener only ever gets called for events that it is interested in. It also results in more generic code inside the EventDispatcher itself:
EventDispatcher Implementation
class EventDispatcher:
def __init__(self):
# Dict that maps event types to lists of listeners
self._listeners = dict()
def add(self, eventcls, listener):
self._listeners.setdefault(eventcls, list()).append(listener)
def post(self, event):
try:
for listener in self._listeners[event.__class__]:
listener(event)
except KeyError:
pass # No listener interested in this event
But there is a problem with this implementation. Inside NotifyThisClass you do this:
self.ed.add(KeyboardEvent, self.on_keyboard_event)
The problem is with self.on_keyboard_event: it is a bound method which you passed to the EventDispatcher. Bound methods hold a reference to self; this means that as long as the EventDispatcher has the bound method, self will not be deleted.
WeakBoundMethod
You will need to create a WeakBoundMethod class that holds only a weak reference to self (I see you already know about weak references) so that the EventDispatcher does not prevent the deletion of self.
An alternative would be to have a NotifyThisClass.remove_listeners function that you call before deleting the object, but that's not really the cleanest solution and I find it very error prone (easy to forget to do).
The implementation of WeakBoundMethod would look something like this:
class WeakBoundMethod:
def __init__(self, meth):
self._self = weakref.ref(meth.__self__)
self._func = meth.__func__
def __call__(self, *args, **kwargs):
self._func(self._self(), *args, **kwargs)
Here's a more robust implementation I posted on CodeReview, and here's an example of how you'd use the class:
from weak_bound_method import WeakBoundMethod as Wbm
class NotifyThisClass:
def __init__(self, event_dispatcher):
self.ed = event_dispatcher
self.ed.add(KeyboardEvent, Wbm(self.on_keyboard_event))
self.ed.add(MouseEvent, Wbm(self.on_mouse_event))
Connection Objects (Optional)
When removing listeners from the manager/ dispatcher, instead of making the EventDispatcher needlessly search through the listeners until it finds the right event type, then search through the list until it finds the right listener, you could have something like this:
class NotifyThisClass:
def __init__(self, event_dispatcher):
self.ed = event_dispatcher
self._connections = [
self.ed.add(KeyboardEvent, Wbm(self.on_keyboard_event)),
self.ed.add(MouseEvent, Wbm(self.on_mouse_event))
]
Here EventDispatcher.add returns a Connection object that knows where in the EventDispatcher's dict of lists it resides. When a NotifyThisClass object is deleted, so is self._connections, which will call Connection.__del__, which will remove the listener from the EventDispatcher.
This could make your code both faster and easier to use because you only have to explicitly add the functions, they are removed automatically, but it's up to you to decide if you want to do this. If you do it, note that EventDispatcher.remove shouldn't exist anymore.
I stumbled upon SJ Brown's tutorial on making games in the past. It's a great page, one of the best I've read. However, like you, I didn't like the calls to isinstance, or the fact that all the listeners receive all the events.
First, isinstance is slower than checking that two strings are equals, so I ended up storing a name on my events and test for the name rather than the class. But still, the notify function with its battery of if was itching me because it felt like a waste of time. We can do two optimizations here:
Most listeners are interested in only a few types of events. For performance reasons, when QuitEvent is posted, only the listeners interested in it should be notified. The event manager keeps track of which listener wants to listen to which event.
Then, to avoid going through a tons of if statements in a single notify method, we will have one method per type of event.
Example:
class GameLoopController(...):
...
def onQuitEvent(self, event):
# Directly called by the event manager when a QuitEvent is posted.
# I call this an event handler.
self._running = False
Because I want the developer to type as little as possible, I made the following thing:
When a listener is registered to an event manager, the event manager scans all the methods of the listener. When one method starts with 'on' (or any prefix you like), then it looks at the rest ("QuitEvent") and binds this name to this method. Later, when the event manager pumps its event list, it looks at the event class name: "QuitEvent". It knows that name, and therefore can directly call all the corresponding event handlers directly. The developer has nothing to do but adding onWhateverEvent methods to have them working.
It has some drawbacks:
If I make a typo in the name of the handler ("onRunPhysicsEvent"
instead of "onPhysicsRanEvent" for example") then my handler will
never be called and I'll wonder why. But I know the trick so I
don't wonder why very long.
I cannot add an event handler after the listener has been
registered. I must un-register and re-register. Indeed, the
events handlers are scanned only during the registration. Then
again, I never had to do that anyway so I don't miss it.
Despite these drawbacks I like it much more than having the constructor of the listener explicitly explain the event manager that it wants to stay tuned of this, this, this and this event. And it's the same execution speed anyway.
Second point:
When designing our event manager, we want to be careful. Very often, a listener will respond to an event by creating-registering or unregistering-destroying listeners. This happens all the time. If we don't think about it then our game may break with RuntimeError: dictionary changed size during iteration. The code that you propose iterates over a copy of the dictionary so you're protected against explosions; but it has consequences to be aware of:
- Listeners registered because of an event will not receive that event.
- Listeners unregistered because of an event will still receive that
event.
I never found it to be a problem though.
I implemented that myself for the game I am developing. I can link you to two articles and a half I wrote on the subject:
http://niriel.wordpress.com/2011/08/06/who-controls-the-controllers/
http://niriel.wordpress.com/2011/08/08/the-event-management-is-in-place/
http://niriel.wordpress.com/2011/08/11/the-first-screenshot-of-infiniworld/
The links to my github account will bring you directly to the source code of the relevant parts. If you cannot wait, here's the thing: https://github.com/Niriel/Infiniworld/blob/v0.0.2/src/evtman.py . In there you'll see that the code for my event class is a bit big, but that every inherited event is declared in 2 lines: the base Event class is making your life easy.
So, this all works using python's introspection mechanism, and using the fact that methods are objects like any other that can be put in dictionaries. I think it's quite pythony :).
Give each event a method (possibly even using __call__), and pass in the Controller object as an argument. The "call" method should then call the controller object. For example...
class QuitEvent:
...
def __call__(self, controller):
controller.on_quit(self) # or possibly... controller.on_quit(self.val1, self.val2)
class CPUSpinnerController:
...
def on_quit(self, event):
...
Whatever code you're using to route your events to your controllers will call the __call__ method with the correct controller.
I stumbled upon the same issue (almost a decade later!), and here is an implementation I've been using so that the EventManager would notify only a subset of listeners.
It is based on defaultdict: the _listeners attribute of EventManager is a defaultdict of WeakKeyDictionary().
Event are all inherited from an empty abstract Event class, so listeners can focus only on some classes of events they want to listen to.
Here is a minimalist code to get the idea behind it:
from collections import defaultdict
from weakref import WeakKeyDictionary
class Event:
def __init__(self):
pass
class KeyboardEvent(Event): # for instance, a keyboard event class with the key pressed
def __init__(self, key):
self._key = key
class EventManager:
def __init__(self):
self._listeners = defaultdict(lambda: WeakKeyDictionary())
def register_listener(self, event_types, listener):
for event_type in event_types:
self._listeners[event_type][listener] = 1
def unregister_listener(self, listener):
for event_type in self._listeners:
self._listeners[event_type].pop(listener, None)
def post_event(self, event):
for listener in self._listeners[event.__class__]:
listener.notify(event)
When registering, a listener tells the event manager which event type it wants to be notified of.
When posting events, the event manager will only notify the listeners which registered to be notified for that type of event.
This piece of code has, of course, much less scope than the very general (and very elegant) solution proposed by #Paul Manta, but in my case, it helped remove some repetitive calls to isinstance and other checks while keeping things as simple as I could.
One of the drawbacks of this is that all type of events have to be objects of some class, but in OO python, this is supposed to be the way to go.

pygtk gtk.Builder.connect_signals onto multiple objects?

I am updating some code from using libglade to GtkBuilder, which is supposed to be the way of the future.
With gtk.glade, you could call glade_xml.signal_autoconnect(...) repeatedly to connect signals onto objects of different classes corresponding to different windows in the program. However Builder.connect_signals seems to work only once, and (therefore) to give warnings about any handlers that aren't defined in the first class that's passed in.
I realize I can connect them manually but this seems a bit laborious. (Or for that matter I could use some getattr hackery to let it connect them through a proxy to all the objects...)
Is it a bug there's no function to hook up handlers across multiple objects? Or am I missing something?
Someone else has a similar problem http://www.gtkforums.com/about1514.html which I assume means this can't be done.
Here's what I currently have. Feel free to use it, or to suggest something better:
class HandlerFinder(object):
"""Searches for handler implementations across multiple objects.
"""
# See <http://stackoverflow.com/questions/4637792> for why this is
# necessary.
def __init__(self, backing_objects):
self.backing_objects = backing_objects
def __getattr__(self, name):
for o in self.backing_objects:
if hasattr(o, name):
return getattr(o, name)
else:
raise AttributeError("%r not found on any of %r"
% (name, self.backing_objects))
I have been looking for a solution to this for some time and found that it can be done by passing a dict of all the handlers to connect_signals.
The inspect module can extract methods using
inspect.getmembers(instance, predicate=inspect.ismethod
These can then be concatenated into a dictionary using d.update(d3), watching out for duplicate functions such as on_delete.
Example code:
import inspect
...
handlers = {}
for c in [win2, win3, win4, self]: # self is the main window
methods = inspect.getmembers(c, predicate=inspect.ismethod)
handlers.update(methods)
builder.connect_signals(handlers)
This will not pick up alias method names declared using #alias. For an example of how to do that, see the code for Builder.py, at def dict_from_callback_obj.
I'm only a novice but this is what I do, maybe it can inspire;-)
I instantiate the major components from a 'control' and pass the builder object so that the instantiated object can make use of any of the builder objects (mainwindow in example) or add to the builder (aboutDialog example). I also pass a dictionary (dic) where each component adds "signals" to it.
Then the 'connect_signals(dic)' is executed.
Of course I need to do some manual signal connecting when I need to pass user arguments to the callback method, but those are few.
#modules.control.py
class Control:
def __init__(self):
# Load the builder obj
guibuilder = gtk.Builder()
guibuilder.add_from_file("gui/mainwindow.ui")
# Create a dictionnary to store signal from loaded components
dic = {}
# Instanciate the components...
aboutdialog = modules.aboutdialog.AboutDialog(guibuilder, dic)
mainwin = modules.mainwindow.MainWindow(guibuilder, dic, self)
...
guibuilder.connect_signals(dic)
del dic
#modules/aboutdialog.py
class AboutDialog:
def __init__(self, builder, dic):
dic["on_OpenAboutWindow_activate"] = self.on_OpenAboutWindow_activate
self.builder = builder
def on_OpenAboutWindow_activate(self, menu_item):
self.builder.add_from_file("gui/aboutdialog.ui")
self.aboutdialog = self.builder.get_object("aboutdialog")
self.aboutdialog.run()
self.aboutdialog.destroy()
#modules/mainwindow.py
class MainWindow:
def __init__(self, builder, dic, controller):
self.control = controller
# get gui xml and/or signals
dic["on_file_new_activate"] = self.control.newFile
dic["on_file_open_activate"] = self.control.openFile
dic["on_file_save_activate"] = self.control.saveFile
dic["on_file_close_activate"] = self.control.closeFile
...
# get needed gui objects
self.mainWindow = builder.get_object("mainWindow")
...
Edit: alternative to auto attach signals to callbacks:
Untested code
def start_element(name, attrs):
if name == "signal":
if attrs["handler"]:
handler = attrs["handler"]
#Insert code to verify if handler is part of the collection
#we want.
self.handlerList.append(handler)
def extractSignals(uiFile)
import xml.parsers.expat
p = xml.parsers.expat.ParserCreate()
p.StartElementHandler = self.start_element
p.ParseFile(uiFile)
self.handlerList = []
extractSignals(uiFile)
for handler in handlerList:
dic[handler] = eval(''. join(["self.", handler, "_cb"]))
builder.connect_signals
({
"on_window_destroy" : gtk.main_quit,
"on_buttonQuit_clicked" : gtk.main_quit
})

Categories

Resources