op_power: reload modules correctly
[openpandora.oe.git] / recipes / pandora-system / pandora-scripts / ConfigModel.py
1 # Types of model changes for view updates 
2 (MODEL_PROFILE_CHANGE, MODEL_VALUE_CHANGE) = range(2)
3
4 class ConfigModel(object):
5     """A simple settings dictionary with support for saving and loading profiles.
6
7     The following four methods need to be implemented by sub-classes:
8     - read_settings: read current system configuration
9     - write_settings: write self.settings to system
10     - profile_to_string: convert a dictionary into a single-line string for storage.
11     - string_to_profile: the inverse of the above.
12
13     The class implements a basic model-view pattern. The user-interface can attach
14     to this to receive updates whenever the values are modified.
15
16     Events: 
17     - MODEL_PROFILE_CHANGE: 
18       Issued when the sets of defined profiles has been modified. 
19       This is relevant for widgets which handle profile creation/deletion.
20     - MODEL_VALUE_CHANGE:
21       Issued whenever a new profile is loaded. This is useful for all widgets
22       which actually display and edit a configuration.
23     """
24     def __init__(self, profiles_file, *views, **kwargs):
25         """Creates a new configuration settings model.
26
27         This currently provides support for a model/view pattern and 
28         unmodifiable default profiles.
29         
30         @profiles_file: name of the file to which to save/store all profiles.
31         @views: all positional arguments are views.
32         @kwargs: all keyword arguments should be default profiles.
33         """
34         self.views = list(views)
35         self.settings = {}
36
37         # these cannot be edited
38         self.default_profiles = kwargs
39
40         self.profiles_file = profiles_file
41         self.profiles = {}
42
43         self.fetch_profiles()
44
45     def notify(self, reason, *args):
46         """Dispatches all views."""
47         for v in self.views:
48             v.update_view(reason, *args)
49
50     def set_profile(self, name, dictionary):
51         """Inserts a new profile into the profile dictionary.
52
53         Issues a MODEL_PROFILE_CHANGE event and passes the name of
54         the profile to the views along with all new profiles.
55         """
56         notify = name not in self.profiles
57         self.profiles.setdefault(name, {}).update(dictionary)
58         if notify:
59             self.notify(MODEL_PROFILE_CHANGE, name, self.profiles)
60
61     def delete_profile(self, name):
62         """Removes profile <name>, if it exists.
63
64         If, and only if, the profile name existed a MODEL_PROFILE_CHANGE
65         event is issued to all views. No new selected profile will be passed.
66         If name was the active profile, then the views can select a new one.
67         """
68         notify = name in self.profiles
69         del self.profiles[name]
70         if notify:
71             self.notify(MODEL_PROFILE_CHANGE, '', self.profiles)
72
73     def load_profile(self, settings):
74         """Directly loads the profile specified by settings.
75
76         Dispatches the views with a MODEL_VALUE_CHANGE event.
77         """
78         self.settings.update(settings)
79         self.notify(MODEL_VALUE_CHANGE, self.settings)
80
81     def load_named_profile(self, name):
82         """If <name> is a valid profile, make it active.
83
84         Inspects the default profiles before the custom profiles and dispatches
85         the views with a MODEL_VALUE_CHANGE event.
86         """
87         notify = True
88         if name in self.default_profiles:
89             self.settings.update(self.default_profiles[name])
90         elif name in self.profiles:
91             self.settings.update(self.profiles[name])
92         else:
93             notify = False
94         if notify:
95             self.notify(MODEL_VALUE_CHANGE, self.settings)
96
97     def fetch_profiles(self):
98         """Load non-default profiles from disk."""
99         with open(self.profiles_file, 'r') as f:
100             name = None
101             for line in f:
102                 if name is None:
103                     name = line.rstrip()
104                 else:
105                     self.profiles[name] = self.string_to_profile(line.rstrip())
106                     name = None
107
108     def store_profiles(self):
109         """Write all non-default profiles to disk."""
110         with open(self.profiles_file, 'w') as f:
111             for name in sorted(self.profiles.keys()):
112                 f.write('%s\n%s\n' % (name, self.profile_to_string(self.profiles[name])))
113
114     def read_settings(self):
115         """Read current configuration from the system.
116
117         Needs to be implemented by sub-classes.
118         """
119         pass
120
121     def write_settings(self):
122         """Write current configuration to system.
123
124         Needs to be implemented by sub-classes.
125         """
126         pass
127
128     def profile_to_string(self, dct):
129         """Convert a configuration profile to a single-line string.
130         
131         Sub-classes should override this with a more optimal implementation.
132         """
133         return str(dct)
134     
135     def string_to_profile(self, s):
136         """Convert a profile string to a configuration profile.
137
138         This is intended to be overridden by sub-classes.
139         It should be the inverse operation of profile_to_string.
140         """
141         return eval(s)