Tuesday, July 30, 2013

Setuptools scandal derails distribute with wheels?

Distribute is dead. Setuptools was a zombie for awhile but has now merged with the distribute fork and is the newest direction in Python packaging. But that's not all, now there are also wheels, which roll truer than eggs but might be a bit wobbly until there are a few more of them and they gain momentum.

the old Distribute graphic
This is a 180 from the new hotness.
If you use pip, uninstall distribute and setuptools.
$ pip uninstall distribute setuptools
Then install the newest version of setuptools and pip. Finally install wheels.
$ pip install setuptools pip wheels
Distribure-0.7.x which was meant as a transition between setuptools-0.6 and 0.8 is no longer necessary since setuptools-0.9.8 is out. Pip is at 1.4 with support for both setuptools and wheels.

Tuesday, July 2, 2013

world wide widgets

Lots of great Tkinter widges abound on the web and even in the Python source code under demos. Here are my newest favs and some others I've posted previously:
  1. scrolled canvas: uses canvas windows with frames and a scrollbar. credit: python source code demo "canvas-with-scrollbars.py"
  2. treeview table: uses a ttk.Treeview to make a table - and they said it couldn't be done. credit: daniweb
  3. rascally resize tk scrollbars
  4. ttk Notebook demo for Py2
Please feel free to copy these and use them for good. They are covered by this license.
from Tkinter import Canvas, GROOVE, BOTH, X, Y, YES, RIGHT, LEFT, W
from ttk import Frame, Scrollbar, Checkbutton
import logging
import tkMessageBox
class ScrolledCanvas(Frame):
"""
A scrolling canvas of frames with checkboxes.
"""
def __init__(self, master, name=None, scrollregion=(0, 0, '5i', '5i'),
items=[], window_size=[160, 30], **canvaskw):
Frame.__init__(self, master, name=name)
self.scrollcanvas = Canvas(self, name='scrollcanvas',
scrollregion=scrollregion, **canvaskw)
self.yscroll = Scrollbar(self, name='yscroll',
command=self.scrollcanvas.yview)
self.scrollcanvas['yscrollcommand'] = self.yscroll.set
self.yscroll.pack(side=RIGHT, fill=Y)
self.scrollcanvas.pack(side=LEFT, fill=BOTH, expand=YES)
self.items = dict.fromkeys(items)
for n, i in enumerate(items):
self.items[i] = {'frame': Frame(self, name=(i.lower() + '_frame'))}
self.items[i]['frame'].config(relief=GROOVE, padding=5)
self.items[i]['chbx'] = Checkbutton(self.items[i]['frame'],
name=(i.lower() + '_chbx'))
self.items[i]['chbx']['text'] = i
self.items[i]['chbx'].pack(side=LEFT, fill=X)
y = window_size[1] / 2 + window_size[1] * n
self.items[i]['window'] = self.scrollcanvas.create_window(0, y)
self.scrollcanvas.itemconfigure(self.items[i]['window'],
window=self.items[i]['frame'],
anchor=W, width=window_size[0],
height=window_size[1])
def onLeftClick(item, event):
logging.debug(event.__dict__)
if tkMessageBox.askokcancel('scrolled canvas', item):
return
if __name__ == "__main__":
import tkFont
from Tkinter import Tk
dummy_items = ['holy grail', 'meaning of life', 'life of brian',
'jabberwocky', "monte python's flying circus",
'time bandits', 'brazil', '12 monkeys',
'adventures of baron von munchausen']
root = Tk()
maxstr = max(dummy_items, key=lambda x: len(x))
pixsz = tkFont.Font().measure(maxstr)+25
chrsz = len(maxstr)+5
scrollcanvas = ScrolledCanvas(root, scrollregion=[0, 0, '5i', '3i'],
items=dummy_items, height='2i',
window_size=[pixsz, 30], width=pixsz)
# add bindings?
for item, val in scrollcanvas.items.iteritems():
logging.debug('binding frame %s', val['frame'])
callback = lambda e, i=item: onLeftClick(i, e)
val['frame'].bind('<Button-1>', callback)
scrollcanvas.pack(expand=YES, fill=BOTH)
scrollcanvas.mainloop()
from Tkinter import N, E, W, S, END, YES, BOTH
from ttk import Frame, Treeview, Scrollbar
import tkFont
import logging
class TreeTable(Frame):
"""
A table based on :class:`ttk.Treeview`.
:Parameters:
**master** : Tkinter or ttk widget
The widget in which the :class:`TableTree` will reside.
**headers** : list of str
The column headers for the table.
**data** : list of tuples
Table data. There must be as many elements in each tuple as there \
are headers. Each tuple in the list corresponds to a row in the \
table.
"""
def __init__(self, master, headers, data, name=None):
Frame.__init__(self, master, name=name)
#: column headers
self.headers = headers
#: table data
self.data = data
#: :class:`~ttk.Treeview` that only shows "headings" not "tree columns"
self.tree = Treeview(self, columns=self.headers, show="headings",
name='tabletree')
#: vertical scrollbar
self.yscroll = Scrollbar(self, orient="vertical",
command=self.tree.yview, name='table_yscroll')
#: horizontal scrollbar
self.xscroll = Scrollbar(self, orient="horizontal",
command=self.tree.xview, name='table_xscroll')
self.tree['yscrollcommand'] = self.yscroll.set # bind to scrollbars
self.tree['xscrollcommand'] = self.xscroll.set
# position widgets and set resize behavior
self.tree.grid(column=0, row=0, sticky=(N + E + W + S))
self.yscroll.grid(column=1, row=0, sticky=(N + S))
self.xscroll.grid(column=0, row=1, sticky=(E + W))
self.grid_columnconfigure(0, weight=1)
self.grid_rowconfigure(0, weight=1)
# build tree
for col in self.headers:
# NOTE: Use col as column identifiers, crafty!
# NOTE: Also change col to title case using str.title()
# NOTE: make lambda behave nicely in a loop using default arg!
callback = lambda c=col: self.sortby(c, False)
self.tree.heading(col, text=col.title(), command=callback)
# adjust the column's width to the header string
self.tree.column(col, width=tkFont.Font().measure(col.title()))
# insert a new top-level treeview item by suing an empty string
for item in self.data:
self.tree.insert('', END, values=item)
# adjust column's width if necessary to fit each value
for idx, val in enumerate(item):
col_width = tkFont.Font().measure(val)
# option can be specified at least 3 ways: as (a) width=None,
# (b) option='width' or (c) 'width', where 'width' can be any
# valid column option.
if self.tree.column(self.headers[idx], 'width') < col_width:
self.tree.column(self.headers[idx], width=col_width)
def sortby(self, col, descending):
"""
Sort table contents when a column header is clicked.
:Parameters:
**col**
The column identifier of the column to sort.
**descending**
False if ascending, True if descending, switches each time.
"""
logging.debug('sortby %s, descending: %s', col, descending)
# grab values to sort
data = [(self.tree.set(child, col), child)
for child in self.tree.get_children('')]
# now sort the data in place
data.sort(reverse=descending)
for idx, item in enumerate(data):
self.tree.move(item[1], '', idx)
# switch the heading so it will sort in the opposite direction
callback = lambda: self.sortby(col, not descending)
self.tree.heading(col, command=callback)
if __name__ == "__main__":
from Tkinter import Tk
dummy_data = [('holy grail', 1975), ('meaning of life', 1983),
('life of brian', 1979), ('jabberwocky', 1977),
('time bandits', 1981), ('brazil', 1985),
('12 monkeys', 1995),
('adventures of baron von munchausen', 1988)]
headers = ['movie title', 'year']
root = Tk()
treetable = TreeTable(root, headers=headers, data=dummy_data)
treetable.tree['height'] = 4
treetable.pack(expand=YES, fill=BOTH)
treetable.mainloop()
Fork me on GitHub