Skip to content

Instantly share code, notes, and snippets.

@amintos
Created September 4, 2015 12:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amintos/635df2eb58811882128d to your computer and use it in GitHub Desktop.
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.
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