Commits

Anonymous committed efe0edf

Add and remove files belonging to the last change, bringing parts of the folder into synch with python trunk. This is work in progress.

  • Participants
  • Parent commits 1163efd

Comments (0)

Files changed (139)

Demo/scripts/ftpstats.py

-#! /usr/bin/env python
-
-# Extract statistics from ftp daemon log.
-
-# Usage:
-# ftpstats [-m maxitems] [-s search] [file]
-# -m maxitems: restrict number of items in "top-N" lists, default 25.
-# -s string:   restrict statistics to lines containing this string.
-# Default file is /usr/adm/ftpd;  a "-" means read standard input.
-
-# The script must be run on the host where the ftp daemon runs.
-# (At CWI this is currently buizerd.)
-
-import os
-import sys
-import re
-import string
-import getopt
-
-pat = '^([a-zA-Z0-9 :]*)!(.*)!(.*)!([<>].*)!([0-9]+)!([0-9]+)$'
-prog = re.compile(pat)
-
-def main():
-    maxitems = 25
-    search = None
-    try:
-        opts, args = getopt.getopt(sys.argv[1:], 'm:s:')
-    except getopt.error, msg:
-        print msg
-        print 'usage: ftpstats [-m maxitems] [file]'
-        sys.exit(2)
-    for o, a in opts:
-        if o == '-m':
-            maxitems = string.atoi(a)
-        if o == '-s':
-            search = a
-    file = '/usr/adm/ftpd'
-    if args: file = args[0]
-    if file == '-':
-        f = sys.stdin
-    else:
-        try:
-            f = open(file, 'r')
-        except IOError, msg:
-            print file, ':', msg
-            sys.exit(1)
-    bydate = {}
-    bytime = {}
-    byfile = {}
-    bydir = {}
-    byhost = {}
-    byuser = {}
-    bytype = {}
-    lineno = 0
-    try:
-        while 1:
-            line = f.readline()
-            if not line: break
-            lineno = lineno + 1
-            if search and string.find(line, search) < 0:
-                continue
-            if prog.match(line) < 0:
-                print 'Bad line', lineno, ':', repr(line)
-                continue
-            items = prog.group(1, 2, 3, 4, 5, 6)
-            (logtime, loguser, loghost, logfile, logbytes,
-             logxxx2) = items
-##                      print logtime
-##                      print '-->', loguser
-##                      print '--> -->', loghost
-##                      print '--> --> -->', logfile
-##                      print '--> --> --> -->', logbytes
-##                      print '--> --> --> --> -->', logxxx2
-##                      for i in logtime, loghost, logbytes, logxxx2:
-##                              if '!' in i: print '???', i
-            add(bydate, logtime[-4:] + ' ' + logtime[:6], items)
-            add(bytime, logtime[7:9] + ':00-59', items)
-            direction, logfile = logfile[0], logfile[1:]
-            # The real path probably starts at the last //...
-            while 1:
-                i = string.find(logfile, '//')
-                if i < 0: break
-                logfile = logfile[i+1:]
-            add(byfile, logfile + ' ' + direction, items)
-            logdir = os.path.dirname(logfile)
-##              logdir = os.path.normpath(logdir) + '/.'
-            while 1:
-                add(bydir, logdir + ' ' + direction, items)
-                dirhead = os.path.dirname(logdir)
-                if dirhead == logdir: break
-                logdir = dirhead
-            add(byhost, loghost, items)
-            add(byuser, loguser, items)
-            add(bytype, direction, items)
-    except KeyboardInterrupt:
-        print 'Interrupted at line', lineno
-    show(bytype, 'by transfer direction', maxitems)
-    show(bydir, 'by directory', maxitems)
-    show(byfile, 'by file', maxitems)
-    show(byhost, 'by host', maxitems)
-    show(byuser, 'by user', maxitems)
-    showbar(bydate, 'by date')
-    showbar(bytime, 'by time of day')
-
-def showbar(dict, title):
-    n = len(title)
-    print '='*((70-n)//2), title, '='*((71-n)//2)
-    list = []
-    keys = dict.keys()
-    keys.sort()
-    for key in keys:
-        n = len(str(key))
-        list.append((len(dict[key]), key))
-    maxkeylength = 0
-    maxcount = 0
-    for count, key in list:
-        maxkeylength = max(maxkeylength, len(key))
-        maxcount = max(maxcount, count)
-    maxbarlength = 72 - maxkeylength - 7
-    for count, key in list:
-        barlength = int(round(maxbarlength*float(count)/maxcount))
-        bar = '*'*barlength
-        print '%5d %-*s %s' % (count, maxkeylength, key, bar)
-
-def show(dict, title, maxitems):
-    if len(dict) > maxitems:
-        title = title + ' (first %d)'%maxitems
-    n = len(title)
-    print '='*((70-n)//2), title, '='*((71-n)//2)
-    list = []
-    keys = dict.keys()
-    for key in keys:
-        list.append((-len(dict[key]), key))
-    list.sort()
-    for count, key in list[:maxitems]:
-        print '%5d %s' % (-count, key)
-
-def add(dict, key, item):
-    if dict.has_key(key):
-        dict[key].append(item)
-    else:
-        dict[key] = [item]
-
-if __name__ == "__main__":
-    main()

Demo/scripts/mkrcs.py

-#! /usr/bin/env python
-
-# A rather specialized script to make sure that a symbolic link named
-# RCS exists pointing to a real RCS directory in a parallel tree
-# referenced as RCStree in an ancestor directory.
-# (I use this because I like my RCS files to reside on a physically
-# different machine).
-
-import os
-
-def main():
-    rcstree = 'RCStree'
-    rcs = 'RCS'
-    if os.path.islink(rcs):
-        print '%r is a symlink to %r' % (rcs, os.readlink(rcs))
-        return
-    if os.path.isdir(rcs):
-        print '%r is an ordinary directory' % (rcs,)
-        return
-    if os.path.exists(rcs):
-        print '%r is a file?!?!' % (rcs,)
-        return
-    #
-    p = os.getcwd()
-    up = ''
-    down = ''
-    # Invariants:
-    # (1) join(p, down) is the current directory
-    # (2) up is the same directory as p
-    # Ergo:
-    # (3) join(up, down) is the current directory
-    #print 'p =', repr(p)
-    while not os.path.isdir(os.path.join(p, rcstree)):
-        head, tail = os.path.split(p)
-        #print 'head = %r; tail = %r' % (head, tail)
-        if not tail:
-            print 'Sorry, no ancestor dir contains %r' % (rcstree,)
-            return
-        p = head
-        up = os.path.join(os.pardir, up)
-        down = os.path.join(tail, down)
-        #print 'p = %r; up = %r; down = %r' % (p, up, down)
-    there = os.path.join(up, rcstree)
-    there = os.path.join(there, down)
-    there = os.path.join(there, rcs)
-    if os.path.isdir(there):
-        print '%r already exists' % (there, )
-    else:
-        print 'making %r' % (there,)
-        makedirs(there)
-    print 'making symlink %r -> %r' % (rcs, there)
-    os.symlink(there, rcs)
-
-def makedirs(p):
-    if not os.path.isdir(p):
-        head, tail = os.path.split(p)
-        makedirs(head)
-        os.mkdir(p, 0777)
-
-if __name__ == "__main__":
-    main()

Demo/scripts/wh.py

-# This is here so I can use 'wh' instead of 'which' in '~/bin/generic_python'
-import which

Demo/tkinter/ttk/combo_themes.py

+"""Ttk Theme Selector.
+
+Although it is a theme selector, you won't notice many changes since
+there is only a combobox and a frame around.
+"""
+import ttk
+
+class App(ttk.Frame):
+    def __init__(self):
+        ttk.Frame.__init__(self)
+
+        self.style = ttk.Style()
+        self._setup_widgets()
+
+    def _change_theme(self, event):
+        if event.widget.current(): # value #0 is not a theme
+            newtheme = event.widget.get()
+            # change to the new theme and refresh all the widgets
+            self.style.theme_use(newtheme)
+
+    def _setup_widgets(self):
+        themes = list(self.style.theme_names())
+        themes.insert(0, "Pick a theme")
+        # Create a readonly Combobox which will display 4 values at max,
+        # which will cause it to create a scrollbar if there are more
+        # than 4 values in total.
+        themes_combo = ttk.Combobox(self, values=themes, state="readonly",
+                                    height=4)
+        themes_combo.set(themes[0]) # sets the combobox value to "Pick a theme"
+        # Combobox widget generates a <<ComboboxSelected>> virtual event
+        # when the user selects an element. This event is generated after
+        # the listbox is unposted (after you select an item, the combobox's
+        # listbox disappears, then it is said that listbox is now unposted).
+        themes_combo.bind("<<ComboboxSelected>>", self._change_theme)
+        themes_combo.pack(fill='x')
+
+        self.pack(fill='both', expand=1)
+
+
+def main():
+    app = App()
+    app.master.title("Ttk Combobox")
+    app.mainloop()
+
+if __name__ == "__main__":
+    main()

Demo/tkinter/ttk/dirbrowser.py

+"""A directory browser using Ttk Treeview.
+
+Based on the demo found in Tk 8.5 library/demos/browse
+"""
+import os
+import glob
+import Tkinter
+import ttk
+
+def populate_tree(tree, node):
+    if tree.set(node, "type") != 'directory':
+        return
+
+    path = tree.set(node, "fullpath")
+    tree.delete(*tree.get_children(node))
+
+    parent = tree.parent(node)
+    special_dirs = [] if parent else glob.glob('.') + glob.glob('..')
+
+    for p in special_dirs + os.listdir(path):
+        ptype = None
+        p = os.path.join(path, p).replace('\\', '/')
+        if os.path.isdir(p): ptype = "directory"
+        elif os.path.isfile(p): ptype = "file"
+
+        fname = os.path.split(p)[1]
+        id = tree.insert(node, "end", text=fname, values=[p, ptype])
+
+        if ptype == 'directory':
+            if fname not in ('.', '..'):
+                tree.insert(id, 0, text="dummy")
+                tree.item(id, text=fname)
+        elif ptype == 'file':
+            size = os.stat(p).st_size
+            tree.set(id, "size", "%d bytes" % size)
+
+
+def populate_roots(tree):
+    dir = os.path.abspath('.').replace('\\', '/')
+    node = tree.insert('', 'end', text=dir, values=[dir, "directory"])
+    populate_tree(tree, node)
+
+def update_tree(event):
+    tree = event.widget
+    populate_tree(tree, tree.focus())
+
+def change_dir(event):
+    tree = event.widget
+    node = tree.focus()
+    if tree.parent(node):
+        path = os.path.abspath(tree.set(node, "fullpath"))
+        if os.path.isdir(path):
+            os.chdir(path)
+            tree.delete(tree.get_children(''))
+            populate_roots(tree)
+
+def autoscroll(sbar, first, last):
+    """Hide and show scrollbar as needed."""
+    first, last = float(first), float(last)
+    if first <= 0 and last >= 1:
+        sbar.grid_remove()
+    else:
+        sbar.grid()
+    sbar.set(first, last)
+
+root = Tkinter.Tk()
+
+vsb = ttk.Scrollbar(orient="vertical")
+hsb = ttk.Scrollbar(orient="horizontal")
+
+tree = ttk.Treeview(columns=("fullpath", "type", "size"),
+    displaycolumns="size", yscrollcommand=lambda f, l: autoscroll(vsb, f, l),
+    xscrollcommand=lambda f, l:autoscroll(hsb, f, l))
+
+vsb['command'] = tree.yview
+hsb['command'] = tree.xview
+
+tree.heading("#0", text="Directory Structure", anchor='w')
+tree.heading("size", text="File Size", anchor='w')
+tree.column("size", stretch=0, width=100)
+
+populate_roots(tree)
+tree.bind('<<TreeviewOpen>>', update_tree)
+tree.bind('<Double-Button-1>', change_dir)
+
+# Arrange the tree and its scrollbars in the toplevel
+tree.grid(column=0, row=0, sticky='nswe')
+vsb.grid(column=1, row=0, sticky='ns')
+hsb.grid(column=0, row=1, sticky='ew')
+root.grid_columnconfigure(0, weight=1)
+root.grid_rowconfigure(0, weight=1)
+
+root.mainloop()

Demo/tkinter/ttk/img/close.gif

Added
New image

Demo/tkinter/ttk/img/close_active.gif

Added
New image

Demo/tkinter/ttk/img/close_pressed.gif

Added
New image

Demo/tkinter/ttk/listbox_scrollcmd.py

+"""Sample taken from: http://www.tkdocs.com/tutorial/morewidgets.html and
+converted to Python, mainly to demonstrate xscrollcommand option.
+
+grid [tk::listbox .l -yscrollcommand ".s set" -height 5] -column 0 -row 0 -sticky nwes
+grid [ttk::scrollbar .s -command ".l yview" -orient vertical] -column 1 -row 0 -sticky ns
+grid [ttk::label .stat -text "Status message here" -anchor w] -column 0 -row 1 -sticky we
+grid [ttk::sizegrip .sz] -column 1 -row 1 -sticky se
+grid columnconfigure . 0 -weight 1; grid rowconfigure . 0 -weight 1
+for {set i 0} {$i<100} {incr i} {
+   .l insert end "Line $i of 100"
+   }
+"""
+import Tkinter
+import ttk
+
+root = Tkinter.Tk()
+
+l = Tkinter.Listbox(height=5)
+l.grid(column=0, row=0, sticky='nwes')
+
+s = ttk.Scrollbar(command=l.yview, orient='vertical')
+l['yscrollcommand'] = s.set
+s.grid(column=1, row=0, sticky="ns")
+
+stat = ttk.Label(text="Status message here", anchor='w')
+stat.grid(column=0, row=1, sticky='we')
+
+sz = ttk.Sizegrip()
+sz.grid(column=1, row=1, sticky='se')
+
+root.grid_columnconfigure(0, weight=1)
+root.grid_rowconfigure(0, weight=1)
+
+for i in range(100):
+    l.insert('end', "Line %d of 100" % i)
+
+root.mainloop()

Demo/tkinter/ttk/mac_searchentry.py

+"""Mac style search widget
+
+Translated from Tcl code by Schelte Bron, http://wiki.tcl.tk/18188
+"""
+import Tkinter
+import ttk
+
+root = Tkinter.Tk()
+
+data = """
+R0lGODlhKgAaAOfnAFdZVllbWFpcWVtdWlxeW11fXF9hXmBiX2ZnZWhpZ2lraGxua25wbXJ0
+cXR2c3V3dHZ4dXh6d3x+e31/fH6AfYSGg4eJhoiKh4qMiYuNio2PjHmUqnqVq3yXrZGTkJKU
+kX+asJSWk32cuJWXlIGcs5aYlX6euZeZloOetZial4SftpqbmIWgt4GhvYahuIKivpudmYei
+uYOjv5yem4ijuoSkwIWlwYmlu56gnYamwp+hnoenw4unvaCin4ioxJCnuZykrImpxZmlsoaq
+zI2pv6KkoZGouoqqxpqms4erzaOloo6qwYurx5Kqu5untIiszqSmo5CrwoysyJeqtpOrvJyo
+tZGsw42typSsvaaopZKtxJWtvp6qt4+uy6epppOuxZCvzKiqp5quuZSvxoyx06mrqJWwx42y
+1JKxzpmwwaqsqZaxyI6z1ZqxwqutqpOzz4+01qyuq56yvpizypS00Jm0y5W10Zq1zJa20rCy
+rpu3zqizwbGzr6C3yZy4z7K0saG4yp250LO1sqK5y5660Z+70qO7zKy4xaC806S8zba4taG9
+1KW9zq66x6+7yLi6t6S/1rC8yrm7uLO8xLG9y7q8ubS9xabB2anB07K+zLW+xrO/za7CzrTA
+zrjAyLXBz77BvbbC0K/G2LjD0bnE0rLK28TGw8bIxcLL07vP28HN28rMycvOyr/T38DU4cnR
+2s/RztHT0NLU0cTY5MrW5MvX5dHX2c3Z59bY1dPb5Nbb3dLe7Nvd2t3f3NXh797g3d3j5dnl
+9OPl4eTm4+Ln6tzo9uXn5Obo5eDp8efp5uHq8uXq7ejq5+nr6OPs9Ovu6unu8O3v6+vw8+7w
+7ezx9O/x7vDy7/Hz8O/19/P18vT38/L3+fb49Pf59vX6/fj69/b7/vn7+Pr8+ff9//v9+vz/
++/7//P//////////////////////////////////////////////////////////////////
+/////////////////////////////////yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJZAD/ACwC
+AAIAKAAWAAAI/gD/CRz4bwUGCg8eQFjIsGHDBw4iTLAQgqBFgisuePCiyJOpUyBDihRpypMi
+Lx8qaLhIMIyGFZ5sAUsmjZrNmzhzWpO2DJgtTysqfGDpxoMbW8ekeQsXzty4p1CjRjUXrps3
+asJsuclQ4uKKSbamMR3n1JzZs2jRkh1HzuxVXX8y4CDYAwqua+DInVrRwMGJU2kDp31KThy1
+XGWGDlxhi1rTPAUICBBAoEAesoIzn6Vm68MKgVAUHftmzhOCBCtQwQKSoABgzZnJdSMmyIPA
+FbCotdUQAIhNa9B6DPCAGbZac+SowVIMRVe4pwkA4GpqDlwuAAmMZx4nTtfnf1mO5JEDNy46
+MHJkxQEDgKC49rPjwC0bqGaZuOoZAKjBPE4NgAzUvYcWOc0QZF91imAnCDHJ5JFAAJN0I2Ba
+4iRDUC/gOEVNDwIUcEABCAgAAATUTIgWOMBYRFp80ghiAQIIVAAEAwJIYI2JZnUji0XSYAYO
+NcsQA8wy0hCTwAASXGOiONFcxAtpTokTHznfiLMNMAkcAMuE43jDC0vLeGOWe2R5o4sn1LgH
+GzkWsvTPMgEOaA433Ag4TjjMuDkQMNi0tZ12sqWoJ0HATMPNffAZZ6U0wLAyqJ62RGoLLrhI
+aqmlpzwaEAAh+QQJZAD/ACwAAAAAKgAaAAAI/gD/CRw40JEhQoEC+fGjcOHCMRAjRkxDsKLF
+f5YcAcID582ZjyBDJhmZZIjJIUySEDHiBMhFghrtdNnRAgSHmzhz6sTZQcSLITx+CHn5bxSk
+Nz5MCMGy55CjTVCjbuJEtSrVQ3uwqDBRQwrFi476SHHxow8qXcemVbPGtm21t3CnTaP27Jgu
+VHtuiIjBsuImQkRiiEEFTNo2cOTMKV7MuLE5cN68QUOGSgwKG1EqJqJDY8+rZt8UjxtNunTj
+cY3DgZOWS46KIFgGjiI0ZIsqaqNNjWjgYMUpx8Adc3v2aosNMAI1DbqyI9WycOb4IAggQEAB
+A3lQBxet/TG4cMpI/tHwYeSfIzxM0uTKNs7UgAQrYL1akaDA7+3bueVqY4NJlUhIcQLNYx8E
+AIQ01mwjTQ8DeNAdfouNA8440GBCQxJY3MEGD6p4Y844CQCAizcSgpMLAAlAuJ03qOyQRBR3
+nEHEK+BMGKIui4kDDAAIPKiiYuSYSMQQRCDCxhiziPMYBgDkEaEaAGQA3Y+MjUPOLFoMoUUh
+cKxRC4ngeILiH8Qkk0cCAUzSDZWpzbLEE1EwggcYqWCj2DNADFDAAQUgIAAAEFDDJmPYqNJF
+F1s4cscTmCDjDTjdSPOHBQggUAEQDAgggTWDPoYMJkFoUdRmddyyjWLeULMMMcAsIw0x4wkM
+IME1g25zyxpHxFYUHmyIggw4H4ojITnfiLMNMAkcAAub4BQjihRdDGTJHmvc4Qo1wD6Imje6
+eILbj+BQ4wqu5Q3ECSJ0FOKKMtv4mBg33Pw4zjbKuBIIE1xYpIkhdQQiyi7OtAucj6dt48wu
+otQhBRa6VvSJIRwhIkotvgRTzMUYZ6xxMcj4QkspeKDxxRhEmUfIHWjAgQcijEDissuXvCyz
+zH7Q8YQURxDhUsn/bCInR3AELfTQZBRt9BBJkCGFFVhMwTNBlnBCSCGEIJQQIAklZMXWRBAR
+RRRWENHwRQEBADs="""
+
+
+s1 = Tkinter.PhotoImage("search1", data=data, format="gif -index 0")
+s2 = Tkinter.PhotoImage("search2", data=data, format="gif -index 1")
+
+style = ttk.Style()
+
+style.element_create("Search.field", "image", "search1",
+    ("focus", "search2"), border=[22, 7, 14], sticky="ew")
+
+style.layout("Search.entry", [
+    ("Search.field", {"sticky": "nswe", "border": 1, "children":
+        [("Entry.padding", {"sticky": "nswe", "children":
+            [("Entry.textarea", {"sticky": "nswe"})]
+        })]
+    })]
+)
+
+style.configure("Search.entry", background="#b2b2b2")
+
+root.configure(background="#b2b2b2")
+
+e1 = ttk.Entry(style="Search.entry", width=20)
+e2 = ttk.Entry(style="Search.entry", width=20)
+
+e1.grid(padx=10, pady=10)
+e2.grid(padx=10, pady=10)
+
+root.mainloop()

Demo/tkinter/ttk/notebook_closebtn.py

+"""A Ttk Notebook with close buttons.
+
+Based on an example by patthoyts, http://paste.tclers.tk/896
+"""
+import os
+import Tkinter
+import ttk
+
+root = Tkinter.Tk()
+
+imgdir = os.path.join(os.path.dirname(__file__), 'img')
+i1 = Tkinter.PhotoImage("img_close", file=os.path.join(imgdir, 'close.gif'))
+i2 = Tkinter.PhotoImage("img_closeactive",
+    file=os.path.join(imgdir, 'close_active.gif'))
+i3 = Tkinter.PhotoImage("img_closepressed",
+    file=os.path.join(imgdir, 'close_pressed.gif'))
+
+style = ttk.Style()
+
+style.element_create("close", "image", "img_close",
+    ("active", "pressed", "!disabled", "img_closepressed"),
+    ("active", "!disabled", "img_closeactive"), border=8, sticky='')
+
+style.layout("ButtonNotebook", [("ButtonNotebook.client", {"sticky": "nswe"})])
+style.layout("ButtonNotebook.Tab", [
+    ("ButtonNotebook.tab", {"sticky": "nswe", "children":
+        [("ButtonNotebook.padding", {"side": "top", "sticky": "nswe",
+                                     "children":
+            [("ButtonNotebook.focus", {"side": "top", "sticky": "nswe",
+                                       "children":
+                [("ButtonNotebook.label", {"side": "left", "sticky": ''}),
+                 ("ButtonNotebook.close", {"side": "left", "sticky": ''})]
+            })]
+        })]
+    })]
+)
+
+def btn_press(event):
+    x, y, widget = event.x, event.y, event.widget
+    elem = widget.identify(x, y)
+    index = widget.index("@%d,%d" % (x, y))
+
+    if "close" in elem:
+        widget.state(['pressed'])
+        widget.pressed_index = index
+
+def btn_release(event):
+    x, y, widget = event.x, event.y, event.widget
+
+    if not widget.instate(['pressed']):
+        return
+
+    elem =  widget.identify(x, y)
+    index = widget.index("@%d,%d" % (x, y))
+
+    if "close" in elem and widget.pressed_index == index:
+        widget.forget(index)
+        widget.event_generate("<<NotebookClosedTab>>")
+
+    widget.state(["!pressed"])
+    widget.pressed_index = None
+
+
+root.bind_class("TNotebook", "<ButtonPress-1>", btn_press, True)
+root.bind_class("TNotebook", "<ButtonRelease-1>", btn_release)
+
+# create a ttk notebook with our custom style, and add some tabs to it
+nb = ttk.Notebook(width=200, height=200, style="ButtonNotebook")
+nb.pressed_index = None
+f1 = Tkinter.Frame(nb, background="red")
+f2 = Tkinter.Frame(nb, background="green")
+f3 = Tkinter.Frame(nb, background="blue")
+nb.add(f1, text='Red', padding=3)
+nb.add(f2, text='Green', padding=3)
+nb.add(f3, text='Blue', padding=3)
+nb.pack(expand=1, fill='both')
+
+root.mainloop()

Demo/tkinter/ttk/plastik_theme.py

+"""This demonstrates good part of the syntax accepted by theme_create.
+
+This is a translation of plastik.tcl to python.
+You will need the images used by the plastik theme to test this. The
+images (and other tile themes) can be retrived by doing:
+
+$ cvs -z3 -d:pserver:anonymous@tktable.cvs.sourceforge.net:/cvsroot/tktable \
+  co tile-themes
+
+To test this module you should do, for example:
+
+import Tkinter
+import plastik_theme
+
+root = Tkinter.Tk()
+plastik_theme.install(plastik_image_dir)
+...
+
+Where plastik_image_dir contains the path to the images directory used by
+the plastik theme, something like: tile-themes/plastik/plastik
+"""
+import os
+import glob
+import ttk
+from Tkinter import PhotoImage
+
+__all__ = ['install']
+
+colors = {
+    "frame": "#efefef",
+    "disabledfg": "#aaaaaa",
+    "selectbg": "#657a9e",
+    "selectfg": "#ffffff"
+    }
+
+imgs = {}
+def _load_imgs(imgdir):
+    imgdir = os.path.expanduser(imgdir)
+    if not os.path.isdir(imgdir):
+        raise Exception("%r is not a directory, can't load images" % imgdir)
+    for f in glob.glob("%s/*.gif" % imgdir):
+        img = os.path.split(f)[1]
+        name = img[:-4]
+        imgs[name] = PhotoImage(name, file=f, format="gif89")
+
+def install(imgdir):
+    _load_imgs(imgdir)
+    style = ttk.Style()
+    style.theme_create("plastik", "default", settings={
+        ".": {
+            "configure":
+                {"background": colors['frame'],
+                 "troughcolor": colors['frame'],
+                 "selectbackground": colors['selectbg'],
+                 "selectforeground": colors['selectfg'],
+                 "fieldbackground": colors['frame'],
+                 "font": "TkDefaultFont",
+                 "borderwidth": 1},
+            "map": {"foreground": [("disabled", colors['disabledfg'])]}
+        },
+
+        "Vertical.TScrollbar": {"layout": [
+            ("Vertical.Scrollbar.uparrow", {"side": "top", "sticky": ''}),
+            ("Vertical.Scrollbar.downarrow", {"side": "bottom", "sticky": ''}),
+            ("Vertical.Scrollbar.uparrow", {"side": "bottom", "sticky": ''}),
+            ("Vertical.Scrollbar.trough", {"sticky": "ns", "children":
+                [("Vertical.Scrollbar.thumb", {"expand": 1, "unit": 1,
+                    "children": [("Vertical.Scrollbar.grip", {"sticky": ''})]
+                })]
+            })]
+        },
+
+        "Horizontal.TScrollbar": {"layout": [
+            ("Horizontal.Scrollbar.leftarrow", {"side": "left", "sticky": ''}),
+            ("Horizontal.Scrollbar.rightarrow",
+                {"side": "right", "sticky": ''}),
+            ("Horizontal.Scrollbar.leftarrow",
+                {"side": "right", "sticky": ''}),
+            ("Horizontal.Scrollbar.trough", {"sticky": "ew", "children":
+                [("Horizontal.Scrollbar.thumb", {"expand": 1, "unit": 1,
+                    "children": [("Horizontal.Scrollbar.grip", {"sticky": ''})]
+                })]
+            })]
+        },
+
+        "TButton": {
+            "configure": {"width": 10, "anchor": "center"},
+            "layout": [
+                ("Button.button", {"children":
+                    [("Button.focus", {"children":
+                        [("Button.padding", {"children":
+                            [("Button.label", {"side": "left", "expand": 1})]
+                        })]
+                    })]
+                })
+            ]
+        },
+
+        "Toolbutton": {
+            "configure": {"anchor": "center"},
+            "layout": [
+                ("Toolbutton.border", {"children":
+                    [("Toolbutton.button", {"children":
+                        [("Toolbutton.padding", {"children":
+                            [("Toolbutton.label", {"side":"left", "expand":1})]
+                        })]
+                    })]
+                })
+            ]
+        },
+
+        "TMenubutton": {"layout": [
+            ("Menubutton.button", {"children":
+                [("Menubutton.indicator", {"side": "right"}),
+                 ("Menubutton.focus", {"children":
+                    [("Menubutton.padding", {"children":
+                        [("Menubutton.label", {"side": "left", "expand": 1})]
+                    })]
+                })]
+            })]
+        },
+
+        "TNotebook": {"configure": {"tabmargins": [0, 2, 0, 0]}},
+        "TNotebook.tab": {
+            "configure": {"padding": [6, 2, 6, 2], "expand": [0, 0, 2]},
+            "map": {"expand": [("selected", [1, 2, 4, 2])]}
+        },
+        "Treeview": {"configure": {"padding": 0}},
+
+        # elements
+        "Button.button": {"element create":
+            ("image", 'button-n',
+                ("pressed", 'button-p'), ("active", 'button-h'),
+                {"border": [4, 10], "padding": 4, "sticky":"ewns"}
+            )
+        },
+
+        "Toolbutton.button": {"element create":
+            ("image", 'tbutton-n',
+                ("selected", 'tbutton-p'), ("pressed", 'tbutton-p'),
+                ("active", 'tbutton-h'),
+                {"border": [4, 9], "padding": 3, "sticky": "news"}
+            )
+        },
+
+        "Checkbutton.indicator": {"element create":
+            ("image", 'check-nu',
+                ('active', 'selected', 'check-hc'),
+                ('pressed', 'selected', 'check-pc'),
+                ('active', 'check-hu'),
+                ("selected", 'check-nc'),
+                {"sticky": ''}
+            )
+        },
+
+        "Radiobutton.indicator": {"element create":
+            ("image", 'radio-nu',
+                ('active', 'selected', 'radio-hc'),
+                ('pressed', 'selected', 'radio-pc'),
+                ('active', 'radio-hu'), ('selected', 'radio-nc'),
+                {"sticky": ''}
+            )
+        },
+
+        "Horizontal.Scrollbar.thumb": {"element create":
+            ("image", 'hsb-n', {"border": 3, "sticky": "ew"})
+        },
+
+        "Horizontal.Scrollbar.grip": {"element create": ("image", 'hsb-g')},
+        "Horizontal.Scrollbar.trough": {"element create": ("image", 'hsb-t')},
+        "Vertical.Scrollbar.thumb": {"element create":
+            ("image", 'vsb-n', {"border": 3, "sticky": "ns"})
+        },
+        "Vertical.Scrollbar.grip": {"element create": ("image", 'vsb-g')},
+        "Vertical.Scrollbar.trough": {"element create": ("image", 'vsb-t')},
+        "Scrollbar.uparrow": {"element create":
+            ("image", 'arrowup-n', ("pressed", 'arrowup-p'), {"sticky": ''})
+        },
+        "Scrollbar.downarrow": {"element create":
+            ("image", 'arrowdown-n',
+            ("pressed", 'arrowdown-p'), {'sticky': ''})
+        },
+        "Scrollbar.leftarrow": {"element create":
+            ("image", 'arrowleft-n',
+            ("pressed", 'arrowleft-p'), {'sticky': ''})
+        },
+        "Scrollbar.rightarrow": {"element create":
+            ("image", 'arrowright-n', ("pressed", 'arrowright-p'),
+            {'sticky': ''})
+        },
+
+        "Horizontal.Scale.slider": {"element create":
+            ("image", 'hslider-n', {'sticky': ''})
+        },
+        "Horizontal.Scale.trough": {"element create":
+            ("image", 'hslider-t', {'border': 1, 'padding': 0})
+        },
+        "Vertical.Scale.slider": {"element create":
+            ("image", 'vslider-n', {'sticky': ''})
+        },
+        "Vertical.Scale.trough": {"element create":
+            ("image", 'vslider-t', {'border': 1, 'padding': 0})
+        },
+
+        "Entry.field": {"element create":
+            ("image", 'entry-n',
+                ("focus", 'entry-f'),
+                {'border': 2, 'padding': [3, 4], 'sticky': 'news'}
+            )
+        },
+
+        "Labelframe.border": {"element create":
+            ("image", 'border', {'border': 4, 'padding': 4, 'sticky': 'news'})
+        },
+
+        "Menubutton.button": {"element create":
+            ("image", 'combo-r',
+                ('active', 'combo-ra'),
+                {'sticky': 'news', 'border': [4, 6, 24, 15],
+                 'padding': [4, 4, 5]}
+            )
+        },
+        "Menubutton.indicator": {"element create":
+            ("image", 'arrow-d', {"sticky": "e", "border": [15, 0, 0, 0]})
+        },
+
+        "Combobox.field": {"element create":
+            ("image", 'combo-n',
+                ('readonly', 'active', 'combo-ra'),
+                ('focus', 'active', 'combo-fa'),
+                ('active', 'combo-a'), ('!readonly', 'focus', 'combo-f'),
+                ('readonly', 'combo-r'),
+                {'border': [4, 6, 24, 15], 'padding': [4, 4, 5],
+                 'sticky': 'news'}
+            )
+        },
+        "Combobox.downarrow": {"element create":
+            ("image", 'arrow-d', {'sticky': 'e', 'border': [15, 0, 0, 0]})
+         },
+
+        "Notebook.client": {"element create":
+            ("image", 'notebook-c', {'border': 4})
+        },
+        "Notebook.tab": {"element create":
+            ("image", 'notebook-tn',
+                ("selected", 'notebook-ts'), ("active", 'notebook-ta'),
+                {'padding': [0, 2, 0, 0], 'border': [4, 10, 4, 10]}
+            )
+        },
+
+        "Progressbar.trough": {"element create":
+            ("image", 'hprogress-t', {'border': 2})
+        },
+        "Horizontal.Progressbar.pbar": {"element create":
+            ("image", 'hprogress-b', {'border': [2, 9]})
+        },
+        "Vertical.Progressbar.pbar": {"element create":
+            ("image", 'vprogress-b', {'border': [9, 2]})
+        },
+
+        "Treeheading.cell": {"element create":
+            ("image", 'tree-n',
+                ("pressed", 'tree-p'),
+                {'border': [4, 10], 'padding': 4, 'sticky': 'news'}
+            )
+        }
+
+    })
+    style.theme_use("plastik")

Demo/tkinter/ttk/roundframe.py

+"""Ttk Frame with rounded corners.
+
+Based on an example by Bryan Oakley, found at: http://wiki.tcl.tk/20152"""
+import Tkinter
+import ttk
+
+root = Tkinter.Tk()
+
+img1 = Tkinter.PhotoImage("frameFocusBorder", data="""
+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
+Ry/99NIz//oGrZpUUEAAOw==""")
+
+img2 = Tkinter.PhotoImage("frameBorder", data="""
+R0lGODlhQABAAPcAAHx+fMTCxKSipOTi5JSSlNTS1LSytPTy9IyKjMzKzKyq
+rOzq7JyanNza3Ly6vPz6/ISChMTGxKSmpOTm5JSWlNTW1LS2tPT29IyOjMzO
+zKyurOzu7JyenNze3Ly+vPz+/OkAKOUA5IEAEnwAAACuQACUAAFBAAB+AFYd
+QAC0AABBAAB+AIjMAuEEABINAAAAAHMgAQAAAAAAAAAAAKjSxOIEJBIIpQAA
+sRgBMO4AAJAAAHwCAHAAAAUAAJEAAHwAAP+eEP8CZ/8Aif8AAG0BDAUAAJEA
+AHwAAIXYAOfxAIESAHwAAABAMQAbMBZGMAAAIEggJQMAIAAAAAAAfqgaXESI
+5BdBEgB+AGgALGEAABYAAAAAAACsNwAEAAAMLwAAAH61MQBIAABCM8B+AAAU
+AAAAAAAApQAAsf8Brv8AlP8AQf8Afv8AzP8A1P8AQf8AfgAArAAABAAADAAA
+AACQDADjAAASAAAAAACAAADVABZBAAB+ALjMwOIEhxINUAAAANIgAOYAAIEA
+AHwAAGjSAGEEABYIAAAAAEoBB+MAAIEAAHwCACABAJsAAFAAAAAAAGjJAGGL
+AAFBFgB+AGmIAAAQAABHAAB+APQoAOE/ABIAAAAAAADQAADjAAASAAAAAPiF
+APcrABKDAAB8ABgAGO4AAJAAqXwAAHAAAAUAAJEAAHwAAP8AAP8AAP8AAP8A
+AG0pIwW3AJGSAHx8AEocI/QAAICpAHwAAAA0SABk6xaDEgB8AAD//wD//wD/
+/wD//2gAAGEAABYAAAAAAAC0/AHj5AASEgAAAAA01gBkWACDTAB8AFf43PT3
+5IASEnwAAOAYd+PuMBKQTwB8AGgAEGG35RaSEgB8AOj/NOL/ZBL/gwD/fMkc
+q4sA5UGpEn4AAIg02xBk/0eD/358fx/4iADk5QASEgAAAALnHABkAACDqQB8
+AMyINARkZA2DgwB8fBABHL0AAEUAqQAAAIAxKOMAPxIwAAAAAIScAOPxABIS
+AAAAAIIAnQwA/0IAR3cAACwAAAAAQABAAAAI/wA/CBxIsKDBgwgTKlzIsKFD
+gxceNnxAsaLFixgzUrzAsWPFCw8kDgy5EeQDkBxPolypsmXKlx1hXnS48UEH
+CwooMCDAgIJOCjx99gz6k+jQnkWR9lRgYYDJkAk/DlAgIMICkVgHLoggQIPT
+ighVJqBQIKvZghkoZDgA8uDJAwk4bDhLd+ABBmvbjnzbgMKBuoA/bKDQgC1F
+gW8XKMgQOHABBQsMI76wIIOExo0FZIhM8sKGCQYCYA4cwcCEDSYPLOgg4Oro
+uhMEdOB84cCAChReB2ZQYcGGkxsGFGCgGzCFCh1QH5jQIW3xugwSzD4QvIIH
+4s/PUgiQYcCG4BkC5P/ObpaBhwreq18nb3Z79+8Dwo9nL9I8evjWsdOX6D59
+fPH71Xeef/kFyB93/sln4EP2Ebjegg31B5+CEDLUIH4PVqiQhOABqKFCF6qn
+34cHcfjffCQaFOJtGaZYkIkUuljQigXK+CKCE3po40A0trgjjDru+EGPI/6I
+Y4co7kikkAMBmaSNSzL5gZNSDjkghkXaaGIBHjwpY4gThJeljFt2WSWYMQpZ
+5pguUnClehS4tuMEDARQgH8FBMBBBExGwIGdAxywXAUBKHCZkAIoEEAFp33W
+QGl47ZgBAwZEwKigE1SQgAUCUDCXiwtQIIAFCTQwgaCrZeCABAzIleIGHDD/
+oIAHGUznmXABGMABT4xpmBYBHGgAKGq1ZbppThgAG8EEAW61KwYMSOBAApdy
+pNp/BkhAAQLcEqCTt+ACJW645I5rLrgEeOsTBtwiQIEElRZg61sTNBBethSw
+CwEA/Pbr778ABywwABBAgAAG7xpAq6mGUUTdAPZ6YIACsRKAAbvtZqzxxhxn
+jDG3ybbKFHf36ZVYpuE5oIGhHMTqcqswvyxzzDS/HDMHEiiggQMLDxCZXh8k
+BnEBCQTggAUGGKCB0ktr0PTTTEfttNRQT22ABR4EkEABDXgnGUEn31ZABglE
+EEAAWaeN9tpqt832221HEEECW6M3wc+Hga3SBgtMODBABw00UEEBgxdO+OGG
+J4744oZzXUEDHQxwN7F5G7QRdXxPoPkAnHfu+eeghw665n1vIKhJBQUEADs=""")
+
+style = ttk.Style()
+
+style.element_create("RoundedFrame", "image", "frameBorder",
+    ("focus", "frameFocusBorder"), border=16, sticky="nsew")
+
+style.layout("RoundedFrame", [("RoundedFrame", {"sticky": "nsew"})])
+style.configure("TEntry", borderwidth=0)
+
+frame = ttk.Frame(style="RoundedFrame", padding=10)
+frame.pack(fill='x')
+
+frame2 = ttk.Frame(style="RoundedFrame", padding=10)
+frame2.pack(fill='both', expand=1)
+
+entry = ttk.Entry(frame, text='Test')
+entry.pack(fill='x')
+entry.bind("<FocusIn>", lambda evt: frame.state(["focus"]))
+entry.bind("<FocusOut>", lambda evt: frame.state(["!focus"]))
+
+text = Tkinter.Text(frame2, borderwidth=0, bg="white", highlightthickness=0)
+text.pack(fill='both', expand=1)
+text.bind("<FocusIn>", lambda evt: frame2.state(["focus"]))
+text.bind("<FocusOut>", lambda evt: frame2.state(["!focus"]))
+
+root.mainloop()

Demo/tkinter/ttk/theme_selector.py

+"""Ttk Theme Selector v2.
+
+This is an improvement from the other theme selector (themes_combo.py)
+since now you can notice theme changes in Ttk Combobox, Ttk Frame,
+Ttk Label and Ttk Button.
+"""
+import Tkinter
+import ttk
+
+class App(ttk.Frame):
+    def __init__(self):
+        ttk.Frame.__init__(self, borderwidth=3)
+
+        self.style = ttk.Style()
+
+        # XXX Ideally I wouldn't want to create a Tkinter.IntVar to make
+        #     it works with Checkbutton variable option.
+        self.theme_autochange = Tkinter.IntVar(self, 0)
+        self._setup_widgets()
+
+    def _change_theme(self):
+        self.style.theme_use(self.themes_combo.get())
+
+    def _theme_sel_changed(self, widget):
+        if self.theme_autochange.get():
+            self._change_theme()
+
+    def _setup_widgets(self):
+        themes_lbl = ttk.Label(self, text="Themes")
+
+        themes = self.style.theme_names()
+        self.themes_combo = ttk.Combobox(self, values=themes, state="readonly")
+        self.themes_combo.set(themes[0])
+        self.themes_combo.bind("<<ComboboxSelected>>", self._theme_sel_changed)
+
+        change_btn = ttk.Button(self, text='Change Theme',
+            command=self._change_theme)
+
+        theme_change_checkbtn = ttk.Checkbutton(self,
+            text="Change themes when combobox item is activated",
+            variable=self.theme_autochange)
+
+        themes_lbl.grid(ipadx=6, sticky="w")
+        self.themes_combo.grid(row=0, column=1, padx=6, sticky="ew")
+        change_btn.grid(row=0, column=2, padx=6, sticky="e")
+        theme_change_checkbtn.grid(row=1, columnspan=3, sticky="w", pady=6)
+
+        top = self.winfo_toplevel()
+        top.rowconfigure(0, weight=1)
+        top.columnconfigure(0, weight=1)
+        self.columnconfigure(1, weight=1)
+        self.grid(row=0, column=0, sticky="nsew", columnspan=3, rowspan=2)
+
+
+def main():
+    app = App()
+    app.master.title("Theme Selector")
+    app.mainloop()
+
+if __name__ == "__main__":
+    main()

Demo/tkinter/ttk/treeview_multicolumn.py

+"""Demo based on the demo mclist included with tk source distribution."""
+import Tkinter
+import tkFont
+import ttk
+
+tree_columns = ("country", "capital", "currency")
+tree_data = [
+    ("Argentina",      "Buenos Aires",     "ARS"),
+    ("Australia",      "Canberra",         "AUD"),
+    ("Brazil",         "Brazilia",         "BRL"),
+    ("Canada",         "Ottawa",           "CAD"),
+    ("China",          "Beijing",          "CNY"),
+    ("France",         "Paris",            "EUR"),
+    ("Germany",        "Berlin",           "EUR"),
+    ("India",          "New Delhi",        "INR"),
+    ("Italy",          "Rome",             "EUR"),
+    ("Japan",          "Tokyo",            "JPY"),
+    ("Mexico",         "Mexico City",      "MXN"),
+    ("Russia",         "Moscow",           "RUB"),
+    ("South Africa",   "Pretoria",         "ZAR"),
+    ("United Kingdom", "London",           "GBP"),
+    ("United States",  "Washington, D.C.", "USD")
+    ]
+
+def sortby(tree, col, descending):
+    """Sort tree contents when a column is clicked on."""
+    # grab values to sort
+    data = [(tree.set(child, col), child) for child in tree.get_children('')]
+
+    # reorder data
+    data.sort(reverse=descending)
+    for indx, item in enumerate(data):
+        tree.move(item[1], '', indx)
+
+    # switch the heading so that it will sort in the opposite direction
+    tree.heading(col,
+        command=lambda col=col: sortby(tree, col, int(not descending)))
+
+class App(object):
+    def __init__(self):
+        self.tree = None
+        self._setup_widgets()
+        self._build_tree()
+
+    def _setup_widgets(self):
+        msg = ttk.Label(wraplength="4i", justify="left", anchor="n",
+            padding=(10, 2, 10, 6),
+            text=("Ttk is the new Tk themed widget set. One of the widgets it "
+                  "includes is a tree widget, which can be configured to "
+                  "display multiple columns of informational data without "
+                  "displaying the tree itself. This is a simple way to build "
+                  "a listbox that has multiple columns. Clicking on the "
+                  "heading for a column will sort the data by that column. "
+                  "You can also change the width of the columns by dragging "
+                  "the boundary between them."))
+        msg.pack(fill='x')
+
+        container = ttk.Frame()
+        container.pack(fill='both', expand=True)
+
+        # XXX Sounds like a good support class would be one for constructing
+        #     a treeview with scrollbars.
+        self.tree = ttk.Treeview(columns=tree_columns, show="headings")
+        vsb = ttk.Scrollbar(orient="vertical", command=self.tree.yview)
+        hsb = ttk.Scrollbar(orient="horizontal", command=self.tree.xview)
+        self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
+        self.tree.grid(column=0, row=0, sticky='nsew', in_=container)
+        vsb.grid(column=1, row=0, sticky='ns', in_=container)
+        hsb.grid(column=0, row=1, sticky='ew', in_=container)
+
+        container.grid_columnconfigure(0, weight=1)
+        container.grid_rowconfigure(0, weight=1)
+
+    def _build_tree(self):
+        for col in tree_columns:
+            self.tree.heading(col, text=col.title(),
+                command=lambda c=col: sortby(self.tree, c, 0))
+            # XXX tkFont.Font().measure expected args are incorrect according
+            #     to the Tk docs
+            self.tree.column(col, width=tkFont.Font().measure(col.title()))
+
+        for item in tree_data:
+            self.tree.insert('', 'end', values=item)
+
+            # adjust columns lenghts if necessary
+            for indx, val in enumerate(item):
+                ilen = tkFont.Font().measure(val)
+                if self.tree.column(tree_columns[indx], width=None) < ilen:
+                    self.tree.column(tree_columns[indx], width=ilen)
+
+def main():
+    root = Tkinter.Tk()
+    root.wm_title("Multi-Column List")
+    root.wm_iconname("mclist")
+
+    import plastik_theme
+    try:
+        plastik_theme.install('~/tile-themes/plastik/plastik')
+    except Exception:
+        import warnings
+        warnings.warn("plastik theme being used without images")
+
+    app = App()
+    root.mainloop()
+
+if __name__ == "__main__":
+    main()

Demo/tkinter/ttk/ttkcalendar.py

+"""
+Simple calendar using ttk Treeview together with calendar and datetime
+classes.
+"""
+import calendar
+import Tkinter
+import tkFont
+import ttk
+
+def get_calendar(locale, fwday):
+    # instantiate proper calendar class
+    if locale is None:
+        return calendar.TextCalendar(fwday)
+    else:
+        return calendar.LocaleTextCalendar(fwday, locale)
+
+class Calendar(ttk.Frame):
+    # XXX ToDo: cget and configure
+
+    datetime = calendar.datetime.datetime
+    timedelta = calendar.datetime.timedelta
+
+    def __init__(self, master=None, **kw):
+        """
+        WIDGET-SPECIFIC OPTIONS
+
+            locale, firstweekday, year, month, selectbackground,
+            selectforeground
+        """
+        # remove custom options from kw before initializating ttk.Frame
+        fwday = kw.pop('firstweekday', calendar.MONDAY)
+        year = kw.pop('year', self.datetime.now().year)
+        month = kw.pop('month', self.datetime.now().month)
+        locale = kw.pop('locale', None)
+        sel_bg = kw.pop('selectbackground', '#ecffc4')
+        sel_fg = kw.pop('selectforeground', '#05640e')
+
+        self._date = self.datetime(year, month, 1)
+        self._selection = None # no date selected
+
+        ttk.Frame.__init__(self, master, **kw)
+
+        self._cal = get_calendar(locale, fwday)
+
+        self.__setup_styles()       # creates custom styles
+        self.__place_widgets()      # pack/grid used widgets
+        self.__config_calendar()    # adjust calendar columns and setup tags
+        # configure a canvas, and proper bindings, for selecting dates
+        self.__setup_selection(sel_bg, sel_fg)
+
+        # store items ids, used for insertion later
+        self._items = [self._calendar.insert('', 'end', values='')
+                            for _ in range(6)]
+        # insert dates in the currently empty calendar
+        self._build_calendar()
+
+        # set the minimal size for the widget
+        self._calendar.bind('<Map>', self.__minsize)
+
+    def __setitem__(self, item, value):
+        if item in ('year', 'month'):
+            raise AttributeError("attribute '%s' is not writeable" % item)
+        elif item == 'selectbackground':
+            self._canvas['background'] = value
+        elif item == 'selectforeground':
+            self._canvas.itemconfigure(self._canvas.text, item=value)
+        else:
+            ttk.Frame.__setitem__(self, item, value)
+
+    def __getitem__(self, item):
+        if item in ('year', 'month'):
+            return getattr(self._date, item)
+        elif item == 'selectbackground':
+            return self._canvas['background']
+        elif item == 'selectforeground':
+            return self._canvas.itemcget(self._canvas.text, 'fill')
+        else:
+            r = ttk.tclobjs_to_py({item: ttk.Frame.__getitem__(self, item)})
+            return r[item]
+
+    def __setup_styles(self):
+        # custom ttk styles
+        style = ttk.Style(self.master)
+        arrow_layout = lambda dir: (
+            [('Button.focus', {'children': [('Button.%sarrow' % dir, None)]})]
+        )
+        style.layout('L.TButton', arrow_layout('left'))
+        style.layout('R.TButton', arrow_layout('right'))
+
+    def __place_widgets(self):
+        # header frame and its widgets
+        hframe = ttk.Frame(self)
+        lbtn = ttk.Button(hframe, style='L.TButton', command=self._prev_month)
+        rbtn = ttk.Button(hframe, style='R.TButton', command=self._next_month)
+        self._header = ttk.Label(hframe, width=15, anchor='center')
+        # the calendar
+        self._calendar = ttk.Treeview(show='', selectmode='none', height=7)
+
+        # pack the widgets
+        hframe.pack(in_=self, side='top', pady=4, anchor='center')
+        lbtn.grid(in_=hframe)
+        self._header.grid(in_=hframe, column=1, row=0, padx=12)
+        rbtn.grid(in_=hframe, column=2, row=0)
+        self._calendar.pack(in_=self, expand=1, fill='both', side='bottom')
+
+    def __config_calendar(self):
+        cols = self._cal.formatweekheader(3).split()
+        self._calendar['columns'] = cols
+        self._calendar.tag_configure('header', background='grey90')
+        self._calendar.insert('', 'end', values=cols, tag='header')
+        # adjust its columns width
+        font = tkFont.Font()
+        maxwidth = max(font.measure(col) for col in cols)
+        for col in cols:
+            self._calendar.column(col, width=maxwidth, minwidth=maxwidth,
+                anchor='e')
+
+    def __setup_selection(self, sel_bg, sel_fg):
+        self._font = tkFont.Font()
+        self._canvas = canvas = Tkinter.Canvas(self._calendar,
+            background=sel_bg, borderwidth=0, highlightthickness=0)
+        canvas.text = canvas.create_text(0, 0, fill=sel_fg, anchor='w')
+
+        canvas.bind('<ButtonPress-1>', lambda evt: canvas.place_forget())
+        self._calendar.bind('<Configure>', lambda evt: canvas.place_forget())
+        self._calendar.bind('<ButtonPress-1>', self._pressed)
+
+    def __minsize(self, evt):
+        width, height = self._calendar.master.geometry().split('x')
+        height = height[:height.index('+')]
+        self._calendar.master.minsize(width, height)
+
+    def _build_calendar(self):
+        year, month = self._date.year, self._date.month
+
+        # update header text (Month, YEAR)
+        header = self._cal.formatmonthname(year, month, 0)
+        self._header['text'] = header.title()
+
+        # update calendar shown dates
+        cal = self._cal.monthdayscalendar(year, month)
+        for indx, item in enumerate(self._items):
+            week = cal[indx] if indx < len(cal) else []
+            fmt_week = [('%02d' % day) if day else '' for day in week]
+            self._calendar.item(item, values=fmt_week)
+
+    def _show_selection(self, text, bbox):
+        """Configure canvas for a new selection."""
+        x, y, width, height = bbox
+
+        textw = self._font.measure(text)
+
+        canvas = self._canvas
+        canvas.configure(width=width, height=height)
+        canvas.coords(canvas.text, width - textw, height / 2 - 1)
+        canvas.itemconfigure(canvas.text, text=text)
+        canvas.place(in_=self._calendar, x=x, y=y)
+
+    # Callbacks
+
+    def _pressed(self, evt):
+        """Clicked somewhere in the calendar."""
+        x, y, widget = evt.x, evt.y, evt.widget
+        item = widget.identify_row(y)
+        column = widget.identify_column(x)
+
+        if not column or not item in self._items:
+            # clicked in the weekdays row or just outside the columns
+            return
+
+        item_values = widget.item(item)['values']
+        if not len(item_values): # row is empty for this month
+            return
+
+        text = item_values[int(column[1]) - 1]
+        if not text: # date is empty
+            return
+
+        bbox = widget.bbox(item, column)
+        if not bbox: # calendar not visible yet
+            return
+
+        # update and then show selection
+        text = '%02d' % text
+        self._selection = (text, item, column)
+        self._show_selection(text, bbox)
+
+    def _prev_month(self):
+        """Updated calendar to show the previous month."""
+        self._canvas.place_forget()
+
+        self._date = self._date - self.timedelta(days=1)
+        self._date = self.datetime(self._date.year, self._date.month, 1)
+        self._build_calendar() # reconstuct calendar
+
+    def _next_month(self):
+        """Update calendar to show the next month."""
+        self._canvas.place_forget()
+
+        year, month = self._date.year, self._date.month
+        self._date = self._date + self.timedelta(
+            days=calendar.monthrange(year, month)[1] + 1)
+        self._date = self.datetime(self._date.year, self._date.month, 1)
+        self._build_calendar() # reconstruct calendar
+
+    # Properties
+
+    @property
+    def selection(self):
+        """Return a datetime representing the current selected date."""
+        if not self._selection:
+            return None
+
+        year, month = self._date.year, self._date.month
+        return self.datetime(year, month, int(self._selection[0]))
+
+def test():
+    import sys
+    root = Tkinter.Tk()
+    root.title('Ttk Calendar')
+    ttkcal = Calendar(firstweekday=calendar.SUNDAY)
+    ttkcal.pack(expand=1, fill='both')
+
+    if 'win' not in sys.platform:
+        style = ttk.Style()
+        style.theme_use('clam')
+
+    root.mainloop()
+
+if __name__ == '__main__':
+    test()

Demo/tkinter/ttk/widget_state.py

+"""Sample demo showing widget states and some font styling."""
+import ttk
+
+states = ['active', 'disabled', 'focus', 'pressed', 'selected',
+          'background', 'readonly', 'alternate', 'invalid']
+
+for state in states[:]:
+    states.append("!" + state)
+
+def reset_state(widget):
+    nostate = states[len(states) // 2:]
+    widget.state(nostate)
+
+class App(ttk.Frame):
+    def __init__(self, title=None):
+        ttk.Frame.__init__(self, borderwidth=6)
+        self.master.title(title)
+
+        self.style = ttk.Style()
+
+        # get default font size and family
+        btn_font = self.style.lookup("TButton", "font")
+        fsize = str(self.tk.eval("font configure %s -size" % btn_font))
+        self.font_family = self.tk.eval("font configure %s -family" % btn_font)
+        if ' ' in self.font_family:
+            self.font_family = '{%s}' % self.font_family
+        self.fsize_prefix = fsize[0] if fsize[0] == '-' else ''
+        self.base_fsize = int(fsize[1 if fsize[0] == '-' else 0:])
+
+        # a list to hold all the widgets that will have their states changed
+        self.update_widgets = []
+
+        self._setup_widgets()
+
+    def _set_font(self, extra=0):
+        self.style.configure("TButton", font="%s %s%d" % (self.font_family,
+            self.fsize_prefix, self.base_fsize + extra))
+
+    def _new_state(self, widget, newtext):
+        widget = self.nametowidget(widget)
+
+        if not newtext:
+            goodstates = ["disabled"]
+            font_extra = 0
+        else:
+            # set widget state according to what has been entered in the entry
+            newstates = set(newtext.split()) # eliminate duplicates
+
+            # keep only the valid states
+            goodstates = [state for state in newstates if state in states]
+            # define a new font size based on amount of states
+            font_extra = 2 * len(goodstates)
+
+        # set new widget state
+        for widget in self.update_widgets:
+            reset_state(widget) # remove any previous state from the widget
+            widget.state(goodstates)
+
+        # update Ttk Button font size
+        self._set_font(font_extra)
+        return 1
+
+    def _setup_widgets(self):
+        btn = ttk.Button(self, text='Enter states and watch')
+
+        entry = ttk.Entry(self, cursor='xterm', validate="key")
+        entry['validatecommand'] = (self.register(self._new_state), '%W', '%P')
+        entry.focus()
+
+        self.update_widgets.append(btn)
+        entry.validate()
+
+        entry.pack(fill='x', padx=6)
+        btn.pack(side='left', pady=6, padx=6, anchor='n')
+        self.pack(fill='both', expand=1)
+
+
+def main():
+    app = App("Widget State Tester")
+    app.mainloop()
+
+if __name__ == "__main__":
+    main()

Demo/turtle/tdemo_nim.py

+"""      turtle-example-suite:
+
+            tdemo_nim.py
+         
+Play nim against the computer. The player
+who takes the last stick is the winner.
+
+Implements the model-view-controller
+design pattern.
+"""
+
+
+import turtle
+import random
+import time
+
+SCREENWIDTH = 640
+SCREENHEIGHT = 480
+
+MINSTICKS = 7
+MAXSTICKS = 31
+
+HUNIT = SCREENHEIGHT // 12
+WUNIT = SCREENWIDTH // ((MAXSTICKS // 5) * 11 + (MAXSTICKS % 5) * 2)
+
+SCOLOR = (63, 63, 31)
+HCOLOR = (255, 204, 204)
+COLOR = (204, 204, 255)
+
+def randomrow():
+    return random.randint(MINSTICKS, MAXSTICKS)
+
+def computerzug(state):
+    xored = state[0] ^ state[1] ^ state[2]
+    if xored == 0:
+        return randommove(state)
+    for z in range(3):
+        s = state[z] ^ xored
+        if s <= state[z]:
+            move = (z, s)
+            return move
+
+def randommove(state):
+    m = max(state)   
+    while True:
+        z = random.randint(0,2)
+        if state[z] > (m > 1):
+            break
+    rand = random.randint(m > 1, state[z]-1)
+    return z, rand
+
+
+class NimModel(object):
+    def __init__(self, game):
+        self.game = game
+
+    def setup(self):
+        if self.game.state not in [Nim.CREATED, Nim.OVER]:
+            return
+        self.sticks = [randomrow(), randomrow(), randomrow()]
+        self.player = 0
+        self.winner = None
+        self.game.view.setup()
+        self.game.state = Nim.RUNNING
+        
+    def move(self, row, col):
+        maxspalte = self.sticks[row]
+        self.sticks[row] = col
+        self.game.view.notify_move(row, col, maxspalte, self.player)
+        if self.game_over():
+            self.game.state = Nim.OVER
+            self.winner = self.player
+            self.game.view.notify_over()
+        elif self.player == 0:
+            self.player = 1
+            row, col = computerzug(self.sticks)
+            self.move(row, col)
+            self.player = 0
+        
+    def game_over(self):
+        return self.sticks == [0, 0, 0]
+
+    def notify_move(self, row, col):
+        if self.sticks[row] <= col:
+            return
+        self.move(row, col)
+
+
+class Stick(turtle.Turtle):
+    def __init__(self, row, col, game):
+        turtle.Turtle.__init__(self, visible=False)
+        self.row = row
+        self.col = col
+        self.game = game
+        x, y = self.coords(row, col)
+        self.shape("square")
+        self.shapesize(HUNIT/10.0, WUNIT/20.0)
+        self.speed(0)
+        self.pu()
+        self.goto(x,y)
+        self.color("white")
+        self.showturtle()
+            
+    def coords(self, row, col):
+        packet, remainder = divmod(col, 5)
+        x = (3 + 11 * packet + 2 * remainder) * WUNIT
+        y = (2 + 3 * row) * HUNIT
+        return x - SCREENWIDTH // 2 + WUNIT // 2, SCREENHEIGHT // 2 - y - HUNIT // 2
+        
+    def makemove(self, x, y):
+        if self.game.state != Nim.RUNNING:
+            return
+        self.game.controller.notify_move(self.row, self.col)
+
+
+class NimView(object):
+    def __init__(self, game):
+        self.game = game
+        self.screen = game.screen
+        self.model = game.model
+        self.screen.colormode(255)
+        self.screen.tracer(False)
+        self.screen.bgcolor((240, 240, 255))
+        self.writer = turtle.Turtle(visible=False)
+        self.writer.pu()
+        self.writer.speed(0)
+        self.sticks = {}
+        for row in range(3):
+            for col in range(MAXSTICKS):
+                self.sticks[(row, col)] = Stick(row, col, game)
+        self.display("... a moment please ...")
+        self.screen.tracer(True)
+
+    def display(self, msg1, msg2=None):
+        self.screen.tracer(False)
+        self.writer.clear()
+        if msg2 is not None:
+            self.writer.goto(0, - SCREENHEIGHT // 2 + 48)
+            self.writer.pencolor("red")
+            self.writer.write(msg2, align="center", font=("Courier",18,"bold"))
+        self.writer.goto(0, - SCREENHEIGHT // 2 + 20)
+        self.writer.pencolor("black")
+        self.writer.write(msg1, align="center", font=("Courier",14,"bold"))
+        self.screen.tracer(True)
+        
+
+    def setup(self):
+        self.screen.tracer(False)
+        for row in range(3):
+            for col in range(self.model.sticks[row]):
+                self.sticks[(row, col)].color(SCOLOR)
+        for row in range(3):
+            for col in range(self.model.sticks[row], MAXSTICKS):
+                self.sticks[(row, col)].color("white")
+        self.display("Your turn! Click leftmost stick to remove.")
+        self.screen.tracer(True)
+
+    def notify_move(self, row, col, maxspalte, player):
+        if player == 0:
+            farbe = HCOLOR
+            for s in range(col, maxspalte):
+                self.sticks[(row, s)].color(farbe)
+        else:
+            self.display(" ... thinking ...         ")
+            time.sleep(0.5)
+            self.display(" ... thinking ... aaah ...")
+            farbe = COLOR
+            for s in range(maxspalte-1, col-1, -1):
+                time.sleep(0.2)
+                self.sticks[(row, s)].color(farbe)
+            self.display("Your turn! Click leftmost stick to remove.")
+
+    def notify_over(self):
+        if self.game.model.winner == 0:
+            msg2 = "Congrats. You're the winner!!!"
+        else:
+            msg2 = "Sorry, the computer is the winner."
+        self.display("To play again press space bar. To leave press ESC.", msg2)
+
+    def clear(self):
+        if self.game.state == Nim.OVER:
+            self.screen.clear()
+
+class NimController(object):
+
+    def __init__(self, game):
+        self.game = game
+        self.sticks = game.view.sticks
+        self.BUSY = False
+        for stick in self.sticks.values():
+            stick.onclick(stick.makemove)
+        self.game.screen.onkey(self.game.model.setup, "space")
+        self.game.screen.onkey(self.game.view.clear, "Escape")
+        self.game.view.display("Press space bar to start game")
+        self.game.screen.listen()
+
+    def notify_move(self, row, col):
+        if self.BUSY:
+            return
+        self.BUSY = True
+        self.game.model.notify_move(row, col)
+        self.BUSY = False
+                
+class Nim(object):
+    CREATED = 0
+    RUNNING = 1
+    OVER = 2
+    def __init__(self, screen):
+        self.state = Nim.CREATED 
+        self.screen = screen
+        self.model = NimModel(self)
+        self.view = NimView(self)
+        self.controller = NimController(self)
+        
+
+mainscreen = turtle.Screen()
+mainscreen.mode("standard")
+mainscreen.setup(SCREENWIDTH, SCREENHEIGHT)
+
+def main():
+    nim = Nim(mainscreen)
+    return "EVENTLOOP!"
+
+if __name__ == "__main__":
+    main()
+    turtle.mainloop()
+    

Doc/TODO.txt

-To do
-=====
-
-* split very large files and add toctrees
-* finish "Documenting Python"
-* care about XXX comments

Doc/c-api/capsule.rst

+.. highlightlang:: c
+
+.. _capsules:
+
+Capsules
+--------
+
+.. index:: object: Capsule
+
+Refer to :ref:`using-capsules` for more information on using these objects.
+
+
+.. ctype:: PyCapsule
+
+   This subtype of :ctype:`PyObject` represents an opaque value, useful for C
+   extension modules who need to pass an opaque value (as a :ctype:`void\*`
+   pointer) through Python code to other C code.  It is often used to make a C
+   function pointer defined in one module available to other modules, so the
+   regular import mechanism can be used to access C APIs defined in dynamically
+   loaded modules.
+
+.. ctype:: PyCapsule_Destructor
+
+   The type of a destructor callback for a capsule.  Defined as::
+
+      typedef void (*PyCapsule_Destructor)(PyObject *);
+
+   See :cfunc:`PyCapsule_New` for the semantics of PyCapsule_Destructor
+   callbacks.
+
+
+.. cfunction:: int PyCapsule_CheckExact(PyObject *p)
+
+   Return true if its argument is a :ctype:`PyCapsule`.
+
+
+.. cfunction:: PyObject* PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor)
+
+   Create a :ctype:`PyCapsule` encapsulating the *pointer*.  The *pointer*
+   argument may not be *NULL*.
+
+   On failure, set an exception and return *NULL*.
+
+   The *name* string may either be *NULL* or a pointer to a valid C string.  If
+   non-*NULL*, this string must outlive the capsule.  (Though it is permitted to
+   free it inside the *destructor*.)
+
+   If the *destructor* argument is not *NULL*, it will be called with the
+   capsule as its argument when it is destroyed.
+
+   If this capsule will be stored as an attribute of a module, the *name* should
+   be specified as ``modulename.attributename``.  This will enable other modules
+   to import the capsule using :cfunc:`PyCapsule_Import`.
+
+
+.. cfunction:: void* PyCapsule_GetPointer(PyObject *capsule, const char *name)
+
+   Retrieve the *pointer* stored in the capsule.  On failure, set an exception
+   and return *NULL*.
+
+   The *name* parameter must compare exactly to the name stored in the capsule.
+   If the name stored in the capsule is *NULL*, the *name* passed in must also
+   be *NULL*.  Python uses the C function :cfunc:`strcmp` to compare capsule
+   names.
+
+
+.. cfunction:: PyCapsule_Destructor PyCapsule_GetDestructor(PyObject *capsule)
+
+   Return the current destructor stored in the capsule.  On failure, set an
+   exception and return *NULL*.
+
+   It is legal for a capsule to have a *NULL* destructor.  This makes a *NULL*
+   return code somewhat ambiguous; use :cfunc:`PyCapsule_IsValid` or
+   :cfunc:`PyErr_Occurred` to disambiguate.
+
+
+.. cfunction:: void* PyCapsule_GetContext(PyObject *capsule)
+
+   Return the current context stored in the capsule.  On failure, set an
+   exception and return *NULL*.
+
+   It is legal for a capsule to have a *NULL* context.  This makes a *NULL*
+   return code somewhat ambiguous; use :cfunc:`PyCapsule_IsValid` or
+   :cfunc:`PyErr_Occurred` to disambiguate.
+
+
+.. cfunction:: const char* PyCapsule_GetName(PyObject *capsule)
+
+   Return the current name stored in the capsule.  On failure, set an exception
+   and return *NULL*.
+
+   It is legal for a capsule to have a *NULL* name.  This makes a *NULL* return
+   code somewhat ambiguous; use :cfunc:`PyCapsule_IsValid` or
+   :cfunc:`PyErr_Occurred` to disambiguate.
+
+
+.. cfunction:: void* PyCapsule_Import(const char *name, int no_block)
+
+   Import a pointer to a C object from a capsule attribute in a module.  The
+   *name* parameter should specify the full name to the attribute, as in
+   ``module.attribute``.  The *name* stored in the capsule must match this
+   string exactly.  If *no_block* is true, import the module without blocking
+   (using :cfunc:`PyImport_ImportModuleNoBlock`).  If *no_block* is false,
+   import the module conventionally (using :cfunc:`PyImport_ImportModule`).
+
+   Return the capsule's internal *pointer* on success.  On failure, set an
+   exception and return *NULL*.  However, if :cfunc:`PyCapsule_Import` failed to
+   import the module, and *no_block* was true, no exception is set.
+
+.. cfunction:: int PyCapsule_IsValid(PyObject *capsule, const char *name)
+
+   Determines whether or not *capsule* is a valid capsule.  A valid capsule is
+   non-*NULL*, passes :cfunc:`PyCapsule_CheckExact`, has a non-*NULL* pointer
+   stored in it, and its internal name matches the *name* parameter.  (See
+   :cfunc:`PyCapsule_GetPointer` for information on how capsule names are
+   compared.)
+
+   In other words, if :cfunc:`PyCapsule_IsValid` returns a true value, calls to
+   any of the accessors (any function starting with :cfunc:`PyCapsule_Get`) are
+   guaranteed to succeed.
+
+   Return a nonzero value if the object is valid and matches the name passed in.
+   Return 0 otherwise.  This function will not fail.
+
+.. cfunction:: int PyCapsule_SetContext(PyObject *capsule, void *context)
+
+   Set the context pointer inside *capsule* to *context*.
+
+   Return 0 on success.  Return nonzero and set an exception on failure.
+
+.. cfunction:: int PyCapsule_SetDestructor(PyObject *capsule, PyCapsule_Destructor destructor)
+
+   Set the destructor inside *capsule* to *destructor*.
+
+   Return 0 on success.  Return nonzero and set an exception on failure.
+
+.. cfunction:: int PyCapsule_SetName(PyObject *capsule, const char *name)
+
+   Set the name inside *capsule* to *name*.  If non-*NULL*, the name must
+   outlive the capsule.  If the previous *name* stored in the capsule was not
+   *NULL*, no attempt is made to free it.
+
+   Return 0 on success.  Return nonzero and set an exception on failure.
+
+.. cfunction:: int PyCapsule_SetPointer(PyObject *capsule, void *pointer)
+
+   Set the void pointer inside *capsule* to *pointer*.  The pointer may not be
+   *NULL*.
+
+   Return 0 on success.  Return nonzero and set an exception on failure.

Doc/c-api/code.rst

+.. highlightlang:: c
+
+.. _codeobjects:
+
+Code Objects
+------------
+
+.. sectionauthor:: Jeffrey Yasskin <jyasskin@gmail.com>
+
+
+.. index::
+   object: code
+
+Code objects are a low-level detail of the CPython implementation.
+Each one represents a chunk of executable code that hasn't yet been
+bound into a function.
+
+.. ctype:: PyCodeObject
+
+   The C structure of the objects used to describe code objects.  The
+   fields of this type are subject to change at any time.
+
+
+.. cvar:: PyTypeObject PyCode_Type
+
+   This is an instance of :ctype:`PyTypeObject` representing the Python
+   :class:`code` type.
+
+
+.. cfunction:: int PyCode_Check(PyObject *co)
+
+   Return true if *co* is a :class:`code` object
+
+.. cfunction:: int PyCode_GetNumFree(PyObject *co)
+
+   Return the number of free variables in *co*.
+
+.. cfunction:: PyCodeObject *PyCode_New(int argcount, int nlocals, int stacksize, int flags, PyObject *code, PyObject *consts, PyObject *names, PyObject *varnames, PyObject *freevars, PyObject *cellvars, PyObject *filename, PyObject *name, int firstlineno, PyObject *lnotab)
+
+   Return a new code object.  If you need a dummy code object to
+   create a frame, use :cfunc:`PyCode_NewEmpty` instead.  Calling
+   :cfunc:`PyCode_New` directly can bind you to a precise Python
+   version since the definition of the bytecode changes often.
+
+
+.. cfunction:: int PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno)
+
+   Return a new empty code object with the specified filename,
+   function name, and first line number.  It is illegal to
+   :keyword:`exec` or :func:`eval` the resulting code object.

Doc/documenting/building.rst

+Building the documentation
+==========================