We're back after a server migration that caused effbot.org to fall over a bit harder than expected. Expect some glitches.

Generating Tkinter User Interfaces from XML

Fredrik Lundh | August 2003 | Originally posted to online.effbot.org

Note: Simon Forman has combined a small custom language with ideas from this article into a tool for generating Tkinter user interfaces from short textual descriptions, called Pygoo.

The following snippet takes an element tree, and uses it to populate a Tkinter Frame widget.

import Tkinter

def realize(master, element):
    if element.tag == "form":
        frame = Tkinter.Frame(master, **element.attrib)
        for subelement in element:
            widget = realize(frame, subelement)
            widget.pack()
        return frame
    else:
        options = element.attrib
        if element:
            options = options.copy()
            for subelement in element:
                options[subelement.tag] = subelement.text
        widget_factory = getattr(Tkinter, element.tag.capitalize())
        return widget_factory(master, **options)

The function supports two kinds of elements; form elements and widget elements such as entry, label, text, etc.

Form elements are used to create and populate Tkinter frame widgets. Each form element can contain one or more widget elements, and can also contain other forms. Attributes on a form element as passed as options to the underlying frame widget.

Widget elements map directly to the corresponding Tkinter widget, with options taken from the element attributes. For widget elements, subelements are also mapped to Tkinter options. For example, you can specify label text using either a text attribute or a text subelement.

Let’s try it out:

from elementtree.ElementTree import XML
from Tkinter import Tk

form = XML("""\
<form>
    <label><text>entry:</text></label>
    <entry width='30' bg='gold' />
    <checkbutton><text>checkbutton</text></checkbutton>
    <button text='OK' />
    <button text='Cancel' />
</form>
""")

root = Tk()
root.title("ElementTk")

frame = realize(root, form)
frame.pack()

root.mainloop()

Here’s the resulting window:

To add some behaviour to this example, you need a way to get your hands on the actual widget instances. Tkinter’s name option comes in handy; by naming the widgets, you can access them via the frame’s children attribute:

from elementtree.ElementTree import XML
from Tkinter import Tk

form = XML("""\
<form>
    <label><text>entry:</text></label>
    <entry name='entry' width='30' bg='gold' />
    <checkbutton><text>checkbutton</text></checkbutton>
    <button name='ok' text='OK' />
    <button name='cancel' text='Cancel' />
</form>
""")

root = Tk()

frame = realize(root, form)
frame.pack()

def ok():
    print frame.children["entry"].get()
    root.quit()

# add button behaviour
frame.children["ok"].config(command=ok)
frame.children["cancel"].config(command=root.quit)

root.mainloop()

Not perfect, but it’s good enough to play with. (If you have any good ideas on how to make it better, feel free to leave a comment).