1 from Tools.Profile import profile
2 profile("LOAD:ElementTree")
3 import xml.etree.cElementTree
6 profile("LOAD:enigma_skin")
7 from enigma import eSize, ePoint, gFont, eWindow, eLabel, ePixmap, eWindowStyleManager, \
8 addFont, gRGB, eWindowStyleSkinned
9 from Components.config import ConfigSubsection, ConfigText, config
10 from Components.Converter.Converter import Converter
11 from Components.Sources.Source import Source, ObsoleteSource
12 from Tools.Directories import resolveFilename, SCOPE_SKIN, SCOPE_SKIN_IMAGE, SCOPE_FONTS, SCOPE_CURRENT_SKIN, SCOPE_CONFIG, fileExists
13 from Tools.Import import my_import
14 from Tools.LoadPixmap import LoadPixmap
19 "Body": ("Regular", 18, 22, 16),
20 "ChoiceList": ("Regular", 20, 24, 18),
26 print " " * i + str(x)
28 for n in x.childNodes:
33 class SkinError(Exception):
34 def __init__(self, message):
38 return "{%s}: %s" % (config.skin.primary_skin.value, self.msg)
42 def loadSkin(name, scope = SCOPE_SKIN):
44 filename = resolveFilename(scope, name)
45 mpath = path.dirname(filename) + "/"
46 dom_skins.append((mpath, xml.etree.cElementTree.parse(filename).getroot()))
48 # we do our best to always select the "right" value
49 # skins are loaded in order of priority: skin with
50 # highest priority is loaded last, usually the user-provided
53 # currently, loadSingleSkinData (colors, bordersets etc.)
54 # are applied one-after-each, in order of ascending priority.
55 # the dom_skin will keep all screens in descending priority,
56 # so the first screen found will be used.
58 # example: loadSkin("nemesis_greenline/skin.xml")
59 config.skin = ConfigSubsection()
60 config.skin.primary_skin = ConfigText(default = "skin.xml")
64 loadSkin('skin_user.xml', SCOPE_CONFIG)
65 except (SkinError, IOError, AssertionError), err:
66 print "not loading user skin: ", err
69 loadSkin(config.skin.primary_skin.value)
70 except (SkinError, IOError, AssertionError), err:
71 print "SKIN ERROR:", err
72 print "defaulting to standard skin..."
73 config.skin.primary_skin.value = 'skin.xml'
76 profile("LoadSkinDefault")
77 loadSkin('skin_default.xml')
78 profile("LoadSkinDefaultDone")
80 def parseCoordinate(str, e, size = 0):
98 val += e * int(str[l:sl-1]) / 100
100 val += int(str[l:sl])
105 def evalPos(pos, wsize, ssize, scale):
107 pos = (ssize - wsize) / 2
109 pos = int(pos) * scale[0] / scale[1]
112 def parsePosition(str, scale, desktop = None, size = None):
113 x, y = str.split(',')
117 if desktop is not None:
118 ssize = desktop.size().width(), desktop.size().height()
120 wsize = size.width(), size.height()
122 x = evalPos(x, wsize[0], ssize[0], scale[0])
123 y = evalPos(y, wsize[1], ssize[1], scale[1])
127 def parseSize(str, scale):
128 x, y = str.split(',')
129 return eSize(int(x) * scale[0][0] / scale[0][1], int(y) * scale[1][0] / scale[1][1])
131 def parseFont(s, scale):
137 name, size = s.split(';')
138 return gFont(name, int(size) * scale[0][0] / scale[0][1])
143 return colorNames[str]
145 raise SkinError("color '%s' must be #aarrggbb or valid named color" % (str))
146 return gRGB(int(str[1:], 0x10))
148 def collectAttributes(skinAttributes, node, context, skin_path_prefix=None, ignore=[]):
149 # walk all attributes
152 for attrib, value in node.items():
153 if attrib not in ignore:
154 if attrib in ("pixmap", "pointer", "seek_pointer", "backgroundPixmap", "selectionPixmap"):
155 value = resolveFilename(SCOPE_SKIN_IMAGE, value, path_prefix=skin_path_prefix)
157 # Bit of a hack this, really. When a window has a flag (e.g. wfNoBorder)
158 # it needs to be set at least before the size is set, in order for the
159 # window dimensions to be calculated correctly in all situations.
160 # If wfNoBorder is applied after the size has been set, the window will fail to clear the title area.
161 # Similar situation for a scrollbar in a listbox; when the scrollbar setting is applied after
162 # the size, a scrollbar will not be shown until the selection moves for the first time
165 size = value.encode("utf-8")
166 elif attrib == 'position':
167 pos = value.encode("utf-8")
169 skinAttributes.append((attrib, value.encode("utf-8")))
172 pos, size = context.parse(pos, size)
173 skinAttributes.append(('position', pos))
175 skinAttributes.append(('size', size))
177 def loadPixmap(path, desktop):
179 option = path.find("#")
181 options = path[option+1:].split(',')
183 cached = "cached" in options
184 ptr = LoadPixmap(path, desktop, cached)
186 raise SkinError("pixmap file %s not found!" % (path))
189 class AttributeParser:
190 def __init__(self, guiObject, desktop, scale = ((1,1),(1,1))):
191 self.guiObject = guiObject
192 self.desktop = desktop
194 def applyOne(self, attrib, value):
196 getattr(self, attrib)(value)
197 except AttributeError:
198 print "[Skin] Attribute not implemented:", attrib, "value:", value
199 except SkinError, ex:
200 print "[Skin] Error:", ex
201 def applyAll(self, attrs):
202 for attrib, value in attrs:
204 getattr(self, attrib)(value)
205 except AttributeError:
206 print "[Skin] Attribute not implemented:", attrib, "value:", value
207 except SkinError, ex:
208 print "[Skin] Error:", ex
209 def position(self, value):
210 if isinstance(value, tuple):
211 self.guiObject.move(ePoint(*value))
213 self.guiObject.move(parsePosition(value, self.scale, self.desktop, self.guiObject.csize()))
214 def size(self, value):
215 if isinstance(value, tuple):
216 self.guiObject.resize(eSize(*value))
218 self.guiObject.resize(parseSize(value, self.scale))
219 def animationPaused(self, value):
221 def animationPaused(self, value):
222 self.guiObject.setAnimationMode(
230 def title(self, value):
231 self.guiObject.setTitle(_(value))
232 def text(self, value):
233 self.guiObject.setText(_(value))
234 def font(self, value):
235 self.guiObject.setFont(parseFont(value, self.scale))
236 def zPosition(self, value):
237 self.guiObject.setZPosition(int(value))
238 def itemHeight(self, value):
239 self.guiObject.setItemHeight(int(value))
240 def pixmap(self, value):
241 ptr = loadPixmap(value, self.desktop)
242 self.guiObject.setPixmap(ptr)
243 def backgroundPixmap(self, value):
244 ptr = loadPixmap(value, self.desktop)
245 self.guiObject.setBackgroundPicture(ptr)
246 def selectionPixmap(self, value):
247 ptr = loadPixmap(value, self.desktop)
248 self.guiObject.setSelectionPicture(ptr)
249 def itemHeight(self, value):
250 self.guiObject.setItemHeight(int(value))
251 def alphatest(self, value):
252 self.guiObject.setAlphatest(
257 def scale(self, value):
258 self.guiObject.setScale(1)
259 def orientation(self, value):
261 self.guiObject.setOrientation(*
262 { "orVertical": (self.guiObject.orVertical, False),
263 "orTopToBottom": (self.guiObject.orVertical, False),
264 "orBottomToTop": (self.guiObject.orVertical, True),
265 "orHorizontal": (self.guiObject.orHorizontal, False),
266 "orLeftToRight": (self.guiObject.orHorizontal, False),
267 "orRightToLeft": (self.guiObject.orHorizontal, True),
270 print "oprientation must be either orVertical or orHorizontal!"
271 def valign(self, value):
273 self.guiObject.setVAlign(
274 { "top": self.guiObject.alignTop,
275 "center": self.guiObject.alignCenter,
276 "bottom": self.guiObject.alignBottom
279 print "valign must be either top, center or bottom!"
280 def halign(self, value):
282 self.guiObject.setHAlign(
283 { "left": self.guiObject.alignLeft,
284 "center": self.guiObject.alignCenter,
285 "right": self.guiObject.alignRight,
286 "block": self.guiObject.alignBlock
289 print "halign must be either left, center, right or block!"
290 def flags(self, value):
291 flags = value.split(',')
294 fv = eWindow.__dict__[f]
295 self.guiObject.setFlag(fv)
297 print "illegal flag %s!" % f
298 def backgroundColor(self, value):
299 self.guiObject.setBackgroundColor(parseColor(value))
300 def backgroundColorSelected(self, value):
301 self.guiObject.setBackgroundColorSelected(parseColor(value))
302 def foregroundColor(self, value):
303 self.guiObject.setForegroundColor(parseColor(value))
304 def foregroundColorSelected(self, value):
305 self.guiObject.setForegroundColorSelected(parseColor(value))
306 def shadowColor(self, value):
307 self.guiObject.setShadowColor(parseColor(value))
308 def selectionDisabled(self, value):
309 self.guiObject.setSelectionEnable(0)
310 def transparent(self, value):
311 self.guiObject.setTransparent(int(value))
312 def borderColor(self, value):
313 self.guiObject.setBorderColor(parseColor(value))
314 def borderWidth(self, value):
315 self.guiObject.setBorderWidth(int(value))
316 def scrollbarMode(self, value):
317 self.guiObject.setScrollbarMode(
318 { "showOnDemand": self.guiObject.showOnDemand,
319 "showAlways": self.guiObject.showAlways,
320 "showNever": self.guiObject.showNever
322 def enableWrapAround(self, value):
323 self.guiObject.setWrapAround(True)
324 def pointer(self, value):
325 (name, pos) = value.split(':')
326 pos = parsePosition(pos, self.scale)
327 ptr = loadPixmap(name, self.desktop)
328 self.guiObject.setPointer(0, ptr, pos)
329 def seek_pointer(self, value):
330 (name, pos) = value.split(':')
331 pos = parsePosition(pos, self.scale)
332 ptr = loadPixmap(name, self.desktop)
333 self.guiObject.setPointer(1, ptr, pos)
334 def shadowOffset(self, value):
335 self.guiObject.setShadowOffset(parsePosition(value, self.scale))
336 def noWrap(self, value):
337 self.guiObject.setNoWrap(1)
341 def applySingleAttribute(guiObject, desktop, attrib, value, scale = ((1,1),(1,1))):
342 # Someone still using applySingleAttribute?
343 AttributeParser(guiObject, desktop, scale).applyOne(attrib, value)
345 def applyAllAttributes(guiObject, desktop, attributes, scale):
346 AttributeParser(guiObject, desktop, scale).applyAll(attributes)
348 def loadSingleSkinData(desktop, skin, path_prefix):
349 """loads skin data like colors, windowstyle etc."""
350 assert skin.tag == "skin", "root element in skin must be 'skin'!"
352 #print "***SKIN: ", path_prefix
354 for c in skin.findall("output"):
355 id = c.attrib.get('id')
360 if id == 0: # framebuffer
361 for res in c.findall("resolution"):
362 get_attr = res.attrib.get
363 xres = get_attr("xres")
368 yres = get_attr("yres")
373 bpp = get_attr("bpp")
378 #print "Resolution:", xres,yres,bpp
379 from enigma import gMainDC
380 gMainDC.getInstance().setResolution(xres, yres)
381 desktop.resize(eSize(xres, yres))
383 # load palette (not yet implemented)
386 for c in skin.findall("colors"):
387 for color in c.findall("color"):
388 get_attr = color.attrib.get
389 name = get_attr("name")
390 color = get_attr("value")
392 colorNames[name] = parseColor(color)
393 #print "Color:", name, color
395 raise SkinError("need color and name, got %s %s" % (name, color))
397 for c in skin.findall("fonts"):
398 for font in c.findall("font"):
399 get_attr = font.attrib.get
400 filename = get_attr("filename", "<NONAME>")
401 name = get_attr("name", "Regular")
402 scale = get_attr("scale")
407 is_replacement = get_attr("replacement") and True or False
408 resolved_font = resolveFilename(SCOPE_FONTS, filename, path_prefix=path_prefix)
409 if not fileExists(resolved_font): #when font is not available look at current skin path
410 skin_path = resolveFilename(SCOPE_CURRENT_SKIN, filename)
411 if fileExists(skin_path):
412 resolved_font = skin_path
413 addFont(resolved_font, name, scale, is_replacement)
414 #print "Font: ", resolved_font, name, scale, is_replacement
416 for alias in c.findall("alias"):
417 get = alias.attrib.get
421 size = int(get("size"))
422 height = int(get("height", size)) # to be calculated some day
423 width = int(get("width", size))
425 fonts[name] = (font, size, height, width)
426 except Exception, ex:
427 print "[SKIN] bad font alias", ex
429 for c in skin.findall("parameters"):
430 for parameter in c.findall("parameter"):
431 get = parameter.attrib.get
435 parameters[name] = map(int, value.split(","))
436 except Exception, ex:
437 print "[SKIN] bad parameter", ex
439 for c in skin.findall("subtitles"):
440 from enigma import eWidget, eSubtitleWidget
441 scale = ((1,1),(1,1))
442 for substyle in c.findall("sub"):
443 get_attr = substyle.attrib.get
444 font = parseFont(get_attr("font"), scale)
445 col = get_attr("foregroundColor")
447 foregroundColor = parseColor(col)
450 foregroundColor = gRGB(0xFFFFFF)
452 col = get_attr("shadowColor")
454 shadowColor = parseColor(col)
456 shadowColor = gRGB(0)
457 shadowOffset = parsePosition(get_attr("shadowOffset"), scale)
458 face = eSubtitleWidget.__dict__[get_attr("name")]
459 eSubtitleWidget.setFontStyle(face, font, haveColor, foregroundColor, shadowColor, shadowOffset)
461 for windowstyle in skin.findall("windowstyle"):
462 style = eWindowStyleSkinned()
463 id = windowstyle.attrib.get("id")
468 #print "windowstyle:", id
471 font = gFont("Regular", 20)
472 offset = eSize(20, 5)
474 for title in windowstyle.findall("title"):
475 get_attr = title.attrib.get
476 offset = parseSize(get_attr("offset"), ((1,1),(1,1)))
477 font = parseFont(get_attr("font"), ((1,1),(1,1)))
479 style.setTitleFont(font);
480 style.setTitleOffset(offset)
481 #print " ", font, offset
483 for borderset in windowstyle.findall("borderset"):
484 bsName = str(borderset.attrib.get("name"))
485 for pixmap in borderset.findall("pixmap"):
486 get_attr = pixmap.attrib.get
487 bpName = get_attr("pos")
488 filename = get_attr("filename")
489 if filename and bpName:
490 png = loadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, filename, path_prefix=path_prefix), desktop)
491 style.setPixmap(eWindowStyleSkinned.__dict__[bsName], eWindowStyleSkinned.__dict__[bpName], png)
492 #print " borderset:", bpName, filename
494 for color in windowstyle.findall("color"):
495 get_attr = color.attrib.get
496 colorType = get_attr("name")
497 color = parseColor(get_attr("color"))
499 style.setColor(eWindowStyleSkinned.__dict__["col" + colorType], color)
501 raise SkinError("Unknown color %s" % (colorType))
504 #print " color:", type, color
506 x = eWindowStyleManager.getInstance()
507 x.setStyle(id, style)
512 def loadSkinData(desktop):
513 global dom_skins, dom_screens, display_skin_id
516 for (path, dom_skin) in skins:
517 loadSingleSkinData(desktop, dom_skin, path)
519 for elem in dom_skin:
520 if elem.tag == 'screen':
521 name = elem.attrib.get('name', None)
523 sid = elem.attrib.get('id', None)
524 if sid and (int(sid) != display_skin_id):
525 # not for this display
528 if name in dom_screens:
529 # Kill old versions, save memory
530 dom_screens[name][0].clear()
531 dom_screens[name] = (elem, path)
533 # without name, it's useless!
536 # non-screen element, no need for it any longer
538 # no longer needed, we know where the screens are now.
541 def lookupScreen(name, style_id):
542 if dom_screens.has_key(name):
543 elem, path = dom_screens[name]
544 screen_style_id = elem.attrib.get('id', '-1')
545 if screen_style_id == '-1' and name.find('ummary') > 0:
546 screen_style_id = '1'
547 if (style_id != 2 and int(screen_style_id) == -1) or int(screen_style_id) == style_id:
552 class additionalWidget:
555 # Class that makes a tuple look like something else. Some plugins just assume
556 # that size is a string and try to parse it. This class makes that work.
557 class SizeTuple(tuple):
558 def split(self, *args):
559 return (str(self[0]), str(self[1]))
560 def strip(self, *args):
561 return '%s,%s' % self
563 return '%s,%s' % self
566 def __init__(self, parent=None, pos=None, size=None):
567 if parent is not None:
569 pos, size = parent.parse(pos, size)
571 self.w, self.h = size
578 return "Context (%s,%s)+(%s,%s) " % (self.x, self.y, self.w, self.h)
579 def parse(self, pos, size):
581 pos = (self.x, self.y)
582 size = (self.w, self.h)
585 elif pos == "bottom":
586 w,h = size.split(',')
588 pos = (self.x, self.y + self.h - h)
592 w,h = size.split(',')
594 pos = (self.x, self.y)
599 w,h = size.split(',')
601 pos = (self.x, self.y)
606 w,h = size.split(',')
608 pos = (self.x + self.w - w, self.y)
612 size = size.split(',')
613 size = (parseCoordinate(size[0], self.w), parseCoordinate(size[1], self.h))
615 pos = (self.x + parseCoordinate(pos[0], self.w, size[0]), self.y + parseCoordinate(pos[1], self.h, size[1]))
616 return (SizeTuple(pos), SizeTuple(size))
618 class SkinContextStack(SkinContext):
619 # A context that stacks things instead of aligning them
620 def parse(self, pos, size):
622 pos = (self.x, self.y)
623 size = (self.w, self.h)
624 elif pos == "bottom":
625 w,h = size.split(',')
627 pos = (self.x, self.y + self.h - h)
630 w,h = size.split(',')
632 pos = (self.x, self.y)
635 w,h = size.split(',')
637 pos = (self.x, self.y)
640 w,h = size.split(',')
642 pos = (self.x + self.w - w, self.y)
645 size = size.split(',')
646 size = (parseCoordinate(size[0], self.w), parseCoordinate(size[1], self.h))
648 pos = (self.x + parseCoordinate(pos[0], self.w, size[0]), self.y + parseCoordinate(pos[1], self.h, size[1]))
649 return (SizeTuple(pos), SizeTuple(size))
651 def readSkin(screen, skin, names, desktop):
652 if not isinstance(names, list):
655 name = "<embedded-in-'%s'>" % screen.__class__.__name__
657 style_id = desktop.getStyleID();
659 # try all skins, first existing one have priority
661 myscreen, path = lookupScreen(n, style_id)
662 if myscreen is not None:
663 # use this name for debug output
667 # otherwise try embedded skin
669 myscreen = getattr(screen, "parsedSkin", None)
671 # try uncompiled embedded skin
672 if myscreen is None and getattr(screen, "skin", None):
673 print "Looking for embedded skin"
674 skin_tuple = screen.skin
675 if not isinstance(skin_tuple, tuple):
676 skin_tuple = (skin_tuple,)
677 for sskin in skin_tuple:
678 parsedSkin = xml.etree.cElementTree.fromstring(sskin)
679 screen_style_id = parsedSkin.attrib.get('id', '-1')
680 if (style_id != 2 and int(screen_style_id) == -1) or int(screen_style_id) == style_id:
681 myscreen = screen.parsedSkin = parsedSkin
684 #assert myscreen is not None, "no skin for screen '" + repr(names) + "' found!"
686 print "No skin to read..."
687 emptySkin = "<screen></screen>"
688 myscreen = screen.parsedSkin = xml.etree.cElementTree.fromstring(emptySkin)
690 screen.skinAttributes = [ ]
692 skin_path_prefix = getattr(screen, "skin_path", path)
694 # collectAttributes(screen.skinAttributes, myscreen, skin_path_prefix, ignore=["name"])
696 context = SkinContext()
700 context.w = s.width()
701 context.h = s.height()
703 collectAttributes(screen.skinAttributes, myscreen, context, skin_path_prefix, ignore=("name",))
704 context = SkinContext(context, myscreen.attrib.get('position'), myscreen.attrib.get('size'))
708 screen.additionalWidgets = [ ]
709 screen.renderer = [ ]
711 visited_components = set()
713 # now walk all widgets and stuff
714 def process_none(widget, context):
717 def process_widget(widget, context):
718 get_attr = widget.attrib.get
719 # ok, we either have 1:1-mapped widgets ('old style'), or 1:n-mapped
720 # widgets (source->renderer).
722 wname = get_attr('name')
723 wsource = get_attr('source')
725 if wname is None and wsource is None:
726 print "widget has no name and no source!"
730 #print "Widget name=", wname
731 visited_components.add(wname)
733 # get corresponding 'gui' object
735 attributes = screen[wname].skinAttributes = [ ]
737 raise SkinError("component with name '" + wname + "' was not found in skin of screen '" + name + "'!")
738 #print "WARNING: component with name '" + wname + "' was not found in skin of screen '" + name + "'!"
740 # assert screen[wname] is not Source
742 # and collect attributes for this
743 collectAttributes(attributes, widget, context, skin_path_prefix, ignore=['name'])
745 # get corresponding source
746 #print "Widget source=", wsource
748 while True: # until we found a non-obsolete source
750 # parse our current "wsource", which might specifiy a "related screen" before the dot,
751 # for example to reference a parent, global or session-global screen.
754 # resolve all path components
755 path = wsource.split('.')
757 scr = screen.getRelatedScreen(path[0])
761 raise SkinError("specified related screen '" + wsource + "' was not found in screen '" + name + "'!")
764 # resolve the source.
765 source = scr.get(path[0])
766 if isinstance(source, ObsoleteSource):
767 # however, if we found an "obsolete source", issue warning, and resolve the real source.
768 print "WARNING: SKIN '%s' USES OBSOLETE SOURCE '%s', USE '%s' INSTEAD!" % (name, wsource, source.new_source)
769 print "OBSOLETE SOURCE WILL BE REMOVED %s, PLEASE UPDATE!" % (source.removal_date)
770 if source.description:
771 print source.description
773 wsource = source.new_source
775 # otherwise, use that source.
779 raise SkinError("source '" + wsource + "' was not found in screen '" + name + "'!")
781 wrender = get_attr('render')
784 raise SkinError("you must define a renderer with render= for source '%s'" % (wsource))
786 for converter in widget.findall("convert"):
787 ctype = converter.get('type')
788 assert ctype, "'convert'-tag needs a 'type'-attribute"
789 #print "Converter:", ctype
791 parms = converter.text.strip()
794 #print "Params:", parms
795 converter_class = my_import('.'.join(("Components", "Converter", ctype))).__dict__.get(ctype)
799 for i in source.downstream_elements:
800 if isinstance(i, converter_class) and i.converter_arguments == parms:
804 print "allocating new converter!"
805 c = converter_class(parms)
808 print "reused converter!"
812 renderer_class = my_import('.'.join(("Components", "Renderer", wrender))).__dict__.get(wrender)
814 renderer = renderer_class() # instantiate renderer
816 renderer.connect(source) # connect to source
817 attributes = renderer.skinAttributes = [ ]
818 collectAttributes(attributes, widget, context, skin_path_prefix, ignore=['render', 'source'])
820 screen.renderer.append(renderer)
822 def process_applet(widget, context):
824 codeText = widget.text.strip()
830 widgetType = widget.attrib.get('type')
832 code = compile(codeText, "skin applet", "exec")
834 if widgetType == "onLayoutFinish":
835 screen.onLayoutFinish.append(code)
836 #print "onLayoutFinish = ", codeText
838 raise SkinError("applet type '%s' unknown!" % widgetType)
839 #print "applet type '%s' unknown!" % type
841 def process_elabel(widget, context):
842 w = additionalWidget()
844 w.skinAttributes = [ ]
845 collectAttributes(w.skinAttributes, widget, context, skin_path_prefix, ignore=['name'])
846 screen.additionalWidgets.append(w)
848 def process_epixmap(widget, context):
849 w = additionalWidget()
851 w.skinAttributes = [ ]
852 collectAttributes(w.skinAttributes, widget, context, skin_path_prefix, ignore=['name'])
853 screen.additionalWidgets.append(w)
855 def process_screen(widget, context):
856 for w in widget.findall("widget"):
857 process_widget(w, context)
859 for w in widget.getchildren():
860 if w.tag == "widget":
863 p = processors.get(w.tag, process_none)
866 def process_panel(widget, context):
867 n = widget.attrib.get('name')
870 s = dom_screens.get(n, None)
872 print "[SKIN] Unable to find screen '%s' referred in screen '%s'" % (n, name)
874 process_screen(s[0], context)
876 layout = widget.attrib.get('layout')
877 if layout == 'stack':
878 cc = SkinContextStack
882 c = cc(context, widget.attrib.get('position'), widget.attrib.get('size'))
883 except Exception, ex:
884 raise SkinError("Failed to create skincontext (%s,%s) in %s: %s" % (widget.attrib.get('position'), widget.attrib.get('size'), context, ex) )
886 process_screen(widget, c)
890 "widget": process_widget,
891 "applet": process_applet,
892 "eLabel": process_elabel,
893 "ePixmap": process_epixmap,
894 "panel": process_panel
898 context.x = 0 # reset offsets, all components are relative to screen
899 context.y = 0 # coordinates.
900 process_screen(myscreen, context)
902 print "[Skin] SKIN ERROR:", e
904 from Components.GUIComponent import GUIComponent
905 nonvisited_components = [x for x in set(screen.keys()) - visited_components if isinstance(x, GUIComponent)]
906 assert not nonvisited_components, "the following components in %s don't have a skin entry: %s" % (name, ', '.join(nonvisited_components))
907 # This may look pointless, but it unbinds 'screen' from the nested scope. A better
908 # solution is to avoid the nested scope above and use the context object to pass
911 visited_components = None