当前位置: 首页 > 知识库问答 >
问题:

Tkinter窗口直到循环完成后才更新

楚志强
2023-03-14

我一直想用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()

我也试过这个应用程序。在(300,self.redrawCanvas)之后,并得到相同的结果

共有3个答案

李勇
2023-03-14

只是线程之外的另一种解决方案。我曾经遇到过这样一个问题:通过线程化函数,它们可能同时访问一些外部设备(我创建了一个GUI来监视一些硬件),并导致冲突。通过使用。在()之后,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))
景鹏云
2023-03-14

您可以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)
拓拔麒
2023-03-14

你做了一次很好的第一次尝试,你几乎成功了。我对您的代码做了一些更改,最重要的是引入了root对象tk.tk()),这样我就可以使用root了。update()以新方法重新绘制到排序画布。为了避免一些“闪烁”,而不是每次都破坏画布,最好只删除“bar”元素。此外,我还随意更改了一些变量名,使其更具python风格(应该使用下划线而不是大写),并添加了if\u name\uuuuu=='\uuuu main\uuu'语句。

看看下面的代码。

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_排序中,不需要temp变量来交换数组[j]和数组[j 1]的值

而不是使用时间。sleep(0.2)设置我使用的延迟:

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

如延迟后更新按钮中所示

您也可以坚持原来的代码,只做一些更改
1)更改排序按钮

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

2) 缩进要与SortingVisualizer对齐的bubbleSort方法

3)更改方法redraCanvas:

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

4) 在bubbleSort中调用重画画布

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

瞧,它会起作用的!

 类似资料:
  • 因此,我不确定如何在.setOnAction事件期间计算节点的高度,我已尝试/不确定还要尝试什么我正在尝试在添加节点后查找vBox的高度,但它只是在添加新节点之前打印节点的高度 运行该示例,然后单击将标签添加到vBox并输出的按钮 实际结果: 预期结果: 但是如果你点击打印VBox高度按钮,它会显示正确的高度:

  • 问题内容: 我编写了一个程序,该程序基本上可以在按不同的按钮时绘制不同的数据。该程序可以在Windows下按预期工作,但是当我尝试将其移植到Linux(Red Hat v6)时,我遇到一个奇怪的问题:要绘制的窗口直到关闭主程序后才出现。无论我尝试绘制到哪个数字(图1,2等),或者尝试键入plt.show()等,都会发生这种情况。 我编写的程序几乎有1000行代码,但是我创建了一个具有相同问题的缩写

  • 问题内容: 我有该代码: 它可以很好地上传图像,但是问题是我找不到一种逐一上传图像的方法,我试图将 async 选项 设置为false, 但是它冻结了网络浏览器,直到所有图像都被上传为止,这不是我所需要的。想要,我想以某种方式模拟此 “ async:false” 选项以执行相同的操作,但不冻结Web浏览器。 这该怎么做 ? 问题答案: 您可以创建一个Promise数组,以便在所有Promise都解

  • 主要内容:创建一个空白窗口,设置窗的位置主窗口控件(window)是一切控件的基础,它好比是一台高速运转的机器,而其他控件则相当于这台机器上的部件,比如齿轮、链条、螺丝等等。由此我们知道,主窗口是一切控件的基础,所有的控件的都需要通过主窗口来显示。 Tkinter 提供了一些关于主窗口对象的常用方法,在本节对这些方法做简单的介绍。 创建一个空白窗口 Tkinter 能够很方便地创建一个空白窗口,示例代码如下: 程序运行结果如下: 图1:

  • 问题内容: 我显示了在Ajax调用之前和Ajax调用之后加载DOM的过程,我将其隐藏。由于某些原因,加载图像仅在ajax调用完成之后出现。结果是,除非我输入以下内容,否则加载图像甚至都不会出现。代码如下: 我已经在我的网站上的其他Ajax调用上进行了测试,并且发生了相同的问题。有人解决过这个吗?我正在使用Chrome 26。 编辑:我忘记指定我正在使用 同步 ajax调用。() 问题答案: 这取决

  • 问题内容: 我一直在尝试用Java制作GUI,而不是一直使用“静态”,并且遇到了“ SwingUtilities.invokeLater()”方法。我设法进行了所有设置,但是在运行应用程序时,JPanel上没有任何显示,直到我调整窗口大小为止。有没有解决的办法,或者我做错了吗? 这是我的代码: } 编辑:我知道密码JTextField是一个JPasswordField ..所以忽略它:P 问题答案