Search code examples
pythonsortingtkinterbubble-sort

How do I correctly implement a bubble sort algorithm in python tkinter?


I am making a series of articles about sorting algorithms, and the first part is about bubble sorting, I have the GUI elements in place, but the sorting algorithm itself is not working correctly. it randomly swaps a series of lines of different lengths, but the sorting doesn't work as expected. It is written in python Tkinter, I think the main problem comes from how I programmed the sorting like two lists, one on the screen and one on memory. It would be helpful if you could also explain my mistake to me.

import tkinter as tk
import random


def swap_two_pos(pos_0, pos_1):
    """This does the graphical swapping of the rectangles on the canvas
    by moving one rectangle to the location of the other, and vice versa
    """    
    x_00, _, x_01, _ = canvas.coords(pos_0)
    x_10, _, x_11, _ = canvas.coords(pos_1)
    # moves each rectangle to the x position of the other; y remains unchanged
    canvas.move(pos_0, x_10-x_00, 0)
    canvas.move(pos_1, x_01-x_11, 0)

def sort_two(pos_0, pos_1):
    x_00, y1, x_01, _ = canvas.coords(pos_0)
    x_10, y2, x_11, _ = canvas.coords(pos_1)
    # moves each rectangle to the x position of the other; y remains unchanged
    if y2 > y1:
        canvas.move(pos_0, x_10-x_00, 0)
        canvas.move(pos_1, x_01-x_11, 0)

def rand_sort():
    for i in range(50000):
        rd1 = random.randint(0, 58)
        rd2 = random.randint(0, 58)
        pos_1 = barList[rd1]
        pos_2 = barList[rd2]
        sort_two(pos_1, pos_2)
        barList[rd1], barList[rd2] = barList[rd2], barList[rd1]



def sort ():
    n = len(barList)
  
    # Traverse through all array elements 
    for i in range(n): 
  
        # Last i elements are already in place 
        for j in range(0, n-i-1):
                sort_two(barList[j], barList[j+1])
                barList[j], barList[j+1] = barList[j+1], barList[j]
        else:
            break

def random_swap():
    """Not a sort yet, but you have the bare bones operations
    so the swap is executed
    """
    for i in range(500):
        rd1 = random.randint(0, 58)
        rd2 = random.randint(0, 58)
        pos_0 = barList[rd1]
        pos_1 = barList[rd2]
        
        swap_two_pos(pos_0, pos_1)
        # it is necessary to swap the values in the list too
        barList[rd1], barList[rd2] = barList[rd2], barList[rd1]

window = tk.Tk()
window.title('Sorting')
window.geometry('600x400')

# button to command the swap
tk.Button(window, text='swap', command=random_swap).pack()
tk.Button(window, text='sort', command=sort).pack()

xstart = 5
xend = 15
canvas = tk.Canvas(window, width='900', height='900')
canvas.pack()
barList = []
lengthList = []
Y = 5

for x in range(1,60):
    bar = canvas.create_rectangle(xstart, Y, xend, 395, fill='red')
    barList.append(bar)
    xstart += 10
    xend += 10
    Y += 5

for bar in barList:
    x = canvas.coords(bar)
    length = x[3]-x[1]
    lengthList.append(length)

window.mainloop()

Solution

  • enter image description here

    The biggest problem is that inside sort_two you have if

    if y2 > y1:
        canvas.move(pos_0, x_10-x_00, 0)
        canvas.move(pos_1, x_01-x_11, 0)
    

    which replaces elements only if y2 > y1

    but after sort_two() you use barList

    sort_two(pos_1, pos_2)
    barList[rd1], barList[rd2] = barList[rd2], barList[rd1]
    

    which always replaces elements on list.

    And this way you have wrong results on screen.

    You could return True/False from sort_two() to control when to change elements on barList

    if y2 > y1:
        canvas.move(pos_0, x_10-x_00, 0)
        canvas.move(pos_1, x_01-x_11, 0)
        return True
    else:
        return False
    

    and

    if sort_two(pos_1, pos_2):
        barList[rd1], barList[rd2] = barList[rd2], barList[rd1]
    

    Here finall code

    I use simple calculation for replacing elements on canvas

    x1, _, _, _ = canvas.coords(pos_0)
    x2, _, _, _ = canvas.coords(pos_1)
    
    diff = x1 - x2
    
    canvas.move(pos_0, -diff, 0)
    canvas.move(pos_1, +diff, 0)
    

    I also removed

     else:
        break
    

    which stop animation after every replace and it needs to click button sort again and again - and I use

        window.update()
        time.sleep(0.1)
    

    so it displays animation (slowly) to the end of sorting and I don't have to click button sort

    import tkinter as tk
    import random
    import time
    
    def swap_two_pos(pos_0, pos_1):
        """This does the graphical swapping of the rectangles on the canvas
        by moving one rectangle to the location of the other, and vice versa
        """    
        
        x1, _, _, _ = canvas.coords(pos_0)
        x2, _, _, _ = canvas.coords(pos_1)
        
        diff = x1 - x2
    
        canvas.move(pos_0, -diff, 0)
        canvas.move(pos_1, +diff, 0)
    
    def sort_two(pos_0, pos_1):
        x1, y1, _, _ = canvas.coords(pos_0)
        x2, y2, _, _ = canvas.coords(pos_1)
    
        diff = x1 - x2
    
        # moves each rectangle to the x position of the other; y remains unchanged
        if y2 > y1:
            canvas.move(pos_0, -diff, 0)
            canvas.move(pos_1, +diff, 0)
            return True
        else:
            return False
    
    def rand_sort():
        for i in range(50000):
            rd1 = random.randint(0, 58)
            rd2 = random.randint(0, 58)
            pos_1 = barList[rd1]
            pos_2 = barList[rd2]
            if sort_two(pos_1, pos_2):
                barList[rd1], barList[rd2] = barList[rd2], barList[rd1]
    
    def sort ():
        n = len(barList)
      
        # Traverse through all array elements 
        for i in range(n): 
      
            # Last i elements are already in place 
            for j in range(0, n-i-1):
                if sort_two(barList[j], barList[j+1]):
                    barList[j], barList[j+1] = barList[j+1], barList[j]
                
            window.update()
            time.sleep(0.1)
            
            
    def random_swap():
        """Not a sort yet, but you have the bare bones operations
        so the swap is executed
        """
        for i in range(500):
            rd1 = random.randint(0, 58)
            rd2 = random.randint(0, 58)
            pos_0 = barList[rd1]
            pos_1 = barList[rd2]
            
            swap_two_pos(pos_0, pos_1)
            # it is necessary to swap the values in the list too
            barList[rd1], barList[rd2] = barList[rd2], barList[rd1]
    
    window = tk.Tk()
    window.title('Sorting')
    window.geometry('600x400')
    
    # button to command the swap
    tk.Button(window, text='swap', command=random_swap).pack()
    tk.Button(window, text='sort', command=sort).pack()
    
    xstart = 5
    xend = 15
    canvas = tk.Canvas(window, width='900', height='900')
    canvas.pack()
    barList = []
    lengthList = []
    Y = 5
    
    for x in range(1,60):
        bar = canvas.create_rectangle(xstart, Y, xend, 395, fill='red')
        barList.append(bar)
        xstart += 10
        xend += 10
        Y += 5
    
    for bar in barList:
        x = canvas.coords(bar)
        length = x[3]-x[1]
        lengthList.append(length)
    
    window.mainloop()