Import Upstream version 2.7.18

This commit is contained in:
geos_one
2025-08-15 16:28:06 +02:00
commit ba1f69ab39
4521 changed files with 1778434 additions and 0 deletions

11
Demo/tkinter/README Normal file
View File

@@ -0,0 +1,11 @@
Several collections of example code for Tkinter.
See the toplevel README for an explanation of the difference between
Tkinter and _tkinter, how to enable the Python Tk interface, and where
to get Matt Conway's lifesaver document.
Subdirectories:
guido my original example set (fairly random collection)
matt Matt Conway's examples, to go with his lifesaver document
ttk Examples using the ttk module

View File

@@ -0,0 +1,452 @@
# The options of a widget are described by the following attributes
# of the Pack and Widget dialogs:
#
# Dialog.current: {name: value}
# -- changes during Widget's lifetime
#
# Dialog.options: {name: (default, klass)}
# -- depends on widget class only
#
# Dialog.classes: {klass: (v0, v1, v2, ...) | 'boolean' | 'other'}
# -- totally static, though different between PackDialog and WidgetDialog
# (but even that could be unified)
from Tkinter import *
class Option:
varclass = StringVar # May be overridden
def __init__(self, dialog, option):
self.dialog = dialog
self.option = option
self.master = dialog.top
self.default, self.klass = dialog.options[option]
self.var = self.varclass(self.master)
self.frame = Frame(self.master)
self.frame.pack(fill=X)
self.label = Label(self.frame, text=(option + ":"))
self.label.pack(side=LEFT)
self.update()
self.addoption()
def refresh(self):
self.dialog.refresh()
self.update()
def update(self):
try:
self.current = self.dialog.current[self.option]
except KeyError:
self.current = self.default
self.var.set(self.current)
def set(self, e=None): # Should be overridden
pass
class BooleanOption(Option):
varclass = BooleanVar
def addoption(self):
self.button = Checkbutton(self.frame,
text='on/off',
onvalue=1,
offvalue=0,
variable=self.var,
relief=RAISED,
borderwidth=2,
command=self.set)
self.button.pack(side=RIGHT)
class EnumOption(Option):
def addoption(self):
self.button = Menubutton(self.frame,
textvariable=self.var,
relief=RAISED, borderwidth=2)
self.button.pack(side=RIGHT)
self.menu = Menu(self.button)
self.button['menu'] = self.menu
for v in self.dialog.classes[self.klass]:
self.menu.add_radiobutton(
label=v,
variable=self.var,
value=v,
command=self.set)
class StringOption(Option):
def addoption(self):
self.entry = Entry(self.frame,
textvariable=self.var,
width=10,
relief=SUNKEN,
borderwidth=2)
self.entry.pack(side=RIGHT, fill=X, expand=1)
self.entry.bind('<Return>', self.set)
class ReadonlyOption(Option):
def addoption(self):
self.label = Label(self.frame, textvariable=self.var,
anchor=E)
self.label.pack(side=RIGHT)
class Dialog:
def __init__(self, master):
self.master = master
self.fixclasses()
self.refresh()
self.top = Toplevel(self.master)
self.top.title(self.__class__.__name__)
self.top.minsize(1, 1)
self.addchoices()
def refresh(self): pass # Must override
def fixclasses(self): pass # May override
def addchoices(self):
self.choices = {}
list = []
for k, dc in self.options.items():
list.append((k, dc))
list.sort()
for k, (d, c) in list:
try:
cl = self.classes[c]
except KeyError:
cl = 'unknown'
if type(cl) == TupleType:
cl = self.enumoption
elif cl == 'boolean':
cl = self.booleanoption
elif cl == 'readonly':
cl = self.readonlyoption
else:
cl = self.stringoption
self.choices[k] = cl(self, k)
# Must override:
options = {}
classes = {}
# May override:
booleanoption = BooleanOption
stringoption = StringOption
enumoption = EnumOption
readonlyoption = ReadonlyOption
class PackDialog(Dialog):
def __init__(self, widget):
self.widget = widget
Dialog.__init__(self, widget)
def refresh(self):
self.current = self.widget.info()
self.current['.class'] = self.widget.winfo_class()
self.current['.name'] = self.widget._w
class packoption: # Mix-in class
def set(self, e=None):
self.current = self.var.get()
try:
apply(self.dialog.widget.pack, (),
{self.option: self.current})
except TclError, msg:
print msg
self.refresh()
class booleanoption(packoption, BooleanOption): pass
class enumoption(packoption, EnumOption): pass
class stringoption(packoption, StringOption): pass
class readonlyoption(packoption, ReadonlyOption): pass
options = {
'.class': (None, 'Class'),
'.name': (None, 'Name'),
'after': (None, 'Widget'),
'anchor': ('center', 'Anchor'),
'before': (None, 'Widget'),
'expand': ('no', 'Boolean'),
'fill': ('none', 'Fill'),
'in': (None, 'Widget'),
'ipadx': (0, 'Pad'),
'ipady': (0, 'Pad'),
'padx': (0, 'Pad'),
'pady': (0, 'Pad'),
'side': ('top', 'Side'),
}
classes = {
'Anchor': (N, NE, E, SE, S, SW, W, NW, CENTER),
'Boolean': 'boolean',
'Class': 'readonly',
'Expand': 'boolean',
'Fill': (NONE, X, Y, BOTH),
'Name': 'readonly',
'Pad': 'pixel',
'Side': (TOP, RIGHT, BOTTOM, LEFT),
'Widget': 'readonly',
}
class RemotePackDialog(PackDialog):
def __init__(self, master, app, widget):
self.master = master
self.app = app
self.widget = widget
self.refresh()
self.top = Toplevel(self.master)
self.top.title(self.app + ' PackDialog')
self.top.minsize(1, 1)
self.addchoices()
def refresh(self):
try:
words = self.master.tk.splitlist(
self.master.send(self.app,
'pack',
'info',
self.widget))
except TclError, msg:
print msg
return
dict = {}
for i in range(0, len(words), 2):
key = words[i][1:]
value = words[i+1]
dict[key] = value
dict['.class'] = self.master.send(self.app,
'winfo',
'class',
self.widget)
dict['.name'] = self.widget
self.current = dict
class remotepackoption: # Mix-in class
def set(self, e=None):
self.current = self.var.get()
try:
self.dialog.master.send(
self.dialog.app,
'pack',
'config',
self.dialog.widget,
'-'+self.option,
self.dialog.master.tk.merge(
self.current))
except TclError, msg:
print msg
self.refresh()
class booleanoption(remotepackoption, BooleanOption): pass
class enumoption(remotepackoption, EnumOption): pass
class stringoption(remotepackoption, StringOption): pass
class readonlyoption(remotepackoption, ReadonlyOption): pass
class WidgetDialog(Dialog):
def __init__(self, widget):
self.widget = widget
self.klass = widget.winfo_class()
Dialog.__init__(self, widget)
def fixclasses(self):
if self.addclasses.has_key(self.klass):
classes = {}
for c in (self.classes,
self.addclasses[self.klass]):
for k in c.keys():
classes[k] = c[k]
self.classes = classes
def refresh(self):
self.configuration = self.widget.config()
self.update()
self.current['.class'] = self.widget.winfo_class()
self.current['.name'] = self.widget._w
def update(self):
self.current = {}
self.options = {}
for k, v in self.configuration.items():
if len(v) > 4:
self.current[k] = v[4]
self.options[k] = v[3], v[2] # default, klass
self.options['.class'] = (None, 'Class')
self.options['.name'] = (None, 'Name')
class widgetoption: # Mix-in class
def set(self, e=None):
self.current = self.var.get()
try:
self.dialog.widget[self.option] = self.current
except TclError, msg:
print msg
self.refresh()
class booleanoption(widgetoption, BooleanOption): pass
class enumoption(widgetoption, EnumOption): pass
class stringoption(widgetoption, StringOption): pass
class readonlyoption(widgetoption, ReadonlyOption): pass
# Universal classes
classes = {
'Anchor': (N, NE, E, SE, S, SW, W, NW, CENTER),
'Aspect': 'integer',
'Background': 'color',
'Bitmap': 'bitmap',
'BorderWidth': 'pixel',
'Class': 'readonly',
'CloseEnough': 'double',
'Command': 'command',
'Confine': 'boolean',
'Cursor': 'cursor',
'CursorWidth': 'pixel',
'DisabledForeground': 'color',
'ExportSelection': 'boolean',
'Font': 'font',
'Foreground': 'color',
'From': 'integer',
'Geometry': 'geometry',
'Height': 'pixel',
'InsertWidth': 'time',
'Justify': (LEFT, CENTER, RIGHT),
'Label': 'string',
'Length': 'pixel',
'MenuName': 'widget',
'Name': 'readonly',
'OffTime': 'time',
'OnTime': 'time',
'Orient': (HORIZONTAL, VERTICAL),
'Pad': 'pixel',
'Relief': (RAISED, SUNKEN, FLAT, RIDGE, GROOVE),
'RepeatDelay': 'time',
'RepeatInterval': 'time',
'ScrollCommand': 'command',
'ScrollIncrement': 'pixel',
'ScrollRegion': 'rectangle',
'ShowValue': 'boolean',
'SetGrid': 'boolean',
'Sliderforeground': 'color',
'SliderLength': 'pixel',
'Text': 'string',
'TickInterval': 'integer',
'To': 'integer',
'Underline': 'index',
'Variable': 'variable',
'Value': 'string',
'Width': 'pixel',
'Wrap': (NONE, CHAR, WORD),
}
# Classes that (may) differ per widget type
_tristate = {'State': (NORMAL, ACTIVE, DISABLED)}
_bistate = {'State': (NORMAL, DISABLED)}
addclasses = {
'Button': _tristate,
'Radiobutton': _tristate,
'Checkbutton': _tristate,
'Entry': _bistate,
'Text': _bistate,
'Menubutton': _tristate,
'Slider': _bistate,
}
class RemoteWidgetDialog(WidgetDialog):
def __init__(self, master, app, widget):
self.app = app
self.widget = widget
self.klass = master.send(self.app,
'winfo',
'class',
self.widget)
Dialog.__init__(self, master)
def refresh(self):
try:
items = self.master.tk.splitlist(
self.master.send(self.app,
self.widget,
'config'))
except TclError, msg:
print msg
return
dict = {}
for item in items:
words = self.master.tk.splitlist(item)
key = words[0][1:]
value = (key,) + words[1:]
dict[key] = value
self.configuration = dict
self.update()
self.current['.class'] = self.klass
self.current['.name'] = self.widget
class remotewidgetoption: # Mix-in class
def set(self, e=None):
self.current = self.var.get()
try:
self.dialog.master.send(
self.dialog.app,
self.dialog.widget,
'config',
'-'+self.option,
self.current)
except TclError, msg:
print msg
self.refresh()
class booleanoption(remotewidgetoption, BooleanOption): pass
class enumoption(remotewidgetoption, EnumOption): pass
class stringoption(remotewidgetoption, StringOption): pass
class readonlyoption(remotewidgetoption, ReadonlyOption): pass
def test():
import sys
root = Tk()
root.minsize(1, 1)
if sys.argv[1:]:
remotetest(root, sys.argv[1])
else:
frame = Frame(root, name='frame')
frame.pack(expand=1, fill=BOTH)
button = Button(frame, name='button', text='button')
button.pack(expand=1)
canvas = Canvas(frame, name='canvas')
canvas.pack()
fpd = PackDialog(frame)
fwd = WidgetDialog(frame)
bpd = PackDialog(button)
bwd = WidgetDialog(button)
cpd = PackDialog(canvas)
cwd = WidgetDialog(canvas)
root.mainloop()
def remotetest(root, app):
from listtree import listtree
list = listtree(root, app)
list.bind('<Any-Double-1>', opendialogs)
list.app = app # Pass it on to handler
def opendialogs(e):
import string
list = e.widget
sel = list.curselection()
for i in sel:
item = list.get(i)
widget = string.split(item)[0]
RemoteWidgetDialog(list, list.app, widget)
if widget == '.': continue
try:
RemotePackDialog(list, list.app, widget)
except TclError, msg:
print msg
test()

View File

@@ -0,0 +1,220 @@
# Widget to display a man page
import re
from Tkinter import *
from Tkinter import _tkinter
from ScrolledText import ScrolledText
# XXX These fonts may have to be changed to match your system
BOLDFONT = '*-Courier-Bold-R-Normal-*-120-*'
ITALICFONT = '*-Courier-Medium-O-Normal-*-120-*'
# XXX Recognizing footers is system dependent
# (This one works for IRIX 5.2 and Solaris 2.2)
footerprog = re.compile(
'^ Page [1-9][0-9]*[ \t]+\|^.*Last change:.*[1-9][0-9]*\n')
emptyprog = re.compile('^[ \t]*\n')
ulprog = re.compile('^[ \t]*[Xv!_][Xv!_ \t]*\n')
# Basic Man Page class -- does not disable editing
class EditableManPage(ScrolledText):
# Initialize instance
def __init__(self, master=None, **cnf):
# Initialize base class
apply(ScrolledText.__init__, (self, master), cnf)
# Define tags for formatting styles
self.tag_config('X', underline=1)
self.tag_config('!', font=BOLDFONT)
self.tag_config('_', font=ITALICFONT)
# Set state to idle
self.fp = None
self.lineno = 0
# Test whether we are busy parsing a file
def busy(self):
return self.fp != None
# Ensure we're not busy
def kill(self):
if self.busy():
self._endparser()
# Parse a file, in the background
def asyncparsefile(self, fp):
self._startparser(fp)
self.tk.createfilehandler(fp, _tkinter.READABLE,
self._filehandler)
parsefile = asyncparsefile # Alias
# I/O handler used by background parsing
def _filehandler(self, fp, mask):
nextline = self.fp.readline()
if not nextline:
self._endparser()
return
self._parseline(nextline)
# Parse a file, now (cannot be aborted)
def syncparsefile(self, fp):
from select import select
def avail(fp=fp, tout=0.0, select=select):
return select([fp], [], [], tout)[0]
height = self.getint(self['height'])
self._startparser(fp)
while 1:
nextline = fp.readline()
if not nextline:
break
self._parseline(nextline)
self._endparser()
# Initialize parsing from a particular file -- must not be busy
def _startparser(self, fp):
if self.busy():
raise RuntimeError, 'startparser: still busy'
fp.fileno() # Test for file-ness
self.fp = fp
self.lineno = 0
self.ok = 0
self.empty = 0
self.buffer = None
savestate = self['state']
self['state'] = NORMAL
self.delete('1.0', END)
self['state'] = savestate
# End parsing -- must be busy, need not be at EOF
def _endparser(self):
if not self.busy():
raise RuntimeError, 'endparser: not busy'
if self.buffer:
self._parseline('')
try:
self.tk.deletefilehandler(self.fp)
except TclError, msg:
pass
self.fp.close()
self.fp = None
del self.ok, self.empty, self.buffer
# Parse a single line
def _parseline(self, nextline):
if not self.buffer:
# Save this line -- we need one line read-ahead
self.buffer = nextline
return
if emptyprog.match(self.buffer) >= 0:
# Buffered line was empty -- set a flag
self.empty = 1
self.buffer = nextline
return
textline = self.buffer
if ulprog.match(nextline) >= 0:
# Next line is properties for buffered line
propline = nextline
self.buffer = None
else:
# Next line is read-ahead
propline = None
self.buffer = nextline
if not self.ok:
# First non blank line after footer must be header
# -- skip that too
self.ok = 1
self.empty = 0
return
if footerprog.match(textline) >= 0:
# Footer -- start skipping until next non-blank line
self.ok = 0
self.empty = 0
return
savestate = self['state']
self['state'] = NORMAL
if TkVersion >= 4.0:
self.mark_set('insert', 'end-1c')
else:
self.mark_set('insert', END)
if self.empty:
# One or more previous lines were empty
# -- insert one blank line in the text
self._insert_prop('\n')
self.lineno = self.lineno + 1
self.empty = 0
if not propline:
# No properties
self._insert_prop(textline)
else:
# Search for properties
p = ''
j = 0
for i in range(min(len(propline), len(textline))):
if propline[i] != p:
if j < i:
self._insert_prop(textline[j:i], p)
j = i
p = propline[i]
self._insert_prop(textline[j:])
self.lineno = self.lineno + 1
self['state'] = savestate
# Insert a string at the end, with at most one property (tag)
def _insert_prop(self, str, prop = ' '):
here = self.index(AtInsert())
self.insert(AtInsert(), str)
if TkVersion <= 4.0:
tags = self.tag_names(here)
for tag in tags:
self.tag_remove(tag, here, AtInsert())
if prop != ' ':
self.tag_add(prop, here, AtInsert())
# Readonly Man Page class -- disables editing, otherwise the same
class ReadonlyManPage(EditableManPage):
# Initialize instance
def __init__(self, master=None, **cnf):
cnf['state'] = DISABLED
apply(EditableManPage.__init__, (self, master), cnf)
# Alias
ManPage = ReadonlyManPage
# Test program.
# usage: ManPage [manpage]; or ManPage [-f] file
# -f means that the file is nroff -man output run through ul -i
def test():
import os
import sys
# XXX This directory may be different on your system
MANDIR = '/usr/local/man/mann'
DEFAULTPAGE = 'Tcl'
formatted = 0
if sys.argv[1:] and sys.argv[1] == '-f':
formatted = 1
del sys.argv[1]
if sys.argv[1:]:
name = sys.argv[1]
else:
name = DEFAULTPAGE
if not formatted:
if name[-2:-1] != '.':
name = name + '.n'
name = os.path.join(MANDIR, name)
root = Tk()
root.minsize(1, 1)
manpage = ManPage(root, relief=SUNKEN, borderwidth=2)
manpage.pack(expand=1, fill=BOTH)
if formatted:
fp = open(name, 'r')
else:
fp = os.popen('nroff -man %s | ul -i' % name, 'r')
manpage.parsefile(fp)
root.mainloop()
# Run the test program when called as a script
if __name__ == '__main__':
test()

143
Demo/tkinter/guido/MimeViewer.py Executable file
View File

@@ -0,0 +1,143 @@
#! /usr/bin/env python
# View a single MIME multipart message.
# Display each part as a box.
import string
from types import *
from Tkinter import *
from ScrolledText import ScrolledText
class MimeViewer:
def __init__(self, parent, title, msg):
self.title = title
self.msg = msg
self.frame = Frame(parent, {'relief': 'raised', 'bd': 2})
self.frame.packing = {'expand': 0, 'fill': 'both'}
self.button = Checkbutton(self.frame,
{'text': title,
'command': self.toggle})
self.button.pack({'anchor': 'w'})
headertext = msg.getheadertext(
lambda x: x != 'received' and x[:5] != 'x400-')
height = countlines(headertext, 4)
if height:
self.htext = ScrolledText(self.frame,
{'height': height,
'width': 80,
'wrap': 'none',
'relief': 'raised',
'bd': 2})
self.htext.packing = {'expand': 1, 'fill': 'both',
'after': self.button}
self.htext.insert('end', headertext)
else:
self.htext = Frame(self.frame,
{'relief': 'raised', 'bd': 2})
self.htext.packing = {'side': 'top',
'ipady': 2,
'fill': 'x',
'after': self.button}
body = msg.getbody()
if type(body) == StringType:
self.pad = None
height = countlines(body, 10)
if height:
self.btext = ScrolledText(self.frame,
{'height': height,
'width': 80,
'wrap': 'none',
'relief': 'raised',
'bd': 2})
self.btext.packing = {'expand': 1,
'fill': 'both'}
self.btext.insert('end', body)
else:
self.btext = None
self.parts = None
else:
self.pad = Frame(self.frame,
{'relief': 'flat', 'bd': 2})
self.pad.packing = {'side': 'left', 'ipadx': 10,
'fill': 'y', 'after': self.htext}
self.parts = []
for i in range(len(body)):
p = MimeViewer(self.frame,
'%s.%d' % (title, i+1),
body[i])
self.parts.append(p)
self.btext = None
self.collapsed = 1
def pack(self):
self.frame.pack(self.frame.packing)
def destroy(self):
self.frame.destroy()
def show(self):
if self.collapsed:
self.button.invoke()
def toggle(self):
if self.collapsed:
self.explode()
else:
self.collapse()
def collapse(self):
self.collapsed = 1
for comp in self.htext, self.btext, self.pad:
if comp:
comp.forget()
if self.parts:
for part in self.parts:
part.frame.forget()
self.frame.pack({'expand': 0})
def explode(self):
self.collapsed = 0
for comp in self.htext, self.btext, self.pad:
if comp: comp.pack(comp.packing)
if self.parts:
for part in self.parts:
part.pack()
self.frame.pack({'expand': 1})
def countlines(str, limit):
i = 0
n = 0
while n < limit:
i = string.find(str, '\n', i)
if i < 0: break
n = n+1
i = i+1
return n
def main():
import sys
import getopt
import mhlib
opts, args = getopt.getopt(sys.argv[1:], '')
for o, a in opts:
pass
message = None
folder = 'inbox'
for arg in args:
if arg[:1] == '+':
folder = arg[1:]
else:
message = string.atoi(arg)
mh = mhlib.MH()
f = mh.openfolder(folder)
if not message:
message = f.getcurrent()
m = f.openmessage(message)
root = Tk()
tk = root.tk
top = MimeViewer(root, '+%s/%d' % (folder, message), m)
top.pack()
top.show()
root.minsize(1, 1)
tk.mainloop()
if __name__ == '__main__': main()

View File

@@ -0,0 +1,147 @@
import os
import sys
import string
from Tkinter import *
from ScrolledText import ScrolledText
from Dialog import Dialog
import signal
BUFSIZE = 512
class ShellWindow(ScrolledText):
def __init__(self, master=None, shell=None, **cnf):
if not shell:
try:
shell = os.environ['SHELL']
except KeyError:
shell = '/bin/sh'
shell = shell + ' -i'
args = string.split(shell)
shell = args[0]
apply(ScrolledText.__init__, (self, master), cnf)
self.pos = '1.0'
self.bind('<Return>', self.inputhandler)
self.bind('<Control-c>', self.sigint)
self.bind('<Control-t>', self.sigterm)
self.bind('<Control-k>', self.sigkill)
self.bind('<Control-d>', self.sendeof)
self.pid, self.fromchild, self.tochild = spawn(shell, args)
self.tk.createfilehandler(self.fromchild, READABLE,
self.outputhandler)
def outputhandler(self, file, mask):
data = os.read(file, BUFSIZE)
if not data:
self.tk.deletefilehandler(file)
pid, sts = os.waitpid(self.pid, 0)
print 'pid', pid, 'status', sts
self.pid = None
detail = sts>>8
cause = sts & 0xff
if cause == 0:
msg = "exit status %d" % detail
else:
msg = "killed by signal %d" % (cause & 0x7f)
if cause & 0x80:
msg = msg + " -- core dumped"
Dialog(self.master,
text=msg,
title="Exit status",
bitmap='warning',
default=0,
strings=('OK',))
return
self.insert(END, data)
self.pos = self.index("end - 1 char")
self.yview_pickplace(END)
def inputhandler(self, *args):
if not self.pid:
self.no_process()
return "break"
self.insert(END, "\n")
line = self.get(self.pos, "end - 1 char")
self.pos = self.index(END)
os.write(self.tochild, line)
return "break"
def sendeof(self, *args):
if not self.pid:
self.no_process()
return "break"
os.close(self.tochild)
return "break"
def sendsig(self, sig):
if not self.pid:
self.no_process()
return "break"
os.kill(self.pid, sig)
return "break"
def sigint(self, *args):
return self.sendsig(signal.SIGINT)
def sigquit(self, *args):
return self.sendsig(signal.SIGQUIT)
def sigterm(self, *args):
return self.sendsig(signal.SIGTERM)
def sigkill(self, *args):
return self.sendsig(signal.SIGKILL)
def no_process(self):
Dialog(self.master,
text="No active process",
title="No process",
bitmap='error',
default=0,
strings=('OK',))
MAXFD = 100 # Max number of file descriptors (os.getdtablesize()???)
def spawn(prog, args):
p2cread, p2cwrite = os.pipe()
c2pread, c2pwrite = os.pipe()
pid = os.fork()
if pid == 0:
# Child
for i in 0, 1, 2:
try:
os.close(i)
except os.error:
pass
if os.dup(p2cread) <> 0:
sys.stderr.write('popen2: bad read dup\n')
if os.dup(c2pwrite) <> 1:
sys.stderr.write('popen2: bad write dup\n')
if os.dup(c2pwrite) <> 2:
sys.stderr.write('popen2: bad write dup\n')
os.closerange(3, MAXFD)
try:
os.execvp(prog, args)
finally:
sys.stderr.write('execvp failed\n')
os._exit(1)
os.close(p2cread)
os.close(c2pwrite)
return pid, c2pread, p2cwrite
def test():
shell = string.join(sys.argv[1:])
root = Tk()
root.minsize(1, 1)
if shell:
w = ShellWindow(root, shell=shell)
else:
w = ShellWindow(root)
w.pack(expand=1, fill=BOTH)
w.focus_set()
w.tk.mainloop()
if __name__ == '__main__':
test()

View File

@@ -0,0 +1,50 @@
# Brownian motion -- an example of a multi-threaded Tkinter program.
from Tkinter import *
import random
import threading
import time
import sys
WIDTH = 400
HEIGHT = 300
SIGMA = 10
BUZZ = 2
RADIUS = 2
LAMBDA = 10
FILL = 'red'
stop = 0 # Set when main loop exits
def particle(canvas):
r = RADIUS
x = random.gauss(WIDTH/2.0, SIGMA)
y = random.gauss(HEIGHT/2.0, SIGMA)
p = canvas.create_oval(x-r, y-r, x+r, y+r, fill=FILL)
while not stop:
dx = random.gauss(0, BUZZ)
dy = random.gauss(0, BUZZ)
dt = random.expovariate(LAMBDA)
try:
canvas.move(p, dx, dy)
except TclError:
break
time.sleep(dt)
def main():
global stop
root = Tk()
canvas = Canvas(root, width=WIDTH, height=HEIGHT)
canvas.pack(fill='both', expand=1)
np = 30
if sys.argv[1:]:
np = int(sys.argv[1])
for i in range(np):
t = threading.Thread(target=particle, args=(canvas,))
t.start()
try:
root.mainloop()
finally:
stop = 1
main()

View File

@@ -0,0 +1,55 @@
# Brownian motion -- an example of a NON multi-threaded Tkinter program ;)
# By Michele Simoniato, inspired by brownian.py
from Tkinter import *
import random
import sys
WIDTH = 400
HEIGHT = 300
SIGMA = 10
BUZZ = 2
RADIUS = 2
LAMBDA = 10
FILL = 'red'
stop = 0 # Set when main loop exits
root = None # main window
def particle(canvas): # particle = iterator over the moves
r = RADIUS
x = random.gauss(WIDTH/2.0, SIGMA)
y = random.gauss(HEIGHT/2.0, SIGMA)
p = canvas.create_oval(x-r, y-r, x+r, y+r, fill=FILL)
while not stop:
dx = random.gauss(0, BUZZ)
dy = random.gauss(0, BUZZ)
try:
canvas.move(p, dx, dy)
except TclError:
break
else:
yield None
def move(particle): # move the particle at random time
particle.next()
dt = random.expovariate(LAMBDA)
root.after(int(dt*1000), move, particle)
def main():
global root, stop
root = Tk()
canvas = Canvas(root, width=WIDTH, height=HEIGHT)
canvas.pack(fill='both', expand=1)
np = 30
if sys.argv[1:]:
np = int(sys.argv[1])
for i in range(np): # start the dance
move(particle(canvas))
try:
root.mainloop()
finally:
stop = 1
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,244 @@
#! /usr/bin/env python
from Tkinter import *
from Canvas import Oval, Group, CanvasText
# Fix a bug in Canvas.Group as distributed in Python 1.4. The
# distributed bind() method is broken. This is what should be used:
class Group(Group):
def bind(self, sequence=None, command=None):
return self.canvas.tag_bind(self.id, sequence, command)
class Object:
"""Base class for composite graphical objects.
Objects belong to a canvas, and can be moved around on the canvas.
They also belong to at most one ``pile'' of objects, and can be
transferred between piles (or removed from their pile).
Objects have a canonical ``x, y'' position which is moved when the
object is moved. Where the object is relative to this position
depends on the object; for simple objects, it may be their center.
Objects have mouse sensitivity. They can be clicked, dragged and
double-clicked. The behavior may actually be determined by the pile
they are in.
All instance attributes are public since the derived class may
need them.
"""
def __init__(self, canvas, x=0, y=0, fill='red', text='object'):
self.canvas = canvas
self.x = x
self.y = y
self.pile = None
self.group = Group(self.canvas)
self.createitems(fill, text)
def __str__(self):
return str(self.group)
def createitems(self, fill, text):
self.__oval = Oval(self.canvas,
self.x-20, self.y-10, self.x+20, self.y+10,
fill=fill, width=3)
self.group.addtag_withtag(self.__oval)
self.__text = CanvasText(self.canvas,
self.x, self.y, text=text)
self.group.addtag_withtag(self.__text)
def moveby(self, dx, dy):
if dx == dy == 0:
return
self.group.move(dx, dy)
self.x = self.x + dx
self.y = self.y + dy
def moveto(self, x, y):
self.moveby(x - self.x, y - self.y)
def transfer(self, pile):
if self.pile:
self.pile.delete(self)
self.pile = None
self.pile = pile
if self.pile:
self.pile.add(self)
def tkraise(self):
self.group.tkraise()
class Bottom(Object):
"""An object to serve as the bottom of a pile."""
def createitems(self, *args):
self.__oval = Oval(self.canvas,
self.x-20, self.y-10, self.x+20, self.y+10,
fill='gray', outline='')
self.group.addtag_withtag(self.__oval)
class Pile:
"""A group of graphical objects."""
def __init__(self, canvas, x, y, tag=None):
self.canvas = canvas
self.x = x
self.y = y
self.objects = []
self.bottom = Bottom(self.canvas, self.x, self.y)
self.group = Group(self.canvas, tag=tag)
self.group.addtag_withtag(self.bottom.group)
self.bindhandlers()
def bindhandlers(self):
self.group.bind('<1>', self.clickhandler)
self.group.bind('<Double-1>', self.doubleclickhandler)
def add(self, object):
self.objects.append(object)
self.group.addtag_withtag(object.group)
self.position(object)
def delete(self, object):
object.group.dtag(self.group)
self.objects.remove(object)
def position(self, object):
object.tkraise()
i = self.objects.index(object)
object.moveto(self.x + i*4, self.y + i*8)
def clickhandler(self, event):
pass
def doubleclickhandler(self, event):
pass
class MovingPile(Pile):
def bindhandlers(self):
Pile.bindhandlers(self)
self.group.bind('<B1-Motion>', self.motionhandler)
self.group.bind('<ButtonRelease-1>', self.releasehandler)
movethis = None
def clickhandler(self, event):
tags = self.canvas.gettags('current')
for i in range(len(self.objects)):
o = self.objects[i]
if o.group.tag in tags:
break
else:
self.movethis = None
return
self.movethis = self.objects[i:]
for o in self.movethis:
o.tkraise()
self.lastx = event.x
self.lasty = event.y
doubleclickhandler = clickhandler
def motionhandler(self, event):
if not self.movethis:
return
dx = event.x - self.lastx
dy = event.y - self.lasty
self.lastx = event.x
self.lasty = event.y
for o in self.movethis:
o.moveby(dx, dy)
def releasehandler(self, event):
objects = self.movethis
if not objects:
return
self.movethis = None
self.finishmove(objects)
def finishmove(self, objects):
for o in objects:
self.position(o)
class Pile1(MovingPile):
x = 50
y = 50
tag = 'p1'
def __init__(self, demo):
self.demo = demo
MovingPile.__init__(self, self.demo.canvas, self.x, self.y, self.tag)
def doubleclickhandler(self, event):
try:
o = self.objects[-1]
except IndexError:
return
o.transfer(self.other())
MovingPile.doubleclickhandler(self, event)
def other(self):
return self.demo.p2
def finishmove(self, objects):
o = objects[0]
p = self.other()
x, y = o.x, o.y
if (x-p.x)**2 + (y-p.y)**2 < (x-self.x)**2 + (y-self.y)**2:
for o in objects:
o.transfer(p)
else:
MovingPile.finishmove(self, objects)
class Pile2(Pile1):
x = 150
y = 50
tag = 'p2'
def other(self):
return self.demo.p1
class Demo:
def __init__(self, master):
self.master = master
self.canvas = Canvas(master,
width=200, height=200,
background='yellow',
relief=SUNKEN, borderwidth=2)
self.canvas.pack(expand=1, fill=BOTH)
self.p1 = Pile1(self)
self.p2 = Pile2(self)
o1 = Object(self.canvas, fill='red', text='o1')
o2 = Object(self.canvas, fill='green', text='o2')
o3 = Object(self.canvas, fill='light blue', text='o3')
o1.transfer(self.p1)
o2.transfer(self.p1)
o3.transfer(self.p2)
# Main function, run when invoked as a stand-alone Python program.
def main():
root = Tk()
demo = Demo(root)
root.protocol('WM_DELETE_WINDOW', root.quit)
root.mainloop()
if __name__ == '__main__':
main()

109
Demo/tkinter/guido/dialog.py Executable file
View File

@@ -0,0 +1,109 @@
#! /usr/bin/env python
# A Python function that generates dialog boxes with a text message,
# optional bitmap, and any number of buttons.
# Cf. Ousterhout, Tcl and the Tk Toolkit, Figs. 27.2-3, pp. 269-270.
from Tkinter import *
import sys
def dialog(master, title, text, bitmap, default, *args):
# 1. Create the top-level window and divide it into top
# and bottom parts.
w = Toplevel(master, class_='Dialog')
w.title(title)
w.iconname('Dialog')
top = Frame(w, relief=RAISED, borderwidth=1)
top.pack(side=TOP, fill=BOTH)
bot = Frame(w, relief=RAISED, borderwidth=1)
bot.pack(side=BOTTOM, fill=BOTH)
# 2. Fill the top part with the bitmap and message.
msg = Message(top, width='3i', text=text,
font='-Adobe-Times-Medium-R-Normal-*-180-*')
msg.pack(side=RIGHT, expand=1, fill=BOTH, padx='3m', pady='3m')
if bitmap:
bm = Label(top, bitmap=bitmap)
bm.pack(side=LEFT, padx='3m', pady='3m')
# 3. Create a row of buttons at the bottom of the dialog.
var = IntVar()
buttons = []
i = 0
for but in args:
b = Button(bot, text=but, command=lambda v=var,i=i: v.set(i))
buttons.append(b)
if i == default:
bd = Frame(bot, relief=SUNKEN, borderwidth=1)
bd.pack(side=LEFT, expand=1, padx='3m', pady='2m')
b.lift()
b.pack (in_=bd, side=LEFT,
padx='2m', pady='2m', ipadx='2m', ipady='1m')
else:
b.pack (side=LEFT, expand=1,
padx='3m', pady='3m', ipadx='2m', ipady='1m')
i = i+1
# 4. Set up a binding for <Return>, if there's a default,
# set a grab, and claim the focus too.
if default >= 0:
w.bind('<Return>',
lambda e, b=buttons[default], v=var, i=default:
(b.flash(),
v.set(i)))
oldFocus = w.focus_get()
w.grab_set()
w.focus_set()
# 5. Wait for the user to respond, then restore the focus
# and return the index of the selected button.
w.waitvar(var)
w.destroy()
if oldFocus: oldFocus.focus_set()
return var.get()
# The rest is the test program.
def go():
i = dialog(mainWidget,
'Not Responding',
"The file server isn't responding right now; "
"I'll keep trying.",
'',
-1,
'OK')
print 'pressed button', i
i = dialog(mainWidget,
'File Modified',
'File "tcl.h" has been modified since '
'the last time it was saved. '
'Do you want to save it before exiting the application?',
'warning',
0,
'Save File',
'Discard Changes',
'Return To Editor')
print 'pressed button', i
def test():
import sys
global mainWidget
mainWidget = Frame()
Pack.config(mainWidget)
start = Button(mainWidget, text='Press Here To Start', command=go)
start.pack()
endit = Button(mainWidget, text="Exit", command=sys.exit)
endit.pack(fill=BOTH)
mainWidget.mainloop()
if __name__ == '__main__':
test()

91
Demo/tkinter/guido/electrons.py Executable file
View File

@@ -0,0 +1,91 @@
#! /usr/bin/env python
# Simulate "electrons" migrating across the screen.
# An optional bitmap file in can be in the background.
#
# Usage: electrons [n [bitmapfile]]
#
# n is the number of electrons to animate; default is 30.
#
# The bitmap file can be any X11 bitmap file (look in
# /usr/include/X11/bitmaps for samples); it is displayed as the
# background of the animation. Default is no bitmap.
from Tkinter import *
import random
# The graphical interface
class Electrons:
# Create our objects
def __init__(self, n, bitmap = None):
self.n = n
self.tk = tk = Tk()
self.canvas = c = Canvas(tk)
c.pack()
width, height = tk.getint(c['width']), tk.getint(c['height'])
# Add background bitmap
if bitmap:
self.bitmap = c.create_bitmap(width/2, height/2,
bitmap=bitmap,
foreground='blue')
self.pieces = []
x1, y1, x2, y2 = 10,70,14,74
for i in range(n):
p = c.create_oval(x1, y1, x2, y2, fill='red')
self.pieces.append(p)
y1, y2 = y1 +2, y2 + 2
self.tk.update()
def random_move(self, n):
c = self.canvas
for p in self.pieces:
x = random.choice(range(-2,4))
y = random.choice(range(-3,4))
c.move(p, x, y)
self.tk.update()
# Run -- allow 500 movemens
def run(self):
try:
for i in range(500):
self.random_move(self.n)
except TclError:
try:
self.tk.destroy()
except TclError:
pass
# Main program
def main():
import sys, string
# First argument is number of electrons, default 30
if sys.argv[1:]:
n = string.atoi(sys.argv[1])
else:
n = 30
# Second argument is bitmap file, default none
if sys.argv[2:]:
bitmap = sys.argv[2]
# Reverse meaning of leading '@' compared to Tk
if bitmap[0] == '@': bitmap = bitmap[1:]
else: bitmap = '@' + bitmap
else:
bitmap = None
# Create the graphical objects...
h = Electrons(n, bitmap)
# ...and run!
h.run()
# Call main when run as script
if __name__ == '__main__':
main()

154
Demo/tkinter/guido/hanoi.py Normal file
View File

@@ -0,0 +1,154 @@
# Animated Towers of Hanoi using Tk with optional bitmap file in
# background.
#
# Usage: tkhanoi [n [bitmapfile]]
#
# n is the number of pieces to animate; default is 4, maximum 15.
#
# The bitmap file can be any X11 bitmap file (look in
# /usr/include/X11/bitmaps for samples); it is displayed as the
# background of the animation. Default is no bitmap.
# This uses Steen Lumholt's Tk interface
from Tkinter import *
# Basic Towers-of-Hanoi algorithm: move n pieces from a to b, using c
# as temporary. For each move, call report()
def hanoi(n, a, b, c, report):
if n <= 0: return
hanoi(n-1, a, c, b, report)
report(n, a, b)
hanoi(n-1, c, b, a, report)
# The graphical interface
class Tkhanoi:
# Create our objects
def __init__(self, n, bitmap = None):
self.n = n
self.tk = tk = Tk()
self.canvas = c = Canvas(tk)
c.pack()
width, height = tk.getint(c['width']), tk.getint(c['height'])
# Add background bitmap
if bitmap:
self.bitmap = c.create_bitmap(width//2, height//2,
bitmap=bitmap,
foreground='blue')
# Generate pegs
pegwidth = 10
pegheight = height//2
pegdist = width//3
x1, y1 = (pegdist-pegwidth)//2, height*1//3
x2, y2 = x1+pegwidth, y1+pegheight
self.pegs = []
p = c.create_rectangle(x1, y1, x2, y2, fill='black')
self.pegs.append(p)
x1, x2 = x1+pegdist, x2+pegdist
p = c.create_rectangle(x1, y1, x2, y2, fill='black')
self.pegs.append(p)
x1, x2 = x1+pegdist, x2+pegdist
p = c.create_rectangle(x1, y1, x2, y2, fill='black')
self.pegs.append(p)
self.tk.update()
# Generate pieces
pieceheight = pegheight//16
maxpiecewidth = pegdist*2//3
minpiecewidth = 2*pegwidth
self.pegstate = [[], [], []]
self.pieces = {}
x1, y1 = (pegdist-maxpiecewidth)//2, y2-pieceheight-2
x2, y2 = x1+maxpiecewidth, y1+pieceheight
dx = (maxpiecewidth-minpiecewidth) // (2*max(1, n-1))
for i in range(n, 0, -1):
p = c.create_rectangle(x1, y1, x2, y2, fill='red')
self.pieces[i] = p
self.pegstate[0].append(i)
x1, x2 = x1 + dx, x2-dx
y1, y2 = y1 - pieceheight-2, y2-pieceheight-2
self.tk.update()
self.tk.after(25)
# Run -- never returns
def run(self):
while 1:
hanoi(self.n, 0, 1, 2, self.report)
hanoi(self.n, 1, 2, 0, self.report)
hanoi(self.n, 2, 0, 1, self.report)
hanoi(self.n, 0, 2, 1, self.report)
hanoi(self.n, 2, 1, 0, self.report)
hanoi(self.n, 1, 0, 2, self.report)
# Reporting callback for the actual hanoi function
def report(self, i, a, b):
if self.pegstate[a][-1] != i: raise RuntimeError # Assertion
del self.pegstate[a][-1]
p = self.pieces[i]
c = self.canvas
# Lift the piece above peg a
ax1, ay1, ax2, ay2 = c.bbox(self.pegs[a])
while 1:
x1, y1, x2, y2 = c.bbox(p)
if y2 < ay1: break
c.move(p, 0, -1)
self.tk.update()
# Move it towards peg b
bx1, by1, bx2, by2 = c.bbox(self.pegs[b])
newcenter = (bx1+bx2)//2
while 1:
x1, y1, x2, y2 = c.bbox(p)
center = (x1+x2)//2
if center == newcenter: break
if center > newcenter: c.move(p, -1, 0)
else: c.move(p, 1, 0)
self.tk.update()
# Move it down on top of the previous piece
pieceheight = y2-y1
newbottom = by2 - pieceheight*len(self.pegstate[b]) - 2
while 1:
x1, y1, x2, y2 = c.bbox(p)
if y2 >= newbottom: break
c.move(p, 0, 1)
self.tk.update()
# Update peg state
self.pegstate[b].append(i)
# Main program
def main():
import sys, string
# First argument is number of pegs, default 4
if sys.argv[1:]:
n = string.atoi(sys.argv[1])
else:
n = 4
# Second argument is bitmap file, default none
if sys.argv[2:]:
bitmap = sys.argv[2]
# Reverse meaning of leading '@' compared to Tk
if bitmap[0] == '@': bitmap = bitmap[1:]
else: bitmap = '@' + bitmap
else:
bitmap = None
# Create the graphical objects...
h = Tkhanoi(n, bitmap)
# ...and run!
h.run()
# Call main when run as script
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,17 @@
# Display hello, world in a button; clicking it quits the program
import sys
from Tkinter import *
def main():
root = Tk()
button = Button(root)
button['text'] = 'Hello, world'
button['command'] = quit_callback # See below
button.pack()
root.mainloop()
def quit_callback():
sys.exit(0)
main()

View File

@@ -0,0 +1,23 @@
"""Draw on top of an image"""
from Tkinter import *
import sys
def main():
filename = sys.argv[1]
root = Tk()
img = PhotoImage(file=filename)
w, h = img.width(), img.height()
canv = Canvas(root, width=w, height=h)
canv.create_image(0, 0, anchor=NW, image=img)
canv.pack()
canv.bind('<Button-1>', blob)
root.mainloop()
def blob(event):
x, y = event.x, event.y
canv = event.widget
r = 5
canv.create_oval(x-r, y-r, x+r, y+r, fill='red', outline="")
main()

View File

@@ -0,0 +1,12 @@
from Tkinter import *
import sys
def main():
filename = sys.argv[1]
root = Tk()
img = PhotoImage(file=filename)
label = Label(root, image=img)
label.pack()
root.mainloop()
main()

98
Demo/tkinter/guido/kill.py Executable file
View File

@@ -0,0 +1,98 @@
#! /usr/bin/env python
# Tkinter interface to Linux `kill' command.
from Tkinter import *
from string import splitfields
from string import split
import commands
import os
class BarButton(Menubutton):
def __init__(self, master=None, **cnf):
apply(Menubutton.__init__, (self, master), cnf)
self.pack(side=LEFT)
self.menu = Menu(self, name='menu')
self['menu'] = self.menu
class Kill(Frame):
# List of (name, option, pid_column)
format_list = [('Default', '', 0),
('Long', '-l', 2),
('User', '-u', 1),
('Jobs', '-j', 1),
('Signal', '-s', 1),
('Memory', '-m', 0),
('VM', '-v', 0),
('Hex', '-X', 0)]
def kill(self, selected):
c = self.format_list[self.format.get()][2]
pid = split(selected)[c]
os.system('kill -9 ' + pid)
self.do_update()
def do_update(self):
name, option, column = self.format_list[self.format.get()]
s = commands.getoutput('ps -w ' + option)
list = splitfields(s, '\n')
self.header.set(list[0])
del list[0]
y = self.frame.vscroll.get()[0]
self.frame.list.delete(0, AtEnd())
for line in list:
self.frame.list.insert(0, line)
self.frame.list.yview(int(y))
def do_motion(self, e):
e.widget.select_clear(0, END)
e.widget.select_set(e.widget.nearest(e.y))
def do_leave(self, e):
e.widget.select_clear(0, END)
def do_1(self, e):
self.kill(e.widget.get(e.widget.nearest(e.y)))
def __init__(self, master=None, **cnf):
Frame.__init__(self, master, cnf)
self.pack(expand=1, fill=BOTH)
self.bar = Frame(self, name='bar', relief=RAISED,
borderwidth=2)
self.bar.pack(fill=X)
self.bar.file = BarButton(self.bar, text='File')
self.bar.file.menu.add_command(
label='Quit', command=self.quit)
self.bar.view = BarButton(self.bar, text='View')
self.format = IntVar(self)
self.format.set(2)
for num in range(len(self.format_list)):
self.bar.view.menu.add_radiobutton(
label=self.format_list[num][0],
command=self.do_update,
variable=self.format,
value=num)
#self.bar.view.menu.add_separator()
#XXX ...
self.bar.tk_menuBar(self.bar.file, self.bar.view)
self.frame = Frame(self, relief=RAISED, borderwidth=2)
self.frame.pack(expand=1, fill=BOTH)
self.header = StringVar(self)
self.frame.label = Label(self.frame, relief=FLAT, anchor=NW,
borderwidth=0,
textvariable=self.header)
self.frame.label.pack(fill=X)
self.frame.vscroll = Scrollbar(self.frame, orient=VERTICAL)
self.frame.list = Listbox(self.frame, relief=SUNKEN,
selectbackground='#eed5b7',
selectborderwidth=0,
yscroll=self.frame.vscroll.set)
self.frame.vscroll['command'] = self.frame.list.yview
self.frame.vscroll.pack(side=RIGHT, fill=Y)
self.frame.list.pack(expand=1, fill=BOTH)
self.update = Button(self, text="Update",
command=self.do_update)
self.update.pack(expand=1, fill=X)
self.frame.list.bind('<Motion>', self.do_motion)
self.frame.list.bind('<Leave>', self.do_leave)
self.frame.list.bind('<1>', self.do_1)
self.do_update()
if __name__ == '__main__':
kill = Kill(None, borderwidth=5)
kill.winfo_toplevel().title('Tkinter Process Killer')
kill.winfo_toplevel().minsize(1, 1)
kill.mainloop()

View File

@@ -0,0 +1,37 @@
# List a remote app's widget tree (names and classes only)
import sys
import string
from Tkinter import *
def listtree(master, app):
list = Listbox(master, name='list')
list.pack(expand=1, fill=BOTH)
listnodes(list, app, '.', 0)
return list
def listnodes(list, app, widget, level):
klass = list.send(app, 'winfo', 'class', widget)
## i = string.rindex(widget, '.')
## list.insert(END, '%s%s (%s)' % ((level-1)*'. ', widget[i:], klass))
list.insert(END, '%s (%s)' % (widget, klass))
children = list.tk.splitlist(
list.send(app, 'winfo', 'children', widget))
for c in children:
listnodes(list, app, c, level+1)
def main():
if not sys.argv[1:]:
sys.stderr.write('Usage: listtree appname\n')
sys.exit(2)
app = sys.argv[1]
tk = Tk()
tk.minsize(1, 1)
f = Frame(tk, name='f')
f.pack(expand=1, fill=BOTH)
list = listtree(f, app)
tk.mainloop()
if __name__ == '__main__':
main()

285
Demo/tkinter/guido/mbox.py Executable file
View File

@@ -0,0 +1,285 @@
#! /usr/bin/env python
# Scan MH folder, display results in window
import os
import sys
import re
import getopt
import string
import mhlib
from Tkinter import *
from dialog import dialog
mailbox = os.environ['HOME'] + '/Mail'
def main():
global root, tk, top, mid, bot
global folderbox, foldermenu, scanbox, scanmenu, viewer
global folder, seq
global mh, mhf
# Parse command line options
folder = 'inbox'
seq = 'all'
try:
opts, args = getopt.getopt(sys.argv[1:], '')
except getopt.error, msg:
print msg
sys.exit(2)
for arg in args:
if arg[:1] == '+':
folder = arg[1:]
else:
seq = arg
# Initialize MH
mh = mhlib.MH()
mhf = mh.openfolder(folder)
# Build widget hierarchy
root = Tk()
tk = root.tk
top = Frame(root)
top.pack({'expand': 1, 'fill': 'both'})
# Build right part: folder list
right = Frame(top)
right.pack({'fill': 'y', 'side': 'right'})
folderbar = Scrollbar(right, {'relief': 'sunken', 'bd': 2})
folderbar.pack({'fill': 'y', 'side': 'right'})
folderbox = Listbox(right, {'exportselection': 0})
folderbox.pack({'expand': 1, 'fill': 'both', 'side': 'left'})
foldermenu = Menu(root)
foldermenu.add('command',
{'label': 'Open Folder',
'command': open_folder})
foldermenu.add('separator')
foldermenu.add('command',
{'label': 'Quit',
'command': 'exit'})
foldermenu.bind('<ButtonRelease-3>', folder_unpost)
folderbox['yscrollcommand'] = (folderbar, 'set')
folderbar['command'] = (folderbox, 'yview')
folderbox.bind('<Double-1>', open_folder, 1)
folderbox.bind('<3>', folder_post)
# Build left part: scan list
left = Frame(top)
left.pack({'expand': 1, 'fill': 'both', 'side': 'left'})
scanbar = Scrollbar(left, {'relief': 'sunken', 'bd': 2})
scanbar.pack({'fill': 'y', 'side': 'right'})
scanbox = Listbox(left, {'font': 'fixed'})
scanbox.pack({'expand': 1, 'fill': 'both', 'side': 'left'})
scanmenu = Menu(root)
scanmenu.add('command',
{'label': 'Open Message',
'command': open_message})
scanmenu.add('command',
{'label': 'Remove Message',
'command': remove_message})
scanmenu.add('command',
{'label': 'Refile Message',
'command': refile_message})
scanmenu.add('separator')
scanmenu.add('command',
{'label': 'Quit',
'command': 'exit'})
scanmenu.bind('<ButtonRelease-3>', scan_unpost)
scanbox['yscrollcommand'] = (scanbar, 'set')
scanbar['command'] = (scanbox, 'yview')
scanbox.bind('<Double-1>', open_message)
scanbox.bind('<3>', scan_post)
# Separator between middle and bottom part
rule2 = Frame(root, {'bg': 'black'})
rule2.pack({'fill': 'x'})
# Build bottom part: current message
bot = Frame(root)
bot.pack({'expand': 1, 'fill': 'both'})
#
viewer = None
# Window manager commands
root.minsize(800, 1) # Make window resizable
# Fill folderbox with text
setfolders()
# Fill scanbox with text
rescan()
# Enter mainloop
root.mainloop()
def folder_post(e):
x, y = e.x_root, e.y_root
foldermenu.post(x - 10, y - 10)
foldermenu.grab_set()
def folder_unpost(e):
tk.call('update', 'idletasks')
foldermenu.grab_release()
foldermenu.unpost()
foldermenu.invoke('active')
def scan_post(e):
x, y = e.x_root, e.y_root
scanmenu.post(x - 10, y - 10)
scanmenu.grab_set()
def scan_unpost(e):
tk.call('update', 'idletasks')
scanmenu.grab_release()
scanmenu.unpost()
scanmenu.invoke('active')
scanparser = re.compile('^ *([0-9]+)')
def open_folder(e=None):
global folder, mhf
sel = folderbox.curselection()
if len(sel) != 1:
if len(sel) > 1:
msg = "Please open one folder at a time"
else:
msg = "Please select a folder to open"
dialog(root, "Can't Open Folder", msg, "", 0, "OK")
return
i = sel[0]
folder = folderbox.get(i)
mhf = mh.openfolder(folder)
rescan()
def open_message(e=None):
global viewer
sel = scanbox.curselection()
if len(sel) != 1:
if len(sel) > 1:
msg = "Please open one message at a time"
else:
msg = "Please select a message to open"
dialog(root, "Can't Open Message", msg, "", 0, "OK")
return
cursor = scanbox['cursor']
scanbox['cursor'] = 'watch'
tk.call('update', 'idletasks')
i = sel[0]
line = scanbox.get(i)
if scanparser.match(line) >= 0:
num = string.atoi(scanparser.group(1))
m = mhf.openmessage(num)
if viewer: viewer.destroy()
from MimeViewer import MimeViewer
viewer = MimeViewer(bot, '+%s/%d' % (folder, num), m)
viewer.pack()
viewer.show()
scanbox['cursor'] = cursor
def interestingheader(header):
return header != 'received'
def remove_message(e=None):
itop = scanbox.nearest(0)
sel = scanbox.curselection()
if not sel:
dialog(root, "No Message To Remove",
"Please select a message to remove", "", 0, "OK")
return
todo = []
for i in sel:
line = scanbox.get(i)
if scanparser.match(line) >= 0:
todo.append(string.atoi(scanparser.group(1)))
mhf.removemessages(todo)
rescan()
fixfocus(min(todo), itop)
lastrefile = ''
tofolder = None
def refile_message(e=None):
global lastrefile, tofolder
itop = scanbox.nearest(0)
sel = scanbox.curselection()
if not sel:
dialog(root, "No Message To Refile",
"Please select a message to refile", "", 0, "OK")
return
foldersel = folderbox.curselection()
if len(foldersel) != 1:
if not foldersel:
msg = "Please select a folder to refile to"
else:
msg = "Please select exactly one folder to refile to"
dialog(root, "No Folder To Refile", msg, "", 0, "OK")
return
refileto = folderbox.get(foldersel[0])
todo = []
for i in sel:
line = scanbox.get(i)
if scanparser.match(line) >= 0:
todo.append(string.atoi(scanparser.group(1)))
if lastrefile != refileto or not tofolder:
lastrefile = refileto
tofolder = None
tofolder = mh.openfolder(lastrefile)
mhf.refilemessages(todo, tofolder)
rescan()
fixfocus(min(todo), itop)
def fixfocus(near, itop):
n = scanbox.size()
for i in range(n):
line = scanbox.get(repr(i))
if scanparser.match(line) >= 0:
num = string.atoi(scanparser.group(1))
if num >= near:
break
else:
i = 'end'
scanbox.select_from(i)
scanbox.yview(itop)
def setfolders():
folderbox.delete(0, 'end')
for fn in mh.listallfolders():
folderbox.insert('end', fn)
def rescan():
global viewer
if viewer:
viewer.destroy()
viewer = None
scanbox.delete(0, 'end')
for line in scanfolder(folder, seq):
scanbox.insert('end', line)
def scanfolder(folder = 'inbox', sequence = 'all'):
return map(
lambda line: line[:-1],
os.popen('scan +%s %s' % (folder, sequence), 'r').readlines())
main()

View File

@@ -0,0 +1,47 @@
#! /usr/bin/env python
"""Play with the new Tk 8.0 toplevel menu option."""
from Tkinter import *
class App:
def __init__(self, master):
self.master = master
self.menubar = Menu(self.master)
self.filemenu = Menu(self.menubar)
self.filemenu.add_command(label="New")
self.filemenu.add_command(label="Open...")
self.filemenu.add_command(label="Close")
self.filemenu.add_separator()
self.filemenu.add_command(label="Quit", command=self.master.quit)
self.editmenu = Menu(self.menubar)
self.editmenu.add_command(label="Cut")
self.editmenu.add_command(label="Copy")
self.editmenu.add_command(label="Paste")
self.helpmenu = Menu(self.menubar, name='help')
self.helpmenu.add_command(label="About...")
self.menubar.add_cascade(label="File", menu=self.filemenu)
self.menubar.add_cascade(label="Edit", menu=self.editmenu)
self.menubar.add_cascade(label="Help", menu=self.helpmenu)
self.top = Toplevel(menu=self.menubar)
# Rest of app goes here...
def main():
root = Tk()
root.withdraw()
app = App(root)
root.mainloop()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,27 @@
# option menu sample (Fredrik Lundh, September 1997)
from Tkinter import *
root = Tk()
#
# standard usage
var1 = StringVar()
var1.set("One") # default selection
menu1 = OptionMenu(root, var1, "One", "Two", "Three")
menu1.pack()
#
# initialize from a sequence
CHOICES = "Aah", "Bee", "Cee", "Dee", "Eff"
var2 = StringVar()
var2.set(CHOICES[0])
menu2 = apply(OptionMenu, (root, var2) + tuple(CHOICES))
menu2.pack()
root.mainloop()

View File

@@ -0,0 +1,60 @@
""""Paint program by Dave Michell.
Subject: tkinter "paint" example
From: Dave Mitchell <davem@magnet.com>
To: python-list@cwi.nl
Date: Fri, 23 Jan 1998 12:18:05 -0500 (EST)
Not too long ago (last week maybe?) someone posted a request
for an example of a paint program using Tkinter. Try as I might
I can't seem to find it in the archive, so i'll just post mine
here and hope that the person who requested it sees this!
All this does is put up a canvas and draw a smooth black line
whenever you have the mouse button down, but hopefully it will
be enough to start with.. It would be easy enough to add some
options like other shapes or colors...
yours,
dave mitchell
davem@magnet.com
"""
from Tkinter import *
"""paint.py: not exactly a paint program.. just a smooth line drawing demo."""
b1 = "up"
xold, yold = None, None
def main():
root = Tk()
drawing_area = Canvas(root)
drawing_area.pack()
drawing_area.bind("<Motion>", motion)
drawing_area.bind("<ButtonPress-1>", b1down)
drawing_area.bind("<ButtonRelease-1>", b1up)
root.mainloop()
def b1down(event):
global b1
b1 = "down" # you only want to draw when the button is down
# because "Motion" events happen -all the time-
def b1up(event):
global b1, xold, yold
b1 = "up"
xold = None # reset the line when you let go of the button
yold = None
def motion(event):
if b1 == "down":
global xold, yold
if xold is not None and yold is not None:
event.widget.create_line(xold,yold,event.x,event.y,smooth=TRUE)
# here's where you draw it. smooth. neat.
xold = event.x
yold = event.y
if __name__ == "__main__":
main()

159
Demo/tkinter/guido/rmt.py Executable file
View File

@@ -0,0 +1,159 @@
#! /usr/bin/env python
# A Python program implementing rmt, an application for remotely
# controlling other Tk applications.
# Cf. Ousterhout, Tcl and the Tk Toolkit, Figs. 27.5-8, pp. 273-276.
# Note that because of forward references in the original, we
# sometimes delay bindings until after the corresponding procedure is
# defined. We also introduce names for some unnamed code blocks in
# the original because of restrictions on lambda forms in Python.
# XXX This should be written in a more Python-like style!!!
from Tkinter import *
import sys
# 1. Create basic application structure: menu bar on top of
# text widget, scrollbar on right.
root = Tk()
tk = root.tk
mBar = Frame(root, relief=RAISED, borderwidth=2)
mBar.pack(fill=X)
f = Frame(root)
f.pack(expand=1, fill=BOTH)
s = Scrollbar(f, relief=FLAT)
s.pack(side=RIGHT, fill=Y)
t = Text(f, relief=RAISED, borderwidth=2, yscrollcommand=s.set, setgrid=1)
t.pack(side=LEFT, fill=BOTH, expand=1)
t.tag_config('bold', font='-Adobe-Courier-Bold-R-Normal-*-120-*')
s['command'] = t.yview
root.title('Tk Remote Controller')
root.iconname('Tk Remote')
# 2. Create menu button and menus.
file = Menubutton(mBar, text='File', underline=0)
file.pack(side=LEFT)
file_m = Menu(file)
file['menu'] = file_m
file_m_apps = Menu(file_m, tearoff=0)
file_m.add_cascade(label='Select Application', underline=0,
menu=file_m_apps)
file_m.add_command(label='Quit', underline=0, command=sys.exit)
# 3. Create bindings for text widget to allow commands to be
# entered and information to be selected. New characters
# can only be added at the end of the text (can't ever move
# insertion point).
def single1(e):
x = e.x
y = e.y
t.setvar('tk_priv(selectMode)', 'char')
t.mark_set('anchor', At(x, y))
# Should focus W
t.bind('<1>', single1)
def double1(e):
x = e.x
y = e.y
t.setvar('tk_priv(selectMode)', 'word')
t.tk_textSelectTo(At(x, y))
t.bind('<Double-1>', double1)
def triple1(e):
x = e.x
y = e.y
t.setvar('tk_priv(selectMode)', 'line')
t.tk_textSelectTo(At(x, y))
t.bind('<Triple-1>', triple1)
def returnkey(e):
t.insert(AtInsert(), '\n')
invoke()
t.bind('<Return>', returnkey)
def controlv(e):
t.insert(AtInsert(), t.selection_get())
t.yview_pickplace(AtInsert())
if t.index(AtInsert())[-2:] == '.0':
invoke()
t.bind('<Control-v>', controlv)
# 4. Procedure to backspace over one character, as long as
# the character isn't part of the prompt.
def backspace(e):
if t.index('promptEnd') != t.index('insert - 1 char'):
t.delete('insert - 1 char', AtInsert())
t.yview_pickplace(AtInsert())
t.bind('<BackSpace>', backspace)
t.bind('<Control-h>', backspace)
t.bind('<Delete>', backspace)
# 5. Procedure that's invoked when return is typed: if
# there's not yet a complete command (e.g. braces are open)
# then do nothing. Otherwise, execute command (locally or
# remotely), output the result or error message, and issue
# a new prompt.
def invoke():
cmd = t.get('promptEnd + 1 char', AtInsert())
if t.getboolean(tk.call('info', 'complete', cmd)): # XXX
if app == root.winfo_name():
msg = tk.call('eval', cmd) # XXX
else:
msg = t.send(app, cmd)
if msg:
t.insert(AtInsert(), msg + '\n')
prompt()
t.yview_pickplace(AtInsert())
def prompt():
t.insert(AtInsert(), app + ': ')
t.mark_set('promptEnd', 'insert - 1 char')
t.tag_add('bold', 'insert linestart', 'promptEnd')
# 6. Procedure to select a new application. Also changes
# the prompt on the current command line to reflect the new
# name.
def newApp(appName):
global app
app = appName
t.delete('promptEnd linestart', 'promptEnd')
t.insert('promptEnd', appName + ':')
t.tag_add('bold', 'promptEnd linestart', 'promptEnd')
def fillAppsMenu():
file_m_apps.add('command')
file_m_apps.delete(0, 'last')
names = root.winfo_interps()
names = list(names)
names.sort()
for name in names:
try:
root.send(name, 'winfo name .')
except TclError:
# Inoperative window -- ignore it
pass
else:
file_m_apps.add_command(
label=name,
command=lambda name=name: newApp(name))
file_m_apps['postcommand'] = fillAppsMenu
mBar.tk_menuBar(file)
# 7. Miscellaneous initialization.
app = root.winfo_name()
prompt()
t.focus()
root.mainloop()

637
Demo/tkinter/guido/solitaire.py Executable file
View File

@@ -0,0 +1,637 @@
#! /usr/bin/env python
"""Solitaire game, much like the one that comes with MS Windows.
Limitations:
- No cute graphical images for the playing cards faces or backs.
- No scoring or timer.
- No undo.
- No option to turn 3 cards at a time.
- No keyboard shortcuts.
- Less fancy animation when you win.
- The determination of which stack you drag to is more relaxed.
Apology:
I'm not much of a card player, so my terminology in these comments may
at times be a little unusual. If you have suggestions, please let me
know!
"""
# Imports
import math
import random
from Tkinter import *
from Canvas import Rectangle, CanvasText, Group, Window
# Fix a bug in Canvas.Group as distributed in Python 1.4. The
# distributed bind() method is broken. Rather than asking you to fix
# the source, we fix it here by deriving a subclass:
class Group(Group):
def bind(self, sequence=None, command=None):
return self.canvas.tag_bind(self.id, sequence, command)
# Constants determining the size and lay-out of cards and stacks. We
# work in a "grid" where each card/stack is surrounded by MARGIN
# pixels of space on each side, so adjacent stacks are separated by
# 2*MARGIN pixels. OFFSET is the offset used for displaying the
# face down cards in the row stacks.
CARDWIDTH = 100
CARDHEIGHT = 150
MARGIN = 10
XSPACING = CARDWIDTH + 2*MARGIN
YSPACING = CARDHEIGHT + 4*MARGIN
OFFSET = 5
# The background color, green to look like a playing table. The
# standard green is way too bright, and dark green is way to dark, so
# we use something in between. (There are a few more colors that
# could be customized, but they are less controversial.)
BACKGROUND = '#070'
# Suits and colors. The values of the symbolic suit names are the
# strings used to display them (you change these and VALNAMES to
# internationalize the game). The COLOR dictionary maps suit names to
# colors (red and black) which must be Tk color names. The keys() of
# the COLOR dictionary conveniently provides us with a list of all
# suits (in arbitrary order).
HEARTS = 'Heart'
DIAMONDS = 'Diamond'
CLUBS = 'Club'
SPADES = 'Spade'
RED = 'red'
BLACK = 'black'
COLOR = {}
for s in (HEARTS, DIAMONDS):
COLOR[s] = RED
for s in (CLUBS, SPADES):
COLOR[s] = BLACK
ALLSUITS = COLOR.keys()
NSUITS = len(ALLSUITS)
# Card values are 1-13. We also define symbolic names for the picture
# cards. ALLVALUES is a list of all card values.
ACE = 1
JACK = 11
QUEEN = 12
KING = 13
ALLVALUES = range(1, 14) # (one more than the highest value)
NVALUES = len(ALLVALUES)
# VALNAMES is a list that maps a card value to string. It contains a
# dummy element at index 0 so it can be indexed directly with the card
# value.
VALNAMES = ["", "A"] + map(str, range(2, 11)) + ["J", "Q", "K"]
# Solitaire constants. The only one I can think of is the number of
# row stacks.
NROWS = 7
# The rest of the program consists of class definitions. These are
# further described in their documentation strings.
class Card:
"""A playing card.
A card doesn't record to which stack it belongs; only the stack
records this (it turns out that we always know this from the
context, and this saves a ``double update'' with potential for
inconsistencies).
Public methods:
moveto(x, y) -- move the card to an absolute position
moveby(dx, dy) -- move the card by a relative offset
tkraise() -- raise the card to the top of its stack
showface(), showback() -- turn the card face up or down & raise it
Public read-only instance variables:
suit, value, color -- the card's suit, value and color
face_shown -- true when the card is shown face up, else false
Semi-public read-only instance variables (XXX should be made
private):
group -- the Canvas.Group representing the card
x, y -- the position of the card's top left corner
Private instance variables:
__back, __rect, __text -- the canvas items making up the card
(To show the card face up, the text item is placed in front of
rect and the back is placed behind it. To show it face down, this
is reversed. The card is created face down.)
"""
def __init__(self, suit, value, canvas):
"""Card constructor.
Arguments are the card's suit and value, and the canvas widget.
The card is created at position (0, 0), with its face down
(adding it to a stack will position it according to that
stack's rules).
"""
self.suit = suit
self.value = value
self.color = COLOR[suit]
self.face_shown = 0
self.x = self.y = 0
self.group = Group(canvas)
text = "%s %s" % (VALNAMES[value], suit)
self.__text = CanvasText(canvas, CARDWIDTH//2, 0,
anchor=N, fill=self.color, text=text)
self.group.addtag_withtag(self.__text)
self.__rect = Rectangle(canvas, 0, 0, CARDWIDTH, CARDHEIGHT,
outline='black', fill='white')
self.group.addtag_withtag(self.__rect)
self.__back = Rectangle(canvas, MARGIN, MARGIN,
CARDWIDTH-MARGIN, CARDHEIGHT-MARGIN,
outline='black', fill='blue')
self.group.addtag_withtag(self.__back)
def __repr__(self):
"""Return a string for debug print statements."""
return "Card(%r, %r)" % (self.suit, self.value)
def moveto(self, x, y):
"""Move the card to absolute position (x, y)."""
self.moveby(x - self.x, y - self.y)
def moveby(self, dx, dy):
"""Move the card by (dx, dy)."""
self.x = self.x + dx
self.y = self.y + dy
self.group.move(dx, dy)
def tkraise(self):
"""Raise the card above all other objects in its canvas."""
self.group.tkraise()
def showface(self):
"""Turn the card's face up."""
self.tkraise()
self.__rect.tkraise()
self.__text.tkraise()
self.face_shown = 1
def showback(self):
"""Turn the card's face down."""
self.tkraise()
self.__rect.tkraise()
self.__back.tkraise()
self.face_shown = 0
class Stack:
"""A generic stack of cards.
This is used as a base class for all other stacks (e.g. the deck,
the suit stacks, and the row stacks).
Public methods:
add(card) -- add a card to the stack
delete(card) -- delete a card from the stack
showtop() -- show the top card (if any) face up
deal() -- delete and return the top card, or None if empty
Method that subclasses may override:
position(card) -- move the card to its proper (x, y) position
The default position() method places all cards at the stack's
own (x, y) position.
userclickhandler(), userdoubleclickhandler() -- called to do
subclass specific things on single and double clicks
The default user (single) click handler shows the top card
face up. The default user double click handler calls the user
single click handler.
usermovehandler(cards) -- called to complete a subpile move
The default user move handler moves all moved cards back to
their original position (by calling the position() method).
Private methods:
clickhandler(event), doubleclickhandler(event),
motionhandler(event), releasehandler(event) -- event handlers
The default event handlers turn the top card of the stack with
its face up on a (single or double) click, and also support
moving a subpile around.
startmoving(event) -- begin a move operation
finishmoving() -- finish a move operation
"""
def __init__(self, x, y, game=None):
"""Stack constructor.
Arguments are the stack's nominal x and y position (the top
left corner of the first card placed in the stack), and the
game object (which is used to get the canvas; subclasses use
the game object to find other stacks).
"""
self.x = x
self.y = y
self.game = game
self.cards = []
self.group = Group(self.game.canvas)
self.group.bind('<1>', self.clickhandler)
self.group.bind('<Double-1>', self.doubleclickhandler)
self.group.bind('<B1-Motion>', self.motionhandler)
self.group.bind('<ButtonRelease-1>', self.releasehandler)
self.makebottom()
def makebottom(self):
pass
def __repr__(self):
"""Return a string for debug print statements."""
return "%s(%d, %d)" % (self.__class__.__name__, self.x, self.y)
# Public methods
def add(self, card):
self.cards.append(card)
card.tkraise()
self.position(card)
self.group.addtag_withtag(card.group)
def delete(self, card):
self.cards.remove(card)
card.group.dtag(self.group)
def showtop(self):
if self.cards:
self.cards[-1].showface()
def deal(self):
if not self.cards:
return None
card = self.cards[-1]
self.delete(card)
return card
# Subclass overridable methods
def position(self, card):
card.moveto(self.x, self.y)
def userclickhandler(self):
self.showtop()
def userdoubleclickhandler(self):
self.userclickhandler()
def usermovehandler(self, cards):
for card in cards:
self.position(card)
# Event handlers
def clickhandler(self, event):
self.finishmoving() # In case we lost an event
self.userclickhandler()
self.startmoving(event)
def motionhandler(self, event):
self.keepmoving(event)
def releasehandler(self, event):
self.keepmoving(event)
self.finishmoving()
def doubleclickhandler(self, event):
self.finishmoving() # In case we lost an event
self.userdoubleclickhandler()
self.startmoving(event)
# Move internals
moving = None
def startmoving(self, event):
self.moving = None
tags = self.game.canvas.gettags('current')
for i in range(len(self.cards)):
card = self.cards[i]
if card.group.tag in tags:
break
else:
return
if not card.face_shown:
return
self.moving = self.cards[i:]
self.lastx = event.x
self.lasty = event.y
for card in self.moving:
card.tkraise()
def keepmoving(self, event):
if not self.moving:
return
dx = event.x - self.lastx
dy = event.y - self.lasty
self.lastx = event.x
self.lasty = event.y
if dx or dy:
for card in self.moving:
card.moveby(dx, dy)
def finishmoving(self):
cards = self.moving
self.moving = None
if cards:
self.usermovehandler(cards)
class Deck(Stack):
"""The deck is a stack with support for shuffling.
New methods:
fill() -- create the playing cards
shuffle() -- shuffle the playing cards
A single click moves the top card to the game's open deck and
moves it face up; if we're out of cards, it moves the open deck
back to the deck.
"""
def makebottom(self):
bottom = Rectangle(self.game.canvas,
self.x, self.y,
self.x+CARDWIDTH, self.y+CARDHEIGHT,
outline='black', fill=BACKGROUND)
self.group.addtag_withtag(bottom)
def fill(self):
for suit in ALLSUITS:
for value in ALLVALUES:
self.add(Card(suit, value, self.game.canvas))
def shuffle(self):
n = len(self.cards)
newcards = []
for i in randperm(n):
newcards.append(self.cards[i])
self.cards = newcards
def userclickhandler(self):
opendeck = self.game.opendeck
card = self.deal()
if not card:
while 1:
card = opendeck.deal()
if not card:
break
self.add(card)
card.showback()
else:
self.game.opendeck.add(card)
card.showface()
def randperm(n):
"""Function returning a random permutation of range(n)."""
r = range(n)
x = []
while r:
i = random.choice(r)
x.append(i)
r.remove(i)
return x
class OpenStack(Stack):
def acceptable(self, cards):
return 0
def usermovehandler(self, cards):
card = cards[0]
stack = self.game.closeststack(card)
if not stack or stack is self or not stack.acceptable(cards):
Stack.usermovehandler(self, cards)
else:
for card in cards:
self.delete(card)
stack.add(card)
self.game.wincheck()
def userdoubleclickhandler(self):
if not self.cards:
return
card = self.cards[-1]
if not card.face_shown:
self.userclickhandler()
return
for s in self.game.suits:
if s.acceptable([card]):
self.delete(card)
s.add(card)
self.game.wincheck()
break
class SuitStack(OpenStack):
def makebottom(self):
bottom = Rectangle(self.game.canvas,
self.x, self.y,
self.x+CARDWIDTH, self.y+CARDHEIGHT,
outline='black', fill='')
def userclickhandler(self):
pass
def userdoubleclickhandler(self):
pass
def acceptable(self, cards):
if len(cards) != 1:
return 0
card = cards[0]
if not self.cards:
return card.value == ACE
topcard = self.cards[-1]
return card.suit == topcard.suit and card.value == topcard.value + 1
class RowStack(OpenStack):
def acceptable(self, cards):
card = cards[0]
if not self.cards:
return card.value == KING
topcard = self.cards[-1]
if not topcard.face_shown:
return 0
return card.color != topcard.color and card.value == topcard.value - 1
def position(self, card):
y = self.y
for c in self.cards:
if c == card:
break
if c.face_shown:
y = y + 2*MARGIN
else:
y = y + OFFSET
card.moveto(self.x, y)
class Solitaire:
def __init__(self, master):
self.master = master
self.canvas = Canvas(self.master,
background=BACKGROUND,
highlightthickness=0,
width=NROWS*XSPACING,
height=3*YSPACING + 20 + MARGIN)
self.canvas.pack(fill=BOTH, expand=TRUE)
self.dealbutton = Button(self.canvas,
text="Deal",
highlightthickness=0,
background=BACKGROUND,
activebackground="green",
command=self.deal)
Window(self.canvas, MARGIN, 3*YSPACING + 20,
window=self.dealbutton, anchor=SW)
x = MARGIN
y = MARGIN
self.deck = Deck(x, y, self)
x = x + XSPACING
self.opendeck = OpenStack(x, y, self)
x = x + XSPACING
self.suits = []
for i in range(NSUITS):
x = x + XSPACING
self.suits.append(SuitStack(x, y, self))
x = MARGIN
y = y + YSPACING
self.rows = []
for i in range(NROWS):
self.rows.append(RowStack(x, y, self))
x = x + XSPACING
self.openstacks = [self.opendeck] + self.suits + self.rows
self.deck.fill()
self.deal()
def wincheck(self):
for s in self.suits:
if len(s.cards) != NVALUES:
return
self.win()
self.deal()
def win(self):
"""Stupid animation when you win."""
cards = []
for s in self.openstacks:
cards = cards + s.cards
while cards:
card = random.choice(cards)
cards.remove(card)
self.animatedmoveto(card, self.deck)
def animatedmoveto(self, card, dest):
for i in range(10, 0, -1):
dx, dy = (dest.x-card.x)//i, (dest.y-card.y)//i
card.moveby(dx, dy)
self.master.update_idletasks()
def closeststack(self, card):
closest = None
cdist = 999999999
# Since we only compare distances,
# we don't bother to take the square root.
for stack in self.openstacks:
dist = (stack.x - card.x)**2 + (stack.y - card.y)**2
if dist < cdist:
closest = stack
cdist = dist
return closest
def deal(self):
self.reset()
self.deck.shuffle()
for i in range(NROWS):
for r in self.rows[i:]:
card = self.deck.deal()
r.add(card)
for r in self.rows:
r.showtop()
def reset(self):
for stack in self.openstacks:
while 1:
card = stack.deal()
if not card:
break
self.deck.add(card)
card.showback()
# Main function, run when invoked as a stand-alone Python program.
def main():
root = Tk()
game = Solitaire(root)
root.protocol('WM_DELETE_WINDOW', root.quit)
root.mainloop()
if __name__ == '__main__':
main()

634
Demo/tkinter/guido/sortvisu.py Executable file
View File

@@ -0,0 +1,634 @@
#! /usr/bin/env python
"""Sorting algorithms visualizer using Tkinter.
This module is comprised of three ``components'':
- an array visualizer with methods that implement basic sorting
operations (compare, swap) as well as methods for ``annotating'' the
sorting algorithm (e.g. to show the pivot element);
- a number of sorting algorithms (currently quicksort, insertion sort,
selection sort and bubble sort, as well as a randomization function),
all using the array visualizer for its basic operations and with calls
to its annotation methods;
- and a ``driver'' class which can be used as a Grail applet or as a
stand-alone application.
"""
from Tkinter import *
from Canvas import Line, Rectangle
import random
XGRID = 10
YGRID = 10
WIDTH = 6
class Array:
def __init__(self, master, data=None):
self.master = master
self.frame = Frame(self.master)
self.frame.pack(fill=X)
self.label = Label(self.frame)
self.label.pack()
self.canvas = Canvas(self.frame)
self.canvas.pack()
self.report = Label(self.frame)
self.report.pack()
self.left = Line(self.canvas, 0, 0, 0, 0)
self.right = Line(self.canvas, 0, 0, 0, 0)
self.pivot = Line(self.canvas, 0, 0, 0, 0)
self.items = []
self.size = self.maxvalue = 0
if data:
self.setdata(data)
def setdata(self, data):
olditems = self.items
self.items = []
for item in olditems:
item.delete()
self.size = len(data)
self.maxvalue = max(data)
self.canvas.config(width=(self.size+1)*XGRID,
height=(self.maxvalue+1)*YGRID)
for i in range(self.size):
self.items.append(ArrayItem(self, i, data[i]))
self.reset("Sort demo, size %d" % self.size)
speed = "normal"
def setspeed(self, speed):
self.speed = speed
def destroy(self):
self.frame.destroy()
in_mainloop = 0
stop_mainloop = 0
def cancel(self):
self.stop_mainloop = 1
if self.in_mainloop:
self.master.quit()
def step(self):
if self.in_mainloop:
self.master.quit()
Cancelled = "Array.Cancelled" # Exception
def wait(self, msecs):
if self.speed == "fastest":
msecs = 0
elif self.speed == "fast":
msecs = msecs//10
elif self.speed == "single-step":
msecs = 1000000000
if not self.stop_mainloop:
self.master.update()
id = self.master.after(msecs, self.master.quit)
self.in_mainloop = 1
self.master.mainloop()
self.master.after_cancel(id)
self.in_mainloop = 0
if self.stop_mainloop:
self.stop_mainloop = 0
self.message("Cancelled")
raise Array.Cancelled
def getsize(self):
return self.size
def show_partition(self, first, last):
for i in range(self.size):
item = self.items[i]
if first <= i < last:
item.item.config(fill='red')
else:
item.item.config(fill='orange')
self.hide_left_right_pivot()
def hide_partition(self):
for i in range(self.size):
item = self.items[i]
item.item.config(fill='red')
self.hide_left_right_pivot()
def show_left(self, left):
if not 0 <= left < self.size:
self.hide_left()
return
x1, y1, x2, y2 = self.items[left].position()
## top, bot = HIRO
self.left.coords([(x1-2, 0), (x1-2, 9999)])
self.master.update()
def show_right(self, right):
if not 0 <= right < self.size:
self.hide_right()
return
x1, y1, x2, y2 = self.items[right].position()
self.right.coords(((x2+2, 0), (x2+2, 9999)))
self.master.update()
def hide_left_right_pivot(self):
self.hide_left()
self.hide_right()
self.hide_pivot()
def hide_left(self):
self.left.coords(((0, 0), (0, 0)))
def hide_right(self):
self.right.coords(((0, 0), (0, 0)))
def show_pivot(self, pivot):
x1, y1, x2, y2 = self.items[pivot].position()
self.pivot.coords(((0, y1-2), (9999, y1-2)))
def hide_pivot(self):
self.pivot.coords(((0, 0), (0, 0)))
def swap(self, i, j):
if i == j: return
self.countswap()
item = self.items[i]
other = self.items[j]
self.items[i], self.items[j] = other, item
item.swapwith(other)
def compare(self, i, j):
self.countcompare()
item = self.items[i]
other = self.items[j]
return item.compareto(other)
def reset(self, msg):
self.ncompares = 0
self.nswaps = 0
self.message(msg)
self.updatereport()
self.hide_partition()
def message(self, msg):
self.label.config(text=msg)
def countswap(self):
self.nswaps = self.nswaps + 1
self.updatereport()
def countcompare(self):
self.ncompares = self.ncompares + 1
self.updatereport()
def updatereport(self):
text = "%d cmps, %d swaps" % (self.ncompares, self.nswaps)
self.report.config(text=text)
class ArrayItem:
def __init__(self, array, index, value):
self.array = array
self.index = index
self.value = value
x1, y1, x2, y2 = self.position()
self.item = Rectangle(array.canvas, x1, y1, x2, y2,
fill='red', outline='black', width=1)
self.item.bind('<Button-1>', self.mouse_down)
self.item.bind('<Button1-Motion>', self.mouse_move)
self.item.bind('<ButtonRelease-1>', self.mouse_up)
def delete(self):
item = self.item
self.array = None
self.item = None
item.delete()
def mouse_down(self, event):
self.lastx = event.x
self.lasty = event.y
self.origx = event.x
self.origy = event.y
self.item.tkraise()
def mouse_move(self, event):
self.item.move(event.x - self.lastx, event.y - self.lasty)
self.lastx = event.x
self.lasty = event.y
def mouse_up(self, event):
i = self.nearestindex(event.x)
if i >= self.array.getsize():
i = self.array.getsize() - 1
if i < 0:
i = 0
other = self.array.items[i]
here = self.index
self.array.items[here], self.array.items[i] = other, self
self.index = i
x1, y1, x2, y2 = self.position()
self.item.coords(((x1, y1), (x2, y2)))
other.setindex(here)
def setindex(self, index):
nsteps = steps(self.index, index)
if not nsteps: return
if self.array.speed == "fastest":
nsteps = 0
oldpts = self.position()
self.index = index
newpts = self.position()
trajectory = interpolate(oldpts, newpts, nsteps)
self.item.tkraise()
for pts in trajectory:
self.item.coords((pts[:2], pts[2:]))
self.array.wait(50)
def swapwith(self, other):
nsteps = steps(self.index, other.index)
if not nsteps: return
if self.array.speed == "fastest":
nsteps = 0
myoldpts = self.position()
otheroldpts = other.position()
self.index, other.index = other.index, self.index
mynewpts = self.position()
othernewpts = other.position()
myfill = self.item['fill']
otherfill = other.item['fill']
self.item.config(fill='green')
other.item.config(fill='yellow')
self.array.master.update()
if self.array.speed == "single-step":
self.item.coords((mynewpts[:2], mynewpts[2:]))
other.item.coords((othernewpts[:2], othernewpts[2:]))
self.array.master.update()
self.item.config(fill=myfill)
other.item.config(fill=otherfill)
self.array.wait(0)
return
mytrajectory = interpolate(myoldpts, mynewpts, nsteps)
othertrajectory = interpolate(otheroldpts, othernewpts, nsteps)
if self.value > other.value:
self.item.tkraise()
other.item.tkraise()
else:
other.item.tkraise()
self.item.tkraise()
try:
for i in range(len(mytrajectory)):
mypts = mytrajectory[i]
otherpts = othertrajectory[i]
self.item.coords((mypts[:2], mypts[2:]))
other.item.coords((otherpts[:2], otherpts[2:]))
self.array.wait(50)
finally:
mypts = mytrajectory[-1]
otherpts = othertrajectory[-1]
self.item.coords((mypts[:2], mypts[2:]))
other.item.coords((otherpts[:2], otherpts[2:]))
self.item.config(fill=myfill)
other.item.config(fill=otherfill)
def compareto(self, other):
myfill = self.item['fill']
otherfill = other.item['fill']
outcome = cmp(self.value, other.value)
if outcome < 0:
myflash = 'white'
otherflash = 'black'
elif outcome > 0:
myflash = 'black'
otherflash = 'white'
else:
myflash = otherflash = 'grey'
try:
self.item.config(fill=myflash)
other.item.config(fill=otherflash)
self.array.wait(500)
finally:
self.item.config(fill=myfill)
other.item.config(fill=otherfill)
return outcome
def position(self):
x1 = (self.index+1)*XGRID - WIDTH//2
x2 = x1+WIDTH
y2 = (self.array.maxvalue+1)*YGRID
y1 = y2 - (self.value)*YGRID
return x1, y1, x2, y2
def nearestindex(self, x):
return int(round(float(x)/XGRID)) - 1
# Subroutines that don't need an object
def steps(here, there):
nsteps = abs(here - there)
if nsteps <= 3:
nsteps = nsteps * 3
elif nsteps <= 5:
nsteps = nsteps * 2
elif nsteps > 10:
nsteps = 10
return nsteps
def interpolate(oldpts, newpts, n):
if len(oldpts) != len(newpts):
raise ValueError, "can't interpolate arrays of different length"
pts = [0]*len(oldpts)
res = [tuple(oldpts)]
for i in range(1, n):
for k in range(len(pts)):
pts[k] = oldpts[k] + (newpts[k] - oldpts[k])*i//n
res.append(tuple(pts))
res.append(tuple(newpts))
return res
# Various (un)sorting algorithms
def uniform(array):
size = array.getsize()
array.setdata([(size+1)//2] * size)
array.reset("Uniform data, size %d" % size)
def distinct(array):
size = array.getsize()
array.setdata(range(1, size+1))
array.reset("Distinct data, size %d" % size)
def randomize(array):
array.reset("Randomizing")
n = array.getsize()
for i in range(n):
j = random.randint(0, n-1)
array.swap(i, j)
array.message("Randomized")
def insertionsort(array):
size = array.getsize()
array.reset("Insertion sort")
for i in range(1, size):
j = i-1
while j >= 0:
if array.compare(j, j+1) <= 0:
break
array.swap(j, j+1)
j = j-1
array.message("Sorted")
def selectionsort(array):
size = array.getsize()
array.reset("Selection sort")
try:
for i in range(size):
array.show_partition(i, size)
for j in range(i+1, size):
if array.compare(i, j) > 0:
array.swap(i, j)
array.message("Sorted")
finally:
array.hide_partition()
def bubblesort(array):
size = array.getsize()
array.reset("Bubble sort")
for i in range(size):
for j in range(1, size):
if array.compare(j-1, j) > 0:
array.swap(j-1, j)
array.message("Sorted")
def quicksort(array):
size = array.getsize()
array.reset("Quicksort")
try:
stack = [(0, size)]
while stack:
first, last = stack[-1]
del stack[-1]
array.show_partition(first, last)
if last-first < 5:
array.message("Insertion sort")
for i in range(first+1, last):
j = i-1
while j >= first:
if array.compare(j, j+1) <= 0:
break
array.swap(j, j+1)
j = j-1
continue
array.message("Choosing pivot")
j, i, k = first, (first+last)//2, last-1
if array.compare(k, i) < 0:
array.swap(k, i)
if array.compare(k, j) < 0:
array.swap(k, j)
if array.compare(j, i) < 0:
array.swap(j, i)
pivot = j
array.show_pivot(pivot)
array.message("Pivot at left of partition")
array.wait(1000)
left = first
right = last
while 1:
array.message("Sweep right pointer")
right = right-1
array.show_right(right)
while right > first and array.compare(right, pivot) >= 0:
right = right-1
array.show_right(right)
array.message("Sweep left pointer")
left = left+1
array.show_left(left)
while left < last and array.compare(left, pivot) <= 0:
left = left+1
array.show_left(left)
if left > right:
array.message("End of partition")
break
array.message("Swap items")
array.swap(left, right)
array.message("Swap pivot back")
array.swap(pivot, right)
n1 = right-first
n2 = last-left
if n1 > 1: stack.append((first, right))
if n2 > 1: stack.append((left, last))
array.message("Sorted")
finally:
array.hide_partition()
def demosort(array):
while 1:
for alg in [quicksort, insertionsort, selectionsort, bubblesort]:
randomize(array)
alg(array)
# Sort demo class -- usable as a Grail applet
class SortDemo:
def __init__(self, master, size=15):
self.master = master
self.size = size
self.busy = 0
self.array = Array(self.master)
self.botframe = Frame(master)
self.botframe.pack(side=BOTTOM)
self.botleftframe = Frame(self.botframe)
self.botleftframe.pack(side=LEFT, fill=Y)
self.botrightframe = Frame(self.botframe)
self.botrightframe.pack(side=RIGHT, fill=Y)
self.b_qsort = Button(self.botleftframe,
text="Quicksort", command=self.c_qsort)
self.b_qsort.pack(fill=X)
self.b_isort = Button(self.botleftframe,
text="Insertion sort", command=self.c_isort)
self.b_isort.pack(fill=X)
self.b_ssort = Button(self.botleftframe,
text="Selection sort", command=self.c_ssort)
self.b_ssort.pack(fill=X)
self.b_bsort = Button(self.botleftframe,
text="Bubble sort", command=self.c_bsort)
self.b_bsort.pack(fill=X)
# Terrible hack to overcome limitation of OptionMenu...
class MyIntVar(IntVar):
def __init__(self, master, demo):
self.demo = demo
IntVar.__init__(self, master)
def set(self, value):
IntVar.set(self, value)
if str(value) != '0':
self.demo.resize(value)
self.v_size = MyIntVar(self.master, self)
self.v_size.set(size)
sizes = [1, 2, 3, 4] + range(5, 55, 5)
if self.size not in sizes:
sizes.append(self.size)
sizes.sort()
self.m_size = apply(OptionMenu,
(self.botleftframe, self.v_size) + tuple(sizes))
self.m_size.pack(fill=X)
self.v_speed = StringVar(self.master)
self.v_speed.set("normal")
self.m_speed = OptionMenu(self.botleftframe, self.v_speed,
"single-step", "normal", "fast", "fastest")
self.m_speed.pack(fill=X)
self.b_step = Button(self.botleftframe,
text="Step", command=self.c_step)
self.b_step.pack(fill=X)
self.b_randomize = Button(self.botrightframe,
text="Randomize", command=self.c_randomize)
self.b_randomize.pack(fill=X)
self.b_uniform = Button(self.botrightframe,
text="Uniform", command=self.c_uniform)
self.b_uniform.pack(fill=X)
self.b_distinct = Button(self.botrightframe,
text="Distinct", command=self.c_distinct)
self.b_distinct.pack(fill=X)
self.b_demo = Button(self.botrightframe,
text="Demo", command=self.c_demo)
self.b_demo.pack(fill=X)
self.b_cancel = Button(self.botrightframe,
text="Cancel", command=self.c_cancel)
self.b_cancel.pack(fill=X)
self.b_cancel.config(state=DISABLED)
self.b_quit = Button(self.botrightframe,
text="Quit", command=self.c_quit)
self.b_quit.pack(fill=X)
def resize(self, newsize):
if self.busy:
self.master.bell()
return
self.size = newsize
self.array.setdata(range(1, self.size+1))
def c_qsort(self):
self.run(quicksort)
def c_isort(self):
self.run(insertionsort)
def c_ssort(self):
self.run(selectionsort)
def c_bsort(self):
self.run(bubblesort)
def c_demo(self):
self.run(demosort)
def c_randomize(self):
self.run(randomize)
def c_uniform(self):
self.run(uniform)
def c_distinct(self):
self.run(distinct)
def run(self, func):
if self.busy:
self.master.bell()
return
self.busy = 1
self.array.setspeed(self.v_speed.get())
self.b_cancel.config(state=NORMAL)
try:
func(self.array)
except Array.Cancelled:
pass
self.b_cancel.config(state=DISABLED)
self.busy = 0
def c_cancel(self):
if not self.busy:
self.master.bell()
return
self.array.cancel()
def c_step(self):
if not self.busy:
self.master.bell()
return
self.v_speed.set("single-step")
self.array.setspeed("single-step")
self.array.step()
def c_quit(self):
if self.busy:
self.array.cancel()
self.master.after_idle(self.master.quit)
# Main program -- for stand-alone operation outside Grail
def main():
root = Tk()
demo = SortDemo(root)
root.protocol('WM_DELETE_WINDOW', demo.c_quit)
root.mainloop()
if __name__ == '__main__':
main()

845
Demo/tkinter/guido/ss1.py Normal file
View File

@@ -0,0 +1,845 @@
"""SS1 -- a spreadsheet."""
import os
import re
import sys
import cgi
import rexec
from xml.parsers import expat
LEFT, CENTER, RIGHT = "LEFT", "CENTER", "RIGHT"
def ljust(x, n):
return x.ljust(n)
def center(x, n):
return x.center(n)
def rjust(x, n):
return x.rjust(n)
align2action = {LEFT: ljust, CENTER: center, RIGHT: rjust}
align2xml = {LEFT: "left", CENTER: "center", RIGHT: "right"}
xml2align = {"left": LEFT, "center": CENTER, "right": RIGHT}
align2anchor = {LEFT: "w", CENTER: "center", RIGHT: "e"}
def sum(seq):
total = 0
for x in seq:
if x is not None:
total += x
return total
class Sheet:
def __init__(self):
self.cells = {} # {(x, y): cell, ...}
self.rexec = rexec.RExec()
m = self.rexec.add_module('__main__')
m.cell = self.cellvalue
m.cells = self.multicellvalue
m.sum = sum
def cellvalue(self, x, y):
cell = self.getcell(x, y)
if hasattr(cell, 'recalc'):
return cell.recalc(self.rexec)
else:
return cell
def multicellvalue(self, x1, y1, x2, y2):
if x1 > x2:
x1, x2 = x2, x1
if y1 > y2:
y1, y2 = y2, y1
seq = []
for y in range(y1, y2+1):
for x in range(x1, x2+1):
seq.append(self.cellvalue(x, y))
return seq
def getcell(self, x, y):
return self.cells.get((x, y))
def setcell(self, x, y, cell):
assert x > 0 and y > 0
assert isinstance(cell, BaseCell)
self.cells[x, y] = cell
def clearcell(self, x, y):
try:
del self.cells[x, y]
except KeyError:
pass
def clearcells(self, x1, y1, x2, y2):
for xy in self.selectcells(x1, y1, x2, y2):
del self.cells[xy]
def clearrows(self, y1, y2):
self.clearcells(0, y1, sys.maxint, y2)
def clearcolumns(self, x1, x2):
self.clearcells(x1, 0, x2, sys.maxint)
def selectcells(self, x1, y1, x2, y2):
if x1 > x2:
x1, x2 = x2, x1
if y1 > y2:
y1, y2 = y2, y1
return [(x, y) for x, y in self.cells
if x1 <= x <= x2 and y1 <= y <= y2]
def movecells(self, x1, y1, x2, y2, dx, dy):
if dx == 0 and dy == 0:
return
if x1 > x2:
x1, x2 = x2, x1
if y1 > y2:
y1, y2 = y2, y1
assert x1+dx > 0 and y1+dy > 0
new = {}
for x, y in self.cells:
cell = self.cells[x, y]
if hasattr(cell, 'renumber'):
cell = cell.renumber(x1, y1, x2, y2, dx, dy)
if x1 <= x <= x2 and y1 <= y <= y2:
x += dx
y += dy
new[x, y] = cell
self.cells = new
def insertrows(self, y, n):
assert n > 0
self.movecells(0, y, sys.maxint, sys.maxint, 0, n)
def deleterows(self, y1, y2):
if y1 > y2:
y1, y2 = y2, y1
self.clearrows(y1, y2)
self.movecells(0, y2+1, sys.maxint, sys.maxint, 0, y1-y2-1)
def insertcolumns(self, x, n):
assert n > 0
self.movecells(x, 0, sys.maxint, sys.maxint, n, 0)
def deletecolumns(self, x1, x2):
if x1 > x2:
x1, x2 = x2, x1
self.clearcells(x1, x2)
self.movecells(x2+1, 0, sys.maxint, sys.maxint, x1-x2-1, 0)
def getsize(self):
maxx = maxy = 0
for x, y in self.cells:
maxx = max(maxx, x)
maxy = max(maxy, y)
return maxx, maxy
def reset(self):
for cell in self.cells.itervalues():
if hasattr(cell, 'reset'):
cell.reset()
def recalc(self):
self.reset()
for cell in self.cells.itervalues():
if hasattr(cell, 'recalc'):
cell.recalc(self.rexec)
def display(self):
maxx, maxy = self.getsize()
width, height = maxx+1, maxy+1
colwidth = [1] * width
full = {}
# Add column heading labels in row 0
for x in range(1, width):
full[x, 0] = text, alignment = colnum2name(x), RIGHT
colwidth[x] = max(colwidth[x], len(text))
# Add row labels in column 0
for y in range(1, height):
full[0, y] = text, alignment = str(y), RIGHT
colwidth[0] = max(colwidth[0], len(text))
# Add sheet cells in columns with x>0 and y>0
for (x, y), cell in self.cells.iteritems():
if x <= 0 or y <= 0:
continue
if hasattr(cell, 'recalc'):
cell.recalc(self.rexec)
if hasattr(cell, 'format'):
text, alignment = cell.format()
assert isinstance(text, str)
assert alignment in (LEFT, CENTER, RIGHT)
else:
text = str(cell)
if isinstance(cell, str):
alignment = LEFT
else:
alignment = RIGHT
full[x, y] = (text, alignment)
colwidth[x] = max(colwidth[x], len(text))
# Calculate the horizontal separator line (dashes and dots)
sep = ""
for x in range(width):
if sep:
sep += "+"
sep += "-"*colwidth[x]
# Now print The full grid
for y in range(height):
line = ""
for x in range(width):
text, alignment = full.get((x, y)) or ("", LEFT)
text = align2action[alignment](text, colwidth[x])
if line:
line += '|'
line += text
print line
if y == 0:
print sep
def xml(self):
out = ['<spreadsheet>']
for (x, y), cell in self.cells.iteritems():
if hasattr(cell, 'xml'):
cellxml = cell.xml()
else:
cellxml = '<value>%s</value>' % cgi.escape(cell)
out.append('<cell row="%s" col="%s">\n %s\n</cell>' %
(y, x, cellxml))
out.append('</spreadsheet>')
return '\n'.join(out)
def save(self, filename):
text = self.xml()
f = open(filename, "w")
f.write(text)
if text and not text.endswith('\n'):
f.write('\n')
f.close()
def load(self, filename):
f = open(filename, 'r')
SheetParser(self).parsefile(f)
f.close()
class SheetParser:
def __init__(self, sheet):
self.sheet = sheet
def parsefile(self, f):
parser = expat.ParserCreate()
parser.StartElementHandler = self.startelement
parser.EndElementHandler = self.endelement
parser.CharacterDataHandler = self.data
parser.ParseFile(f)
def startelement(self, tag, attrs):
method = getattr(self, 'start_'+tag, None)
if method:
for key, value in attrs.iteritems():
attrs[key] = str(value) # XXX Convert Unicode to 8-bit
method(attrs)
self.texts = []
def data(self, text):
text = str(text) # XXX Convert Unicode to 8-bit
self.texts.append(text)
def endelement(self, tag):
method = getattr(self, 'end_'+tag, None)
if method:
method("".join(self.texts))
def start_cell(self, attrs):
self.y = int(attrs.get("row"))
self.x = int(attrs.get("col"))
def start_value(self, attrs):
self.fmt = attrs.get('format')
self.alignment = xml2align.get(attrs.get('align'))
start_formula = start_value
def end_int(self, text):
try:
self.value = int(text)
except:
self.value = None
def end_long(self, text):
try:
self.value = long(text)
except:
self.value = None
def end_double(self, text):
try:
self.value = float(text)
except:
self.value = None
def end_complex(self, text):
try:
self.value = complex(text)
except:
self.value = None
def end_string(self, text):
try:
self.value = text
except:
self.value = None
def end_value(self, text):
if isinstance(self.value, BaseCell):
self.cell = self.value
elif isinstance(self.value, str):
self.cell = StringCell(self.value,
self.fmt or "%s",
self.alignment or LEFT)
else:
self.cell = NumericCell(self.value,
self.fmt or "%s",
self.alignment or RIGHT)
def end_formula(self, text):
self.cell = FormulaCell(text,
self.fmt or "%s",
self.alignment or RIGHT)
def end_cell(self, text):
self.sheet.setcell(self.x, self.y, self.cell)
class BaseCell:
__init__ = None # Must provide
"""Abstract base class for sheet cells.
Subclasses may but needn't provide the following APIs:
cell.reset() -- prepare for recalculation
cell.recalc(rexec) -> value -- recalculate formula
cell.format() -> (value, alignment) -- return formatted value
cell.xml() -> string -- return XML
"""
class NumericCell(BaseCell):
def __init__(self, value, fmt="%s", alignment=RIGHT):
assert isinstance(value, (int, long, float, complex))
assert alignment in (LEFT, CENTER, RIGHT)
self.value = value
self.fmt = fmt
self.alignment = alignment
def recalc(self, rexec):
return self.value
def format(self):
try:
text = self.fmt % self.value
except:
text = str(self.value)
return text, self.alignment
def xml(self):
method = getattr(self, '_xml_' + type(self.value).__name__)
return '<value align="%s" format="%s">%s</value>' % (
align2xml[self.alignment],
self.fmt,
method())
def _xml_int(self):
if -2**31 <= self.value < 2**31:
return '<int>%s</int>' % self.value
else:
return self._xml_long()
def _xml_long(self):
return '<long>%s</long>' % self.value
def _xml_float(self):
return '<double>%s</double>' % repr(self.value)
def _xml_complex(self):
return '<complex>%s</double>' % repr(self.value)
class StringCell(BaseCell):
def __init__(self, text, fmt="%s", alignment=LEFT):
assert isinstance(text, (str, unicode))
assert alignment in (LEFT, CENTER, RIGHT)
self.text = text
self.fmt = fmt
self.alignment = alignment
def recalc(self, rexec):
return self.text
def format(self):
return self.text, self.alignment
def xml(self):
s = '<value align="%s" format="%s"><string>%s</string></value>'
return s % (
align2xml[self.alignment],
self.fmt,
cgi.escape(self.text))
class FormulaCell(BaseCell):
def __init__(self, formula, fmt="%s", alignment=RIGHT):
assert alignment in (LEFT, CENTER, RIGHT)
self.formula = formula
self.translated = translate(self.formula)
self.fmt = fmt
self.alignment = alignment
self.reset()
def reset(self):
self.value = None
def recalc(self, rexec):
if self.value is None:
try:
# A hack to evaluate expressions using true division
rexec.r_exec("from __future__ import division\n" +
"__value__ = eval(%s)" % repr(self.translated))
self.value = rexec.r_eval("__value__")
except:
exc = sys.exc_info()[0]
if hasattr(exc, "__name__"):
self.value = exc.__name__
else:
self.value = str(exc)
return self.value
def format(self):
try:
text = self.fmt % self.value
except:
text = str(self.value)
return text, self.alignment
def xml(self):
return '<formula align="%s" format="%s">%s</formula>' % (
align2xml[self.alignment],
self.fmt,
self.formula)
def renumber(self, x1, y1, x2, y2, dx, dy):
out = []
for part in re.split('(\w+)', self.formula):
m = re.match('^([A-Z]+)([1-9][0-9]*)$', part)
if m is not None:
sx, sy = m.groups()
x = colname2num(sx)
y = int(sy)
if x1 <= x <= x2 and y1 <= y <= y2:
part = cellname(x+dx, y+dy)
out.append(part)
return FormulaCell("".join(out), self.fmt, self.alignment)
def translate(formula):
"""Translate a formula containing fancy cell names to valid Python code.
Examples:
B4 -> cell(2, 4)
B4:Z100 -> cells(2, 4, 26, 100)
"""
out = []
for part in re.split(r"(\w+(?::\w+)?)", formula):
m = re.match(r"^([A-Z]+)([1-9][0-9]*)(?::([A-Z]+)([1-9][0-9]*))?$", part)
if m is None:
out.append(part)
else:
x1, y1, x2, y2 = m.groups()
x1 = colname2num(x1)
if x2 is None:
s = "cell(%s, %s)" % (x1, y1)
else:
x2 = colname2num(x2)
s = "cells(%s, %s, %s, %s)" % (x1, y1, x2, y2)
out.append(s)
return "".join(out)
def cellname(x, y):
"Translate a cell coordinate to a fancy cell name (e.g. (1, 1)->'A1')."
assert x > 0 # Column 0 has an empty name, so can't use that
return colnum2name(x) + str(y)
def colname2num(s):
"Translate a column name to number (e.g. 'A'->1, 'Z'->26, 'AA'->27)."
s = s.upper()
n = 0
for c in s:
assert 'A' <= c <= 'Z'
n = n*26 + ord(c) - ord('A') + 1
return n
def colnum2name(n):
"Translate a column number to name (e.g. 1->'A', etc.)."
assert n > 0
s = ""
while n:
n, m = divmod(n-1, 26)
s = chr(m+ord('A')) + s
return s
import Tkinter as Tk
class SheetGUI:
"""Beginnings of a GUI for a spreadsheet.
TO DO:
- clear multiple cells
- Insert, clear, remove rows or columns
- Show new contents while typing
- Scroll bars
- Grow grid when window is grown
- Proper menus
- Undo, redo
- Cut, copy and paste
- Formatting and alignment
"""
def __init__(self, filename="sheet1.xml", rows=10, columns=5):
"""Constructor.
Load the sheet from the filename argument.
Set up the Tk widget tree.
"""
# Create and load the sheet
self.filename = filename
self.sheet = Sheet()
if os.path.isfile(filename):
self.sheet.load(filename)
# Calculate the needed grid size
maxx, maxy = self.sheet.getsize()
rows = max(rows, maxy)
columns = max(columns, maxx)
# Create the widgets
self.root = Tk.Tk()
self.root.wm_title("Spreadsheet: %s" % self.filename)
self.beacon = Tk.Label(self.root, text="A1",
font=('helvetica', 16, 'bold'))
self.entry = Tk.Entry(self.root)
self.savebutton = Tk.Button(self.root, text="Save",
command=self.save)
self.cellgrid = Tk.Frame(self.root)
# Configure the widget lay-out
self.cellgrid.pack(side="bottom", expand=1, fill="both")
self.beacon.pack(side="left")
self.savebutton.pack(side="right")
self.entry.pack(side="left", expand=1, fill="x")
# Bind some events
self.entry.bind("<Return>", self.return_event)
self.entry.bind("<Shift-Return>", self.shift_return_event)
self.entry.bind("<Tab>", self.tab_event)
self.entry.bind("<Shift-Tab>", self.shift_tab_event)
self.entry.bind("<Delete>", self.delete_event)
self.entry.bind("<Escape>", self.escape_event)
# Now create the cell grid
self.makegrid(rows, columns)
# Select the top-left cell
self.currentxy = None
self.cornerxy = None
self.setcurrent(1, 1)
# Copy the sheet cells to the GUI cells
self.sync()
def delete_event(self, event):
if self.cornerxy != self.currentxy and self.cornerxy is not None:
self.sheet.clearcells(*(self.currentxy + self.cornerxy))
else:
self.sheet.clearcell(*self.currentxy)
self.sync()
self.entry.delete(0, 'end')
return "break"
def escape_event(self, event):
x, y = self.currentxy
self.load_entry(x, y)
def load_entry(self, x, y):
cell = self.sheet.getcell(x, y)
if cell is None:
text = ""
elif isinstance(cell, FormulaCell):
text = '=' + cell.formula
else:
text, alignment = cell.format()
self.entry.delete(0, 'end')
self.entry.insert(0, text)
self.entry.selection_range(0, 'end')
def makegrid(self, rows, columns):
"""Helper to create the grid of GUI cells.
The edge (x==0 or y==0) is filled with labels; the rest is real cells.
"""
self.rows = rows
self.columns = columns
self.gridcells = {}
# Create the top left corner cell (which selects all)
cell = Tk.Label(self.cellgrid, relief='raised')
cell.grid_configure(column=0, row=0, sticky='NSWE')
cell.bind("<ButtonPress-1>", self.selectall)
# Create the top row of labels, and confiure the grid columns
for x in range(1, columns+1):
self.cellgrid.grid_columnconfigure(x, minsize=64)
cell = Tk.Label(self.cellgrid, text=colnum2name(x), relief='raised')
cell.grid_configure(column=x, row=0, sticky='WE')
self.gridcells[x, 0] = cell
cell.__x = x
cell.__y = 0
cell.bind("<ButtonPress-1>", self.selectcolumn)
cell.bind("<B1-Motion>", self.extendcolumn)
cell.bind("<ButtonRelease-1>", self.extendcolumn)
cell.bind("<Shift-Button-1>", self.extendcolumn)
# Create the leftmost column of labels
for y in range(1, rows+1):
cell = Tk.Label(self.cellgrid, text=str(y), relief='raised')
cell.grid_configure(column=0, row=y, sticky='WE')
self.gridcells[0, y] = cell
cell.__x = 0
cell.__y = y
cell.bind("<ButtonPress-1>", self.selectrow)
cell.bind("<B1-Motion>", self.extendrow)
cell.bind("<ButtonRelease-1>", self.extendrow)
cell.bind("<Shift-Button-1>", self.extendrow)
# Create the real cells
for x in range(1, columns+1):
for y in range(1, rows+1):
cell = Tk.Label(self.cellgrid, relief='sunken',
bg='white', fg='black')
cell.grid_configure(column=x, row=y, sticky='NSWE')
self.gridcells[x, y] = cell
cell.__x = x
cell.__y = y
# Bind mouse events
cell.bind("<ButtonPress-1>", self.press)
cell.bind("<B1-Motion>", self.motion)
cell.bind("<ButtonRelease-1>", self.release)
cell.bind("<Shift-Button-1>", self.release)
def selectall(self, event):
self.setcurrent(1, 1)
self.setcorner(sys.maxint, sys.maxint)
def selectcolumn(self, event):
x, y = self.whichxy(event)
self.setcurrent(x, 1)
self.setcorner(x, sys.maxint)
def extendcolumn(self, event):
x, y = self.whichxy(event)
if x > 0:
self.setcurrent(self.currentxy[0], 1)
self.setcorner(x, sys.maxint)
def selectrow(self, event):
x, y = self.whichxy(event)
self.setcurrent(1, y)
self.setcorner(sys.maxint, y)
def extendrow(self, event):
x, y = self.whichxy(event)
if y > 0:
self.setcurrent(1, self.currentxy[1])
self.setcorner(sys.maxint, y)
def press(self, event):
x, y = self.whichxy(event)
if x > 0 and y > 0:
self.setcurrent(x, y)
def motion(self, event):
x, y = self.whichxy(event)
if x > 0 and y > 0:
self.setcorner(x, y)
release = motion
def whichxy(self, event):
w = self.cellgrid.winfo_containing(event.x_root, event.y_root)
if w is not None and isinstance(w, Tk.Label):
try:
return w.__x, w.__y
except AttributeError:
pass
return 0, 0
def save(self):
self.sheet.save(self.filename)
def setcurrent(self, x, y):
"Make (x, y) the current cell."
if self.currentxy is not None:
self.change_cell()
self.clearfocus()
self.beacon['text'] = cellname(x, y)
self.load_entry(x, y)
self.entry.focus_set()
self.currentxy = x, y
self.cornerxy = None
gridcell = self.gridcells.get(self.currentxy)
if gridcell is not None:
gridcell['bg'] = 'yellow'
def setcorner(self, x, y):
if self.currentxy is None or self.currentxy == (x, y):
self.setcurrent(x, y)
return
self.clearfocus()
self.cornerxy = x, y
x1, y1 = self.currentxy
x2, y2 = self.cornerxy or self.currentxy
if x1 > x2:
x1, x2 = x2, x1
if y1 > y2:
y1, y2 = y2, y1
for (x, y), cell in self.gridcells.iteritems():
if x1 <= x <= x2 and y1 <= y <= y2:
cell['bg'] = 'lightBlue'
gridcell = self.gridcells.get(self.currentxy)
if gridcell is not None:
gridcell['bg'] = 'yellow'
self.setbeacon(x1, y1, x2, y2)
def setbeacon(self, x1, y1, x2, y2):
if x1 == y1 == 1 and x2 == y2 == sys.maxint:
name = ":"
elif (x1, x2) == (1, sys.maxint):
if y1 == y2:
name = "%d" % y1
else:
name = "%d:%d" % (y1, y2)
elif (y1, y2) == (1, sys.maxint):
if x1 == x2:
name = "%s" % colnum2name(x1)
else:
name = "%s:%s" % (colnum2name(x1), colnum2name(x2))
else:
name1 = cellname(*self.currentxy)
name2 = cellname(*self.cornerxy)
name = "%s:%s" % (name1, name2)
self.beacon['text'] = name
def clearfocus(self):
if self.currentxy is not None:
x1, y1 = self.currentxy
x2, y2 = self.cornerxy or self.currentxy
if x1 > x2:
x1, x2 = x2, x1
if y1 > y2:
y1, y2 = y2, y1
for (x, y), cell in self.gridcells.iteritems():
if x1 <= x <= x2 and y1 <= y <= y2:
cell['bg'] = 'white'
def return_event(self, event):
"Callback for the Return key."
self.change_cell()
x, y = self.currentxy
self.setcurrent(x, y+1)
return "break"
def shift_return_event(self, event):
"Callback for the Return key with Shift modifier."
self.change_cell()
x, y = self.currentxy
self.setcurrent(x, max(1, y-1))
return "break"
def tab_event(self, event):
"Callback for the Tab key."
self.change_cell()
x, y = self.currentxy
self.setcurrent(x+1, y)
return "break"
def shift_tab_event(self, event):
"Callback for the Tab key with Shift modifier."
self.change_cell()
x, y = self.currentxy
self.setcurrent(max(1, x-1), y)
return "break"
def change_cell(self):
"Set the current cell from the entry widget."
x, y = self.currentxy
text = self.entry.get()
cell = None
if text.startswith('='):
cell = FormulaCell(text[1:])
else:
for cls in int, long, float, complex:
try:
value = cls(text)
except:
continue
else:
cell = NumericCell(value)
break
if cell is None and text:
cell = StringCell(text)
if cell is None:
self.sheet.clearcell(x, y)
else:
self.sheet.setcell(x, y, cell)
self.sync()
def sync(self):
"Fill the GUI cells from the sheet cells."
self.sheet.recalc()
for (x, y), gridcell in self.gridcells.iteritems():
if x == 0 or y == 0:
continue
cell = self.sheet.getcell(x, y)
if cell is None:
gridcell['text'] = ""
else:
if hasattr(cell, 'format'):
text, alignment = cell.format()
else:
text, alignment = str(cell), LEFT
gridcell['text'] = text
gridcell['anchor'] = align2anchor[alignment]
def test_basic():
"Basic non-gui self-test."
import os
a = Sheet()
for x in range(1, 11):
for y in range(1, 11):
if x == 1:
cell = NumericCell(y)
elif y == 1:
cell = NumericCell(x)
else:
c1 = cellname(x, 1)
c2 = cellname(1, y)
formula = "%s*%s" % (c1, c2)
cell = FormulaCell(formula)
a.setcell(x, y, cell)
## if os.path.isfile("sheet1.xml"):
## print "Loading from sheet1.xml"
## a.load("sheet1.xml")
a.display()
a.save("sheet1.xml")
def test_gui():
"GUI test."
if sys.argv[1:]:
filename = sys.argv[1]
else:
filename = "sheet1.xml"
g = SheetGUI(filename)
g.root.mainloop()
if __name__ == '__main__':
#test_basic()
test_gui()

128
Demo/tkinter/guido/svkill.py Executable file
View File

@@ -0,0 +1,128 @@
#! /usr/bin/env python
# Tkinter interface to SYSV `ps' and `kill' commands.
from Tkinter import *
if TkVersion < 4.0:
raise ImportError, "This version of svkill requires Tk 4.0 or later"
from string import splitfields
from string import split
import commands
import os
user = os.environ['LOGNAME']
class BarButton(Menubutton):
def __init__(self, master=None, **cnf):
apply(Menubutton.__init__, (self, master), cnf)
self.pack(side=LEFT)
self.menu = Menu(self, name='menu')
self['menu'] = self.menu
class Kill(Frame):
# List of (name, option, pid_column)
view_list = [
('Default', ''),
('Every (-e)', '-e'),
('Non process group leaders (-d)', '-d'),
('Non leaders with tty (-a)', '-a'),
('For this user (-u %s)' % user, '-u %s' % user),
]
format_list = [
('Default', '', 0),
('Long (-l)', '-l', 3),
('Full (-f)', '-f', 1),
('Full Long (-f -l)', '-l -f', 3),
('Session and group ID (-j)', '-j', 0),
('Scheduler properties (-c)', '-c', 0),
]
def kill(self, selected):
c = self.format_list[self.format.get()][2]
pid = split(selected)[c]
os.system('kill -9 ' + pid)
self.do_update()
def do_update(self):
format = self.format_list[self.format.get()][1]
view = self.view_list[self.view.get()][1]
s = commands.getoutput('ps %s %s' % (view, format))
list = splitfields(s, '\n')
self.header.set(list[0] + ' ')
del list[0]
self.frame.list.delete(0, AtEnd())
for line in list:
self.frame.list.insert(0, line)
def do_motion(self, e):
e.widget.select_clear('0', 'end')
e.widget.select_set(e.widget.nearest(e.y))
def do_leave(self, e):
e.widget.select_clear('0', 'end')
def do_1(self, e):
self.kill(e.widget.get(e.widget.nearest(e.y)))
def __init__(self, master=None, **cnf):
apply(Frame.__init__, (self, master), cnf)
self.pack(expand=1, fill=BOTH)
self.bar = Frame(self, name='bar', relief=RAISED,
borderwidth=2)
self.bar.pack(fill=X)
self.bar.file = BarButton(self.bar, text='File')
self.bar.file.menu.add_command(
label='Quit', command=self.quit)
self.bar.view = BarButton(self.bar, text='View')
self.bar.format = BarButton(self.bar, text='Format')
self.view = IntVar(self)
self.view.set(0)
self.format = IntVar(self)
self.format.set(0)
for num in range(len(self.view_list)):
label, option = self.view_list[num]
self.bar.view.menu.add_radiobutton(
label=label,
command=self.do_update,
variable=self.view,
value=num)
for num in range(len(self.format_list)):
label, option, col = self.format_list[num]
self.bar.format.menu.add_radiobutton(
label=label,
command=self.do_update,
variable=self.format,
value=num)
self.bar.tk_menuBar(self.bar.file,
self.bar.view,
self.bar.format)
self.frame = Frame(self, relief=RAISED, borderwidth=2)
self.frame.pack(expand=1, fill=BOTH)
self.header = StringVar(self)
self.frame.label = Label(
self.frame, relief=FLAT, anchor=NW, borderwidth=0,
font='*-Courier-Bold-R-Normal-*-120-*',
textvariable=self.header)
self.frame.label.pack(fill=Y, anchor=W)
self.frame.vscroll = Scrollbar(self.frame, orient=VERTICAL)
self.frame.list = Listbox(
self.frame,
relief=SUNKEN,
font='*-Courier-Medium-R-Normal-*-120-*',
width=40, height=10,
selectbackground='#eed5b7',
selectborderwidth=0,
selectmode=BROWSE,
yscroll=self.frame.vscroll.set)
self.frame.vscroll['command'] = self.frame.list.yview
self.frame.vscroll.pack(side=RIGHT, fill=Y)
self.frame.list.pack(expand=1, fill=BOTH)
self.update = Button(self, text='Update',
command=self.do_update)
self.update.pack(fill=X)
self.frame.list.bind('<Motion>', self.do_motion)
self.frame.list.bind('<Leave>', self.do_leave)
self.frame.list.bind('<1>', self.do_1)
self.do_update()
if __name__ == '__main__':
kill = Kill(None, borderwidth=5)
kill.winfo_toplevel().title('Tkinter Process Killer (SYSV)')
kill.winfo_toplevel().minsize(1, 1)
kill.mainloop()

View File

@@ -0,0 +1,55 @@
# Show how to do switchable panels.
from Tkinter import *
class App:
def __init__(self, top=None, master=None):
if top is None:
if master is None:
top = Tk()
else:
top = Toplevel(master)
self.top = top
self.buttonframe = Frame(top)
self.buttonframe.pack()
self.panelframe = Frame(top, borderwidth=2, relief=GROOVE)
self.panelframe.pack(expand=1, fill=BOTH)
self.panels = {}
self.curpanel = None
def addpanel(self, name, klass):
button = Button(self.buttonframe, text=name,
command=lambda self=self, name=name: self.show(name))
button.pack(side=LEFT)
frame = Frame(self.panelframe)
instance = klass(frame)
self.panels[name] = (button, frame, instance)
if self.curpanel is None:
self.show(name)
def show(self, name):
(button, frame, instance) = self.panels[name]
if self.curpanel:
self.curpanel.pack_forget()
self.curpanel = frame
frame.pack(expand=1, fill="both")
class LabelPanel:
def __init__(self, frame):
self.label = Label(frame, text="Hello world")
self.label.pack()
class ButtonPanel:
def __init__(self, frame):
self.button = Button(frame, text="Press me")
self.button.pack()
def main():
app = App()
app.addpanel("label", LabelPanel)
app.addpanel("button", ButtonPanel)
app.top.mainloop()
if __name__ == '__main__':
main()

267
Demo/tkinter/guido/tkman.py Executable file
View File

@@ -0,0 +1,267 @@
#! /usr/bin/env python
# Tk man page browser -- currently only shows the Tcl/Tk man pages
import sys
import os
import string
import re
from Tkinter import *
from ManPage import ManPage
MANNDIRLIST = ['/depot/sundry/man/mann','/usr/local/man/mann']
MAN3DIRLIST = ['/depot/sundry/man/man3','/usr/local/man/man3']
foundmanndir = 0
for dir in MANNDIRLIST:
if os.path.exists(dir):
MANNDIR = dir
foundmanndir = 1
foundman3dir = 0
for dir in MAN3DIRLIST:
if os.path.exists(dir):
MAN3DIR = dir
foundman3dir = 1
if not foundmanndir or not foundman3dir:
sys.stderr.write('\n')
if not foundmanndir:
msg = """\
Failed to find mann directory.
Please add the correct entry to the MANNDIRLIST
at the top of %s script.""" % \
sys.argv[0]
sys.stderr.write("%s\n\n" % msg)
if not foundman3dir:
msg = """\
Failed to find man3 directory.
Please add the correct entry to the MAN3DIRLIST
at the top of %s script.""" % \
sys.argv[0]
sys.stderr.write("%s\n\n" % msg)
sys.exit(1)
del foundmanndir
del foundman3dir
def listmanpages(mandir):
files = os.listdir(mandir)
names = []
for file in files:
if file[-2:-1] == '.' and (file[-1] in 'ln123456789'):
names.append(file[:-2])
names.sort()
return names
class SelectionBox:
def __init__(self, master=None):
self.choices = []
self.frame = Frame(master, name="frame")
self.frame.pack(expand=1, fill=BOTH)
self.master = self.frame.master
self.subframe = Frame(self.frame, name="subframe")
self.subframe.pack(expand=0, fill=BOTH)
self.leftsubframe = Frame(self.subframe, name='leftsubframe')
self.leftsubframe.pack(side=LEFT, expand=1, fill=BOTH)
self.rightsubframe = Frame(self.subframe, name='rightsubframe')
self.rightsubframe.pack(side=RIGHT, expand=1, fill=BOTH)
self.chaptervar = StringVar(master)
self.chapter = Menubutton(self.rightsubframe, name='chapter',
text='Directory', relief=RAISED,
borderwidth=2)
self.chapter.pack(side=TOP)
self.chaptermenu = Menu(self.chapter, name='chaptermenu')
self.chaptermenu.add_radiobutton(label='C functions',
value=MAN3DIR,
variable=self.chaptervar,
command=self.newchapter)
self.chaptermenu.add_radiobutton(label='Tcl/Tk functions',
value=MANNDIR,
variable=self.chaptervar,
command=self.newchapter)
self.chapter['menu'] = self.chaptermenu
self.listbox = Listbox(self.rightsubframe, name='listbox',
relief=SUNKEN, borderwidth=2,
width=20, height=5)
self.listbox.pack(expand=1, fill=BOTH)
self.l1 = Button(self.leftsubframe, name='l1',
text='Display manual page named:',
command=self.entry_cb)
self.l1.pack(side=TOP)
self.entry = Entry(self.leftsubframe, name='entry',
relief=SUNKEN, borderwidth=2,
width=20)
self.entry.pack(expand=0, fill=X)
self.l2frame = Frame(self.leftsubframe, name='l2frame')
self.l2frame.pack(expand=0, fill=NONE)
self.l2 = Button(self.l2frame, name='l2',
text='Search regexp:',
command=self.search_cb)
self.l2.pack(side=LEFT)
self.casevar = BooleanVar()
self.casesense = Checkbutton(self.l2frame, name='casesense',
text='Case sensitive',
variable=self.casevar,
relief=FLAT)
self.casesense.pack(side=LEFT)
self.search = Entry(self.leftsubframe, name='search',
relief=SUNKEN, borderwidth=2,
width=20)
self.search.pack(expand=0, fill=X)
self.title = Label(self.leftsubframe, name='title',
text='(none)')
self.title.pack(side=BOTTOM)
self.text = ManPage(self.frame, name='text',
relief=SUNKEN, borderwidth=2,
wrap=NONE, width=72,
selectbackground='pink')
self.text.pack(expand=1, fill=BOTH)
self.entry.bind('<Return>', self.entry_cb)
self.search.bind('<Return>', self.search_cb)
self.listbox.bind('<Double-1>', self.listbox_cb)
self.entry.bind('<Tab>', self.entry_tab)
self.search.bind('<Tab>', self.search_tab)
self.text.bind('<Tab>', self.text_tab)
self.entry.focus_set()
self.chaptervar.set(MANNDIR)
self.newchapter()
def newchapter(self):
mandir = self.chaptervar.get()
self.choices = []
self.addlist(listmanpages(mandir))
def addchoice(self, choice):
if choice not in self.choices:
self.choices.append(choice)
self.choices.sort()
self.update()
def addlist(self, list):
self.choices[len(self.choices):] = list
self.choices.sort()
self.update()
def entry_cb(self, *e):
self.update()
def listbox_cb(self, e):
selection = self.listbox.curselection()
if selection and len(selection) == 1:
name = self.listbox.get(selection[0])
self.show_page(name)
def search_cb(self, *e):
self.search_string(self.search.get())
def entry_tab(self, e):
self.search.focus_set()
def search_tab(self, e):
self.entry.focus_set()
def text_tab(self, e):
self.entry.focus_set()
def updatelist(self):
key = self.entry.get()
ok = filter(lambda name, key=key, n=len(key): name[:n]==key,
self.choices)
if not ok:
self.frame.bell()
self.listbox.delete(0, AtEnd())
exactmatch = 0
for item in ok:
if item == key: exactmatch = 1
self.listbox.insert(AtEnd(), item)
if exactmatch:
return key
n = self.listbox.size()
if n == 1:
return self.listbox.get(0)
# Else return None, meaning not a unique selection
def update(self):
name = self.updatelist()
if name:
self.show_page(name)
self.entry.delete(0, AtEnd())
self.updatelist()
def show_page(self, name):
file = '%s/%s.?' % (self.chaptervar.get(), name)
fp = os.popen('nroff -man %s | ul -i' % file, 'r')
self.text.kill()
self.title['text'] = name
self.text.parsefile(fp)
def search_string(self, search):
if not search:
self.frame.bell()
print 'Empty search string'
return
if not self.casevar.get():
map = re.IGNORECASE
else:
map = None
try:
if map:
prog = re.compile(search, map)
else:
prog = re.compile(search)
except re.error, msg:
self.frame.bell()
print 'Regex error:', msg
return
here = self.text.index(AtInsert())
lineno = string.atoi(here[:string.find(here, '.')])
end = self.text.index(AtEnd())
endlineno = string.atoi(end[:string.find(end, '.')])
wraplineno = lineno
found = 0
while 1:
lineno = lineno + 1
if lineno > endlineno:
if wraplineno <= 0:
break
endlineno = wraplineno
lineno = 0
wraplineno = 0
line = self.text.get('%d.0 linestart' % lineno,
'%d.0 lineend' % lineno)
i = prog.search(line)
if i >= 0:
found = 1
n = max(1, len(prog.group(0)))
try:
self.text.tag_remove('sel',
AtSelFirst(),
AtSelLast())
except TclError:
pass
self.text.tag_add('sel',
'%d.%d' % (lineno, i),
'%d.%d' % (lineno, i+n))
self.text.mark_set(AtInsert(),
'%d.%d' % (lineno, i))
self.text.yview_pickplace(AtInsert())
break
if not found:
self.frame.bell()
def main():
root = Tk()
sb = SelectionBox(root)
if sys.argv[1:]:
sb.show_page(sys.argv[1])
root.minsize(1, 1)
root.mainloop()
main()

View File

@@ -0,0 +1,27 @@
# This is about all it requires to write a wish shell in Python!
import _tkinter
import os
tk = _tkinter.create(os.environ['DISPLAY'], 'wish', 'Tk', 1)
tk.call('update')
cmd = ''
while 1:
if cmd: prompt = ''
else: prompt = '% '
try:
line = raw_input(prompt)
except EOFError:
break
cmd = cmd + (line + '\n')
if tk.getboolean(tk.call('info', 'complete', cmd)):
tk.record(line)
try:
result = tk.call('eval', cmd)
except _tkinter.TclError, msg:
print 'TclError:', msg
else:
if result: print result
cmd = ''

View File

@@ -0,0 +1,27 @@
from Tkinter import *
# note that there is no explicit call to start Tk.
# Tkinter is smart enough to start the system if it's not already going.
class Test(Frame):
def printit(self):
print "hi"
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=LEFT, fill=BOTH)
# a hello button
self.hi_there = Button(self, text='Hello',
command=self.printit)
self.hi_there.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

30
Demo/tkinter/matt/README Normal file
View File

@@ -0,0 +1,30 @@
This directory contains some ad-hoc examples of Tkinter widget
creation. The files named
*-simple.py
are the ones to start with if you're looking for a bare-bones usage of
a widget. The other files are meant to show common usage patters that
are a tad more involved.
If you have a suggestion for an example program, please send mail to
conway@virginia.edu
and I'll include it.
matt
TODO
-------
The X selection
Dialog Boxes
More canvas examples
Message widgets
Text Editors
Scrollbars
Listboxes

View File

@@ -0,0 +1,35 @@
from Tkinter import *
# This program shows how to use the "after" function to make animation.
class Test(Frame):
def printit(self):
print "hi"
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=LEFT, fill=BOTH)
self.draw = Canvas(self, width="5i", height="5i")
# all of these work..
self.draw.create_rectangle(0, 0, 10, 10, tags="thing", fill="blue")
self.draw.pack(side=LEFT)
def moveThing(self, *args):
# move 1/10 of an inch every 1/10 sec (1" per second, smoothly)
self.draw.move("thing", "0.01i", "0.01i")
self.after(10, self.moveThing)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
self.after(10, self.moveThing)
test = Test()
test.mainloop()

View File

@@ -0,0 +1,44 @@
from Tkinter import *
# this is the same as simple-demo-1.py, but uses
# subclassing.
# note that there is no explicit call to start Tk.
# Tkinter is smart enough to start the system if it's not already going.
class Test(Frame):
def printit(self):
print "hi"
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=BOTTOM, fill=BOTH)
self.draw = Canvas(self, width="5i", height="5i")
self.speed = Scale(self, orient=HORIZONTAL, from_=-100, to=100)
self.speed.pack(side=BOTTOM, fill=X)
# all of these work..
self.draw.create_rectangle(0, 0, 10, 10, tags="thing", fill="blue")
self.draw.pack(side=LEFT)
def moveThing(self, *args):
velocity = self.speed.get()
str = float(velocity) / 1000.0
str = "%ri" % (str,)
self.draw.move("thing", str, str)
self.after(10, self.moveThing)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
self.after(10, self.moveThing)
test = Test()
test.mainloop()

View File

@@ -0,0 +1,44 @@
from Tkinter import *
import string
# This program shows how to use a simple type-in box
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.entrythingy = Entry()
self.entrythingy.pack()
# and here we get a callback when the user hits return. we could
# make the key that triggers the callback anything we wanted to.
# other typical options might be <Key-Tab> or <Key> (for anything)
self.entrythingy.bind('<Key-Return>', self.print_contents)
# Note that here is where we bind a completely different callback to
# the same event. We pass "+" here to indicate that we wish to ADD
# this callback to the list associated with this event type.
# Not specifying "+" would simply override whatever callback was
# defined on this event.
self.entrythingy.bind('<Key-Return>', self.print_something_else, "+")
def print_contents(self, event):
print "hi. contents of entry is now ---->", self.entrythingy.get()
def print_something_else(self, event):
print "hi. Now doing something completely different"
root = App()
root.master.title("Foo")
root.mainloop()
# secret tip for experts: if you pass *any* non-false value as
# the third parameter to bind(), Tkinter.py will accumulate
# callbacks instead of overwriting. I use "+" here because that's
# the Tk notation for getting this sort of behavior. The perfect GUI
# interface would use a less obscure notation.

View File

@@ -0,0 +1,28 @@
from Tkinter import *
# this program creates a canvas and puts a single polygon on the canvas
class Test(Frame):
def printit(self):
print "hi"
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=BOTTOM, fill=BOTH)
self.draw = Canvas(self, width="5i", height="5i")
# see the other demos for other ways of specifying coords for a polygon
self.draw.create_rectangle(0, 0, "3i", "3i", fill="black")
self.draw.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,61 @@
from Tkinter import *
# this is the same as simple-demo-1.py, but uses
# subclassing.
# note that there is no explicit call to start Tk.
# Tkinter is smart enough to start the system if it's not already going.
class Test(Frame):
def printit(self):
print "hi"
def createWidgets(self):
self.QUIT = Button(self, text='QUIT',
background='red',
foreground='white',
height=3,
command=self.quit)
self.QUIT.pack(side=BOTTOM, fill=BOTH)
self.canvasObject = Canvas(self, width="5i", height="5i")
self.canvasObject.pack(side=LEFT)
def mouseDown(self, event):
# canvas x and y take the screen coords from the event and translate
# them into the coordinate system of the canvas object
self.startx = self.canvasObject.canvasx(event.x, self.griddingSize)
self.starty = self.canvasObject.canvasy(event.y, self.griddingSize)
def mouseMotion(self, event):
# canvas x and y take the screen coords from the event and translate
# them into the coordinate system of the canvas object
x = self.canvasObject.canvasx(event.x, self.griddingSize)
y = self.canvasObject.canvasy(event.y, self.griddingSize)
if (self.startx != event.x) and (self.starty != event.y) :
self.canvasObject.delete(self.rubberbandBox)
self.rubberbandBox = self.canvasObject.create_rectangle(
self.startx, self.starty, x, y)
# this flushes the output, making sure that
# the rectangle makes it to the screen
# before the next event is handled
self.update_idletasks()
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
# this is a "tagOrId" for the rectangle we draw on the canvas
self.rubberbandBox = None
# this is the size of the gridding squares
self.griddingSize = 50
Widget.bind(self.canvasObject, "<Button-1>", self.mouseDown)
Widget.bind(self.canvasObject, "<Button1-Motion>", self.mouseMotion)
test = Test()
test.mainloop()

View File

@@ -0,0 +1,62 @@
from Tkinter import *
# this file demonstrates a more sophisticated movement --
# move dots or create new ones if you click outside the dots
class Test(Frame):
###################################################################
###### Event callbacks for THE CANVAS (not the stuff drawn on it)
###################################################################
def mouseDown(self, event):
# see if we're inside a dot. If we are, it
# gets tagged as CURRENT for free by tk.
if not event.widget.find_withtag(CURRENT):
# there is no dot here, so we can make one,
# and bind some interesting behavior to it.
# ------
# create a dot, and mark it as CURRENT
fred = self.draw.create_oval(
event.x - 10, event.y -10, event.x +10, event.y + 10,
fill="green", tags=CURRENT)
self.draw.tag_bind(fred, "<Any-Enter>", self.mouseEnter)
self.draw.tag_bind(fred, "<Any-Leave>", self.mouseLeave)
self.lastx = event.x
self.lasty = event.y
def mouseMove(self, event):
self.draw.move(CURRENT, event.x - self.lastx, event.y - self.lasty)
self.lastx = event.x
self.lasty = event.y
###################################################################
###### Event callbacks for canvas ITEMS (stuff drawn on the canvas)
###################################################################
def mouseEnter(self, event):
# the CURRENT tag is applied to the object the cursor is over.
# this happens automatically.
self.draw.itemconfig(CURRENT, fill="red")
def mouseLeave(self, event):
# the CURRENT tag is applied to the object the cursor is over.
# this happens automatically.
self.draw.itemconfig(CURRENT, fill="blue")
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=LEFT, fill=BOTH)
self.draw = Canvas(self, width="5i", height="5i")
self.draw.pack(side=LEFT)
Widget.bind(self.draw, "<1>", self.mouseDown)
Widget.bind(self.draw, "<B1-Motion>", self.mouseMove)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,55 @@
from Tkinter import *
# this file demonstrates the movement of a single canvas item under mouse control
class Test(Frame):
###################################################################
###### Event callbacks for THE CANVAS (not the stuff drawn on it)
###################################################################
def mouseDown(self, event):
# remember where the mouse went down
self.lastx = event.x
self.lasty = event.y
def mouseMove(self, event):
# whatever the mouse is over gets tagged as CURRENT for free by tk.
self.draw.move(CURRENT, event.x - self.lastx, event.y - self.lasty)
self.lastx = event.x
self.lasty = event.y
###################################################################
###### Event callbacks for canvas ITEMS (stuff drawn on the canvas)
###################################################################
def mouseEnter(self, event):
# the CURRENT tag is applied to the object the cursor is over.
# this happens automatically.
self.draw.itemconfig(CURRENT, fill="red")
def mouseLeave(self, event):
# the CURRENT tag is applied to the object the cursor is over.
# this happens automatically.
self.draw.itemconfig(CURRENT, fill="blue")
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=LEFT, fill=BOTH)
self.draw = Canvas(self, width="5i", height="5i")
self.draw.pack(side=LEFT)
fred = self.draw.create_oval(0, 0, 20, 20,
fill="green", tags="selected")
self.draw.tag_bind(fred, "<Any-Enter>", self.mouseEnter)
self.draw.tag_bind(fred, "<Any-Leave>", self.mouseLeave)
Widget.bind(self.draw, "<1>", self.mouseDown)
Widget.bind(self.draw, "<B1-Motion>", self.mouseMove)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,78 @@
from Tkinter import *
# allows moving dots with multiple selection.
SELECTED_COLOR = "red"
UNSELECTED_COLOR = "blue"
class Test(Frame):
###################################################################
###### Event callbacks for THE CANVAS (not the stuff drawn on it)
###################################################################
def mouseDown(self, event):
# see if we're inside a dot. If we are, it
# gets tagged as CURRENT for free by tk.
if not event.widget.find_withtag(CURRENT):
# we clicked outside of all dots on the canvas. unselect all.
# re-color everything back to an unselected color
self.draw.itemconfig("selected", fill=UNSELECTED_COLOR)
# unselect everything
self.draw.dtag("selected")
else:
# mark as "selected" the thing the cursor is under
self.draw.addtag("selected", "withtag", CURRENT)
# color it as selected
self.draw.itemconfig("selected", fill=SELECTED_COLOR)
self.lastx = event.x
self.lasty = event.y
def mouseMove(self, event):
self.draw.move("selected", event.x - self.lastx, event.y - self.lasty)
self.lastx = event.x
self.lasty = event.y
def makeNewDot(self):
# create a dot, and mark it as current
fred = self.draw.create_oval(0, 0, 20, 20,
fill=SELECTED_COLOR, tags=CURRENT)
# and make it selected
self.draw.addtag("selected", "withtag", CURRENT)
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
################
# make the canvas and bind some behavior to it
################
self.draw = Canvas(self, width="5i", height="5i")
Widget.bind(self.draw, "<1>", self.mouseDown)
Widget.bind(self.draw, "<B1-Motion>", self.mouseMove)
# and other things.....
self.button = Button(self, text="make a new dot", foreground="blue",
command=self.makeNewDot)
message = ("%s dots are selected and can be dragged.\n"
"%s are not selected.\n"
"Click in a dot to select it.\n"
"Click on empty space to deselect all dots."
) % (SELECTED_COLOR, UNSELECTED_COLOR)
self.label = Message(self, width="5i", text=message)
self.QUIT.pack(side=BOTTOM, fill=BOTH)
self.label.pack(side=BOTTOM, fill=X, expand=1)
self.button.pack(side=BOTTOM, fill=X)
self.draw.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,49 @@
from Tkinter import *
class Test(Frame):
def printit(self):
print "hi"
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=BOTTOM, fill=BOTH)
self.drawing = Canvas(self, width="5i", height="5i")
# make a shape
pgon = self.drawing.create_polygon(
10, 10, 110, 10, 110, 110, 10 , 110,
fill="red", tags=("weee", "foo", "groo"))
# this is how you query an object for its attributes
# config options FOR CANVAS ITEMS always come back in tuples of length 5.
# 0 attribute name
# 1 BLANK
# 2 BLANK
# 3 default value
# 4 current value
# the blank spots are for consistency with the config command that
# is used for widgets. (remember, this is for ITEMS drawn
# on a canvas widget, not widgets)
option_value = self.drawing.itemconfig(pgon, "stipple")
print "pgon's current stipple value is -->", option_value[4], "<--"
option_value = self.drawing.itemconfig(pgon, "fill")
print "pgon's current fill value is -->", option_value[4], "<--"
print " when he is usually colored -->", option_value[3], "<--"
## here we print out all the tags associated with this object
option_value = self.drawing.itemconfig(pgon, "tags")
print "pgon's tags are", option_value[4]
self.drawing.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,36 @@
from Tkinter import *
# this file demonstrates the creation of widgets as part of a canvas object
class Test(Frame):
def printhi(self):
print "hi"
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=BOTTOM, fill=BOTH)
self.draw = Canvas(self, width="5i", height="5i")
self.button = Button(self, text="this is a button",
command=self.printhi)
# note here the coords are given in pixels (form the
# upper right and corner of the window, as usual for X)
# but might just have well been given in inches or points or
# whatever...use the "anchor" option to control what point of the
# widget (in this case the button) gets mapped to the given x, y.
# you can specify corners, edges, center, etc...
self.draw.create_window(300, 300, window=self.button)
self.draw.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,60 @@
from Tkinter import *
# This example program creates a scrolling canvas, and demonstrates
# how to tie scrollbars and canvases together. The mechanism
# is analogus for listboxes and other widgets with
# "xscroll" and "yscroll" configuration options.
class Test(Frame):
def printit(self):
print "hi"
def createWidgets(self):
self.question = Label(self, text="Can Find The BLUE Square??????")
self.question.pack()
self.QUIT = Button(self, text='QUIT', background='red',
height=3, command=self.quit)
self.QUIT.pack(side=BOTTOM, fill=BOTH)
spacer = Frame(self, height="0.25i")
spacer.pack(side=BOTTOM)
# notice that the scroll region (20" x 20") is larger than
# displayed size of the widget (5" x 5")
self.draw = Canvas(self, width="5i", height="5i",
background="white",
scrollregion=(0, 0, "20i", "20i"))
self.draw.scrollX = Scrollbar(self, orient=HORIZONTAL)
self.draw.scrollY = Scrollbar(self, orient=VERTICAL)
# now tie the three together. This is standard boilerplate text
self.draw['xscrollcommand'] = self.draw.scrollX.set
self.draw['yscrollcommand'] = self.draw.scrollY.set
self.draw.scrollX['command'] = self.draw.xview
self.draw.scrollY['command'] = self.draw.yview
# draw something. Note that the first square
# is visible, but you need to scroll to see the second one.
self.draw.create_rectangle(0, 0, "3.5i", "3.5i", fill="black")
self.draw.create_rectangle("10i", "10i", "13.5i", "13.5i", fill="blue")
# pack 'em up
self.draw.scrollX.pack(side=BOTTOM, fill=X)
self.draw.scrollY.pack(side=RIGHT, fill=Y)
self.draw.pack(side=LEFT)
def scrollCanvasX(self, *args):
print "scrolling", args
print self.draw.scrollX.get()
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,64 @@
from Tkinter import *
from Dialog import Dialog
# this shows how to create a new window with a button in it
# that can create new windows
class Test(Frame):
def printit(self):
print "hi"
def makeWindow(self):
"""Create a top-level dialog with some buttons.
This uses the Dialog class, which is a wrapper around the Tcl/Tk
tk_dialog script. The function returns 0 if the user clicks 'yes'
or 1 if the user clicks 'no'.
"""
# the parameters to this call are as follows:
d = Dialog(
self, ## name of a toplevel window
title="fred the dialog box",## title on the window
text="click on a choice", ## message to appear in window
bitmap="info", ## bitmap (if any) to appear;
## if none, use ""
# legal values here are:
# string what it looks like
# ----------------------------------------------
# error a circle with a slash through it
# grey25 grey square
# grey50 darker grey square
# hourglass use for "wait.."
# info a large, lower case "i"
# questhead a human head with a "?" in it
# question a large "?"
# warning a large "!"
# @fname X bitmap where fname is the path to the file
#
default=0, # the index of the default button choice.
# hitting return selects this
strings=("yes", "no"))
# values of the 'strings' key are the labels for the
# buttons that appear left to right in the dialog box
return d.num
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=LEFT, fill=BOTH)
# a hello button
self.hi_there = Button(self, text='Make a New Window',
command=self.makeWindow)
self.hi_there.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.windownum = 0
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,24 @@
from Tkinter import *
import string
# This program shows how to use a simple type-in box
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.entrythingy = Entry()
self.entrythingy.pack()
# and here we get a callback when the user hits return. we could
# make the key that triggers the callback anything we wanted to.
# other typical options might be <Key-Tab> or <Key> (for anything)
self.entrythingy.bind('<Key-Return>', self.print_contents)
def print_contents(self, event):
print "hi. contents of entry is now ---->", self.entrythingy.get()
root = App()
root.master.title("Foo")
root.mainloop()

View File

@@ -0,0 +1,46 @@
from Tkinter import *
import string
# This program shows how to make a typein box shadow a program variable.
class App(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
self.entrythingy = Entry(self)
self.entrythingy.pack()
self.button = Button(self, text="Uppercase The Entry",
command=self.upper)
self.button.pack()
# here we have the text in the entry widget tied to a variable.
# changes in the variable are echoed in the widget and vice versa.
# Very handy.
# there are other Variable types. See Tkinter.py for all
# the other variable types that can be shadowed
self.contents = StringVar()
self.contents.set("this is a variable")
self.entrythingy.config(textvariable=self.contents)
# and here we get a callback when the user hits return. we could
# make the key that triggers the callback anything we wanted to.
# other typical options might be <Key-Tab> or <Key> (for anything)
self.entrythingy.bind('<Key-Return>', self.print_contents)
def upper(self):
# notice here, we don't actually refer to the entry box.
# we just operate on the string variable and we
# because it's being looked at by the entry widget, changing
# the variable changes the entry widget display automatically.
# the strange get/set operators are clunky, true...
str = string.upper(self.contents.get())
self.contents.set(str)
def print_contents(self, event):
print "hi. contents of entry is now ---->", self.contents.get()
root = App()
root.master.title("Foo")
root.mainloop()

View File

@@ -0,0 +1,42 @@
from Tkinter import *
# This file shows how to trap the killing of a window
# when the user uses window manager menus (typ. upper left hand corner
# menu in the decoration border).
### ******* this isn't really called -- read the comments
def my_delete_callback():
print "whoops -- tried to delete me!"
class Test(Frame):
def deathHandler(self, event):
print self, "is now getting nuked. performing some save here...."
def createWidgets(self):
# a hello button
self.hi_there = Button(self, text='Hello')
self.hi_there.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
###
### PREVENT WM kills from happening
###
# the docs would have you do this:
# self.master.protocol("WM_DELETE_WINDOW", my_delete_callback)
# unfortunately, some window managers will not send this request to a window.
# the "protocol" function seems incapable of trapping these "aggressive" window kills.
# this line of code catches everything, tho. The window is deleted, but you have a chance
# of cleaning up first.
self.bind_all("<Destroy>", self.deathHandler)
test = Test()
test.mainloop()

View File

@@ -0,0 +1,244 @@
from Tkinter import *
# some vocabulary to keep from getting confused. This terminology
# is something I cooked up for this file, but follows the man pages
# pretty closely
#
#
#
# This is a MENUBUTTON
# V
# +-------------+
# | |
#
# +------------++------------++------------+
# | || || |
# | File || Edit || Options | <-------- the MENUBAR
# | || || |
# +------------++------------++------------+
# | New... |
# | Open... |
# | Print |
# | | <-------- This is a MENU. The lines of text in the menu are
# | | MENU ENTRIES
# | +---------------+
# | Open Files > | file1 |
# | | file2 |
# | | another file | <------ this cascading part is also a MENU
# +----------------| |
# | |
# | |
# | |
# +---------------+
# some miscellaneous callbacks
def new_file():
print "opening new file"
def open_file():
print "opening OLD file"
def print_something():
print "picked a menu item"
anchovies = 0
def print_anchovies():
global anchovies
anchovies = not anchovies
print "anchovies?", anchovies
def makeCommandMenu():
# make menu button
Command_button = Menubutton(mBar, text='Simple Button Commands',
underline=0)
Command_button.pack(side=LEFT, padx="2m")
# make the pulldown part of the File menu. The parameter passed is the master.
# we attach it to the button as a python attribute called "menu" by convention.
# hopefully this isn't too confusing...
Command_button.menu = Menu(Command_button)
# just to be cute, let's disable the undo option:
Command_button.menu.add_command(label="Undo")
# undo is the 0th entry...
Command_button.menu.entryconfig(0, state=DISABLED)
Command_button.menu.add_command(label='New...', underline=0,
command=new_file)
Command_button.menu.add_command(label='Open...', underline=0,
command=open_file)
Command_button.menu.add_command(label='Different Font', underline=0,
font='-*-helvetica-*-r-*-*-*-180-*-*-*-*-*-*',
command=print_something)
# we can make bitmaps be menu entries too. File format is X11 bitmap.
# if you use XV, save it under X11 bitmap format. duh-uh.,..
Command_button.menu.add_command(
bitmap="info")
#bitmap='@/home/mjc4y/dilbert/project.status.is.doomed.last.panel.bm')
# this is just a line
Command_button.menu.add('separator')
# change the color
Command_button.menu.add_command(label='Quit', underline=0,
background='red',
activebackground='green',
command=Command_button.quit)
# set up a pointer from the file menubutton back to the file menu
Command_button['menu'] = Command_button.menu
return Command_button
def makeCascadeMenu():
# make menu button
Cascade_button = Menubutton(mBar, text='Cascading Menus', underline=0)
Cascade_button.pack(side=LEFT, padx="2m")
# the primary pulldown
Cascade_button.menu = Menu(Cascade_button)
# this is the menu that cascades from the primary pulldown....
Cascade_button.menu.choices = Menu(Cascade_button.menu)
# ...and this is a menu that cascades from that.
Cascade_button.menu.choices.weirdones = Menu(Cascade_button.menu.choices)
# then you define the menus from the deepest level on up.
Cascade_button.menu.choices.weirdones.add_command(label='avacado')
Cascade_button.menu.choices.weirdones.add_command(label='belgian endive')
Cascade_button.menu.choices.weirdones.add_command(label='beefaroni')
# definition of the menu one level up...
Cascade_button.menu.choices.add_command(label='Chocolate')
Cascade_button.menu.choices.add_command(label='Vanilla')
Cascade_button.menu.choices.add_command(label='TuttiFruiti')
Cascade_button.menu.choices.add_command(label='WopBopaLoopBapABopBamBoom')
Cascade_button.menu.choices.add_command(label='Rocky Road')
Cascade_button.menu.choices.add_command(label='BubbleGum')
Cascade_button.menu.choices.add_cascade(
label='Weird Flavors',
menu=Cascade_button.menu.choices.weirdones)
# and finally, the definition for the top level
Cascade_button.menu.add_cascade(label='more choices',
menu=Cascade_button.menu.choices)
Cascade_button['menu'] = Cascade_button.menu
return Cascade_button
def makeCheckbuttonMenu():
global fred
# make menu button
Checkbutton_button = Menubutton(mBar, text='Checkbutton Menus',
underline=0)
Checkbutton_button.pack(side=LEFT, padx='2m')
# the primary pulldown
Checkbutton_button.menu = Menu(Checkbutton_button)
# and all the check buttons. Note that the "variable" "onvalue" and "offvalue" options
# are not supported correctly at present. You have to do all your application
# work through the calback.
Checkbutton_button.menu.add_checkbutton(label='Pepperoni')
Checkbutton_button.menu.add_checkbutton(label='Sausage')
Checkbutton_button.menu.add_checkbutton(label='Extra Cheese')
# so here's a callback
Checkbutton_button.menu.add_checkbutton(label='Anchovy',
command=print_anchovies)
# and start with anchovies selected to be on. Do this by
# calling invoke on this menu option. To refer to the "anchovy" menu
# entry we need to know it's index. To do this, we use the index method
# which takes arguments of several forms:
#
# argument what it does
# -----------------------------------
# a number -- this is useless.
# "last" -- last option in the menu
# "none" -- used with the activate command. see the man page on menus
# "active" -- the currently active menu option. A menu option is made active
# with the 'activate' method
# "@number" -- where 'number' is an integer and is treated like a y coordinate in pixels
# string pattern -- this is the option used below, and attempts to match "labels" using the
# rules of Tcl_StringMatch
Checkbutton_button.menu.invoke(Checkbutton_button.menu.index('Anchovy'))
# set up a pointer from the file menubutton back to the file menu
Checkbutton_button['menu'] = Checkbutton_button.menu
return Checkbutton_button
def makeRadiobuttonMenu():
# make menu button
Radiobutton_button = Menubutton(mBar, text='Radiobutton Menus',
underline=0)
Radiobutton_button.pack(side=LEFT, padx='2m')
# the primary pulldown
Radiobutton_button.menu = Menu(Radiobutton_button)
# and all the Radio buttons. Note that the "variable" "onvalue" and "offvalue" options
# are not supported correctly at present. You have to do all your application
# work through the calback.
Radiobutton_button.menu.add_radiobutton(label='Republican')
Radiobutton_button.menu.add_radiobutton(label='Democrat')
Radiobutton_button.menu.add_radiobutton(label='Libertarian')
Radiobutton_button.menu.add_radiobutton(label='Commie')
Radiobutton_button.menu.add_radiobutton(label='Facist')
Radiobutton_button.menu.add_radiobutton(label='Labor Party')
Radiobutton_button.menu.add_radiobutton(label='Torie')
Radiobutton_button.menu.add_radiobutton(label='Independent')
Radiobutton_button.menu.add_radiobutton(label='Anarchist')
Radiobutton_button.menu.add_radiobutton(label='No Opinion')
# set up a pointer from the file menubutton back to the file menu
Radiobutton_button['menu'] = Radiobutton_button.menu
return Radiobutton_button
def makeDisabledMenu():
Dummy_button = Menubutton(mBar, text='Dead Menu', underline=0)
Dummy_button.pack(side=LEFT, padx='2m')
# this is the standard way of turning off a whole menu
Dummy_button["state"] = DISABLED
return Dummy_button
#################################################
#### Main starts here ...
root = Tk()
# make a menu bar
mBar = Frame(root, relief=RAISED, borderwidth=2)
mBar.pack(fill=X)
Command_button = makeCommandMenu()
Cascade_button = makeCascadeMenu()
Checkbutton_button = makeCheckbuttonMenu()
Radiobutton_button = makeRadiobuttonMenu()
NoMenu = makeDisabledMenu()
# finally, install the buttons in the menu bar.
# This allows for scanning from one menubutton to the next.
mBar.tk_menuBar(Command_button, Cascade_button, Checkbutton_button, Radiobutton_button, NoMenu)
root.title('menu demo')
root.iconname('menu demo')
root.mainloop()

View File

@@ -0,0 +1,112 @@
from Tkinter import *
# some vocabulary to keep from getting confused. This terminology
# is something I cooked up for this file, but follows the man pages
# pretty closely
#
#
#
# This is a MENUBUTTON
# V
# +-------------+
# | |
#
# +------------++------------++------------+
# | || || |
# | File || Edit || Options | <-------- the MENUBAR
# | || || |
# +------------++------------++------------+
# | New... |
# | Open... |
# | Print |
# | | <------ This is a MENU. The lines of text in the menu are
# | | MENU ENTRIES
# | +---------------+
# | Open Files > | file1 |
# | | file2 |
# | | another file | <------ this cascading part is also a MENU
# +----------------| |
# | |
# | |
# | |
# +---------------+
def new_file():
print "opening new file"
def open_file():
print "opening OLD file"
def makeFileMenu():
# make menu button : "File"
File_button = Menubutton(mBar, text='File', underline=0)
File_button.pack(side=LEFT, padx="1m")
File_button.menu = Menu(File_button)
# add an item. The first param is a menu entry type,
# must be one of: "cascade", "checkbutton", "command", "radiobutton", "separator"
# see menu-demo-2.py for examples of use
File_button.menu.add_command(label='New...', underline=0,
command=new_file)
File_button.menu.add_command(label='Open...', underline=0,
command=open_file)
File_button.menu.add_command(label='Quit', underline=0,
command='exit')
# set up a pointer from the file menubutton back to the file menu
File_button['menu'] = File_button.menu
return File_button
def makeEditMenu():
Edit_button = Menubutton(mBar, text='Edit', underline=0)
Edit_button.pack(side=LEFT, padx="1m")
Edit_button.menu = Menu(Edit_button)
# just to be cute, let's disable the undo option:
Edit_button.menu.add('command', label="Undo")
# Since the tear-off bar is the 0th entry,
# undo is the 1st entry...
Edit_button.menu.entryconfig(1, state=DISABLED)
# and these are just for show. No "command" callbacks attached.
Edit_button.menu.add_command(label="Cut")
Edit_button.menu.add_command(label="Copy")
Edit_button.menu.add_command(label="Paste")
# set up a pointer from the file menubutton back to the file menu
Edit_button['menu'] = Edit_button.menu
return Edit_button
#################################################
#### Main starts here ...
root = Tk()
# make a menu bar
mBar = Frame(root, relief=RAISED, borderwidth=2)
mBar.pack(fill=X)
File_button = makeFileMenu()
Edit_button = makeEditMenu()
# finally, install the buttons in the menu bar.
# This allows for scanning from one menubutton to the next.
mBar.tk_menuBar(File_button, Edit_button)
root.title('menu demo')
root.iconname('packer')
root.mainloop()

View File

@@ -0,0 +1,28 @@
from Tkinter import *
class Test(Frame):
def createWidgets(self):
self.Gpanel = Frame(self, width='1i', height='1i',
background='green')
self.Gpanel.pack(side=LEFT)
# a QUIT button
self.Gpanel.QUIT = Button(self.Gpanel, text='QUIT',
foreground='red',
command=self.quit)
self.Gpanel.QUIT.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.master.title('packer demo')
test.master.iconname('packer')
test.mainloop()

View File

@@ -0,0 +1,30 @@
from Tkinter import *
class Test(Frame):
def createWidgets(self):
self.Gpanel = Frame(self, width='1i', height='1i',
background='green')
# this line turns off the recalculation of geometry by masters.
self.Gpanel.propagate(0)
self.Gpanel.pack(side=LEFT)
# a QUIT button
self.Gpanel.QUIT = Button(self.Gpanel, text='QUIT', foreground='red',
command=self.quit)
self.Gpanel.QUIT.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.master.title('packer demo')
test.master.iconname('packer')
test.mainloop()

View File

@@ -0,0 +1,41 @@
from Tkinter import *
# This is a program that tests the placer geom manager in conjunction with
# the packer. The background (green) is packed, while the widget inside is placed
def do_motion(event):
app.button.place(x=event.x, y=event.y)
def dothis():
print 'calling me!'
def createWidgets(top):
# make a frame. Note that the widget is 200 x 200
# and the window containing is 400x400. We do this
# simply to show that this is possible. The rest of the
# area is inaccesssible.
f = Frame(top, width=200, height=200, background='green')
# note that we use a different manager here.
# This way, the top level frame widget resizes when the
# application window does.
f.pack(fill=BOTH, expand=1)
# now make a button
f.button = Button(f, foreground='red', text='amazing', command=dothis)
# and place it so that the nw corner is
# 1/2 way along the top X edge of its' parent
f.button.place(relx=0.5, rely=0.0, anchor=NW)
# allow the user to move the button SUIT-style.
f.bind('<Control-Shift-Motion>', do_motion)
return f
root = Tk()
app = createWidgets(root)
root.geometry("400x400")
root.maxsize(1000, 1000)
root.mainloop()

View File

@@ -0,0 +1,32 @@
from Tkinter import *
class Test(Frame):
def printit(self):
print self.hi_there["command"]
def createWidgets(self):
# a hello button
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=LEFT, fill=BOTH)
self.hi_there = Button(self, text='Hello',
command=self.printit)
self.hi_there.pack(side=LEFT)
# note how Packer defaults to side=TOP
self.guy2 = Button(self, text='button 2')
self.guy2.pack()
self.guy3 = Button(self, text='button 3')
self.guy3.pack()
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,39 @@
from Tkinter import *
# This is a program that tests the placer geom manager
def do_motion(event):
app.button.place(x=event.x, y=event.y)
def dothis():
print 'calling me!'
def createWidgets(top):
# make a frame. Note that the widget is 200 x 200
# and the window containing is 400x400. We do this
# simply to show that this is possible. The rest of the
# area is inaccesssible.
f = Frame(top, width=200, height=200, background='green')
# place it so the upper left hand corner of
# the frame is in the upper left corner of
# the parent
f.place(relx=0.0, rely=0.0)
# now make a button
f.button = Button(f, foreground='red', text='amazing', command=dothis)
# and place it so that the nw corner is
# 1/2 way along the top X edge of its' parent
f.button.place(relx=0.5, rely=0.0, anchor=NW)
# allow the user to move the button SUIT-style.
f.bind('<Control-Shift-Motion>', do_motion)
return f
root = Tk()
app = createWidgets(root)
root.geometry("400x400")
root.maxsize(1000, 1000)
root.mainloop()

View File

@@ -0,0 +1,54 @@
from Tkinter import *
import string
class Pong(Frame):
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=LEFT, fill=BOTH)
## The playing field
self.draw = Canvas(self, width="5i", height="5i")
## The speed control for the ball
self.speed = Scale(self, orient=HORIZONTAL, label="ball speed",
from_=-100, to=100)
self.speed.pack(side=BOTTOM, fill=X)
# The ball
self.ball = self.draw.create_oval("0i", "0i", "0.10i", "0.10i",
fill="red")
self.x = 0.05
self.y = 0.05
self.velocity_x = 0.3
self.velocity_y = 0.5
self.draw.pack(side=LEFT)
def moveBall(self, *args):
if (self.x > 5.0) or (self.x < 0.0):
self.velocity_x = -1.0 * self.velocity_x
if (self.y > 5.0) or (self.y < 0.0):
self.velocity_y = -1.0 * self.velocity_y
deltax = (self.velocity_x * self.speed.get() / 100.0)
deltay = (self.velocity_y * self.speed.get() / 100.0)
self.x = self.x + deltax
self.y = self.y + deltay
self.draw.move(self.ball, "%ri" % deltax, "%ri" % deltay)
self.after(10, self.moveBall)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
self.after(10, self.moveBall)
game = Pong()
game.mainloop()

View File

@@ -0,0 +1,61 @@
from Tkinter import *
# this file demonstrates the creation of widgets as part of a canvas object
class Test(Frame):
###################################################################
###### Event callbacks for THE CANVAS (not the stuff drawn on it)
###################################################################
def mouseDown(self, event):
# see if we're inside a dot. If we are, it
# gets tagged as CURRENT for free by tk.
if not event.widget.find_withtag(CURRENT):
# there is no dot here, so we can make one,
# and bind some interesting behavior to it.
# ------
# create a dot, and mark it as current
fred = self.draw.create_oval(
event.x - 10, event.y -10, event.x +10, event.y + 10,
fill="green")
self.draw.tag_bind(fred, "<Enter>", self.mouseEnter)
self.draw.tag_bind(fred, "<Leave>", self.mouseLeave)
self.lastx = event.x
self.lasty = event.y
def mouseMove(self, event):
self.draw.move(CURRENT, event.x - self.lastx, event.y - self.lasty)
self.lastx = event.x
self.lasty = event.y
###################################################################
###### Event callbacks for canvas ITEMS (stuff drawn on the canvas)
###################################################################
def mouseEnter(self, event):
# the "current" tag is applied to the object the cursor is over.
# this happens automatically.
self.draw.itemconfig(CURRENT, fill="red")
print self.draw.coords(CURRENT)
def mouseLeave(self, event):
# the "current" tag is applied to the object the cursor is over.
# this happens automatically.
self.draw.itemconfig(CURRENT, fill="blue")
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=LEFT, fill=BOTH)
self.draw = Canvas(self, width="5i", height="5i")
self.draw.pack(side=LEFT)
Widget.bind(self.draw, "<1>", self.mouseDown)
Widget.bind(self.draw, "<B1-Motion>", self.mouseMove)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,62 @@
from Tkinter import *
# This is a demo program that shows how to
# create radio buttons and how to get other widgets to
# share the information in a radio button.
#
# There are other ways of doing this too, but
# the "variable" option of radiobuttons seems to be the easiest.
#
# note how each button has a value it sets the variable to as it gets hit.
class Test(Frame):
def printit(self):
print "hi"
def createWidgets(self):
self.flavor = StringVar()
self.flavor.set("chocolate")
self.radioframe = Frame(self)
self.radioframe.pack()
# 'text' is the label
# 'variable' is the name of the variable that all these radio buttons share
# 'value' is the value this variable takes on when the radio button is selected
# 'anchor' makes the text appear left justified (default is centered. ick)
self.radioframe.choc = Radiobutton(
self.radioframe, text="Chocolate Flavor",
variable=self.flavor, value="chocolate",
anchor=W)
self.radioframe.choc.pack(fill=X)
self.radioframe.straw = Radiobutton(
self.radioframe, text="Strawberry Flavor",
variable=self.flavor, value="strawberry",
anchor=W)
self.radioframe.straw.pack(fill=X)
self.radioframe.lemon = Radiobutton(
self.radioframe, text="Lemon Flavor",
variable=self.flavor, value="lemon",
anchor=W)
self.radioframe.lemon.pack(fill=X)
# this is a text entry that lets you type in the name of a flavor too.
self.entry = Entry(self, textvariable=self.flavor)
self.entry.pack(fill=X)
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=BOTTOM, fill=BOTH)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,58 @@
from Tkinter import *
class Test(Frame):
def printit(self):
print "hi"
def createWidgets(self):
self.QUIT = Button(self, text='QUIT',
background='red',
foreground='white',
height=3,
command=self.quit)
self.QUIT.pack(side=BOTTOM, fill=BOTH)
self.canvasObject = Canvas(self, width="5i", height="5i")
self.canvasObject.pack(side=LEFT)
def mouseDown(self, event):
# canvas x and y take the screen coords from the event and translate
# them into the coordinate system of the canvas object
self.startx = self.canvasObject.canvasx(event.x)
self.starty = self.canvasObject.canvasy(event.y)
def mouseMotion(self, event):
# canvas x and y take the screen coords from the event and translate
# them into the coordinate system of the canvas object
x = self.canvasObject.canvasx(event.x)
y = self.canvasObject.canvasy(event.y)
if (self.startx != event.x) and (self.starty != event.y) :
self.canvasObject.delete(self.rubberbandBox)
self.rubberbandBox = self.canvasObject.create_rectangle(
self.startx, self.starty, x, y)
# this flushes the output, making sure that
# the rectangle makes it to the screen
# before the next event is handled
self.update_idletasks()
def mouseUp(self, event):
self.canvasObject.delete(self.rubberbandBox)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
# this is a "tagOrId" for the rectangle we draw on the canvas
self.rubberbandBox = None
# and the bindings that make it work..
Widget.bind(self.canvasObject, "<Button-1>", self.mouseDown)
Widget.bind(self.canvasObject, "<Button1-Motion>", self.mouseMotion)
Widget.bind(self.canvasObject, "<Button1-ButtonRelease>", self.mouseUp)
test = Test()
test.mainloop()

View File

@@ -0,0 +1,51 @@
from Tkinter import *
class Test(Frame):
def printit(self):
print "hi"
def createWidgets(self):
self.QUIT = Button(self, text='QUIT',
background='red',
foreground='white',
height=3,
command=self.quit)
self.QUIT.pack(side=BOTTOM, fill=BOTH)
self.canvasObject = Canvas(self, width="5i", height="5i")
self.canvasObject.pack(side=LEFT)
def mouseDown(self, event):
# canvas x and y take the screen coords from the event and translate
# them into the coordinate system of the canvas object
self.startx = self.canvasObject.canvasx(event.x)
self.starty = self.canvasObject.canvasy(event.y)
def mouseMotion(self, event):
# canvas x and y take the screen coords from the event and translate
# them into the coordinate system of the canvas object
x = self.canvasObject.canvasx(event.x)
y = self.canvasObject.canvasy(event.y)
if (self.startx != event.x) and (self.starty != event.y) :
self.canvasObject.delete(self.rubberbandLine)
self.rubberbandLine = self.canvasObject.create_line(
self.startx, self.starty, x, y)
# this flushes the output, making sure that
# the rectangle makes it to the screen
# before the next event is handled
self.update_idletasks()
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
# this is a "tagOrId" for the rectangle we draw on the canvas
self.rubberbandLine = None
Widget.bind(self.canvasObject, "<Button-1>", self.mouseDown)
Widget.bind(self.canvasObject, "<Button1-Motion>", self.mouseMotion)
test = Test()
test.mainloop()

View File

@@ -0,0 +1,36 @@
from Tkinter import *
# shows how to make a slider, set and get its value under program control
class Test(Frame):
def print_value(self, val):
print "slider now at", val
def reset(self):
self.slider.set(0)
def createWidgets(self):
self.slider = Scale(self, from_=0, to=100,
orient=HORIZONTAL,
length="3i",
label="happy slider",
command=self.print_value)
self.reset = Button(self, text='reset slider',
command=self.reset)
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.slider.pack(side=LEFT)
self.reset.pack(side=LEFT)
self.QUIT.pack(side=LEFT, fill=BOTH)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,28 @@
from Tkinter import *
# This is a program that makes a simple two button application
class New_Button(Button):
def callback(self):
print self.counter
self.counter = self.counter + 1
def createWidgets(top):
f = Frame(top)
f.pack()
f.QUIT = Button(f, text='QUIT', foreground='red', command=top.quit)
f.QUIT.pack(side=LEFT, fill=BOTH)
# a hello button
f.hi_there = New_Button(f, text='Hello')
# we do this on a different line because we need to reference f.hi_there
f.hi_there.config(command=f.hi_there.callback)
f.hi_there.pack(side=LEFT)
f.hi_there.counter = 43
root = Tk()
createWidgets(root)
root.mainloop()

View File

@@ -0,0 +1,110 @@
from Tkinter import *
# The way to think about this is that each radio button menu
# controls a different variable -- clicking on one of the
# mutually exclusive choices in a radiobutton assigns some value
# to an application variable you provide. When you define a
# radiobutton menu choice, you have the option of specifying the
# name of a varaible and value to assign to that variable when
# that choice is selected. This clever mechanism relieves you,
# the programmer, from having to write a dumb callback that
# probably wouldn't have done anything more than an assignment
# anyway. The Tkinter options for this follow their Tk
# counterparts:
# {"variable" : my_flavor_variable, "value" : "strawberry"}
# where my_flavor_variable is an instance of one of the
# subclasses of Variable, provided in Tkinter.py (there is
# StringVar(), IntVar(), DoubleVar() and BooleanVar() to choose
# from)
def makePoliticalParties(var):
# make menu button
Radiobutton_button = Menubutton(mBar, text='Political Party',
underline=0)
Radiobutton_button.pack(side=LEFT, padx='2m')
# the primary pulldown
Radiobutton_button.menu = Menu(Radiobutton_button)
Radiobutton_button.menu.add_radiobutton(label='Republican',
variable=var, value=1)
Radiobutton_button.menu.add('radiobutton', {'label': 'Democrat',
'variable' : var,
'value' : 2})
Radiobutton_button.menu.add('radiobutton', {'label': 'Libertarian',
'variable' : var,
'value' : 3})
var.set(2)
# set up a pointer from the file menubutton back to the file menu
Radiobutton_button['menu'] = Radiobutton_button.menu
return Radiobutton_button
def makeFlavors(var):
# make menu button
Radiobutton_button = Menubutton(mBar, text='Flavors',
underline=0)
Radiobutton_button.pack(side=LEFT, padx='2m')
# the primary pulldown
Radiobutton_button.menu = Menu(Radiobutton_button)
Radiobutton_button.menu.add_radiobutton(label='Strawberry',
variable=var, value='Strawberry')
Radiobutton_button.menu.add_radiobutton(label='Chocolate',
variable=var, value='Chocolate')
Radiobutton_button.menu.add_radiobutton(label='Rocky Road',
variable=var, value='Rocky Road')
# choose a default
var.set("Chocolate")
# set up a pointer from the file menubutton back to the file menu
Radiobutton_button['menu'] = Radiobutton_button.menu
return Radiobutton_button
def printStuff():
print "party is", party.get()
print "flavor is", flavor.get()
print
#################################################
#### Main starts here ...
root = Tk()
# make a menu bar
mBar = Frame(root, relief=RAISED, borderwidth=2)
mBar.pack(fill=X)
# make two application variables,
# one to control each radio button set
party = IntVar()
flavor = StringVar()
Radiobutton_button = makePoliticalParties(party)
Radiobutton_button2 = makeFlavors(flavor)
# finally, install the buttons in the menu bar.
# This allows for scanning from one menubutton to the next.
mBar.tk_menuBar(Radiobutton_button, Radiobutton_button2)
b = Button(root, text="print party and flavor", foreground="red",
command=printStuff)
b.pack(side=TOP)
root.title('menu demo')
root.iconname('menu demo')
root.mainloop()

View File

@@ -0,0 +1,35 @@
from Tkinter import *
# this shows how to create a new window with a button in it
# that can create new windows
class Test(Frame):
def printit(self):
print "hi"
def makeWindow(self):
fred = Toplevel()
fred.label = Button(fred,
text="This is window number %d." % self.windownum,
command=self.makeWindow)
fred.label.pack()
self.windownum = self.windownum + 1
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=LEFT, fill=BOTH)
# a hello button
self.hi_there = Button(self, text='Make a New Window',
command=self.makeWindow)
self.hi_there.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.windownum = 0
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,31 @@
from Tkinter import *
# this shows how to spawn off new windows at a button press
class Test(Frame):
def printit(self):
print "hi"
def makeWindow(self):
fred = Toplevel()
fred.label = Label(fred, text="Here's a new window")
fred.label.pack()
def createWidgets(self):
self.QUIT = Button(self, text='QUIT', foreground='red',
command=self.quit)
self.QUIT.pack(side=LEFT, fill=BOTH)
# a hello button
self.hi_there = Button(self, text='Make a New Window',
command=self.makeWindow)
self.hi_there.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,45 @@
from Tkinter import *
import sys
##sys.path.append("/users/mjc4y/projects/python/tkinter/utils")
##from TkinterUtils import *
# this shows how to create a new window with a button in it that
# can create new windows
class QuitButton(Button):
def __init__(self, master, *args, **kwargs):
if not kwargs.has_key("text"):
kwargs["text"] = "QUIT"
if not kwargs.has_key("command"):
kwargs["command"] = master.quit
apply(Button.__init__, (self, master) + args, kwargs)
class Test(Frame):
def makeWindow(self, *args):
fred = Toplevel()
fred.label = Canvas (fred, width="2i", height="2i")
fred.label.create_line("0", "0", "2i", "2i")
fred.label.create_line("0", "2i", "2i", "0")
fred.label.pack()
##centerWindow(fred, self.master)
def createWidgets(self):
self.QUIT = QuitButton(self)
self.QUIT.pack(side=LEFT, fill=BOTH)
self.makeWindow = Button(self, text='Make a New Window',
width=50, height=20,
command=self.makeWindow)
self.makeWindow.pack(side=LEFT)
def __init__(self, master=None):
Frame.__init__(self, master)
Pack.config(self)
self.createWidgets()
test = Test()
test.mainloop()

View File

@@ -0,0 +1,46 @@
"""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()

View File

@@ -0,0 +1,93 @@
"""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()

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 B

View File

@@ -0,0 +1,37 @@
"""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()

View File

@@ -0,0 +1,78 @@
"""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()

View File

@@ -0,0 +1,78 @@
"""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()

View File

@@ -0,0 +1,269 @@
"""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")

View File

@@ -0,0 +1,111 @@
"""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="""
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/DlAgIMICZlizat3KtatX
rAsiCNDgtCJClQkoFMgqsu3ArBkoZDgA8uDJAwk4bGDmtm9BZgcYzK078m4D
Cgf4+l0skNkGCg3oUhR4d4GCDIoZM2ZWQMECyZQvLMggIbPmzQIyfCZ5YcME
AwFMn/bLLIKBCRtMHljQQcDV2ZqZTRDQYfWFAwMqUJANvC8zBhUWbDi5YUAB
Bsybt2VGoUKH3AcmdP+Im127xOcJih+oXsEDdvOLuQfIMGBD9QwBlsOnzcBD
hfrsuVfefgzJR599A+CnH4Hb9fcfgu29x6BIBgKYYH4DTojQc/5ZGGGGGhpU
IYIKghgiQRw+GKCEJxZIwXwWlthiQyl6KOCMLsJIIoY4LlQjhDf2mNCI9/Eo
5IYO2sjikX+9eGCRCzL5V5JALillY07GaOSVb1G5ookzEnlhlFx+8OOXZb6V
5Y5kcnlmckGmKaaMaZrpJZxWXjnnlmW++WGdZq5ZXQEetKmnlxPgl6eUYhJq
KKOI0imnoNbF2ScFHQJJwW99TsBAAAVYWEAAHEQAZoi1cQDqAAeEV0EACpT/
JqcACgRQAW6uNWCbYKcyyEwGDBgQwa2tTlBBAhYIQMFejC5AgQAWJNDABK3y
loEDEjCgV6/aOcYBAwp4kIF6rVkXgAEc8IQZVifCBRQHGqya23HGIpsTBgSU
OsFX/PbrVVjpYsCABA4kQCxHu11ogAQUIOAwATpBLDFQFE9sccUYS0wAxD5h
4DACFEggbAHk3jVBA/gtTIHHEADg8sswxyzzzDQDAAEECGAQsgHiTisZResN
gLIHBijwLQEYePzx0kw37fTSSjuMr7ZMzfcgYZUZi58DGsTKwbdgayt22GSP
bXbYY3MggQIaONDzAJ8R9kFlQheQQAAOWGCAARrwdt23Bn8H7vfggBMueOEG
WOBBAAkU0EB9oBGUdXIFZJBABAEEsPjmmnfO+eeeh/55BBEk0Ph/E8Q9meQq
bbDABAN00EADFRRQ++2254777rr3jrvjFTTQwQCpz7u6QRut5/oEzA/g/PPQ
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()

View File

@@ -0,0 +1,61 @@
"""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()

View File

@@ -0,0 +1,107 @@
"""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()

View File

@@ -0,0 +1,231 @@
"""
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() # reconstruct 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()

View File

@@ -0,0 +1,83 @@
"""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()