/GUI/Generic/Models.py

https://bitbucket.org/alsh/pygui-mirror · Python · 88 lines · 70 code · 7 blank · 11 comment · 0 complexity · bcea77250ae0a095ea83cbaa9e9fc108 MD5 · raw file

  1. #
  2. # Python GUI - Models - Generic
  3. #
  4. import weakref
  5. from Properties import Properties, overridable_property
  6. # List of views for a model is kept separately so that models
  7. # can be pickled without fear of accidentally trying to pickle
  8. # the user interface.
  9. _model_views = weakref.WeakKeyDictionary() # {Model: [object]}
  10. class Model(Properties):
  11. """A Model represents an application object which can appear in a View.
  12. Each Model can have any number of Views attached to it. When a Model is
  13. changed, it should notify all of its Views so that they can update
  14. themselves."""
  15. views = overridable_property('views',
  16. "List of objects observing this model. Do not modify directly.")
  17. def __init__(self, **kwds):
  18. Properties.__init__(self, **kwds)
  19. def destroy(self):
  20. """All views currently observing this model are removed, and their
  21. 'model_destroyed' methods, if any, are called with the model as
  22. an argument."""
  23. for view in self.views[:]:
  24. self.remove_view(view)
  25. self._call_if_present(view, 'model_destroyed', self)
  26. #
  27. # Properties
  28. #
  29. def get_views(self):
  30. views = _model_views.get(self)
  31. if views is None:
  32. views = []
  33. _model_views[self] = views
  34. return views
  35. #
  36. # Adding and removing views
  37. #
  38. def add_view(self, view):
  39. """Add the given object as an observer of this model. The view will
  40. typically be a View subclass, but need not be. If the view is not
  41. already an observer of this model and defines an 'add_model' method,
  42. this method is called with the model as an argument."""
  43. views = self.views
  44. if view not in views:
  45. views.append(view)
  46. self._call_if_present(view, 'add_model', self)
  47. def remove_view(self, view):
  48. """If the given object is currently an observer of this model, it
  49. is removed, and if it defines a 'remove_model' method, this method
  50. is called with the model as an argument."""
  51. views = self.views
  52. if view in views:
  53. views.remove(view)
  54. self._call_if_present(view, 'remove_model', self)
  55. #
  56. # View notification
  57. #
  58. def notify_views(self, message = 'model_changed', *args, **kwds):
  59. """For each observer, if the observer defines a method with the name of the
  60. message, call it with the given arguments. Otherwise, if it defines a
  61. method called 'model_changed', call it with no arguments. Otherwise,
  62. do nothing for that observer."""
  63. for view in self.views:
  64. if not self._call_if_present(view, message, self, *args, **kwds):
  65. self._call_if_present(view, 'model_changed', self)
  66. def _call_if_present(self, obj, method_name, *args, **kwds):
  67. method = getattr(obj, method_name, None)
  68. if method:
  69. method(*args, **kwds)
  70. return 1
  71. else:
  72. return 0