Search code examples
pythonsortingmatplotlibcanvasupdating

Updating a plot in Python faster, not using plot.pause()


This is my first time posting something on StackOverflow, so I'll try my best to explain my problem.

I'm trying to write a code that shows by plotting how different sorting algorithms work. For this I plot every step the algorithm is doing, so every time I need to update my graph in order to show that. My code can be seen below. Currently I'm doing that by using plt.cla(), plt.draw() and plt.pause(), as seen under 'def Bubble_sort()'. But if there is a big array which is sorted, this is very slow!

I did try to look for a different method, and I found that using figure.canvas.draw(), figure.canvas.update() and such can speed up the process. I tried to apply it in 'def Bubble_sort_fast' But I can't get it to work... Also when I try it, I get the error:

AttributeError: 'FigureCanvasTkAgg' object has no attribute 'update'

I saw some things that it can be due to the 'backend' TkAgg I'm using, but trying others like Qt4Agg or someting, I get an import error.

It would be amazing if you guys could help me out! Let me know if you're missing information!

import random
from matplotlib import pyplot as plt
import numpy as np

end = 100
x = np.array(range(0,end+1))
y = np.array(range(0,end+1))
random.shuffle(y)

def Bubble_sort():
    plt.figure()
    plt.bar(x,y,color='blue')

    sorted = False
    while not sorted:
        for j in range(0,len(x)):
            for i in range(1,len(x)):
                if y[i-1] > y[i]:
                    y[i-1], y[i] = y[i],y[i-1]
                    plt.cla()
                    plt.bar(x,y,color='blue')
                    plt.bar(x[i],y[i],color='red')
                    plt.draw()
                    plt.pause(.01)

        for k in range(1,len(x)+1):
            plt.cla()
            plt.bar(x, y, color='blue')
            plt.bar(x[:k], y[:k], color='#63f542')
            plt.pause(0.01)
            if k == len(x):
                sorted = True

    plt.bar(x,y,color='#63f542')
    plt.show()

def Bubble_sort_fast():
    fig, ax = plt.subplots()
    barz = ax.bar(x, y, 0.95, color='blue')
    fig.canvas.draw()
    plt.show(block=False)

    sorted = False
    while not sorted:
        for j in range(0, len(x)):
            for i in range(1, len(x)):
                if y[i - 1] > y[i]:
                    y[i - 1], y[i] = y[i], y[i - 1]
                    fig.canvas.update()
                    fig.canvas.flush_events()

        for k in range(1,len(x)+1):
            plt.cla()
            plt.bar(x, y, color='blue')
            plt.bar(x[:k], y[:k], color='#63f542')
            plt.pause(0.01)
            if k == len(x):
                sorted = True

    plt.bar(x, y, color='#63f542')
    plt.show()

Bubble_sort()
# Bubble_sort_fast()

Solution

  • One would use FuncAnimation for such animations. In this case it could look like

    import numpy as np
    import random
    from matplotlib import pyplot as plt
    from matplotlib.animation import FuncAnimation
    
    end = 100
    x = np.array(range(0,end+1))
    y = np.array(range(0,end+1))
    random.shuffle(y)
    
    
    fig, ax = plt.subplots()
    bars = ax.bar(x,y,color='blue')
    
    def update(y, i=None, k=None):
        for yi, bar in zip(y,bars):
            bar.set_height(yi)
            bar.set_facecolor("blue")
        if i is not None:
            bars[i].set_facecolor("red")
        if k is not None:
            for l in range(k):
                bars[l].set_facecolor('#63f542')
    
    def gen():
        sorted = False
        while not sorted:
            for j in range(0,len(x)):
                for i in range(1,len(x)):
                    if y[i-1] > y[i]:
                        y[i-1], y[i] = y[i],y[i-1]
                        yield (y, i, None)
    
            for k in range(1,len(x)+1):
                yield (y, None, k)
    
                if k == len(x):
                    sorted = True
        ani.event_source.stop()
        yield (y, None, k)
    
    
    def animate(args):
        update(*args)
        return bars
    
    ani =  FuncAnimation(fig, animate, frames=gen, repeat=False, interval=15, blit=True)  
    
    plt.show()