【问题标题】:Sort Algorithm Visualization: How to pull values to animate the canvas, from inside a tight loop排序算法可视化:如何从紧密循环中提取值以动画画布
【发布时间】:2019-09-09 08:09:10
【问题描述】:

我正在使用 tkinter 使用不同条形的高度来可视化不同的排序算法。在一些帮助后,我已经能够洗牌并对其进行排序。我现在遇到的问题是减慢条形的排序速度,因此可以看到每种算法的工作原理。 这是我目前所拥有的:

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()

如您所见,我尝试在插入排序函数中使用after() 方法,但它所做的只是冻结窗口并使其不响应。如果没有这种方法,它可以正常工作,只是没有以可见的速度进行。

【问题讨论】:

  • 呵呵,不错,你快到了;你想要的动画需要一个第一次就正确的模式;有关详细信息,请参阅我的答案。您的 GUI 冻结问题发生是因为您正在从一个紧密的循环中重复启动一个带有回调 (window.after()) 的计时器。通常,您希望避免这种情况,并使用after 替换循环,而不是循环内。
  • 嗨,我有一个类似的问题link 我正在尝试解决,有人告诉我这应该可以解决我的问题,但我是 python 的菜鸟,所以我想知道你是否可以解释对我来说 swap_two_pos() 方法是如何工作的?...我知道它应该做什么,但我不明白它是如何做到的...感谢您的帮助

标签: python python-3.x tkinter tkinter-canvas algorithm-animation


【解决方案1】:

利用生成器函数(关键字yield),您可以暂停代码中间循环的执行以花时间显示已更改的画布元素,更新计算等,然后恢复执行在生成器上反复调用next,直到排序完成。

我在代码中添加了一些 cmets,但最好的方法可能是盯着它看,直到你说服自己它按预期工作。这是您需要了解的模式,因为它对构建您想要的那种动画非常有用 构建。

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()

【讨论】:

  • 你是救生员!非常感谢您对这个项目的帮助!我认为这是一件小而有趣的事情,最终对我来说是一次巨大的学习经历。我显然还有很多东西要学,但你给了我很大的帮助。再次感谢
  • 动画算法是一种很好的学习方式,但并不像看起来那么简单,需要一些微妙的机制来增加算法本身的代码。确实有很多要学习的实现,我很高兴能帮助你。也许您想在完成后发布一个指向您的代码的链接?我很乐意看看它,我相信其他人也一样。
  • 当然!我一定会在完成/发布时通知您。
【解决方案2】:

耗时的函数是“swap_two_pos()”,这是灾难性的,你在每个循环中运行它,你应该做的是完成排序然后重新绘制条形, 下面是修改后的代码,在没有 GUI 冻结的情况下工作,添加了一个函数并删除了“swap_two_pos()”函数。

import tkinter as tk
import random


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
    refresh()
        # swap_two_pos(barList[pos],cursorBar)

def refresh():
    canvas.delete('all')
    xstart = 5
    xend = 15

    for i, length in enumerate(lengthList): #range(1,60):
        y = random.randint(1,390)
        x = canvas.create_rectangle(xstart,length,xend,395, fill='red')
        barList.append(x)
        xstart += 10
        xend += 10


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()

【讨论】:

  • swap_two_pos 是通过仅更新已更改的图形元素来为画布设置动画的关键;无需重新绘制整个画布。
猜你喜欢
  • 1970-01-01
  • 2015-09-10
  • 1970-01-01
  • 2011-11-26
  • 1970-01-01
  • 1970-01-01
  • 2013-03-06
  • 1970-01-01
相关资源
最近更新 更多