Python Tkinter异步错误…没有多线程?

Python Tkinter异步错误…没有多线程?,python,timer,tkinter,Python,Timer,Tkinter,我最近需要用Python做一些GUI工作,偶然发现了Tkinter。在很大程度上,我喜欢它;它是干净的,大部分是直观和简短的。然而,对我来说,有一个小小的症结:有时它不知从何而来。程序将连续正常运行五次,然后第六次,在运行到一半时将冻结,我将得到错误 Tcl_AsyncDelete: async handler deleted by the wrong thread 我在这个网站和其他网站上为解决这个问题所做的努力都与多线程有关,但我不使用多线程。无论如何,没有明确地说。我怀疑GUI中有一个计

我最近需要用Python做一些GUI工作,偶然发现了Tkinter。在很大程度上,我喜欢它;它是干净的,大部分是直观和简短的。然而,对我来说,有一个小小的症结:有时它不知从何而来。程序将连续正常运行五次,然后第六次,在运行到一半时将冻结,我将得到错误

Tcl_AsyncDelete: async handler deleted by the wrong thread
我在这个网站和其他网站上为解决这个问题所做的努力都与多线程有关,但我不使用多线程。无论如何,没有明确地说。我怀疑GUI中有一个计时器这一事实是罪魁祸首,但我无法弄清楚为什么会出现错误,或者为什么它如此罕见

下面是我写的最短的tkinter程序。所有这些都会发生这种情况,所以我想在这里最容易看到这个问题。非常感谢您提供的任何帮助,请不要在不告诉我它如何应用于我的代码的情况下向我指出另一个解决方案,因为我向您保证,我已经看过它,它要么不适用,要么我不理解它是如何应用的。我不介意错过答案,但我对tkinter(以及一般的多线程)是新手,所以我可能需要更明确地告诉它

这段代码是为了模拟我在网上看到的一个游戏节目。大部分都可以安全地忽略,但我将其全部粘贴在这里,因为我不知道错误来自何处

import re
from tkinter import Tk, Frame, DISABLED, Button, Label, font, NORMAL, ttk
from random import choice
import winsound

class Question:
    def __init__(self, t, a, o):
        self.text = t.strip().capitalize() + "?"
        self.answer = a.strip().title()
        self.options = o
        self.firstIsRight = self.answer.lower() == self.options[0].lower()
        assert self.firstIsRight or self.answer.lower() == self.options[1].lower(), self

    def __eq__(self, other):
        return self.text == other.text

    def __repr__(self):
        return "{1} or {2}, {0}".format(self.text, self.options[0], self.options[1])

class Application(Frame):
    def __init__(self, master=None):
        self.setup()
        Frame.__init__(self, master)
        self.grid()
        self.customFont = font.Font(family="Times New Roman", size=30)
        self.createWidgets()

    def setup(self):
        self.questions = []
        with open("twentyone.txt",'r') as file:
            for line in file:
                groups = re.split("[,\.\?]",line)
                answers = re.split(" or ",groups[0])
                self.questions.append(Question(groups[1], groups[2], answers))

    def createWidgets(self):
        self.gamePanel = Frame(self)
        self.gamePanel.grid(column=0,row=0)
        self.displayPanel = Frame(self)
        self.displayPanel.grid(column=0,row=1)
        self.buttonPanel = Frame(self)
        self.buttonPanel.grid(column=0,row=2)

        self.QUIT = Button(self.buttonPanel,text="QUIT",font=self.customFont,command=self.quit)
        self.QUIT.grid(row=0,column=2)

        self.BEGIN = Button(self.buttonPanel, text="BEGIN",font=self.customFont, command = self.begin)
        self.BEGIN.grid(row=0,column=0)

        self.STOP = Button(self.buttonPanel, text="STOP",font=self.customFont, command = self.stop)
        self.STOP.grid(row=0,column=1)
        self.STOP["state"] = DISABLED

        self.TITLE = Label(self.gamePanel,text="21 Questions Wrong",font=self.customFont,bg="Black",fg="White")
        self.TITLE.grid(columnspan=2)
        self.questionText = Label(self.gamePanel,text="Questions go here",font=self.customFont,wraplength=400)
        self.questionText.grid(row=1,columnspan=2)
        self.leftChoice = Button(self.gamePanel,text="Option 1",font=self.customFont)
        self.leftChoice.grid(row=2,column=0)
        self.rightChoice = Button(self.gamePanel,text="Option 2",font=self.customFont)
        self.rightChoice.grid(row=2,column=1)

        self.timerText = Label(self.displayPanel, text="150",font=self.customFont)
        self.timerText.grid(row=0)
        self.progress = ttk.Progressbar(self.displayPanel, length=100,maximum=22)
        self.progress.grid(row=0,column=1,padx=10)

    def begin(self):       
        self.timer(250)
        self.asked = []
        self.STOP["state"] = NORMAL
        self.leftChoice["state"] = NORMAL
        self.rightChoice["state"] = NORMAL
        self.restart = False
        self.askNewQuestion()

    def askNewQuestion(self):   
        if self.restart:
            self.currentQuestion = self.asked[int(self.progress["value"])]
        else:
            self.currentQuestion = choice([i for i in self.questions if i not in self.asked])
            self.asked.append(self.currentQuestion)
        self.questionDisplay()

    def questionDisplay(self):
        self.questionText["text"] = self.currentQuestion.text
        self.leftChoice["text"] = self.currentQuestion.options[0]
        self.rightChoice["text"] = self.currentQuestion.options[1]
        if self.currentQuestion.firstIsRight:
            self.leftChoice["command"] = self.correct 
            self.rightChoice["command"] = self.wrong
        else:
            self.leftChoice["command"] = self.wrong
            self.rightChoice["command"] = self.correct

    def correct(self):
        self.progress.step()
        if self.progress["value"] >= 21:
            self.gameOver(True, 21)
        else:
            if self.progress["value"] >= len(self.asked):
                self.restart = False
            self.askNewQuestion()

    def wrong(self):
        self.restart = True
        self.progress["value"] = 0
        winsound.Beep(750,700)
        self.askNewQuestion()

    def stop(self):
        self.after_cancel(self.timerAfter)
        self.BEGIN["state"] = NORMAL
        self.STOP["state"] = DISABLED

    def gameOver(self, success, longest):
        self.after_cancel(self.timerAfter)
        self.BEGIN["state"] = NORMAL
        self.STOP["state"] = DISABLED
        self.questionText["text"] = "Congratulations!" if success else "Too Bad!"
        self.leftChoice["text"] = "Game"
        self.leftChoice["state"] = DISABLED
        self.rightChoice["text"] = "Over"
        self.rightChoice["state"] = DISABLED
        self.showPoints(success, longest)

    def showPoints(self, s, l):
        if s:
            timeTaken = max(0, 100-int(self.timerText["text"]))
            print("You scored {0} points".format(1000-10*timeTaken))
        else:
            print("You scored no points")

    def timer(self, time):
        self.BEGIN["state"] = DISABLED
        self.STOP["state"] = NORMAL
        self.runTimer(time)

    def runTimer(self, current=None, resume=False):
        if current is not None:
            self.current = current

        self.timerText["text"] = self.current

        if self.current == 0:
            self.gameOver(False, len(self.asked)-1)
        else:
            self.current -= 1
            self.timerAfter = self.after(1000,self.runTimer)


root = Tk()
app = Application(master=root)
app.master.title("Score Calculator!")
app.anchor("center")
root.mainloop()
root.destroy()

你能把你的代码编辑到一个最短的长度,这样我们就不必去挖掘所有不相关的部分吗?winsound可能会使用线程,这样它可以在程序继续进行的时候播放更长的声音,但我不明白为什么这会让Tkinter不高兴。如果你取消winsound的使用,问题会消失吗?@Kevin和Bryan,winsound不是问题所在。我用tkinter编写的其他程序没有使用它,并且遇到了相同的问题。我也试着从这一行中删除它,但这并没有解决问题。groups=re.split(“[,\。\?]”,line)-->文件的最后一行可能有一个新行,或者一个空行,等等。因此这不会返回任何结果。添加一些打印语句,这样您就可以知道它能走多远。您能将代码编辑到一个较低的级别,这样我们就不必深入研究所有不相关的部分吗?winsound可能使用线程,这样它可以在程序的其余部分继续运行时播放更长的声音,但我不明白为什么这会让Tkinter不高兴。如果您取消winsound的使用,问题解决了吗?@Kevin和Bryan,winsound不是问题所在。我用tkinter编写的其他程序没有使用它,并且遇到了相同的问题。我也试着从这一行中删除它,但这并没有解决问题。groups=re.split(“[,\。\?]”,line)-->文件的最后一行可能有一个新行,或者一个空行,等等。因此这不会返回任何结果。添加一些打印语句,这样您就可以知道它能走多远。您能将代码编辑到一个较低的级别,这样我们就不必深入研究所有不相关的部分吗?winsound可能使用线程,这样它可以在程序的其余部分继续运行时播放更长的声音,但我不明白为什么这会让Tkinter不高兴。如果您取消winsound的使用,问题解决了吗?@Kevin和Bryan,winsound不是问题所在。我用tkinter编写的其他程序没有使用它,并且遇到了相同的问题。我也试着从这一行中删除它,但这并没有解决问题。groups=re.split(“[,\。\?]”,line)-->文件的最后一行可能有一个新行,或者一个空行,等等。因此这不会返回任何结果。添加一些打印语句,这样您就可以知道它能走多远。