Python Tkinter快速读取器

Python Tkinter快速读取器,python,tkinter,Python,Tkinter,我在用tkinter实现一个快速读取器程序时遇到了一个问题,这是因为它在大约5秒后挂起 我知道这是因为while循环,但我没有找到解决方法。也许在根主循环之外循环 代码如下: import tkinter import tkinter.ttk import tkinter.filedialog import tkinter.messagebox class FastReader(object): SLEEP_TIME = 250 def __init__(self):

我在用tkinter实现一个快速读取器程序时遇到了一个问题,这是因为它在大约5秒后挂起

我知道这是因为while循环,但我没有找到解决方法。也许在根主循环之外循环

代码如下:

import tkinter
import tkinter.ttk
import tkinter.filedialog
import tkinter.messagebox

class FastReader(object):

    SLEEP_TIME = 250

    def __init__(self):
        self.filename = self.getFile()
        self.running = True

    def window(self):
        self.root = tkinter.Tk()
        self.root.wm_title("Fast Reader.")

        self.text = tkinter.ttk.Label(self.root, text = "word")
        self.text.grid(row = 0, column = 0)

        # Stop button
        self.stop = tkinter.ttk.Button(self.root, text = "Stop", command = lambda: self.close())
        self.stop.grid(row = 1, column = 1, columnspan = 2)

        while self.running:

            for word in self.getWords():
                self.text.after(FastReader.SLEEP_TIME)
                self.text.config(text = word)

            self.text.update_idletasks()
            self.root.update_idletasks()
            self.root.mainloop()    

    def close(self):
        self.running = False

    def getFile(self):
        file_ = tkinter.filedialog.askopenfilename()
        return file_

    def getWords(self):
        with open(self.filename) as file_:
            for line in file_:
                for word in line.strip("\n").split(" "):
                    yield word

if __name__ == "__main__":
    fr = FastReader()
    fr.window()

您的程序似乎挂起,因为您在给事件循环更新显示的机会之前处理了所有单词,然后在无限循环中调用事件循环

一个很好的经验法则是永远不要在Tkinter GUI中拥有自己的无限循环。您已经有一个无限循环在运行,所以请充分利用它

您应该编写一个函数,将下一个单词放入标签中,然后在将来再次调用自己。它看起来有点递归,但不完全是。您只是在事件循环必须完成的事情列表中添加了一些内容。因为它不是严格递归的,所以您不必担心堆栈空间不足

一个简单的第一步是首先一次读入所有单词。然后,程序将删除列表中的第一个单词并将其显示在标签中。如果在此之后列表不为空,请安排命令在延迟后再次运行。它看起来像这样:

def showNextWord(self):
    word = self.words.pop(0)
    self.text.configure(text=word)
    if len(self.words) > 0:
        self.root.after(self.SLEEP_TIME, self.showNextWord)
然后,您可以将while语句替换为两个语句:一个用于获取单词的完整列表,另一个用于显示第一个单词:

    self.words = self.getWords()
    self.showNextWord()

当然,您需要修改
getWords()
以立即返回整个列表。您可以使用生成器,但它增加了一点似乎不必要的复杂性,除非您计划显示数百万字(每秒4个字,将运行数天以上)。

繁重的工作应该在单独的线程中完成。@nymk:这不是繁重的工作。Tkinter完全能够在主线程中处理这个问题。实际上,它不能。它只是挂起,挂起是因为你让它在处理完所有单词后挂起
mainloop()
是一个无限循环,您正在无限循环中调用它。解决方案不是增加线程的复杂性,而是消除嵌套无限循环的复杂性。将
mainloop
部分移到代码末尾。然后将while循环放入另一个线程。