#C#-Style Events in Python
#Taken from http://code.activestate.com/recipes/410686/
#Copyright 2005-2008 Zoran Isailovski
#
#This file is distributed under the terms of the PSF (Python Software
#Foundation) license. See the COPYING file in this directory

class Events:
	def __getattr__(self, name):
		if hasattr(self.__class__, '__events__'):
			assert name in self.__class__.__events__, \
					 "Event '%s' is not declared" % name
		self.__dict__[name] = ev = _EventSlot(name)
		return ev
	def __repr__(self): return 'Events' + str(list(self))
	__str__ = __repr__
	def __len__(self): return NotImplemented
	def __iter__(self):
		def gen(dictitems=self.__dict__.items()):
			for attr, val in dictitems:
				if isinstance(val, _EventSlot):
					yield val
		return gen()

#------------------------------------------------------------------------------

class _EventSlot:
	def __init__(self, name):
		self.targets = []
		self.__name__ = name
	def __repr__(self):
		return 'event ' + self.__name__
	def __call__(self, *a, **kw):
		for f in 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

#------------------------------------------------------------------------------

if __name__ == '__main__':

	class MyEvents(Events):
		__events__ = ('OnChange', )

	class ValueModel(object):
		def __init__(self):
			self.events = MyEvents()
			self.__value = None
		def __set(self, value):
			if (self.__value == value): return
			self.__value = value
			self.events.OnChange()
			##self.events.OnChange2() # would fail
		def __get(self):
			return self.__value
		Value = property(__get, __set, None, 'The actual value')

	class SillyView(object):
		def __init__(self, model):
			self.model = model
			model.events.OnChange += self.DisplayValue
			##model.events.OnChange2 += self.DisplayValue # would raise exeception
		def DisplayValue(self):
			print(self.model.Value)


	model = ValueModel()
	view = SillyView(model)

	print('\n--- Events Demo ---')
	# Events in action
	for i in range(5):
		model.Value = 2*i + 1
	# Events introspection
	print(model.events)
	for event in model.events:
		print(event)
