Package screenlets :: Module backend
[hide private]
[frames] | no frames]

Source Code for Module screenlets.backend

  1  # This program is free software: you can redistribute it and/or modify 
  2  # it under the terms of the GNU General Public License as published by 
  3  # the Free Software Foundation, either version 3 of the License, or 
  4  # (at your option) any later version. 
  5  #  
  6  # This program is distributed in the hope that it will be useful, 
  7  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  8  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  9  # GNU General Public License for more details. 
 10  #  
 11  # You should have received a copy of the GNU General Public License 
 12  # along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 13  # 
 14  # screenlets.backend (c) RYX (aka Rico Pfaus) 2007 <ryx@ryxperience.com> 
 15  # 
 16  # INFO: 
 17  # - The backend offers an abstracted way of saving a Screenlet's data 
 18  # 
 19  # TODO:  
 20  # - add "type"-argument to save_option and read_option to be able to correctly 
 21  #   set the values in GconfBackend (instead of storing only strings). 
 22  # 
 23   
 24  import glob 
 25  import os 
 26  import gtk 
 27  import gobject 
 28  import gettext 
 29  import screenlets 
 30   
 31  gettext.textdomain('screenlets') 
 32  gettext.bindtextdomain('screenlets', screenlets.INSTALL_PREFIX +  '/share/locale') 
 33   
34 -def _(s):
35 return gettext.gettext(s)
36 37 38 try: 39 import gconf 40 except: 41 print "GConf python module not found. GConf settings backend is disabled." 42 43
44 -class ScreenletsBackend(object):
45 """The backend performs the loading/saving of the 'key=value'-strings. 46 Extend this superclass to implement different saving-backends.""" 47
48 - def __init__ (self):
49 pass
50
51 - def delete_instance (self, id):
52 """Delete an instance's configuration by its id.""" 53 pass
54
55 - def flush (self):
56 """Immediately store all values to disk (in case the backend doesn't 57 save in realtime anyway.""" 58 pass
59
60 - def load_option (self, id, name):
61 """Load one option for the instance with the given id.""" 62 pass
63
64 - def load_instance (self, id):
65 """Load all options for the instance with the given id.""" 66 pass
67
68 - def save_option (self, id, name, value):
69 """Save one option for the instance with the given id.""" 70 pass
71 72
73 -class GconfBackend (ScreenletsBackend):
74 """Backend for storing settings in the GConf registry""" 75 76 gconf_dir = '/apps/screenlets/' 77
78 - def __init__ (self):
79 ScreenletsBackend.__init__(self) 80 print 'GConfBackend: initializing' 81 self.client = gconf.client_get_default()
82
83 - def delete_instance (self, id):
84 """Delete an instance's configuration by its id.""" 85 os.system('gconftool-2 --recursive-unset ' + self.key + id) 86 return True
87
88 - def flush (self):
89 """Immediately store all values to disk (in case the backend doesn't 90 save in realtime anyway.""" 91 pass #No need, GConf saves in realtime
92
93 - def load_option (self, id, name):
94 """Load one option for the instance with the given id.""" 95 return self.client.get_string(self.gconf_dir + id + '/' + name)
96
97 - def load_instance (self, id):
98 """Load all options for the instance with the given id.""" 99 keys = [] 100 vals = [] 101 for i in self.client.all_entries(self.gconf_dir + id): 102 keys.append(i.key.split('/')[4]) 103 vals.append(self.client.get_string(i.key)) 104 return dict(zip(keys, vals)) 105 return None
106
107 - def save_option (self, id, name, value):
108 """Save one option for the instance with the given id.""" 109 self.client.set_string(self.gconf_dir + id + '/' + name, value) 110 print 'Saved option %s%s/%s = %s' % (self.gconf_dir, id, name, value)
111 112
113 -class CachingBackend (ScreenletsBackend):
114 """A backend that stores the settings in arrays and saves after a short 115 interval to avoid overhead when multiple values are set within a short time. 116 The data gets saved into $HOME/.config/Screenlets/<Screenletname>/, in a 117 file for each element (named like its id with the extension '.ini').""" 118 119 # internals 120 __instances = {} # a dict with (id:dict)-entries cntaining the data 121 __delay_time = 3000 # delay to wait before performing save 122 __timeout = None # the id of the timeout-function 123 __queue = [] # list with ids of instances that need saving 124 125 # attribs 126 path = '' # the path to store the files 127 128 # Constructor
129 - def __init__ (self, path):
130 ScreenletsBackend.__init__(self) 131 self.path = path 132 self.__load_cache()
133
134 - def delete_instance (self, id):
135 """Delete an instance from the list and from the filesystem.""" 136 if self.__instances.has_key(id): 137 del self.__instances[id] 138 try: 139 import os 140 os.remove(self.path + id + '.ini') 141 except Exception,ex: 142 print ex 143 print "Temporary file didn't exist - nothing to remove." 144 return False 145 print "CachingBackend: <#%s> removed!" % id 146 return True
147
148 - def flush (self):
149 """Immediately save all pending data.""" 150 self.__save_cache()
151
152 - def save_option (self, id, name, value):
153 """Save option for an instance to cache and start saving-timeout 154 for that element (value must be of type string).""" 155 # create key for option, if not existent yet 156 if self.__instances.has_key(id) == False: 157 self.__instances[id] = {} 158 # set option in array 159 self.__instances[id][name] = str(value) 160 #print "CachingBackend.save_option: "+name+"="+self.__instances[id][name] 161 # if id is not already in queue, add the id to the queue 162 if self.__queue.count(id) == 0: 163 self.__queue.append(id) 164 # reset timeout and start new 165 if self.__timeout: 166 gobject.source_remove(self.__timeout) 167 self.__timeout = gobject.timeout_add(self.__delay_time, 168 self.__save_cache)#, id)
169
170 - def load_option (self, id, name):
171 """TODO: Load option from the backend (returned as str).""" 172 return self.__instances[id][name]
173
174 - def load_instance (self, id):
175 """Load all options for the instance with the given id.""" 176 #print "Load element: "+id 177 if self.__instances.has_key(id): 178 return self.__instances[id] 179 return None
180
181 - def __load_cache (self):
182 """Load all cached files from path.""" 183 # perform saving 184 print "CachingBackend: Loading instances from cache" 185 # get dir content of self.path 186 dirname = self.path 187 dirlst = glob.glob(dirname + '*') 188 tdlen = len(dirname) 189 lst = [] 190 for fname in dirlst: 191 dname = fname[tdlen:] 192 if dname.endswith('.ini'): 193 id = dname[:-4] 194 print "CachingBackend: Loading <%s>" % id 195 #print "ID: "+id 196 if self.__instances.has_key(id) == False: 197 self.__instances[id] = {} 198 # open file 199 try: 200 f = open(fname, 'r') 201 lines = f.readlines() 202 # read all options for this element from file 203 for line in lines: 204 #print "LOAD: "+line[:-1] 205 parts = line[:-1].split('=', 1) 206 if len(parts) > 1: 207 # undocumented features to resize screenlet dynamically on first launch 208 # width, height must precede rel_x and rel_y with "_" 209 # by boamaod for Estobuntu 210 if parts[0] == 'x': 211 if parts[1].startswith("*"): # if * is added, take distance from the opposite side 212 parts[1] = parts[1].strip("*") 213 add_width = 0 214 if parts[1].startswith("_"): # if _ is added, take it to be right corner 215 add_width = int(float(self.__instances[id]["width"])*float(self.__instances[id]["scale"])) 216 print "ADD W", add_width 217 parts[1] = str(gtk.gdk.screen_width() - int(parts[1].strip("_")) - add_width) 218 print ">>>X", parts[1] 219 if parts[0] == 'y': 220 if parts[1].startswith("*"): # if * is added, take distance from the opposite side 221 parts[1] = parts[1].strip("*") 222 add_height = 0 223 if parts[1].startswith("_"): # if _ is added, take it to be bottom corner 224 add_height = int(float(self.__instances[id]["height"])*float(self.__instances[id]["scale"])) 225 print "ADD H", add_height 226 parts[1] = str(gtk.gdk.screen_height() - int(parts[1].strip("_")) - add_height) 227 print ">>>Y", parts[1] 228 if parts[0] == 'rel_x': 229 parts[0] = 'x' 230 add_width = 0 231 if parts[1].startswith("_"): # if _ is added, take it to be right corner 232 add_width = int(float(self.__instances[id]["width"])*float(self.__instances[id]["scale"])) 233 print "ADD W", add_width 234 parts[1] = str(int(gtk.gdk.screen_width()*float(parts[1].strip("_"))) - add_width) 235 print ">>>X", parts[1] 236 if parts[0] == 'rel_y': 237 parts[0] = 'y' 238 add_height = 0 239 if parts[1].startswith("_"): # if _ is added, take it to be bottom corner 240 add_height = int(float(self.__instances[id]["height"])*float(self.__instances[id]["scale"])) 241 print "ADD H", add_height 242 parts[1] = str(int(gtk.gdk.screen_height()*float(parts[1].strip("_"))) - add_height) 243 print ">>>Y", parts[1] 244 if parts[0] == 'rel_scale': 245 parts[0] = 'scale' 246 scale = float(self.__instances[id]["scale"]) 247 initial_scale = scale + float(gtk.gdk.screen_height()*gtk.gdk.screen_width())/float(parts[1]) 248 if initial_scale < 1.5: 249 initial_scale = 1.5 250 if initial_scale > 3: 251 initial_scale = 3 252 parts[1] = str(initial_scale) 253 # parts[1] = str(gtk.gdk.screen_height()/float(parts[1])) 254 print ">>>SCALE", parts[1] 255 if parts[0] == 'rel_font_name': 256 parts[0] = 'font_name' 257 print "|||", parts[1] 258 font_parts = parts[1].rsplit(" ") 259 parts[1]="" 260 for fp in font_parts: 261 if len(fp)>0 and len(fp.strip("0123456789."))==0: 262 parts[1]+= str( round(float(fp)*float(self.__instances[id]["scale"]), 1) ) + " " 263 else: 264 parts[1]+= fp + " " 265 parts[1] = parts[1].strip(" ") 266 print ">>>FONT_NAME", parts[1] 267 # End of dynamic resize section 268 print "%s='%s'" % (parts[0], parts[1]) 269 self.__instances[id][parts[0]] = parts[1] 270 f.close() 271 except Exception, ex: 272 print "Error while loading options: %s" % str(ex)
273
274 - def __save_cache (self):
275 """Save the cache (for all pending instances in queue) to self.path.""" 276 # loop through all instances in queue: 277 for id in self.__queue: 278 # if element with id not exists, remove it and break 279 if self.__instances.has_key(id) == False: 280 print "Queue-element <%s> not found (already removed?)!" % id 281 self.__queue.remove(id) 282 break 283 # create list with options 284 #print "CachingBackend: Saving <#%s> :) ..." % id 285 lst = [] 286 for oname in self.__instances[id]: 287 lst.append([oname, self.__instances[id][oname]]) 288 # and save them (if any) 289 if len(lst) > 0: 290 self.__save_settings (self.path + id + '.ini', lst) 291 # clear queue 292 self.__queue = [] 293 # NOT continue the timeout-function (!!!!!) 294 return False
295
296 - def __save_settings (self, filename, lst):
297 """ Try to save settings in a file, first save this to a temporal file avoid encodings a disk full errors """ 298 filenametmp = filename + '.tmp' 299 isOk = True 300 newini = '' 301 try: 302 # Is posible to fail with encoding error? 303 for el in lst: 304 newini += "%s=%s\n" % (el[0], el[1]) 305 except: 306 isOk = False 307 print "error while convert config to string (encoding error?), I lose your last changes :'(" 308 309 if isOk: 310 # Write the new settings to a temporal file, disk full, encoding, rights may fails at this point. 311 try: 312 open(filenametmp, 'w').write(newini) 313 except: 314 isOk = False 315 print "error while saving configuration to a temporal file %s, disk full?" % filenametmp 316 317 if isOk: 318 # Move saved settings to definitive configuration file, disk error o incorrect rights may fails at this point. 319 try: 320 import shutil 321 shutil.move(filenametmp, filename) 322 except: 323 print "error while moving temporal file to configuration file, %s > %s, sorry, I lose your settings. :'(" % (filenametmp, filename)
324