New function: Use events globally
vojj opened this issue · 5 comments
Hello all,
Thanks for this great library.
I use python-dispatch in my small project.
I want to propose an easier way to use the functionality.
My req.:
- use the eventDispatcher globally
- any class can subscribe and emit an event
My code is in my app.py (main)
_event = None
_event = EventDispatcher()
_event.register_event('on_quit')
new wrapper class
from pydispatch import Dispatcher
class Eventhandler(Dispatcher):
def __init__(self, event=None):
pass
# Events are defined in classes and subclasses with the '_events_' attribute
_events_ = ['on_test']
_event: Eventhandler = None
class EventDispatcher:
def __init__(self, event: Eventhandler = None):
global _event
if _event is None:
_event = Eventhandler()
def emit(self, event, data="emitted event"):
global _event
_event.emit(event, data=data)
def register_event(self, event):
global _event
_event.register_event(event)
print('Event added')
def register(self, name, callback):
global _event
kwargs = {name: callback}
_event.bind(**kwargs)
Any class can emit the event by name.
EventDispatcher().emit("on_quit", "PleaseDoSomethink")
It is not ready to use, but I can add the changes finally in a new brunch.
What do you think?
Best Regards
Daniel
An interesting concept, and I can see there being use cases for sure.
What if the front-facing API for this used module-level functions instead of exposing the global Dispatcher
instance itself? Something like:
import pydispatch # just importing the module itself, no classes, etc
pydispatch.register_event('on_quit')
def my_shutdown_callback(*args, **kwargs):
pass
pydispatcher.bind(on_quit=my_shutdown_callback)
pydispatcher.emit('on_quit', 'foo', bar='bar')
pydispatcher.unbind(my_shutdown_callback)
Making things work like above would be fairly trivial:
# Somewhere in pydispatch, maybe __init__.py after the imports
_global_dispatcher = Dispatcher()
def register_event(name: str):
_global_dispatcher.register_event(name)
def bind(**kwargs):
_global_dispatcher.bind(**kwargs)
# etc
Hi,
I like your ideas. I guess the second one is a good way. But the changes are not so much. :-)
init.py could be an excellent way to generalize this uses case.
I've tested the code with a base-view to inherit in my views.
class Baseview:
def __init__(self):
# events
self.events = EventDispatcher()
class motor_control(Baseview):
def __init__(self, targetFrame, title, motor, event=None):
super().__init__(self)
It is working very well.
The critical case in this idea is to catch the same instance.
All best.
Now that I'm thinking about this with a fresh set of eyes, I think this would be generally something quite useful.
The approach that I'm thinking is a little along the lines of how Django signals work (only less restrictive).
The decorator shortcut for binding an event to a callback could also be implemented.
I'd like to ponder this and toy with it.
General Notes:
- A global
Dispatcher
instance should be made available at the package level. - All necessary
Dispatcher
methods of the global instance should be added as functions at the package level mapping to the instance. - Thread safety needs to be addressed and thought through.
- To avoid conflict with a single instance holding all events:
- should event names may need to be name-spaced?
- should an exception be raised in
register_event
if an event already exists with that name?
- Binding to an event that hasn't been registered yet would be nice. Would require caching the callback and event name (shouldn't be too difficult).
- Event emission can be done using the global
Dispatcher.emit(<name>, ...)
method, but exposing theEvent
instances themselves should be available:
import pydispatch
# Dispatcher.emit() method
pydispatch.register_event('signal_a')
pydispatch.emit('signal_a')
# Event object method
signal_b = pydispatch.register_event('signal_b')
signal_b.emit()