mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
Checked in some new Tk demos that I wrote a while ago.
This commit is contained in:
parent
387c575d5c
commit
9a8cb84072
4 changed files with 1047 additions and 0 deletions
244
Demo/tkinter/guido/canvasevents.py
Normal file
244
Demo/tkinter/guido/canvasevents.py
Normal 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 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()
|
42
Demo/tkinter/guido/newmenubardemo.py
Normal file
42
Demo/tkinter/guido/newmenubardemo.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
#! /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.menubar.add_cascade(label="File", menu=self.filemenu)
|
||||
self.menubar.add_cascade(label="Edit", menu=self.editmenu)
|
||||
|
||||
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()
|
127
Demo/tkinter/guido/regexdemo.py
Normal file
127
Demo/tkinter/guido/regexdemo.py
Normal file
|
@ -0,0 +1,127 @@
|
|||
"""Basic regular expression demostration facility.
|
||||
|
||||
This displays a window with two type-in boxes. In the top box, you enter a
|
||||
regular expression. In the bottom box, you enter a string. The first
|
||||
match in the string of the regular expression is highlighted with a yellow
|
||||
background (or red if the match is empty -- then the character at the match
|
||||
is highlighted). The highlighting is continuously updated. At the bottom
|
||||
are a number of checkboxes which control the regular expression syntax used
|
||||
(see the regex_syntax module for descriptions). When there's no match, or
|
||||
when the regular expression is syntactically incorrect, an error message is
|
||||
displayed.
|
||||
|
||||
"""
|
||||
|
||||
from Tkinter import *
|
||||
import regex
|
||||
import regex_syntax
|
||||
|
||||
class RegexDemo:
|
||||
|
||||
def __init__(self, master):
|
||||
self.master = master
|
||||
self.topframe = Frame(self.master)
|
||||
self.topframe.pack(fill=X)
|
||||
self.promptdisplay = Label(self.topframe, text="Enter a string:")
|
||||
self.promptdisplay.pack(side=LEFT)
|
||||
self.statusdisplay = Label(self.topframe, text="", anchor=W)
|
||||
self.statusdisplay.pack(side=LEFT, fill=X)
|
||||
self.regexdisplay = Entry(self.master)
|
||||
self.regexdisplay.pack(fill=X)
|
||||
self.regexdisplay.focus_set()
|
||||
self.labeldisplay = Label(self.master, anchor=W,
|
||||
text="Enter a string:")
|
||||
self.labeldisplay.pack(fill=X)
|
||||
self.labeldisplay.pack(fill=X)
|
||||
self.stringdisplay = Text(self.master, width=60, height=4)
|
||||
self.stringdisplay.pack(fill=BOTH, expand=1)
|
||||
self.stringdisplay.tag_configure("hit", background="yellow")
|
||||
self.addoptions()
|
||||
self.regexdisplay.bind('<Key>', self.recompile)
|
||||
self.stringdisplay.bind('<Key>', self.reevaluate)
|
||||
self.compiled = None
|
||||
self.recompile()
|
||||
btags = self.regexdisplay.bindtags()
|
||||
self.regexdisplay.bindtags(btags[1:] + btags[:1])
|
||||
btags = self.stringdisplay.bindtags()
|
||||
self.stringdisplay.bindtags(btags[1:] + btags[:1])
|
||||
|
||||
def addoptions(self):
|
||||
self.frames = []
|
||||
self.boxes = []
|
||||
self.vars = []
|
||||
for name in ( 'RE_NO_BK_PARENS',
|
||||
'RE_NO_BK_VBAR',
|
||||
'RE_BK_PLUS_QM',
|
||||
'RE_TIGHT_VBAR',
|
||||
'RE_NEWLINE_OR',
|
||||
'RE_CONTEXT_INDEP_OPS'):
|
||||
if len(self.boxes) % 3 == 0:
|
||||
frame = Frame(self.master)
|
||||
frame.pack(fill=X)
|
||||
self.frames.append(frame)
|
||||
val = getattr(regex_syntax, name)
|
||||
var = IntVar()
|
||||
box = Checkbutton(frame,
|
||||
variable=var, text=name,
|
||||
offvalue=0, onvalue=val,
|
||||
command=self.newsyntax)
|
||||
box.pack(side=LEFT)
|
||||
self.boxes.append(box)
|
||||
self.vars.append(var)
|
||||
|
||||
def newsyntax(self):
|
||||
syntax = 0
|
||||
for var in self.vars:
|
||||
syntax = syntax | var.get()
|
||||
regex.set_syntax(syntax)
|
||||
self.recompile()
|
||||
|
||||
def recompile(self, event=None):
|
||||
try:
|
||||
self.compiled = regex.compile(self.regexdisplay.get())
|
||||
self.statusdisplay.config(text="")
|
||||
except regex.error, msg:
|
||||
self.compiled = None
|
||||
self.statusdisplay.config(text="regex.error: %s" % str(msg))
|
||||
self.reevaluate()
|
||||
|
||||
def reevaluate(self, event=None):
|
||||
try:
|
||||
self.stringdisplay.tag_remove("hit", "1.0", END)
|
||||
except TclError:
|
||||
pass
|
||||
if not self.compiled:
|
||||
return
|
||||
text = self.stringdisplay.get("1.0", END)
|
||||
i = self.compiled.search(text)
|
||||
if i < 0:
|
||||
self.statusdisplay.config(text="(no match)")
|
||||
else:
|
||||
self.statusdisplay.config(text="")
|
||||
regs = self.compiled.regs
|
||||
first, last = regs[0]
|
||||
if last == first:
|
||||
last = first+1
|
||||
self.stringdisplay.tag_configure("hit",
|
||||
background="red")
|
||||
else:
|
||||
self.stringdisplay.tag_configure("hit",
|
||||
background="yellow")
|
||||
pfirst = "1.0 + %d chars" % first
|
||||
plast = "1.0 + %d chars" % last
|
||||
self.stringdisplay.tag_add("hit", pfirst, plast)
|
||||
self.stringdisplay.yview_pickplace(pfirst)
|
||||
|
||||
|
||||
|
||||
# Main function, run when invoked as a stand-alone Python program.
|
||||
|
||||
def main():
|
||||
root = Tk()
|
||||
demo = RegexDemo(root)
|
||||
root.protocol('WM_DELETE_WINDOW', root.quit)
|
||||
root.mainloop()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
634
Demo/tkinter/guido/sortvisu.py
Normal file
634
Demo/tkinter/guido/sortvisu.py
Normal 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()
|
Loading…
Add table
Add a link
Reference in a new issue