Source code for serge.blocks.themes

"""Classes to implement themes

Themes are sets of settings that may affect anything. The idea is that
you may have a number of settings to do with visuals on a world and you
want to control those centrally, potentially also allowing things
to switch during a game.

The themes are managed by a manager.

"""

[docs]class BadThemeDefinition(Exception): """The theme was not of the right format"""
[docs]class MissingDefault(Exception): """There was no default theme"""
[docs]class MissingSchema(Exception): """There was no schema in the theme definition"""
[docs]class ThemeNotFound(Exception): """The named scheme was not found"""
[docs]class BadInheritance(Exception): """A theme subclass was not found"""
[docs]class BadThemeFile(Exception): """The specified theme file was not found"""
[docs]class PropertyNotFound(Exception): """Could not find a property"""
[docs]class InvalidFormat(Exception): """The format for the data was invalid"""
[docs]class Manager(object): """Manages a theme""" def __init__(self): """Initialise the manager""" self.themes = {} self.current_theme = None
[docs] def loadFrom(self, text): """Load a theme from some text The theme is a dictionary where each entry is either a theme or the definition of the schema or the special entry __default__, which gives the name of the default theme. If there is an entry then it is a tuple with the name of the base theme class followed by a dictionary of entries which overide the base class. Classes are really just the name of another theme. """ # # Read the definition try: temp_themes = eval(text) except Exception, err: raise BadThemeDefinition('Could not read themes from text: %s' % err) return self.load(temp_themes)
[docs] def load(self, themes): """Load definitions from a dictionary""" if not isinstance(themes, dict): raise BadThemeDefinition('Theme definition was not a dictionary') # # Get the default theme try: self.current_theme = themes['__default__'] except KeyError: raise MissingDefault('The theme definition did not specify a default theme') # self.themes = themes self._verifyThemes()
[docs] def loadFromFile(self, filename): """Load a theme definition from a file""" try: with file(filename, 'r') as f: return self.loadFrom(f.read()) except (IOError, OSError), err: raise BadThemeFile('Unable to load theme from "%s": %s' % (filename, err))
[docs] def selectTheme(self, name): """Select the named theme""" if name in self.themes: self.current_theme = name else: raise ThemeNotFound('The theme "%s" was not found' % name)
[docs] def getTheme(self, name): """Return a theme object with a default of the given name""" new = Manager() new.themes = self.themes new.selectTheme(name) return new
[docs] def hasTheme(self, name): """Return True if we have this theme""" return name in self.themes
[docs] def getProperty(self, name, from_theme=None): """Return the named property""" try: theme = self.themes[from_theme if from_theme else self.current_theme] except KeyError: raise BadInheritance('The theme "%s" was not found in the inheritance tree' % from_theme) try: return theme[1][name] except KeyError: if theme[0] == '': raise PropertyNotFound('Could not find property "%s"' % name) else: return self.getProperty(name, theme[0])
[docs] def getPropertyWithDefault(self, name, default, from_theme=None): """Return a property and if it is missing then return the default value Use this method sparingly. It puts default values in source code rather than in the theme files. """ try: return self.getProperty(name, from_theme) except PropertyNotFound: return default
[docs] def setProperty(self, name, value, from_theme=None): """Set a property in a theme""" try: theme = self.themes[from_theme if from_theme else self.current_theme] except KeyError: raise BadInheritance('The theme "%s" was not found in the inheritance tree' % from_theme) theme[1][name] = value
[docs] def updateFromString(self, string): """Update the theme from a string of data Data should be provided as comma separated values like name="bob",value=123,etc """ for item in string.split(','): parts = item.split('=') if len(parts) != 2: raise InvalidFormat('The string was invalid, should be a=b,c=d etc (%s)' % item) self.setProperty(parts[0], eval(parts[1]))
def _verifyThemes(self): """Sanity check on themes""" # # Is there a base schema and are all inheritance items found schema = False for name, theme in self.themes.iteritems(): if name != '__default__': if theme[0] == '': schema = True elif theme[0] not in self.themes: raise BadInheritance('The inherited name "%s" for theme "%s" was not found' % (theme[0], name)) if not schema: raise MissingSchema('There was no schema definition in the theme')