Окно Tkinter не обновляется до окончания цикла - PullRequest
3 голосов
/ 09 октября 2019

Я хотел создать визуализатор алгоритма сортировки с использованием python и решил использовать библиотеку Tkinter в качестве моего способа визуализации данных (если у кого-то есть лучшие библиотеки для использования, я открыт для предложений, я заглянул в matplotlib, нообескуражен). Моя проблема в том, что, сортируя массив, я хочу сделать своп, показать обновленный массив после свопинга, а затем продолжить сортировку;но в итоге происходит сортировка массива, а затем обновляется весь отсортированный массив.

import tkinter as tk
from tkinter import ttk
import random
import time

class SortingVisualizer(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.wm_title(self, "Sorting Visualizer")
        tk.Tk.wm_minsize(self, width=600, height=500)
        tk.Tk.wm_resizable(self, width=False, height=False)


        self.topFrame = tk.Frame(self)
        self.topFrame.grid(row=0, sticky='w')

        self.sortOptions = ['Select Algorithm','Bubble sort','Quicksort', 'Merge sort']
        self.optionVar = tk.StringVar()
        self.optionDrop = ttk.OptionMenu(self.topFrame, self.optionVar, *self.sortOptions)
        self.optionDrop.config(width=15)
        self.optionDrop.grid(row=0, column=1, sticky='ew')

        self.sortButton = ttk.Button(self.topFrame, text = "Sort", command = lambda: bubbleSort(self))
        self.sortButton.grid(row=0, column=2, sticky='w')
        self.genButton = ttk.Button(self.topFrame, text = "Generate New Array", command = self.newArray)
        self.genButton.grid(row=0, column=0)

        self.generateArray()

    def newArray(self):
        self.sortCanvas.destroy()
        self.generateArray()

    def generateArray(self):
        self.array = []
        self.numOperations = 0
        i = 0
        while i < 15:
            height = random.randint(15, 200)
            self.array.append(height)
            i = i + 1
        self.drawCanvas()

    def drawCanvas(self):
        self.sortCanvas = tk.Canvas(self, width=600, height=450)
        self.sortCanvas.grid(row=1)
        self.sortCanvas.create_line(15, 15, 585, 15)
        label = "Number of Operations: " + str(self.numOperations)
        self.numLabel = tk.Label(self.topFrame, text = label)
        self.numLabel.grid(row=1)

        bar_width = 20
        bar_gap = bar_width + 10
        start_x = 30
        start_y = 15
        for bar_height in self.array:
            x1 = start_x + bar_width
            y1 = start_y + bar_height
            self.sortCanvas.create_rectangle(start_x, start_y, x1, y1*2, fill='green')
            start_x = start_x + bar_gap

    def redrawCanvas(self):
        self.sortCanvas.destroy()
        self.drawCanvas()

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.after(300, self.redrawCanvas)

app = SortingVisualizer()
app.mainloop()

Я также попробовал app.after (300, self.redrawCanvas) и получил тот же результат

Ответы [ 3 ]

1 голос
/ 09 октября 2019

Просто альтернативное решение, кроме многопоточности. Раньше я сталкивался с проблемой, заключающейся в том, что при работе с функциями они могут одновременно получать доступ к некоторым внешним устройствам (я создал графический интерфейс для наблюдения за некоторым оборудованием) и вызывать конфликты. Используя .after (), tkinter будет обрабатывать порядок задач, чтобы избежать конфликтов.

Вы можете переопределить вашу функцию bubbleSort, чтобы каждая итерация цикла for изменялась на рекурсию, вызывая функцию снова.

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))
1 голос
/ 09 октября 2019

Вы сделали очень хорошую первую попытку, и вы были почти там. Я сделал некоторые изменения в вашем коде, и самое главное, добавил объект root (tk.Tk()), чтобы я мог выполнить root.update () для перерисовки до sort_canvas в новом методе blip_canvas. Чтобы избежать некоторого «мерцания», а не разрушать холст каждый раз, лучше удалять только элементы «bar». Кроме того, я позволил себе сменить некоторые имена переменных, чтобы сделать их немного более Pythonic (следует использовать подчеркивания, а не заглавные буквы) и добавил оператор if _name__ == '__main__'.

Посмотрите на приведенный ниже код.

import tkinter as tk
from tkinter import ttk
import random

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

Обратите внимание, что в bubble_sort вам не нужна переменная temp для обмена значениями массива [j] и массива [j +1]

Вместо того, чтобы использовать time.sleep(0.2) для установки задержки, которую я использовал:

self.root.update()
self.root.after(200)

, как предложено в Кнопка обновления после задержки

Вы также можете придерживаться своего исходного кода и просто внести несколько изменений.
1) Измените sortButton

self.sortButton = ttk.Button(self.topFrame, text = "Sort", command=self.bubbleSort)

2) Отступ метода bubbleSort для выравнивания с SortingVisualizer

3) Измените метод redrawCanvas на:

    def redrawCanvas(self):
        self.sortCanvas.destroy()
        self.drawCanvas()
        self.update()
        self.after(300)

и

4) в bubbleSort выполните вызов 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,это будет работать!

1 голос
/ 09 октября 2019

Вы можете Thread свою функцию bubbleSort. Также представляется более целесообразным, чтобы bubbleSort был методом класса:

import threading, time

class 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...