# -*- coding: utf-8 -*-
"""
Events
~~~~~~
Implements C#-Style Events.
Derived from the original work by Zoran Isailovski:
http://code.activestate.com/recipes/410686/ - Copyright (c) 2005
:copyright: (c) 2014-2017 by Nicola Iarocci.
:license: BSD, see LICENSE for more details.
"""
class _EventSlot:
def __init__(self, name):
self.targets = []
self.__name__ = name
def __repr__(self):
return "event '%s'" % self.__name__
def __call__(self, *a, **kw):
for f in tuple(self.targets):
f(*a, **kw)
def __iadd__(self, f):
self.targets.append(f)
return self
def __isub__(self, f):
while f in self.targets:
self.targets.remove(f)
return self
def __len__(self):
return len(self.targets)
def __iter__(self):
def gen():
for target in self.targets:
yield target
return gen()
def __getitem__(self, key):
return self.targets[key]
class EventsException(Exception):
pass
class Events:
"""
Encapsulates the core to event subscription and event firing, and feels
like a "natural" part of the language.
The class Events is there mainly for 3 reasons:
- Events (Slots) are added automatically, so there is no need to
declare/create them separately. This is great for prototyping. (Note
that `__events__` is optional and should primarilly help detect
misspelled event names.)
- To provide (and encapsulate) some level of introspection.
- To "steel the name" and hereby remove unneeded redundancy in a call
like:
xxx.OnChange = event('OnChange')
"""
def __init__(self, events=None, event_slot_cls=_EventSlot):
self.__event_slot_cls__ = event_slot_cls
if events is not None:
try:
for _ in events:
break
except:
raise AttributeError("type object %s is not iterable" %
(type(events)))
else:
self.__events__ = events
def __getattr__(self, name):
if name.startswith('__'):
raise AttributeError("type object '%s' has no attribute '%s'" %
(self.__class__.__name__, name))
if hasattr(self, '__events__'):
if name not in self.__events__:
raise EventsException("Event '%s' is not declared" % name)
elif hasattr(self.__class__, '__events__'):
if name not in self.__class__.__events__:
raise EventsException("Event '%s' is not declared" % name)
self.__dict__[name] = ev = self.__event_slot_cls__(name)
return ev
def __getitem__(self, item):
return self.__dict__[item]
def __repr__(self):
return '<%s.%s object at %s>' % (self.__class__.__module__,
self.__class__.__name__,
hex(id(self)))
__str__ = __repr__
def __len__(self):
return len(list(self.__iter__()))
def __iter__(self):
def gen(dictitems=self.__dict__.items()):
for attr, val in dictitems:
if isinstance(val, self.__event_slot_cls__):
yield val
return gen()