I am working on a visualization of different sorting algorithms using the height of different bars with tkinter. I have been able to shuffle the bars and also sort them after some help. The problem I am having now is slowing the sorting of the bars down so it can be seen how each algorithm works. Here is what I have so far:
import tkinter as tk
import random
def swap_two_pos(pos_0, pos_1):
Bar1x1, _, Bar1x2, _ = canvas.coords(pos_0)
Bar2x1, _, Bar2x2, _ = canvas.coords(pos_1)
canvas.move(pos_0, Bar2x1-Bar1x1, 0)
canvas.move(pos_1, Bar1x2-Bar2x2, 0)
def insertion_sort():
global barList
global lengthList
for i in range(len(lengthList)):
cursor = lengthList[i]
cursorBar = barList[i]
pos = i
while pos > 0 and lengthList[pos - 1] > cursor:
lengthList[pos] = lengthList[pos - 1]
barList[pos], barList[pos - 1] = barList[pos - 1], barList[pos]
canvas.after(1000,swap_two_pos(barList[pos],barList[pos-1]))
pos -= 1
lengthList[pos] = cursor
barList[pos] = cursorBar
swap_two_pos(barList[pos],cursorBar)
def shuffle():
global barList
global lengthList
canvas.delete('all')
xstart = 5
xend = 15
barList = []
lengthList = []
for x in range(1,60):
randomY = random.randint(1,390)
x = canvas.create_rectangle(xstart,randomY,xend,395, fill='red')
barList.append(x)
xstart += 10
xend += 10
for bar in barList:
x = canvas.coords(bar)
length = x[3]-x[1]
lengthList.append(length)
for i in range(len(lengthList)-1):
if lengthList[i] == min(lengthList):
canvas.itemconfig(barList[i], fill='blue')
elif lengthList[i] == max(lengthList):
canvas.itemconfig(barList[i], fill='green')
window = tk.Tk()
window.title('Sorting')
window.geometry('600x435')
canvas = tk.Canvas(window, width='600', height='400')
canvas.grid(column=0,row=0, columnspan = 50)
insert = tk.Button(window, text='Insertion Sort', command=insertion_sort)
shuf = tk.Button(window, text='Shuffle', command=shuffle)
insert.grid(column=1,row=1)
shuf.grid(column=0, row=1)
shuffle()
window.mainloop()
As you can see I tried using the after()
method in the insertion sort function, but all it does is freeze the window and make it not respond. Without that method, it works fine, just doesn't go at a pace that can be seen.
Making use of a generator function (keyword yield
), you can suspend the execution of the code mid loop to take the time to display the canvas elements that have changed, update calculations, etc., then resume the execution upon calling next
on the generator repeatedly, until completion of the sort.
I put some comments in the code, but the best way, is probably to stare at it until you convince yourself that it works as it is supposed to. This is a pattern that you need to understand, as it is very useful to construct the sort of animations you want to build.
import tkinter as tk
import random
def swap_two_pos(pos_0, pos_1):
Bar1x1, _, Bar1x2, _ = canvas.coords(pos_0)
Bar2x1, _, Bar2x2, _ = canvas.coords(pos_1)
canvas.move(pos_0, Bar2x1-Bar1x1, 0)
canvas.move(pos_1, Bar1x2-Bar2x2, 0)
def _insertion_sort():
global barList
global lengthList
for i in range(len(lengthList)):
cursor = lengthList[i]
cursorBar = barList[i]
pos = i
while pos > 0 and lengthList[pos - 1] > cursor:
lengthList[pos] = lengthList[pos - 1]
barList[pos], barList[pos - 1] = barList[pos - 1], barList[pos]
swap_two_pos(barList[pos],barList[pos-1]) # <-- updates the display
yield # <-- suspends the execution
pos -= 1 # <-- execution resumes here when next is called
lengthList[pos] = cursor
barList[pos] = cursorBar
swap_two_pos(barList[pos],cursorBar)
worker = None # <-- Not a thread in spite of the name.
def insertion_sort(): # <-- commands the start of both the animation, and the sort
global worker
worker = _insertion_sort()
animate()
def animate(): # <-- commands resuming the sort once the display has been updated
# controls the pace of the animation
global worker
if worker is not None:
try:
next(worker)
window.after(10, animate) # <-- repeats until the sort is complete,
except StopIteration: # when the generator is exhausted
worker = None
finally:
window.after_cancel(animate) # <-- stop the callbacks
def shuffle():
global barList
global lengthList
canvas.delete('all')
xstart = 5
xend = 15
barList = []
lengthList = []
for x in range(1, 60):
randomY = random.randint(1, 390)
x = canvas.create_rectangle(xstart, randomY, xend, 395, fill='red')
barList.append(x)
xstart += 10
xend += 10
for bar in barList:
x = canvas.coords(bar)
length = x[3] - x[1]
lengthList.append(length)
for i in range(len(lengthList)-1):
if lengthList[i] == min(lengthList):
canvas.itemconfig(barList[i], fill='blue')
elif lengthList[i] == max(lengthList):
canvas.itemconfig(barList[i], fill='green')
window = tk.Tk()
window.title('Sorting')
window.geometry('600x435')
canvas = tk.Canvas(window, width='600', height='400')
canvas.grid(column=0,row=0, columnspan = 50)
insert = tk.Button(window, text='Insertion Sort', command=insertion_sort)
shuf = tk.Button(window, text='Shuffle', command=shuffle)
insert.grid(column=1,row=1)
shuf.grid(column=0, row=1)
shuffle()
window.mainloop()