| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | #! /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): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def setdata(self, data): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     speed = "normal" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def setspeed(self, speed): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.speed = speed | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def destroy(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.frame.destroy() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     in_mainloop = 0 | 
					
						
							|  |  |  |     stop_mainloop = 0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def cancel(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.stop_mainloop = 1 | 
					
						
							|  |  |  |         if self.in_mainloop: | 
					
						
							|  |  |  |             self.master.quit() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def step(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         if self.in_mainloop: | 
					
						
							|  |  |  |             self.master.quit() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |     Cancelled = "Array.Cancelled"       # Exception | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def wait(self, msecs): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def getsize(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         return self.size | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def show_partition(self, first, last): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def hide_partition(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         for i in range(self.size): | 
					
						
							|  |  |  |             item = self.items[i] | 
					
						
							|  |  |  |             item.item.config(fill='red') | 
					
						
							|  |  |  |         self.hide_left_right_pivot() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def show_left(self, left): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def show_right(self, right): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def hide_left_right_pivot(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.hide_left() | 
					
						
							|  |  |  |         self.hide_right() | 
					
						
							|  |  |  |         self.hide_pivot() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def hide_left(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.left.coords(((0, 0), (0, 0))) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def hide_right(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.right.coords(((0, 0), (0, 0))) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def show_pivot(self, pivot): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         x1, y1, x2, y2 = self.items[pivot].position() | 
					
						
							|  |  |  |         self.pivot.coords(((0, y1-2), (9999, y1-2))) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def hide_pivot(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.pivot.coords(((0, 0), (0, 0))) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def swap(self, i, j): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def compare(self, i, j): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.countcompare() | 
					
						
							|  |  |  |         item = self.items[i] | 
					
						
							|  |  |  |         other = self.items[j] | 
					
						
							|  |  |  |         return item.compareto(other) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def reset(self, msg): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.ncompares = 0 | 
					
						
							|  |  |  |         self.nswaps = 0 | 
					
						
							|  |  |  |         self.message(msg) | 
					
						
							|  |  |  |         self.updatereport() | 
					
						
							|  |  |  |         self.hide_partition() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def message(self, msg): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.label.config(text=msg) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def countswap(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.nswaps = self.nswaps + 1 | 
					
						
							|  |  |  |         self.updatereport() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def countcompare(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.ncompares = self.ncompares + 1 | 
					
						
							|  |  |  |         self.updatereport() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def updatereport(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         text = "%d cmps, %d swaps" % (self.ncompares, self.nswaps) | 
					
						
							|  |  |  |         self.report.config(text=text) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class ArrayItem: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, array, index, value): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def delete(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         item = self.item | 
					
						
							|  |  |  |         self.array = None | 
					
						
							|  |  |  |         self.item = None | 
					
						
							|  |  |  |         item.delete() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def mouse_down(self, event): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.lastx = event.x | 
					
						
							|  |  |  |         self.lasty = event.y | 
					
						
							|  |  |  |         self.origx = event.x | 
					
						
							|  |  |  |         self.origy = event.y | 
					
						
							|  |  |  |         self.item.tkraise() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  |     def mouse_move(self, event): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.item.move(event.x - self.lastx, event.y - self.lasty) | 
					
						
							|  |  |  |         self.lastx = event.x | 
					
						
							|  |  |  |         self.lasty = event.y | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def mouse_up(self, event): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def setindex(self, index): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def swapwith(self, other): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def compareto(self, other): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def position(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def nearestindex(self, x): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         return int(round(float(x)/XGRID)) - 1 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Subroutines that don't need an object | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def steps(here, there): | 
					
						
							|  |  |  |     nsteps = abs(here - there) | 
					
						
							|  |  |  |     if nsteps <= 3: | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         nsteps = nsteps * 3 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  |     elif nsteps <= 5: | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         nsteps = nsteps * 2 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  |     elif nsteps > 10: | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         nsteps = 10 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  |     return nsteps | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def interpolate(oldpts, newpts, n): | 
					
						
							|  |  |  |     if len(oldpts) != len(newpts): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         raise ValueError, "can't interpolate arrays of different length" | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  |     pts = [0]*len(oldpts) | 
					
						
							|  |  |  |     res = [tuple(oldpts)] | 
					
						
							|  |  |  |     for i in range(1, n): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         for k in range(len(pts)): | 
					
						
							|  |  |  |             pts[k] = oldpts[k] + (newpts[k] - oldpts[k])*i/n | 
					
						
							|  |  |  |         res.append(tuple(pts)) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  |     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): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         j = random.randint(0, n-1) | 
					
						
							|  |  |  |         array.swap(i, j) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  |     array.message("Randomized") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def insertionsort(array): | 
					
						
							|  |  |  |     size = array.getsize() | 
					
						
							|  |  |  |     array.reset("Insertion sort") | 
					
						
							|  |  |  |     for i in range(1, size): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         j = i-1 | 
					
						
							|  |  |  |         while j >= 0: | 
					
						
							|  |  |  |             if array.compare(j, j+1) <= 0: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |             array.swap(j, j+1) | 
					
						
							|  |  |  |             j = j-1 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  |     array.message("Sorted") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def selectionsort(array): | 
					
						
							|  |  |  |     size = array.getsize() | 
					
						
							|  |  |  |     array.reset("Selection sort") | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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") | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  |     finally: | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         array.hide_partition() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def bubblesort(array): | 
					
						
							|  |  |  |     size = array.getsize() | 
					
						
							|  |  |  |     array.reset("Bubble sort") | 
					
						
							|  |  |  |     for i in range(size): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         for j in range(1, size): | 
					
						
							|  |  |  |             if array.compare(j-1, j) > 0: | 
					
						
							|  |  |  |                 array.swap(j-1, j) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  |     array.message("Sorted") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def quicksort(array): | 
					
						
							|  |  |  |     size = array.getsize() | 
					
						
							|  |  |  |     array.reset("Quicksort") | 
					
						
							|  |  |  |     try: | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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") | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  |     finally: | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         array.hide_partition() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def demosort(array): | 
					
						
							|  |  |  |     while 1: | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         for alg in [quicksort, insertionsort, selectionsort, bubblesort]: | 
					
						
							|  |  |  |             randomize(array) | 
					
						
							|  |  |  |             alg(array) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Sort demo class -- usable as a Grail applet | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SortDemo: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, master, size=15): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def resize(self, newsize): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         if self.busy: | 
					
						
							|  |  |  |             self.master.bell() | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self.size = newsize | 
					
						
							|  |  |  |         self.array.setdata(range(1, self.size+1)) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def c_qsort(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.run(quicksort) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def c_isort(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.run(insertionsort) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def c_ssort(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.run(selectionsort) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def c_bsort(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.run(bubblesort) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def c_demo(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.run(demosort) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def c_randomize(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.run(randomize) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def c_uniform(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.run(uniform) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def c_distinct(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         self.run(distinct) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def run(self, func): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         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 | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def c_cancel(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         if not self.busy: | 
					
						
							|  |  |  |             self.master.bell() | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self.array.cancel() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def c_step(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         if not self.busy: | 
					
						
							|  |  |  |             self.master.bell() | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  |         self.v_speed.set("single-step") | 
					
						
							|  |  |  |         self.array.setspeed("single-step") | 
					
						
							|  |  |  |         self.array.step() | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def c_quit(self): | 
					
						
							| 
									
										
										
										
											2004-07-18 06:16:08 +00:00
										 |  |  |         if self.busy: | 
					
						
							|  |  |  |             self.array.cancel() | 
					
						
							|  |  |  |         self.master.after_idle(self.master.quit) | 
					
						
							| 
									
										
										
										
											1997-04-03 00:04:51 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # 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() |