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

Source Code for Module screenlets.backend

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