Tkinter window not updating till after the loop is done Tkinter window not updating till after the loop is done tkinter tkinter

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!