Mercurial > fife-parpg
view clients/editor/scripts/settings.py @ 271:987a4ea829c1
Save editor settings to proper directory (~/.fife on linux, %APPDATA%\fife on Win32)
author | cheesesucker@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Tue, 16 Jun 2009 02:33:35 +0000 |
parents | 51cc05d862f2 |
children | 815354ba295e |
line wrap: on
line source
import shutil import os try: import xml.etree.cElementTree as ET except: import xml.etree.ElementTree as ET class Settings(object): """ This class provides an interface for retrieving and putting settings to an XML-file. Only one instance of this class may exist, or a RuntimeWarning exception will be raised. Use Settings.instance or Editor.getSettings() to retrieve an instance of this class. """ instance = None # Points to the first initialized instance of this class def __init__(self, *args, **kwargs): if Settings.instance is not None: raise RuntimeWarning("Settings instance has already been initialized! Use Editor.getSettings instead") Settings.instance = self self._appdata = GetUserDataDirectory("fife", "editor") if os.path.exists(self._appdata+'/settings.xml') is False: shutil.copyfile('settings-dist.xml', self._appdata+'/settings.xml') self.tree = ET.parse(self._appdata+'/settings.xml') self.root_element = self.tree.getroot() self.validateTree() def validateTree(self): """ Iterates the settings tree and prints warning when an invalid tag is found """ for c in self.root_element.getchildren(): if c.tag != "Module": print "Invalid tag in settings.xml. Expected Module, got: ", c.tag elif c.get("name", "") == "": print "Invalid tag in settings.xml. Module name is empty." else: for e in c.getchildren(): if e.tag != "Setting": print "Invalid tag in settings.xml in module: ",c.tag, print ". Expected Setting, got: ", e.tag elif c.get("name", "") == "": print "Invalid tag in settings.xml in module: ",c.tag, print ". Setting name is empty", e.tag def getModuleTree(self, module): """ Returns a module element from the settings tree. If no module with the specified name exists, a new element will be created. """ if not isinstance(module, str) and not isinstance(module, unicode): raise AttributeError("Settings:getModuleTree: Invalid type for module argument.") for c in self.root_element.getchildren(): if c.tag == "Module" and c.get("name", "") == module: return c # Create module return ET.SubElement(self.root_element, "Module", {"name":module}) def get(self, module, name, defaultValue=None): """ Gets the value of a specified setting defaultValue is returned if the setting does not exist """ if not isinstance(name, str) and not isinstance(name, unicode): raise AttributeError("Settings:get: Invalid type for name argument.") moduleTree = self.getModuleTree(module) element = None for e in moduleTree.getchildren(): if e.tag == "Setting" and e.get("name", "") == name: element = e break else: return defaultValue e_value = element.text e_strip = element.get("strip", "1").strip().lower() e_type = str(element.get("type", "str")).strip() if e_value is None: return defaultValue # Strip value if e_strip == "" or e_strip == "false" or e_strip == "no" or e_strip == "0": e_strip = False else: e_strip = True if e_type == "str" or e_type == "unicode": if e_strip: e_value = e_value.strip() else: e_value = e_value.strip() # Return value if e_type == 'int': return int(e_value) elif e_type == 'float': return float(e_value) elif e_type == 'bool': e_value = e_value.lower() if e_value == "" or e_value == "false" or e_value == "no" or e_value == "0": return False else: return True elif e_type == 'str': return str(e_value) elif e_type == 'unicode': return unicode(e_value) elif e_type == 'list': return self._deserializeList(e_value) elif e_type == 'dict': return self._deserializeDict(e_value) def set(self, module, name, value, extra_attrs={}): """ Sets a setting to specified value. Parameters: module (str|unicode): Module where the setting should be set name (str|unicode): Name of setting value: Value to assign to setting extra_attrs (dict): Extra attributes to be stored in the XML-file """ if not isinstance(name, str) and not isinstance(name, unicode): raise AttributeError("Settings:set: Invalid type for name argument.") moduleTree = self.getModuleTree(module) e_type = "str" if isinstance(value, bool): # This must be before int e_type = "bool" value = str(value) elif isinstance(value, int): e_type = "int" value = str(value) elif isinstance(value, float): e_type = "float" value = str(value) elif isinstance(value, unicode): e_type = "unicode" value = unicode(value) elif isinstance(value, list): e_type = "list" value = self._serializeList(value) elif isinstance(value, dict): e_type = "dict" value = self._serializeDict(value) else: e_type = "str" value = str(value) for e in moduleTree.getchildren(): if e.tag != "Setting": continue if e.get("name", "") == name: e.text = value break else: attrs = {"name":name, "type":e_type} for k in extra_attrs: if k not in attrs: attrs[k] = extra_args[k] elm = ET.SubElement(moduleTree, "Setting", attrs) elm.text = value def saveSettings(self): """ Save settings into settings.xml """ self._indent(self.root_element) self.tree.write(self._appdata+'/settings.xml', 'UTF-8') def _indent(self, elem, level=0): """ Adds whitespace, so the resulting XML-file is properly indented. Shamelessly stolen from http://effbot.org/zone/element-lib.htm """ i = "\n" + level*" " if len(elem): if not elem.text or not elem.text.strip(): elem.text = i + " " if not elem.tail or not elem.tail.strip(): elem.tail = i for elem in elem: self._indent(elem, level+1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = i # FIXME: # These serialization functions are not reliable at all # This will only serialize the first level of a dict or list # It will not check the types nor the content for conflicts. # Perhaps we should add a small serialization library? def _serializeList(self, list): """ Serializes a list, so it can be stored in a text file """ return " ; ".join(list) def _deserializeList(self, string): """ Deserializes a list back into a list object """ return string.split(" ; ") def _serializeDict(self, dict): """ Serializes a list, so it can be stored in a text file """ serial = "" for key in dict: value = dict[key] if serial != "": serial += " ; " serial += str(key)+" : "+str(value) return serial def _deserializeDict(self, serial): """ Deserializes a list back into a dict object """ dict = {} items = serial.split(" ; ") for i in items: kv_pair = i.split(" : ") dict[kv_pair[0]] = kv_pair[1] return dict def GetUserDataDirectory(vendor, appname): """ Gets location to store the settings file, depending on OS. See: Brian Vanderburg II @ http://mail.python.org/pipermail/python-list/2008-May/660779.html """ dir = "" # WINDOWS if os.name == "nt": # Try env APPDATA or USERPROFILE or HOMEDRIVE/HOMEPATH if "APPDATA" in os.environ: dir = os.environ["APPDATA"] if ((dir is None) or (not os.path.isdir(dir))) and ("USERPROFILE" in os.environ): dir = os.environ["USERPROFILE"] if os.path.isdir(os.path.join(dir, "Application Data")): dir = os.path.join(dir, "Application Data") if ((dir is None) or (not os.path.isdir(dir))) and ("HOMEDRIVE" in os.environ) and ("HOMEPATH" in os.environ): dir = os.environ["HOMEDRIVE"] + os.environ["HOMEPATH"] if os.path.isdir(os.path.join(dir, "Application Data")): dir = os.path.join(dir, "Application Data") if (dir is None) or (not os.path.isdir(dir)): dir = os.path.expanduser("~") # On windows, add vendor and app name dir = os.path.join(dir, vendor, appname) # Mac elif os.name == "mac": # ?? may not be entirely correct dir = os.path.expanduser("~") dir = os.path.join(dir, "Library", "Application Support") dir = os.path.join(dir, vendor, appname) # Unix/Linux/all others if dir is None: dir = os.path.expanduser("~") dir = os.path.join(dir, "."+vendor, appname) # Create vendor/appname folder if it doesn't exist if not os.path.isdir(dir): os.makedirs(dir) print dir return dir