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