from Tools.Profile import profile, profile_final profile("LOAD:ElementTree") import xml.etree.cElementTree from os import path profile("LOAD:enigma_skin") from enigma import eSize, ePoint, gFont, eWindow, eLabel, ePixmap, eWindowStyleManager, \ addFont, gRGB, eWindowStyleSkinned from Components.config import ConfigSubsection, ConfigText, config from Components.Converter.Converter import Converter from Components.Sources.Source import Source, ObsoleteSource from Tools.Directories import resolveFilename, SCOPE_SKIN, SCOPE_SKIN_IMAGE, SCOPE_FONTS, SCOPE_CURRENT_SKIN, fileExists from Tools.Import import my_import from Tools.LoadPixmap import LoadPixmap from Tools.XMLTools import elementsWithTag, mergeText colorNames = dict() def dump(x, i=0): print " " * i + str(x) try: for n in x.childNodes: dump(n, i + 1) except: None class SkinError(Exception): def __init__(self, message): self.message = message def __str__(self): return self.message dom_skins = [ ] def loadSkin(name): # read the skin filename = resolveFilename(SCOPE_SKIN, name) mpath = path.dirname(filename) + "/" dom_skins.append((mpath, xml.etree.cElementTree.parse(filename).getroot())) # we do our best to always select the "right" value # skins are loaded in order of priority: skin with # highest priority is loaded last, usually the user-provided # skin. # currently, loadSingleSkinData (colors, bordersets etc.) # are applied one-after-each, in order of ascending priority. # the dom_skin will keep all screens in descending priority, # so the first screen found will be used. # example: loadSkin("nemesis_greenline/skin.xml") config.skin = ConfigSubsection() config.skin.primary_skin = ConfigText(default = "skin.xml") profile("LoadSkin") try: loadSkin(config.skin.primary_skin.value) except (SkinError, IOError, AssertionError), err: print "SKIN ERROR:", err print "defaulting to standard skin..." config.skin.primary_skin.value = 'skin.xml' loadSkin('skin.xml') profile("LoadSkinDefault") loadSkin('skin_default.xml') profile("LoadSkinDefaultDone") def parsePosition(str): x, y = str.split(',') return ePoint(int(x), int(y)) def parseSize(str): x, y = str.split(',') return eSize(int(x), int(y)) def parseFont(str): name, size = str.split(';') return gFont(name, int(size)) def parseColor(str): if str[0] != '#': try: return colorNames[str] except: raise SkinError("color '%s' must be #aarrggbb or valid named color" % (str)) return gRGB(int(str[1:], 0x10)) def collectAttributes(skinAttributes, node, skin_path_prefix=None, ignore=[]): # walk all attributes for a in node.items(): #print a attrib = a[0] value = a[1] if attrib in ["pixmap", "pointer", "seek_pointer", "backgroundPixmap", "selectionPixmap"]: value = resolveFilename(SCOPE_SKIN_IMAGE, value, path_prefix=skin_path_prefix) if attrib not in ignore: skinAttributes.append((attrib, value)) def loadPixmap(path, desktop): cached = False option = path.find("#") if option != -1: options = path[option+1:].split(',') path = path[:option] cached = "cached" in options ptr = LoadPixmap(path, desktop, cached) if ptr is None: raise SkinError("pixmap file %s not found!" % (path)) return ptr def applySingleAttribute(guiObject, desktop, attrib, value): # and set attributes try: if attrib == 'position': guiObject.move(parsePosition(value)) elif attrib == 'size': guiObject.resize(parseSize(value)) elif attrib == 'title': guiObject.setTitle(_(value)) elif attrib == 'text': guiObject.setText(_(value)) elif attrib == 'font': guiObject.setFont(parseFont(value)) elif attrib == 'zPosition': guiObject.setZPosition(int(value)) elif attrib in ["pixmap", "backgroundPixmap", "selectionPixmap"]: ptr = loadPixmap(value, desktop) # this should already have been filename-resolved. if attrib == "pixmap": guiObject.setPixmap(ptr) elif attrib == "backgroundPixmap": guiObject.setBackgroundPicture(ptr) elif attrib == "selectionPixmap": guiObject.setSelectionPicture(ptr) # guiObject.setPixmapFromFile(value) elif attrib == "alphatest": # used by ePixmap guiObject.setAlphatest( { "on": 1, "off": 0, "blend": 2, }[value]) elif attrib == "orientation": # used by eSlider try: guiObject.setOrientation( { "orVertical": guiObject.orVertical, "orHorizontal": guiObject.orHorizontal }[value]) except KeyError: print "oprientation must be either orVertical or orHorizontal!" elif attrib == "valign": try: guiObject.setVAlign( { "top": guiObject.alignTop, "center": guiObject.alignCenter, "bottom": guiObject.alignBottom }[value]) except KeyError: print "valign must be either top, center or bottom!" elif attrib == "halign": try: guiObject.setHAlign( { "left": guiObject.alignLeft, "center": guiObject.alignCenter, "right": guiObject.alignRight, "block": guiObject.alignBlock }[value]) except KeyError: print "halign must be either left, center, right or block!" elif attrib == "flags": flags = value.split(',') for f in flags: try: fv = eWindow.__dict__[f] guiObject.setFlag(fv) except KeyError: print "illegal flag %s!" % f elif attrib == "backgroundColor": guiObject.setBackgroundColor(parseColor(value)) elif attrib == "backgroundColorSelected": guiObject.setBackgroundColorSelected(parseColor(value)) elif attrib == "foregroundColor": guiObject.setForegroundColor(parseColor(value)) elif attrib == "foregroundColorSelected": guiObject.setForegroundColorSelected(parseColor(value)) elif attrib == "shadowColor": guiObject.setShadowColor(parseColor(value)) elif attrib == "selectionDisabled": guiObject.setSelectionEnable(0) elif attrib == "transparent": guiObject.setTransparent(int(value)) elif attrib == "borderColor": guiObject.setBorderColor(parseColor(value)) elif attrib == "borderWidth": guiObject.setBorderWidth(int(value)) elif attrib == "scrollbarMode": guiObject.setScrollbarMode( { "showOnDemand": guiObject.showOnDemand, "showAlways": guiObject.showAlways, "showNever": guiObject.showNever }[value]) elif attrib == "enableWrapAround": guiObject.setWrapAround(True) elif attrib == "pointer" or attrib == "seek_pointer": (name, pos) = value.split(':') pos = parsePosition(pos) ptr = loadPixmap(name, desktop) guiObject.setPointer({"pointer": 0, "seek_pointer": 1}[attrib], ptr, pos) elif attrib == 'shadowOffset': guiObject.setShadowOffset(parsePosition(value)) elif attrib == 'noWrap': guiObject.setNoWrap(1) else: raise SkinError("unsupported attribute " + attrib + "=" + value) except int: # AttributeError: print "widget %s (%s) doesn't support attribute %s!" % ("", guiObject.__class__.__name__, attrib) def applyAllAttributes(guiObject, desktop, attributes): for (attrib, value) in attributes: applySingleAttribute(guiObject, desktop, attrib, value) def loadSingleSkinData(desktop, skin, path_prefix): """loads skin data like colors, windowstyle etc.""" assert skin.tag == "skin", "root element in skin must be 'skin'!" #print "***SKIN: ", path_prefix for c in skin.getiterator("output"): id = c.get('id') if id: id = int(id) else: id = 0 if id == 0: # framebuffer for res in c.findall("resolution"): get_attr = c.attrib.get xres = get_attr("xres") if xres: xres = int(xres) else: xres = 720 yres = get_attr("yres") if yres: yres = int(yres) else: yres = 576 bpp = get_attr("bpp") if bpp: bpp = int(bpp) else: bpp = 32 #print "Resolution:", xres,yres,bpp from enigma import gFBDC i = gFBDC.getInstance() i.setResolution(xres, yres) if bpp != 32: # load palette (not yet implemented) pass for c in skin.getiterator("colors"): for color in c.findall("color"): get_attr = color.attrib.get name = get_attr("name") color = get_attr("value") if name and color: colorNames[name] = parseColor(color) #print "Color:", name, color else: raise ("need color and name, got %s %s" % (name, color)) for c in skin.getiterator("fonts"): for font in c.findall("font"): get_attr = font.attrib.get filename = get_attr("filename", "") name = get_attr("name", "Regular") scale = get_attr("scale") if scale: scale = int(scale) else: scale = 100 is_replacement = get_attr("replacement") and True or False resolved_font = resolveFilename(SCOPE_FONTS, filename, path_prefix=path_prefix) if not fileExists(resolved_font): #when font is not available look at current skin path skin_path = resolveFilename(SCOPE_CURRENT_SKIN, filename) if fileExists(skin_path): resolved_font = skin_path addFont(resolved_font, name, scale, is_replacement) #print "Font: ", resolved_font, name, scale, is_replacement for windowstyle in skin.getiterator("windowstyle"): style = eWindowStyleSkinned() id = windowstyle.attrib.get("id") if id: id = int(id) else: id = 0 #print "windowstyle:", id # defaults font = gFont("Regular", 20) offset = eSize(20, 5) for title in windowstyle.findall("title"): get_attr = title.attrib.get offset = parseSize(get_attr("offset")) font = parseFont(get_attr("font")) style.setTitleFont(font); style.setTitleOffset(offset) #print " ", font, offset for borderset in windowstyle.getiterator("borderset"): bsName = str(borderset.get("name")) for pixmap in borderset.findall("pixmap"): get_attr = pixmap.attrib.get bpName = get_attr("pos") filename = get_attr("filename") if filename and bpName: png = loadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, filename, path_prefix=path_prefix), desktop) style.setPixmap(eWindowStyleSkinned.__dict__[bsName], eWindowStyleSkinned.__dict__[bpName], png) #print " borderset:", bpName, filename for color in windowstyle.findall("color"): get_attr = color.attrib.get type = get_attr("name") color = parseColor(get_attr("color")) try: style.setColor(eWindowStyleSkinned.__dict__["col" + type], color) except: raise ("Unknown color %s" % (type)) #pass #print " color:", type, color x = eWindowStyleManager.getInstance() x.setStyle(id, style) def loadSkinData(desktop): skins = dom_skins[:] skins.reverse() for (path, dom_skin) in skins: loadSingleSkinData(desktop, dom_skin, path) def lookupScreen(name): for (path, skin) in dom_skins: # first, find the corresponding screen element for x in skin.findall("screen"): if x.attrib.get('name', '') == name: return x, path return None, None def readSkin(screen, skin, names, desktop): if not isinstance(names, list): names = [names] name = "" % screen.__class__.__name__ # try all skins, first existing one have priority for n in names: myscreen, path = lookupScreen(n) if myscreen is not None: # use this name for debug output name = n break # otherwise try embedded skin myscreen = myscreen or getattr(screen, "parsedSkin", None) # try uncompiled embedded skin if myscreen is None and getattr(screen, "skin", None): print "Looking for embedded skin" myscreen = screen.parsedSkin = xml.etree.cElementTree.fromstring(screen.skin) #assert myscreen is not None, "no skin for screen '" + repr(names) + "' found!" if myscreen is None: print "No skin to read..." emptySkin = "" myscreen = screen.parsedSkin = xml.etree.cElementTree.fromstring(emptySkin) screen.skinAttributes = [ ] skin_path_prefix = getattr(screen, "skin_path", path) collectAttributes(screen.skinAttributes, myscreen, skin_path_prefix, ignore=["name"]) screen.additionalWidgets = [ ] screen.renderer = [ ] visited_components = set() # now walk all widgets for widget in myscreen.getiterator("widget"): # ok, we either have 1:1-mapped widgets ('old style'), or 1:n-mapped # widgets (source->renderer). wname = widget.get('name') wsource = widget.get('source') if wname is None and wsource is None: print "widget has no name and no source!" continue if wname: #print "Widget name=", wname visited_components.add(wname) # get corresponding 'gui' object try: attributes = screen[wname].skinAttributes = [ ] except: raise SkinError("component with name '" + wname + "' was not found in skin of screen '" + name + "'!") #print "WARNING: component with name '" + wname + "' was not found in skin of screen '" + name + "'!" # assert screen[wname] is not Source # and collect attributes for this collectAttributes(attributes, widget, skin_path_prefix, ignore=['name']) elif wsource: # get corresponding source #print "Widget source=", wsource while True: # until we found a non-obsolete source # parse our current "wsource", which might specifiy a "related screen" before the dot, # for example to reference a parent, global or session-global screen. scr = screen # resolve all path components path = wsource.split('.') while len(path) > 1: scr = screen.getRelatedScreen(path[0]) if scr is None: #print wsource #print name raise SkinError("specified related screen '" + wsource + "' was not found in screen '" + name + "'!") path = path[1:] # resolve the source. source = scr.get(path[0]) if isinstance(source, ObsoleteSource): # however, if we found an "obsolete source", issue warning, and resolve the real source. print "WARNING: SKIN '%s' USES OBSOLETE SOURCE '%s', USE '%s' INSTEAD!" % (name, wsource, source.new_source) print "OBSOLETE SOURCE WILL BE REMOVED %s, PLEASE UPDATE!" % (source.removal_date) if source.description: print source.description wsource = source.new_source else: # otherwise, use that source. break if source is None: raise SkinError("source '" + wsource + "' was not found in screen '" + name + "'!") wrender = widget.get('render') if not wrender: raise SkinError("you must define a renderer with render= for source '%s'" % (wsource)) for converter in widget.getiterator("convert"): ctype = converter.get('type') assert ctype, "'convert'-tag needs a 'type'-attribute" #print "Converter:", ctype #parms = mergeText(converter.childNodes).strip() try: parms = converter.text.strip() except: parms = "" #print "Params:", ctype converter_class = my_import('.'.join(["Components", "Converter", ctype])).__dict__.get(ctype) c = None for i in source.downstream_elements: if isinstance(i, converter_class) and i.converter_arguments == parms: c = i if c is None: print "allocating new converter!" c = converter_class(parms) c.connect(source) else: print "reused converter!" source = c renderer_class = my_import('.'.join(["Components", "Renderer", wrender])).__dict__.get(wrender) renderer = renderer_class() # instantiate renderer renderer.connect(source) # connect to source attributes = renderer.skinAttributes = [ ] collectAttributes(attributes, widget, skin_path_prefix, ignore=['render', 'source']) screen.renderer.append(renderer) from Components.GUIComponent import GUIComponent nonvisited_components = [x for x in set(screen.keys()) - visited_components if isinstance(x, GUIComponent)] assert not nonvisited_components, "the following components in %s don't have a skin entry: %s" % (name, ', '.join(nonvisited_components)) # now walk additional objects for widget in myscreen.getchildren(): if widget.tag == "widget": continue if widget.tag == "applet": try: codeText = widget.text.strip() except: codeText = "" #print "Found code:" #print codeText type = widget.get('type') code = compile(codeText, "skin applet", "exec") if type == "onLayoutFinish": screen.onLayoutFinish.append(code) #print "onLayoutFinish = ", codeText else: raise SkinError("applet type '%s' unknown!" % type) #print "applet type '%s' unknown!" % type continue class additionalWidget: pass w = additionalWidget() if widget.tag == "eLabel": w.widget = eLabel elif widget.tag == "ePixmap": w.widget = ePixmap else: raise SkinError("unsupported stuff : %s" % widget.tag) #print "unsupported stuff : %s" % widget.tag w.skinAttributes = [ ] collectAttributes(w.skinAttributes, widget, skin_path_prefix, ignore=['name']) # applyAttributes(guiObject, widget, desktop) # guiObject.thisown = 0 screen.additionalWidgets.append(w)