event.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. import sys
  2. import odml
  3. class Event (object):
  4. def __init__(self, name):
  5. self.handlers = []
  6. self.name = name
  7. def add_handler(self, handler):
  8. try:
  9. if handler not in self.handlers:
  10. self.handlers.append(handler)
  11. except:
  12. print("cannot add handler.")
  13. return self
  14. def remove_handler(self, handler):
  15. if handler in self.handlers:
  16. self.handlers.remove(handler)
  17. return self
  18. def fire(self, *args, **kargs):
  19. for handler in self.handlers:
  20. handler(*args, **kargs)
  21. self.finish(*args, **kargs)
  22. def finish(self, *args, **kargs):
  23. """
  24. a final handler for this event
  25. (typically passing the event on to higher layers)
  26. """
  27. pass
  28. def __len__(self):
  29. return len(self.handlers)
  30. def __repr__(self):
  31. return "<%sEvent>" % self.name
  32. __iadd__ = add_handler
  33. __isub__ = remove_handler
  34. __call__ = fire
  35. class ChangeContext(object):
  36. """
  37. A ChangeContext holds information about a change event
  38. the context object stays the same within preChange and postChange
  39. events, thus you can store information in a preChange event and use
  40. it later in the postChange event where the information might not be
  41. available any more.
  42. Attributes:
  43. * action: the action that caused the event
  44. * obj: the object upon which the action is executed
  45. * val: a value assotiated with a action
  46. * preChange: True if the change has not yet occurred
  47. * postChange: True if the change has already occured
  48. Actions defined so far:
  49. * "set": val = (attribute_name, new_value)
  50. * "insert", "append": val = object to be inserted
  51. * "remove": val = object to be remove
  52. Events may be passed on in the hierarchy, thus the context also
  53. holds state for this. *cur* holds the current node receiving the
  54. event and is a direct or indirect parent of obj.
  55. The hidden attribute *_obj* holds the complete pass stack, where
  56. obj is obj[0] and cur is _obj[-1]
  57. """
  58. def __init__(self, val):
  59. self._obj = []
  60. self.val = val
  61. self.action = None
  62. self.when = None
  63. @property
  64. def preChange(self):
  65. return self.when is True
  66. @preChange.setter
  67. def preChange(self, val):
  68. self.when = True if val else None
  69. @property
  70. def postChange(self):
  71. return self.when is False
  72. @postChange.setter
  73. def postChange(self, val):
  74. self.when = False if val else None
  75. @property
  76. def obj(self):
  77. return self._obj[0]
  78. def getStack(self, count):
  79. """
  80. helper function used to obtain a event-pass-stack for a certain hierarchy
  81. i.e. for a property-change event caught at the parent section, _obj will be
  82. [property, section] with getStack you can now obtain:
  83. >>> value, property, section = getStack(3)
  84. without checking the depth of the level, this will also hold true for longer stacks
  85. such as [val, prop, sec, doc] and will still work as expected
  86. """
  87. if len(self._obj) < count:
  88. return ([None] * count + self._obj)[-count:]
  89. return self._obj[:count]
  90. @property
  91. def cur(self):
  92. return self._obj[-1]
  93. def passOn(self, obj):
  94. """
  95. pass the event to obj
  96. appends obj to the event-pass-stack and calls obj._Changed
  97. after handling obj is removed from the stack again
  98. """
  99. self._obj.append(obj)
  100. if obj._change_handler is not None: #hasattr(obj, "_change_handler"):
  101. obj._change_handler(self)
  102. obj._Changed(self)
  103. self._obj.remove(obj)
  104. def reset(self):
  105. self._obj = []
  106. def __repr__(self):
  107. v = ""
  108. if self.preChange: v = "Pre"
  109. if self.postChange: v = "Post"
  110. return "<%sChange %s.%s(%s)>" % (v, repr(self.obj), self.action, repr(self.val))
  111. def dump(self):
  112. return repr(self) + "\nObject stack:\n\t" + "\n\t".join(map(repr, self._obj))
  113. class ChangedEvent(object):
  114. def __init__(self):
  115. pass
  116. class EventHandler(object):
  117. def __init__(self, func):
  118. self._func = func
  119. def __call__(self, *args, **kargs):
  120. return self._func(*args, **kargs)
  121. class ChangeHandlable(object):
  122. """
  123. For objects that support the add_change_handler
  124. and remove_change_handler functions.
  125. """
  126. _change_handler = None
  127. def add_change_handler(self, func):
  128. if self._change_handler is None:
  129. self._change_handler = Event(self.__class__.__name__)
  130. self._change_handler += func
  131. def remove_change_handler(self, func):
  132. if self._change_handler is not None:
  133. self._change_handler -= func
  134. if len(self._change_handler) == 0:
  135. del self._change_handler
  136. class ModificationNotifier(ChangeHandlable):
  137. """
  138. Override some methods, to get notification on their calls
  139. """
  140. def __setattr__(self, name, value):
  141. fire = not name.startswith('_') and hasattr(self, name)
  142. func = lambda: super(ModificationNotifier, self).__setattr__(name, value)
  143. if fire:
  144. self.__fireChange("set", (name, value), func)
  145. else:
  146. func()
  147. def __fireChange(self, action, obj, func):
  148. """
  149. create a ChangeContext and
  150. * fire a preChange-event
  151. * call func
  152. * fire a postChange-event
  153. """
  154. c = ChangeContext(obj)
  155. c.action = action
  156. c.preChange = True
  157. c.passOn(self)
  158. res = func()
  159. c.reset()
  160. c.postChange = True
  161. c.passOn(self)
  162. return res
  163. def append(self, obj, *args, **kwargs):
  164. func = lambda: super(ModificationNotifier, self).append(obj, *args, **kwargs)
  165. self.__fireChange("append", obj, func)
  166. def remove(self, obj):
  167. func = lambda: super(ModificationNotifier, self).remove(obj)
  168. self.__fireChange("remove", obj, func)
  169. def insert(self, position, obj):
  170. func = lambda: super(ModificationNotifier, self).insert(position, obj)
  171. self.__fireChange("insert", obj, func)
  172. def _reorder(self, childlist, new_index):
  173. func = lambda: super(ModificationNotifier, self)._reorder(childlist, new_index)
  174. return self.__fireChange("reorder", (childlist, new_index), func)
  175. # create a seperate global Event listeners for each class
  176. # and provide ModificationNotifier Capabilities
  177. name = "event"
  178. provides = odml.getImplementation().provides + ["event"]
  179. class Value(ModificationNotifier, odml.getImplementation().Value):
  180. _Changed = Event("value")
  181. class Property(ModificationNotifier, odml.getImplementation().Property):
  182. _Changed = Event("prop")
  183. class Section(ModificationNotifier, odml.getImplementation().Section):
  184. _Changed = Event("sec")
  185. class Document(ModificationNotifier, odml.getImplementation().Document):
  186. _Changed = Event("doc")
  187. def pass_on_change(context):
  188. """
  189. pass the change event to the parent node
  190. """
  191. parent = context.cur.parent
  192. if parent is not None:
  193. context.passOn(parent)
  194. def pass_on_change_section(context):
  195. """
  196. pass the change event directly to the document in question
  197. don't go through all parents
  198. """
  199. document = context.cur.document
  200. if document is not None:
  201. context.passOn(document)
  202. Value._Changed.finish = pass_on_change
  203. Property._Changed.finish = pass_on_change
  204. Section._Changed.finish = pass_on_change_section
  205. odml.addImplementation(sys.modules[__name__])