我正在尝试创建一个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仍然没有出现。我的问题是什么?
答案 0 :(得分:1)
Jason S
给出的答案并不是一个好例子。您只需使用after()
即可避免任何睡眠问题。不要满足于“有点作品”。
以下介绍如何在不遇到与sleep()
和tkinter相关的问题的情况下完成所需的工作。
首先,您输入Tk()
错误。不要tk.Tk
做tk.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