Tkinter文本窗口,打开和关闭时更新

时间:2017-07-10 18:28:51

标签: python tkinter

我正在尝试创建一个Tkinter应用程序,当您按下按钮时,会打开一个新窗口,该窗口会不断更新有关程序某些部分的文本。我的问题是我试图将文本添加到屏幕的代码部分。这就是我写的:

import tkinter as tk
import time

class TextWindow(tk.Frame):

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.textArea = tk.Text(self, height = 10, width = 30)
        self.textArea.pack(side = "left", fill = "y")

        bar = tk.Scrollbar(self)
        bar.pack(side = "right", fill = "y")
        bar.config(command = self.textArea.yview)

    def output(self, value):
        outputVal = str(value)
        self.textArea.inser('end', "{0}\n".format(outputVal))
        self.textArea.see('end')

def openWindow():
    textWindow = tk.Toplevel(root)
    textFrame = TextWindow(textWindow)
    textFrame.pack()
    value = 0.0
    alive = True
    while alive:
        if textWindow.winfo_exists:
            value = value + 0.1
            textFrame.output(value)
            time.sleep(0.1)
        else:
            alive = False

root = tk.Tk
btn = tk.Button(root, text = "Click", command = openWindow)
btn.pack()
root.mainloop()

当我在while方法中注释掉openWindow循环时,窗口会打开和关闭,然后重新打开,没问题。但是当代码存在时,我按下按钮时从未看到窗口。

我尝试通过IDLE调试器运行它,我没有收到任何错误,并且所有内容都在循环中运行,但Window仍然没有出现。我的问题是什么?

2 个答案:

答案 0 :(得分:1)

Jason S给出的答案并不是一个好例子。您只需使用after()即可避免任何睡眠问题。不要满足于“有点作品”。

以下介绍如何在不遇到与sleep()和tkinter相关的问题的情况下完成所需的工作。

首先,您输入Tk()错误。不要tk.Tktk.Tk()

现在让我们将整个程序移动到一个类中。这将使我们能够使用类属性并使事情更容易使用。

在这里,我们创建一个名为guiapp(tk.Frame):的类,您可以将其命名为您想要的名称,但这仅仅是我的示例。然后确保使用root传递guiapp(root),以便我们可以在tk.Tk()实例上使用此类。这将显示在实例化类的程序的底部。

因为我们已将root传递给该类,所以我们可以在我们的self.master属性上放置打开Toplevel窗口的按钮。

更新:更改了数据在Toplevel中发送到文本框的方式,以便我们保留信息,以防您想重新打开顶级。根据你的评论。

import tkinter as tk


class guiapp(tk.Frame):

    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.master = master
        self.value = 0.0
        self.alive = True
        self.list_for_toplevel = [] # added list to retain values for Toplevel
        btn = tk.Button(self.master, text = "Click", command = self.TextWindow)
        btn.pack()

这里我们添加方法来定义我们要创建的Topelevel。 因为所有内容都在这一个类中,所以我们可以将此Topelevel创建为self.master的Toplevel。在这个方法的最后,我们调用我添加的self.timed_loop()方法来管理程序的定时部分。更新:添加了对新功能的调用。

    def TextWindow(self):
        self.textWindow = tk.Toplevel(self.master)
        self.textFrame = tk.Frame(self.textWindow)
        self.textFrame.pack()
        self.textArea = tk.Text(self.textWindow, height = 10, width = 30)
        self.textArea.pack(side = "left", fill = "y")

        bar = tk.Scrollbar(self.textWindow)
        bar.pack(side = "right", fill = "y")
        bar.config(command = self.textArea.yview)
        self.alive = True
        self.add_list_first()

更新:添加了一个名为add_list_first(self):的新功能。这将允许我们首先添加存储在列表中的任何值,然后我们可以调用timed_loop()继续附加列表并进行计数。

    def add_list_first(self):
        for item in self.list_for_toplevel:
            self.textArea.insert('end', "{}\n".format(item))
            self.textArea.see('end')
        self.timed_loop()

这里我们创建了一个方法来执行Toplevel代码中的任务,该代码使用tkinter中的after()函数。永远1000等于1秒,所以如果你愿意,可以使用该计时器。 after()的第一部分是以毫秒为单位的时间,第二部分是被调用的函数。在这种情况下,它会调用自身继续循环,直到Toplevel窗口self.textWindow关闭或self.alive变量不再为True。 更新:我添加了一个for循环来插入列表而不是直接输入每个值。这样,如果我们想重新打开Toplevel,我们就可以保留数据。

    def timed_loop(self):
        if self.alive == True and tk.Toplevel.winfo_exists(self.textWindow):
            self.master.after(1000, self.timed_loop)
            self.value += 1
            self.list_for_toplevel.append(self.value)
            self.textArea.delete(1.0, "end-1c")
            for item in self.list_for_toplevel:
                self.textArea.insert('end', "{}\n".format(item))
                self.textArea.see('end')

        else:
            self.alive = False

这是在tkinter中开始课程的首选方法。如您所见,我们已将root创建为tk.Tk()并将root传递给类guiapp()。另请注意,我将该类的实例分配给变量名称myapp。如果您需要,这将允许我们从课堂外与班级互动。它在这种情况下没有什么区别,但我想我会添加它。

if __name__ == "__main__":

    root = tk.Tk()
    myapp = guiapp(root)
    root.mainloop()

以下是您要使用的复制粘贴版本。

import tkinter as tk


class guiapp(tk.Frame):

    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.master = master
        self.value = 0.0
        self.alive = True
        self.list_for_toplevel = []
        btn = tk.Button(self.master, text = "Click", command = self.TextWindow)
        btn.pack()

    def TextWindow(self):
        self.textWindow = tk.Toplevel(self.master)
        self.textFrame = tk.Frame(self.textWindow)
        self.textFrame.pack()
        self.textArea = tk.Text(self.textWindow, height = 10, width = 30)
        self.textArea.pack(side = "left", fill = "y")

        bar = tk.Scrollbar(self.textWindow)
        bar.pack(side = "right", fill = "y")
        bar.config(command = self.textArea.yview)
        self.alive = True
        self.add_list_first()

    def add_list_first(self):
        for item in self.list_for_toplevel:
            self.textArea.insert('end', "{}\n".format(item))
            self.textArea.see('end')
        self.timed_loop()

    def timed_loop(self):
        if self.alive == True and tk.Toplevel.winfo_exists(self.textWindow):
            self.master.after(1000, self.timed_loop)
            self.value += 1
            self.list_for_toplevel.append(self.value)
            outputVal = str(self.value)
            self.textArea.insert('end', "{0}\n".format(outputVal))
            self.textArea.see('end')


        else:
            self.alive = False



if __name__ == "__main__":

    root = tk.Tk()
    myapp = guiapp(root)
    root.mainloop()

答案 1 :(得分:0)

问题是你永远不会把控制权交还给Tkinter主循环。代码卡在while循环中执行,这意味着Tkinter永远不会刷新显示或处理任何其他事件。您可以通过在time.sleep(0.1)之前调用root.update()来强制更新,但这不是最佳选择,并且在睡眠时显示将无响应。根据您的工作情况,这可能已经足够了。

有关其他说明,请参阅here