Tkinter window not updating till after the loop is done
You can Thread
your function bubbleSort
. Also it seems to make more sense for bubbleSort
to be a method of the class:
import threading, timeclass SortingVisualizer(tk.Tk): def __init__(self, *args, **kwargs): ... self.sortButton = ttk.Button(self.topFrame, text = "Sort", command = lambda: threading.Thread(target=self.bubbleSort).start()) ... ... def bubbleSort(self): n = len(self.array) for i in range(n): for j in range(0, n-i-1): if self.array[j]>self.array[j+1]: temp = self.array[j] self.array[j] = self.array[j+1] self.array[j+1] = temp self.numOperations += 1 self.redrawCanvas() time.sleep(0.1)
Just an alternative solution besides threading. I used to encounter a problem that by threading the functions, they may access some external devices (I created a GUI to monitor some hardware) at the same time and cause clashes. By using .after(), tkinter will handle the order of the tasks to avoid clashes.
You can redefine your bubbleSort function so that each iterations of the for loop is changed to a recursion by calling the function again.
def bubbleSort(self, i = 1, j = 0): n = len(self.array) if self.array[j]>self.array[j+1]: temp = self.array[j] self.array[j] = self.array[j+1] self.array[j+1] = temp self.numOperations += 1 j += 1 if j == n-i-1: j = 0 i += 1 if i < n: self.after(1, lambda: self.bubbleSort(i,j))
You made a very good first attempt and you were almost there. I have made some changes in your code, most importantly introduced a root
object (tk.Tk()
) so I can do root.update() in order to redraw to sort_canvas
in a new method blip_canvas
. To avoid some 'flickering' rather to destroy the canvas each time it is better to delete the 'bar' elements only.Further I took the liberty to change some of the variable names to make it a bit more Pythonic (should make use of underscores rather than capitals) and added the if _name__ == '__main__'
statement.
Have a look at the code below.
import tkinter as tkfrom tkinter import ttkimport randomclass SortingVisualizer: def __init__(self): self.root = tk.Tk() self.root.wm_title("Sorting Visualizer") self.root.wm_minsize(width=600, height=500) self.root.wm_resizable(width=False, height=False) self.top_frame = tk.Frame(self.root) self.top_frame.grid(row=0, sticky='w') self.sort_options = ['Select Algorithm', 'Bubble sort', 'Quicksort', 'Merge sort'] self.option_var = tk.StringVar() self.option_drop = ttk.OptionMenu( self.top_frame, self.option_var, *self.sort_options) self.option_drop.config(width=15) self.option_drop.grid(row=0, column=1, sticky='ew') self.sort_button = ttk.Button( self.top_frame, text="Sort", command=self.bubble_sort) self.sort_button.grid(row=0, column=2, sticky='w') self.gen_button = ttk.Button( self.top_frame, text="Generate New Array", command=self.new_array) self.gen_button.grid(row=0, column=0) self.sort_canvas = tk.Canvas(self.root) self.bars = [] def new_array(self): self.generate_array() self.blip_canvas() def generate_array(self): self.array = [] self.num_operations = 0 i = 0 while i < 15: height = random.randint(15, 200) self.array.append(height) i = i + 1 def draw_canvas(self): label = "Number of Operations: " + str(self.num_operations) self.num_label = tk.Label(self.top_frame, text=label) self.num_label.grid(row=1) self.sort_canvas = tk.Canvas(self.root, width=600, height=450) self.sort_canvas.grid(row=1) self.sort_canvas.create_line(15, 15, 585, 15) bar_width = 20 bar_gap = bar_width + 10 start_x = 30 start_y = 15 self.bars = [] for bar_height in self.array: x1 = start_x + bar_width y1 = start_y + bar_height self.bars.append(self.sort_canvas.create_rectangle( start_x, start_y, x1, y1*2, fill='green')) start_x = start_x + bar_gap def blip_canvas(self): self.sort_canvas.delete(self.bars) self.draw_canvas() self.root.update() self.root.after(200) def bubble_sort(self): n = len(self.array) for i in range(n): for j in range(n-i-1): if self.array[j] > self.array[j+1]: self.array[j], self.array[j+1] = self.array[j+1], self.array[j] self.num_operations += 1 self.blip_canvas() def start(self): tk.mainloop()if __name__ == '__main__': app = SortingVisualizer() app.start()
Note in bubble_sort you do not need the temp variable to swap the values of array[j] and array[j+1]
Rather than to use time.sleep(0.2)
to set a delay I have used:
self.root.update()self.root.after(200)
as suggested in Update button after delay
You can also stick to your original code and just make a few changes.
1) Change the sortButton
self.sortButton = ttk.Button(self.topFrame, text = "Sort", command=self.bubbleSort)
2) Indent the bubbleSort method to be aligned with SortingVisualizer
3) Change method redrawCanvas to:
def redrawCanvas(self): self.sortCanvas.destroy() self.drawCanvas() self.update() self.after(300)
and
4) in bubbleSort make the call to redrawCanvas
:
for j in range(0, n-i-1): if self.array[j]>self.array[j+1]: temp = self.array[j] self.array[j] = self.array[j+1] self.array[j+1] = temp self.numOperations += 1 self.redrawCanvas()
et voila, it will work!