Python、Tkinter:对生成器内的函数应用延迟

问题描述 投票:0回答:1

我正在 python tkinter 中制作一个应用程序来可视化算法。我想对

window.after()
内部调用的函数
partition()
应用延迟 (
quick_sort
)。
window.after()
似乎只影响
quick_sort
函数的“速度”。运行程序时,将速度设置得较低(在 sort_speed 范围内使用较高的数字),程序的速度波动会很大。我认为程序仅在 1 个排序部分延迟,而在其他部分则没有延迟?

算法文件:

"""Module to implement algorithms"""


def bubble_sort(data, draw_data):
    """Bubble sort algorithm"""

    size = len(data)

    for i in range(size):
        for j in range(0, size - i - 1):

            if data[j] > data[j + 1]:
                data[j], data[j + 1] = data[j + 1], data[j]

                draw_data(data, ['Green' if x == j + 1 else 'Red' for x in range(len(data))])
                yield

    draw_data(data, ['Green' for _ in range(len(data))])


def partition(data, draw_data, low, high):
    """Quick sort: Find partition position"""

    pivot = data[high]
    i = low - 1

    draw_data(data, get_color_array(len(data), low, high, i, i))

    for j in range(low, high):

        if data[j] <= pivot:
            draw_data(data, get_color_array(len(data), low, high, i, j, True))

            i += 1

            data[i], data[j] = data[j], data[i]

        draw_data(data, get_color_array(len(data), low, high, i, j))

    draw_data(data, get_color_array(len(data), low, high, i, low, True))

    data[i + 1], data[high] = data[high], data[i + 1]

    return i + 1


def quick_sort(data, draw_data, low, high):
    """Quick sort algorithm"""

    if low < high:
        pivot_index = partition(data, draw_data, low, high)

        yield

        yield from quick_sort(data, draw_data, low, pivot_index - 1)
        yield from quick_sort(data, draw_data, pivot_index + 1, high)


# Grey - Unsorted elements
# Blue - Pivot point element
# White - Sorted half/partition
# Red - Starting pointer
# Yellow - Ending pointer
# Green - All elements are sorted
def get_color_array(data_len, low, high, border, cur_id_x, is_swapping=False):
    """Quick sort: Get color for data"""
    
    color_array = []
    for i in range(data_len):

        if high <= i <= low:
            color_array.append('Grey')
        else:
            color_array.append('White')

        if i == low:
            color_array[i] = 'Blue'
        elif i == border:
            color_array[i] = 'Red'
        elif i == cur_id_x:
            color_array[i] = 'Yellow'

        if is_swapping:
            if i == border or i == cur_id_x:
                color_array[i] = 'Green'

    return color_array

主文件:

"""Module for visualizing data structures and algorithms"""


import tkinter as tk
from tkinter import HORIZONTAL
import random
import algorithms

def repeater(data_array, draw_data_update, speed_ms, gen=None):

    if not gen and navbar.curselection():

        algorithm.config(text=navbar.get(navbar.curselection()))

        match navbar.get(navbar.curselection()):
            case "Bubble sort":
                time_complexity.config(text="Time complexity: O(n^2)")
                gen = algorithms.bubble_sort(data_array, draw_data_update)
            case "Quick sort":
                time_complexity.config(text="Time complexity: O(n^2)")
                gen = algorithms.quick_sort(data_array, draw_data_update, 0, len(data_array) - 1)
            case _:
                pass

    try:
        next(gen)

        if running_animation:
            window.after(speed_ms, repeater, data_array, draw_data_update, speed_ms, gen)

        return

    except StopIteration:
        draw_data(data_array, ['Green' for _ in range(len(data))])


def regenerate():
    global data
    global running_animation

    if running_animation:
        running_animation = False

    min_value = int(min_entry.get())
    max_value = int(max_entry.get())
    input_value = int(input_size.get())

    data = []
    for _ in range(input_value):
        data.append(random.randint(min_value, max_value + 1))

    draw_data(data, ['Red' for _ in range(len(data))])


def draw_data(algo_data, color_list):
    sort_canvas.delete("all")

    canvas_height, canvas_width = 380, 500
    x_width = canvas_width / (len(algo_data) + 1)
    offset, spacing = 30, 10

    normalized_data = [i / max(algo_data) for i in algo_data]

    for i, height in enumerate(normalized_data):
        x0 = i * x_width + offset + spacing
        y0 = canvas_height - height * 340

        x1 = ((i + 1) * x_width + offset)
        y1 = canvas_height

        sort_canvas.create_rectangle(x0, y0, x1, y1, fill=color_list[i])
        sort_canvas.create_text(x0 + 2, y0, anchor='se', text=str(algo_data[i]))
    window.update()


def start_algorithm():
    global running_animation

    if not running_animation and navbar.curselection():
        running_animation = True

        speed_ms = int(sort_speed.get() * 1000)

        repeater(data, draw_data, speed_ms, generator)
        start_button.config(text='Stop')
    else:
        running_animation = False
        start_button.config(text='Start')


window = tk.Tk()
window.minsize(700, 580)

app_width, app_height = 700, 580
screen_width, screen_height = window.winfo_screenwidth(), window.winfo_screenheight()

mid_screen_x = (screen_width - app_width) // 2
mid_screen_y = (screen_height - app_height) // 2

window.title("StructViz")
window.iconbitmap("./assets/favicon.ico")
window.geometry(f'{app_width}x{app_height}+{mid_screen_x}+{mid_screen_y}')

select_alg = tk.StringVar()
data = []
generator = None
running_animation = False

window.columnconfigure(0, weight=0)
window.columnconfigure(1, weight=45)
window.rowconfigure((0, 1), weight=1)
window.rowconfigure(2, weight=45)

navbar = tk.Listbox(window, selectmode=tk.SINGLE, activestyle='none')
for item in ["Bubble sort", "Quick sort"]:
    navbar.insert(tk.END, item)
navbar.grid(row=0, column=0, rowspan=3, sticky='nsew')

user_settings = (tk.Frame(window, background='bisque2')
                 .grid(row=0, column=1, columnspan=2, rowspan=2, sticky='news', padx=7, pady=5))

algorithm = tk.Label(user_settings, text='No algorithm selected', background='bisque2')
algorithm.grid(row=0, column=1, sticky='nw', padx=10, pady=10)

time_complexity = tk.Label(user_settings, text='Time complexity: ', background='bisque2')
time_complexity.grid(row=0, column=1, sticky='sw', padx=10, pady=10)

input_size = tk.Scale(user_settings, from_=5, to=60, label='Amount', background='bisque2',
                      orient=HORIZONTAL, resolution=1, cursor='arrow')
input_size.grid(row=0, column=1, sticky='n', padx=10, pady=10)

min_entry = tk.Scale(user_settings, from_=0, to=10, resolution=1, background='bisque2',
                     orient=HORIZONTAL, label="Minimum Value")
min_entry.grid(row=1, column=1, sticky='s', padx=10, pady=10)

max_entry = tk.Scale(user_settings, from_=10, to=100, resolution=1, background='bisque2',
                     orient=HORIZONTAL, label="Maximum Value")
max_entry.grid(row=1, column=1, sticky='se', padx=10, pady=10)

sort_speed = tk.Scale(user_settings, from_=0.01, to=2.0, length=100, digits=3, background='bisque2',
                      resolution=0.01, orient=HORIZONTAL, label="Speed")
sort_speed.grid(row=0, column=1, sticky='ne', padx=10, pady=10)

start_button = (tk.Button(user_settings, text="Start", command=start_algorithm, background='bisque2'))
start_button.grid(row=1, column=1, sticky='nw', padx=10, pady=10)

tk.Button(user_settings, text="Regenerate", command=regenerate, background='bisque2').grid(
    row=1, column=1, sticky='sw', padx=10, pady=10)

sort_canvas = tk.Canvas(window, background='bisque2')
sort_canvas.grid(row=2, column=1, sticky='news', padx=5, pady=5)

window.mainloop()

尝试让排序速度恒定,但是在可视化快速排序时,程序时慢时快。

python algorithm tkinter generator yield
1个回答
0
投票

您需要对

partition()
中的交换操作应用延迟,而不是
partition()
本身。所以,就这样做吧。

...

def partition(data, draw_data, low, high):
    pivot = data[high]
    i = low - 1
    ...
    for j in range(low, high):

        if data[j] <= pivot:
            ...
            i += 1
            data[i], data[j] = data[j], data[i]
            yield
    ...
    data[i + 1], data[high] = data[high], data[i + 1]
    ...
    yield i + 1


def quick_sort(data, draw_data, low, high):
    if low < high:
        for item in partition(data, draw_data, low, high):
             if item is not None:
                 pivot_index = item
             yield

        yield from quick_sort(data, draw_data, low, pivot_index - 1)
        yield from quick_sort(data, draw_data, pivot_index + 1, high)

...
© www.soinside.com 2019 - 2024. All rights reserved.