Created
September 4, 2015 12:28
-
-
Save amintos/635df2eb58811882128d to your computer and use it in GitHub Desktop.
Example for modular event handling using "activities" as Python generators, which are stepped by external events. Activities reacting to events are a single method and not scattered across callbacks or multiple handlers.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Element: | |
"""I represent an unspecific UI component""" | |
def __init__(self, id, env): | |
self.id = id | |
self.env = env | |
self.children = [] | |
def trigger(self, event): | |
"""Receive an (asynchronous) event""" | |
self.env.notify(self, event) | |
class Environment(Element): | |
"""I represent the world and register event handlers""" | |
def __init__(self): | |
super(Environment, self).__init__('$env', self) | |
# this stores continuations along with their activation conditions | |
self.handlers = [] | |
def add_element(self, id): | |
return Element(id, self) | |
def notify(self, element, event): | |
for handler in self.handlers: | |
if handler.handles(element, event): | |
handler.activate(element, event) | |
def register(self, selector, process): | |
self.handlers.append( | |
Handler(selector, process, self)) | |
def unregister(self, handler): | |
self.handlers.remove(handler) | |
def activity(self, generator): | |
process = generator() | |
selector = next(process) | |
self.register(selector, process) | |
return generator | |
class Handler: | |
"""I represent a continuation with an activation condition (selector)""" | |
def __init__(self, selector, process, env): | |
self.selector = selector | |
self.process = process | |
self.env = env | |
def handles(self, element, event): | |
return self.selector(element, event) | |
def activate(self, element, event): | |
"""activate continuation and replace myself by new continuation""" | |
try: | |
selector = self.process.send((element, event)) | |
self.env.register(selector, self.process) | |
except StopIteration: | |
pass | |
finally: | |
# this continuation is fulfilled, remove it | |
self.env.unregister(self) | |
# ----------------------------------------------------------------------------- | |
# Example | |
my_env = Environment() | |
@my_env.activity | |
def drag_drop(): | |
"""A drag-and-drop activity""" | |
# accept mouse-down on draggable | |
source, source_event = yield lambda el, ev: \ | |
ev == 'mouse-down' and el.id == 'draggable' | |
print('picked up %s' % source.id) | |
while True: | |
# accept mouse-up or moves on anything | |
target, target_event = yield lambda el, ev: \ | |
ev == 'mouse-up' or ev == 'move' | |
# dispatch event | |
if target_event == 'mouse-up': | |
print('dropped %s on %s' % (source.id, target.id)) | |
break | |
elif target_event == 'move': | |
print('moved over %s' % target.id) | |
# replace myself by fresh activity | |
my_env.activity(drag_drop) | |
# Setup environment/UI | |
draggable = my_env.add_element('draggable') | |
target_1 = my_env.add_element('target_1') | |
target_2 = my_env.add_element('target_2') | |
# fire events | |
draggable.trigger('mouse-down') | |
draggable.trigger('random-event') | |
target_1.trigger('move') | |
target_2.trigger('move') | |
target_2.trigger('mouse-up') | |
draggable.trigger('mouse-down') | |
target_2.trigger('move') | |
target_1.trigger('move') | |
target_1.trigger('mouse-up') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment