XML Skinning Framework
We needed to build applications fast that also looked gorgeous. So we created:
- A skinning framework with source stored in XML that a graphic artist can skin like a web app. Oh yeah, let's be able to ship those interfaces around over, um, Jabber. Ok, got that one.
- Python code in CDATA for when you don't want to mar your beautiful class with GUI customization details
- A W3C compliant CSS Engine to go with framework, or the graphics guys won't play :)
- Ability to create skins without the graphics guy because not every app gets to be beautified
- It's got to be easy to hook your GUIs to your model, so we make that trivial
- XML sucks to deal with -- It's just not Pythonic enough. So, to fix that, we turn the XML into objects. I'll never go back to the old way if I have to.
Yes, there are lots of GUI description schemas, some of them XML based, like PythonCard and wxGlade. However, they lack a couple of things:
- Simple editing. GUI Builder tools are not always able to round trip with source form. Or, they may be fragile when round-tripping. Sometimes, using your familiar ol'text-editor will let you complete changes in half the time. By using standards-based XML and CSS as our GUI-source, we hope to have laid the groundwork for these files to round trip well.
- Extensibility. This framework was written with the intent of being extended in YOUR codebase. (or ours!) We provide the toolkit, you provide the customizations and applications.
- Raw power. They don't do inline python. 'nuff said.
A Little Structure
Every xml node in this skin gets "skinned" into a "skin element". Each skin element can then create a corresponding "skin object" to represent the element in the user interface. For instance, frame creates an instance of TG.skinning.toolkits.wx.frames.frame.frame which then create an instance of wx.Frame initialized according to the settings and environment specified in the skin. This covers the xml nodes and the attributes they contain. The content is a little different.
In the wx skinning framework, the CData portion of the nodes is interpreted as python code. Therefore indentation still matters. But you can do most anything you can do in a normal python module, subject to normal XML escaping rules for &, <, and >. Ex. & becomes &, and < becomes <. Each xml node's cdata is given a clean module instance to populate, and is insulated from all the other elements. Given this, we also provide three special variables to the module for the context.
First and most important variable is "obj", which is a reference back to the "skin object" instance mentioned above. Generally this is a wx object instance of some type. The variable "elem" is a reference to the "skin element" that created "obj", and it can contain various helper methods and style settings, but is not often referenced. Last is the "ctx" variable, which is a stack-style construct for exchanging data between contexts. Not every element pushes the context, usually main ones like "frame", "dialog", and "skin", although you can push and pop it manually through "elem" or with attributes on the xml nodes. See TG.skinning.common.element for more details.
Genrally the most important item on the context is the model, which is instance of WebChatApp? below. It passes itself into the skinning process. See TG.skinning.skinModel for more details about that. This leaves one last major item to talk about, and that is the xml attributes ctxelem and ctxobj. The "ctxelem" attributes simply ask the skin element to add itself to ctx at a dotted name. No dot just puts it on the ctx stack, whereas ctx.model.thingy would put it on the WebChatApp? instance under the "thingy" name. The "ctxobj" attribute asks the skin element to put the "skin object" on the ctx in a similar fashion.
Code
- Tests: source:trunk/test/skinning/
- Demos: source:trunk/demo/skinning/
- Documentation: source:trunk/doc/skinning/
FAQ
Short list of frequently asked questions, with an eventual link to a longer-list
Example
![]() |
![]() |
![]() |
With the following code in notebook.py
from TG.skinning.toolkits.wx import wxSkinModel
model = wxSkinModel.fromSkinHref('/notebook.skin')
model.skinModel()
and this xml skin in notebook.skin:
<?xml version='1.0'?>
<skin xmlns='TG.skinning.toolkits.wx'>
<style>
frame {frame-main:1; locking:1; size:'800,600'}
frame>layout {layout-cfg:'1,EXPAND'}
layout>layout {layout-cfg:'1,EXPAND'}
frame>layout>panel {layout-cfg:'1,EXPAND'}
</style>
<frame title='My Frame' show='1'>
<layout>
<panel>
<layout>
<notebook layout-cfg='1,EXPAND'>
<image-list size='16,16'>
<image-art art='ART_INFORMATION' size='16,16' for='Me'/>
<image-art art='ART_QUESTION ' size='16,16'/>
<image-art art='ART_WARNING' size='16,16' for='you'/>
</image-list>
<panel page-name='Web' page-image='Me'>
<layout>
<htmlwin href='http://python.org' layout-cfg='1,EXPAND'/>
</layout>
</panel>
<page page-name='Pycrust' page-image='you'>
<layout>
<pycrust layout-cfg='1,EXPAND'/>
</layout>
</page>
<calendar page-name='Calendar' page-image='1'/>
<event>
print "You selected a page \"%s\" in notebook" % (evt.GetString(),)
evt.Skip()
</event>
</notebook>
</layout>
</panel>
</layout>
obj.Center()
</frame>
</skin>



