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 from Components.SystemInfo import SystemInfo
60 DEFAULT_SKIN = SystemInfo.get("DefaultFullHDSkin", False) and "Vu_HD_1080P/skin.xml" or "Vu_HD/skin.xml"
62 config.skin = ConfigSubsection()
63 config.skin.primary_skin = ConfigText(default = DEFAULT_SKIN)
67 loadSkin('skin_user.xml', SCOPE_CONFIG)
68 except (SkinError, IOError, AssertionError), err:
69 print "not loading user skin: ", err
72 loadSkin(config.skin.primary_skin.value)
73 except (SkinError, IOError, AssertionError), err:
74 print "SKIN ERROR:", err
75 print "defaulting to standard skin..."
76 config.skin.primary_skin.value = 'skin.xml'
79 profile("LoadSkinDefault")
80 loadSkin('skin_default.xml')
81 profile("LoadSkinDefaultDone")
83 def parseCoordinate(str, e, size = 0):
101 val += e * int(str[l:sl-1]) / 100
103 val += int(str[l:sl])
108 def evalPos(pos, wsize, ssize, scale):
110 pos = (ssize - wsize) / 2
112 pos = int(pos) * scale[0] / scale[1]
115 def parsePosition(str, scale, desktop = None, size = None):
116 x, y = str.split(',')
120 if desktop is not None:
121 ssize = desktop.size().width(), desktop.size().height()
123 wsize = size.width(), size.height()
125 x = evalPos(x, wsize[0], ssize[0], scale[0])
126 y = evalPos(y, wsize[1], ssize[1], scale[1])
130 def parseSize(str, scale):
131 x, y = str.split(',')
132 return eSize(int(x) * scale[0][0] / scale[0][1], int(y) * scale[1][0] / scale[1][1])
134 def parseFont(s, scale):
140 name, size = s.split(';')
141 return gFont(name, int(size) * scale[0][0] / scale[0][1])
146 return colorNames[str]
148 raise SkinError("color '%s' must be #aarrggbb or valid named color" % (str))
149 return gRGB(int(str[1:], 0x10))
151 def collectAttributes(skinAttributes, node, context, skin_path_prefix=None, ignore=[]):
152 # walk all attributes
155 for attrib, value in node.items():
156 if attrib not in ignore:
157 if attrib in ("pixmap", "pointer", "seek_pointer", "backgroundPixmap", "selectionPixmap"):
158 value = resolveFilename(SCOPE_SKIN_IMAGE, value, path_prefix=skin_path_prefix)
160 # Bit of a hack this, really. When a window has a flag (e.g. wfNoBorder)
161 # it needs to be set at least before the size is set, in order for the
162 # window dimensions to be calculated correctly in all situations.
163 # If wfNoBorder is applied after the size has been set, the window will fail to clear the title area.
164 # Similar situation for a scrollbar in a listbox; when the scrollbar setting is applied after
165 # the size, a scrollbar will not be shown until the selection moves for the first time
168 size = value.encode("utf-8")
169 elif attrib == 'position':
170 pos = value.encode("utf-8")
172 skinAttributes.append((attrib, value.encode("utf-8")))
175 pos, size = context.parse(pos, size)
176 skinAttributes.append(('position', pos))
178 skinAttributes.append(('size', size))
180 def morphRcImagePath(value):
181 if value.startswith('/usr/share/enigma2') and path.basename(value) in ('rc.png', 'rcold.png'):
182 value = resolveFilename(SCOPE_SKIN, 'rc/' + 'rc_%d.png' % config.misc.rcused.value)
185 def loadPixmap(path, desktop):
187 option = path.find("#")
189 options = path[option+1:].split(',')
191 cached = "cached" in options
192 ptr = LoadPixmap(morphRcImagePath(path), desktop, cached)
194 raise SkinError("pixmap file %s not found!" % (path))
197 class AttributeParser:
198 def __init__(self, guiObject, desktop, scale = ((1,1),(1,1))):
199 self.guiObject = guiObject
200 self.desktop = desktop
202 def applyOne(self, attrib, value):
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 applyAll(self, attrs):
210 for attrib, value in attrs:
212 getattr(self, attrib)(value)
213 except AttributeError:
214 print "[Skin] Attribute not implemented:", attrib, "value:", value
215 except SkinError, ex:
216 print "[Skin] Error:", ex
217 def position(self, value):
218 if isinstance(value, tuple):
219 self.guiObject.move(ePoint(*value))
221 self.guiObject.move(parsePosition(value, self.scale, self.desktop, self.guiObject.csize()))
222 def size(self, value):
223 if isinstance(value, tuple):
224 self.guiObject.resize(eSize(*value))
226 self.guiObject.resize(parseSize(value, self.scale))
227 def animationPaused(self, value):
229 def animationPaused(self, value):
230 self.guiObject.setAnimationMode(
238 def title(self, value):
239 self.guiObject.setTitle(_(value))
240 def text(self, value):
241 self.guiObject.setText(_(value))
242 def font(self, value):
243 self.guiObject.setFont(parseFont(value, self.scale))
244 def zPosition(self, value):
245 self.guiObject.setZPosition(int(value))
246 def itemHeight(self, value):
247 self.guiObject.setItemHeight(int(value))
248 def pixmap(self, value):
249 ptr = loadPixmap(value, self.desktop)
250 self.guiObject.setPixmap(ptr)
251 def backgroundPixmap(self, value):
252 ptr = loadPixmap(value, self.desktop)
253 self.guiObject.setBackgroundPicture(ptr)
254 def selectionPixmap(self, value):
255 ptr = loadPixmap(value, self.desktop)
256 self.guiObject.setSelectionPicture(ptr)
257 def itemHeight(self, value):
258 self.guiObject.setItemHeight(int(value))
259 def alphatest(self, value):
260 self.guiObject.setAlphatest(
265 def scale(self, value):
266 self.guiObject.setScale(1)
267 def orientation(self, value):
269 self.guiObject.setOrientation(*
270 { "orVertical": (self.guiObject.orVertical, False),
271 "orTopToBottom": (self.guiObject.orVertical, False),
272 "orBottomToTop": (self.guiObject.orVertical, True),
273 "orHorizontal": (self.guiObject.orHorizontal, False),
274 "orLeftToRight": (self.guiObject.orHorizontal, False),
275 "orRightToLeft": (self.guiObject.orHorizontal, True),
278 print "oprientation must be either orVertical or orHorizontal!"
279 def valign(self, value):
281 self.guiObject.setVAlign(
282 { "top": self.guiObject.alignTop,
283 "center": self.guiObject.alignCenter,
284 "bottom": self.guiObject.alignBottom
287 print "valign must be either top, center or bottom!"
288 def halign(self, value):
290 self.guiObject.setHAlign(
291 { "left": self.guiObject.alignLeft,
292 "center": self.guiObject.alignCenter,
293 "right": self.guiObject.alignRight,
294 "block": self.guiObject.alignBlock
297 print "halign must be either left, center, right or block!"
298 def flags(self, value):
299 flags = value.split(',')
302 fv = eWindow.__dict__[f]
303 self.guiObject.setFlag(fv)
305 print "illegal flag %s!" % f
306 def backgroundColor(self, value):
307 self.guiObject.setBackgroundColor(parseColor(value))
308 def backgroundColorSelected(self, value):
309 self.guiObject.setBackgroundColorSelected(parseColor(value))
310 def foregroundColor(self, value):
311 self.guiObject.setForegroundColor(parseColor(value))
312 def foregroundColorSelected(self, value):
313 self.guiObject.setForegroundColorSelected(parseColor(value))
314 def shadowColor(self, value):
315 self.guiObject.setShadowColor(parseColor(value))
316 def selectionDisabled(self, value):
317 self.guiObject.setSelectionEnable(0)
318 def transparent(self, value):
319 self.guiObject.setTransparent(int(value))
320 def borderColor(self, value):
321 self.guiObject.setBorderColor(parseColor(value))
322 def borderWidth(self, value):
323 self.guiObject.setBorderWidth(int(value))
324 def scrollbarMode(self, value):
325 self.guiObject.setScrollbarMode(
326 { "showOnDemand": self.guiObject.showOnDemand,
327 "showAlways": self.guiObject.showAlways,
328 "showNever": self.guiObject.showNever
330 def enableWrapAround(self, value):
331 self.guiObject.setWrapAround(True)
332 def pointer(self, value):
333 (name, pos) = value.split(':')
334 pos = parsePosition(pos, self.scale)
335 ptr = loadPixmap(name, self.desktop)
336 self.guiObject.setPointer(0, ptr, pos)
337 def seek_pointer(self, value):
338 (name, pos) = value.split(':')
339 pos = parsePosition(pos, self.scale)
340 ptr = loadPixmap(name, self.desktop)
341 self.guiObject.setPointer(1, ptr, pos)
342 def shadowOffset(self, value):
343 self.guiObject.setShadowOffset(parsePosition(value, self.scale))
344 def noWrap(self, value):
345 self.guiObject.setNoWrap(1)
349 def applySingleAttribute(guiObject, desktop, attrib, value, scale = ((1,1),(1,1))):
350 # Someone still using applySingleAttribute?
351 AttributeParser(guiObject, desktop, scale).applyOne(attrib, value)
353 def applyAllAttributes(guiObject, desktop, attributes, scale):
354 AttributeParser(guiObject, desktop, scale).applyAll(attributes)
356 def loadSingleSkinData(desktop, skin, path_prefix):
357 """loads skin data like colors, windowstyle etc."""
358 assert skin.tag == "skin", "root element in skin must be 'skin'!"
360 #print "***SKIN: ", path_prefix
362 for c in skin.findall("output"):
363 id = c.attrib.get('id')
368 if id == 0: # framebuffer
369 for res in c.findall("resolution"):
370 get_attr = res.attrib.get
371 xres = get_attr("xres")
376 yres = get_attr("yres")
381 bpp = get_attr("bpp")
386 #print "Resolution:", xres,yres,bpp
387 from enigma import gMainDC
388 gMainDC.getInstance().setResolution(xres, yres)
389 desktop.resize(eSize(xres, yres))
391 # load palette (not yet implemented)
394 for c in skin.findall("colors"):
395 for color in c.findall("color"):
396 get_attr = color.attrib.get
397 name = get_attr("name")
398 color = get_attr("value")
400 colorNames[name] = parseColor(color)
401 #print "Color:", name, color
403 raise SkinError("need color and name, got %s %s" % (name, color))
405 for c in skin.findall("fonts"):
406 for font in c.findall("font"):
407 get_attr = font.attrib.get
408 filename = get_attr("filename", "<NONAME>")
409 name = get_attr("name", "Regular")
410 scale = get_attr("scale")
415 is_replacement = get_attr("replacement") and True or False
416 resolved_font = resolveFilename(SCOPE_FONTS, filename, path_prefix=path_prefix)
417 if not fileExists(resolved_font): #when font is not available look at current skin path
418 skin_path = resolveFilename(SCOPE_CURRENT_SKIN, filename)
419 if fileExists(skin_path):
420 resolved_font = skin_path
421 addFont(resolved_font, name, scale, is_replacement)
422 #print "Font: ", resolved_font, name, scale, is_replacement
424 for alias in c.findall("alias"):
425 get = alias.attrib.get
429 size = int(get("size"))
430 height = int(get("height", size)) # to be calculated some day
431 width = int(get("width", size))
433 fonts[name] = (font, size, height, width)
434 except Exception, ex:
435 print "[SKIN] bad font alias", ex
437 for c in skin.findall("parameters"):
438 for parameter in c.findall("parameter"):
439 get = parameter.attrib.get
443 parameters[name] = map(int, value.split(","))
444 except Exception, ex:
445 print "[SKIN] bad parameter", ex
447 for c in skin.findall("subtitles"):
448 from enigma import eWidget, eSubtitleWidget
449 scale = ((1,1),(1,1))
450 for substyle in c.findall("sub"):
451 get_attr = substyle.attrib.get
452 font = parseFont(get_attr("font"), scale)
453 col = get_attr("foregroundColor")
455 foregroundColor = parseColor(col)
458 foregroundColor = gRGB(0xFFFFFF)
460 col = get_attr("shadowColor")
462 shadowColor = parseColor(col)
464 shadowColor = gRGB(0)
465 shadowOffset = parsePosition(get_attr("shadowOffset"), scale)
466 face = eSubtitleWidget.__dict__[get_attr("name")]
467 eSubtitleWidget.setFontStyle(face, font, haveColor, foregroundColor, shadowColor, shadowOffset)
469 for windowstyle in skin.findall("windowstyle"):
470 style = eWindowStyleSkinned()
471 id = windowstyle.attrib.get("id")
476 #print "windowstyle:", id
479 font = gFont("Regular", 20)
480 offset = eSize(20, 5)
482 for title in windowstyle.findall("title"):
483 get_attr = title.attrib.get
484 offset = parseSize(get_attr("offset"), ((1,1),(1,1)))
485 font = parseFont(get_attr("font"), ((1,1),(1,1)))
487 style.setTitleFont(font);
488 style.setTitleOffset(offset)
489 #print " ", font, offset
491 for borderset in windowstyle.findall("borderset"):
492 bsName = str(borderset.attrib.get("name"))
493 for pixmap in borderset.findall("pixmap"):
494 get_attr = pixmap.attrib.get
495 bpName = get_attr("pos")
496 filename = get_attr("filename")
497 if filename and bpName:
498 png = loadPixmap(resolveFilename(SCOPE_SKIN_IMAGE, filename, path_prefix=path_prefix), desktop)
499 style.setPixmap(eWindowStyleSkinned.__dict__[bsName], eWindowStyleSkinned.__dict__[bpName], png)
500 #print " borderset:", bpName, filename
502 for color in windowstyle.findall("color"):
503 get_attr = color.attrib.get
504 colorType = get_attr("name")
505 color = parseColor(get_attr("color"))
507 style.setColor(eWindowStyleSkinned.__dict__["col" + colorType], color)
509 raise SkinError("Unknown color %s" % (colorType))
512 #print " color:", type, color
514 x = eWindowStyleManager.getInstance()
515 x.setStyle(id, style)
520 def loadSkinData(desktop):
521 global dom_skins, dom_screens, display_skin_id
524 for (path, dom_skin) in skins:
525 loadSingleSkinData(desktop, dom_skin, path)
527 for elem in dom_skin:
528 if elem.tag == 'screen':
529 name = elem.attrib.get('name', None)
531 sid = elem.attrib.get('id', None)
532 if sid and (int(sid) != display_skin_id):
533 # not for this display
536 if name in dom_screens:
537 # Kill old versions, save memory
538 dom_screens[name][0].clear()
539 dom_screens[name] = (elem, path)
541 # without name, it's useless!
544 # non-screen element, no need for it any longer
546 # no longer needed, we know where the screens are now.
549 def lookupScreen(name, style_id):
550 if dom_screens.has_key(name):
551 elem, path = dom_screens[name]
552 screen_style_id = elem.attrib.get('id', '-1')
553 if screen_style_id == '-1' and name.find('ummary') > 0:
554 screen_style_id = '1'
555 if (style_id != 2 and int(screen_style_id) == -1) or int(screen_style_id) == style_id:
560 class additionalWidget:
563 # Class that makes a tuple look like something else. Some plugins just assume
564 # that size is a string and try to parse it. This class makes that work.
565 class SizeTuple(tuple):
566 def split(self, *args):
567 return (str(self[0]), str(self[1]))
568 def strip(self, *args):
569 return '%s,%s' % self
571 return '%s,%s' % self
574 def __init__(self, parent=None, pos=None, size=None):
575 if parent is not None:
577 pos, size = parent.parse(pos, size)
579 self.w, self.h = size
586 return "Context (%s,%s)+(%s,%s) " % (self.x, self.y, self.w, self.h)
587 def parse(self, pos, size):
589 pos = (self.x, self.y)
590 size = (self.w, self.h)
593 elif pos == "bottom":
594 w,h = size.split(',')
596 pos = (self.x, self.y + self.h - h)
600 w,h = size.split(',')
602 pos = (self.x, self.y)
607 w,h = size.split(',')
609 pos = (self.x, self.y)
614 w,h = size.split(',')
616 pos = (self.x + self.w - w, self.y)
620 size = size.split(',')
621 size = (parseCoordinate(size[0], self.w), parseCoordinate(size[1], self.h))
623 pos = (self.x + parseCoordinate(pos[0], self.w, size[0]), self.y + parseCoordinate(pos[1], self.h, size[1]))
624 return (SizeTuple(pos), SizeTuple(size))
626 class SkinContextStack(SkinContext):
627 # A context that stacks things instead of aligning them
628 def parse(self, pos, size):
630 pos = (self.x, self.y)
631 size = (self.w, self.h)
632 elif pos == "bottom":
633 w,h = size.split(',')
635 pos = (self.x, self.y + self.h - h)
638 w,h = size.split(',')
640 pos = (self.x, self.y)
643 w,h = size.split(',')
645 pos = (self.x, self.y)
648 w,h = size.split(',')
650 pos = (self.x + self.w - w, self.y)
653 size = size.split(',')
654 size = (parseCoordinate(size[0], self.w), parseCoordinate(size[1], self.h))
656 pos = (self.x + parseCoordinate(pos[0], self.w, size[0]), self.y + parseCoordinate(pos[1], self.h, size[1]))
657 return (SizeTuple(pos), SizeTuple(size))
659 def readSkin(screen, skin, names, desktop):
660 if not isinstance(names, list):
663 name = "<embedded-in-'%s'>" % screen.__class__.__name__
665 style_id = desktop.getStyleID();
667 # try all skins, first existing one have priority
669 myscreen, path = lookupScreen(n, style_id)
670 if myscreen is not None:
671 # use this name for debug output
675 # otherwise try embedded skin
677 myscreen = getattr(screen, "parsedSkin", None)
679 # try uncompiled embedded skin
680 if myscreen is None and getattr(screen, "skin", None):
681 print "Looking for embedded skin"
682 skin_tuple = screen.skin
683 if not isinstance(skin_tuple, tuple):
684 skin_tuple = (skin_tuple,)
685 for sskin in skin_tuple:
686 parsedSkin = xml.etree.cElementTree.fromstring(sskin)
687 screen_style_id = parsedSkin.attrib.get('id', '-1')
688 if (style_id != 2 and int(screen_style_id) == -1) or int(screen_style_id) == style_id:
689 myscreen = screen.parsedSkin = parsedSkin
692 #assert myscreen is not None, "no skin for screen '" + repr(names) + "' found!"
694 print "No skin to read..."
695 emptySkin = "<screen></screen>"
696 myscreen = screen.parsedSkin = xml.etree.cElementTree.fromstring(emptySkin)
698 screen.skinAttributes = [ ]
700 skin_path_prefix = getattr(screen, "skin_path", path)
702 # collectAttributes(screen.skinAttributes, myscreen, skin_path_prefix, ignore=["name"])
704 context = SkinContext()
708 context.w = s.width()
709 context.h = s.height()
711 collectAttributes(screen.skinAttributes, myscreen, context, skin_path_prefix, ignore=("name",))
712 context = SkinContext(context, myscreen.attrib.get('position'), myscreen.attrib.get('size'))
716 screen.additionalWidgets = [ ]
717 screen.renderer = [ ]
719 visited_components = set()
721 # now walk all widgets and stuff
722 def process_none(widget, context):
725 def process_widget(widget, context):
726 get_attr = widget.attrib.get
727 # ok, we either have 1:1-mapped widgets ('old style'), or 1:n-mapped
728 # widgets (source->renderer).
730 wname = get_attr('name')
731 wsource = get_attr('source')
733 if wname is None and wsource is None:
734 print "widget has no name and no source!"
738 #print "Widget name=", wname
739 visited_components.add(wname)
741 # get corresponding 'gui' object
743 attributes = screen[wname].skinAttributes = [ ]
745 raise SkinError("component with name '" + wname + "' was not found in skin of screen '" + name + "'!")
746 #print "WARNING: component with name '" + wname + "' was not found in skin of screen '" + name + "'!"
748 # assert screen[wname] is not Source
750 # and collect attributes for this
751 collectAttributes(attributes, widget, context, skin_path_prefix, ignore=['name'])
753 # get corresponding source
754 #print "Widget source=", wsource
756 while True: # until we found a non-obsolete source
758 # parse our current "wsource", which might specifiy a "related screen" before the dot,
759 # for example to reference a parent, global or session-global screen.
762 # resolve all path components
763 path = wsource.split('.')
765 scr = screen.getRelatedScreen(path[0])
769 raise SkinError("specified related screen '" + wsource + "' was not found in screen '" + name + "'!")
772 # resolve the source.
773 source = scr.get(path[0])
774 if isinstance(source, ObsoleteSource):
775 # however, if we found an "obsolete source", issue warning, and resolve the real source.
776 print "WARNING: SKIN '%s' USES OBSOLETE SOURCE '%s', USE '%s' INSTEAD!" % (name, wsource, source.new_source)
777 print "OBSOLETE SOURCE WILL BE REMOVED %s, PLEASE UPDATE!" % (source.removal_date)
778 if source.description:
779 print source.description
781 wsource = source.new_source
783 # otherwise, use that source.
787 raise SkinError("source '" + wsource + "' was not found in screen '" + name + "'!")
789 wrender = get_attr('render')
792 raise SkinError("you must define a renderer with render= for source '%s'" % (wsource))
794 for converter in widget.findall("convert"):
795 ctype = converter.get('type')
796 assert ctype, "'convert'-tag needs a 'type'-attribute"
797 #print "Converter:", ctype
799 parms = converter.text.strip()
802 #print "Params:", parms
803 converter_class = my_import('.'.join(("Components", "Converter", ctype))).__dict__.get(ctype)
807 for i in source.downstream_elements:
808 if isinstance(i, converter_class) and i.converter_arguments == parms:
812 print "allocating new converter!"
813 c = converter_class(parms)
816 print "reused converter!"
820 renderer_class = my_import('.'.join(("Components", "Renderer", wrender))).__dict__.get(wrender)
822 renderer = renderer_class() # instantiate renderer
824 renderer.connect(source) # connect to source
825 attributes = renderer.skinAttributes = [ ]
826 collectAttributes(attributes, widget, context, skin_path_prefix, ignore=['render', 'source'])
828 screen.renderer.append(renderer)
830 def process_applet(widget, context):
832 codeText = widget.text.strip()
838 widgetType = widget.attrib.get('type')
840 code = compile(codeText, "skin applet", "exec")
842 if widgetType == "onLayoutFinish":
843 screen.onLayoutFinish.append(code)
844 #print "onLayoutFinish = ", codeText
846 raise SkinError("applet type '%s' unknown!" % widgetType)
847 #print "applet type '%s' unknown!" % type
849 def process_elabel(widget, context):
850 w = additionalWidget()
852 w.skinAttributes = [ ]
853 collectAttributes(w.skinAttributes, widget, context, skin_path_prefix, ignore=['name'])
854 screen.additionalWidgets.append(w)
856 def process_epixmap(widget, context):
857 w = additionalWidget()
859 w.skinAttributes = [ ]
860 collectAttributes(w.skinAttributes, widget, context, skin_path_prefix, ignore=['name'])
861 screen.additionalWidgets.append(w)
863 def process_screen(widget, context):
864 for w in widget.findall("widget"):
865 process_widget(w, context)
867 for w in widget.getchildren():
868 if w.tag == "widget":
871 p = processors.get(w.tag, process_none)
874 def process_panel(widget, context):
875 n = widget.attrib.get('name')
878 s = dom_screens.get(n, None)
880 print "[SKIN] Unable to find screen '%s' referred in screen '%s'" % (n, name)
882 process_screen(s[0], context)
884 layout = widget.attrib.get('layout')
885 if layout == 'stack':
886 cc = SkinContextStack
890 c = cc(context, widget.attrib.get('position'), widget.attrib.get('size'))
891 except Exception, ex:
892 raise SkinError("Failed to create skincontext (%s,%s) in %s: %s" % (widget.attrib.get('position'), widget.attrib.get('size'), context, ex) )
894 process_screen(widget, c)
898 "widget": process_widget,
899 "applet": process_applet,
900 "eLabel": process_elabel,
901 "ePixmap": process_epixmap,
902 "panel": process_panel
906 context.x = 0 # reset offsets, all components are relative to screen
907 context.y = 0 # coordinates.
908 process_screen(myscreen, context)
910 print "[Skin] SKIN ERROR:", e
912 from Components.GUIComponent import GUIComponent
913 nonvisited_components = [x for x in set(screen.keys()) - visited_components if isinstance(x, GUIComponent)]
914 assert not nonvisited_components, "the following components in %s don't have a skin entry: %s" % (name, ', '.join(nonvisited_components))
915 # This may look pointless, but it unbinds 'screen' from the nested scope. A better
916 # solution is to avoid the nested scope above and use the context object to pass
919 visited_components = None