为什么在tkinter上销毁两个不同的主循环窗口会冻结程序?

时间:2017-02-26 00:22:33

标签: python user-interface tkinter

我正在为学校做一个倒计时(如电视节目)项目,这涉及用户在tkinter Text()条目中输入响应的一小段时间(在时间限制下)。感谢此网站上的先前答案,我知道如何制作屏幕上的tkinter计时器,并且我使用了一个非常基本的线程设备来与用户Text()输入窗口同时打开它。

如果我不想在用户提前关闭Text()输入窗口时关闭时钟,那么它可以正常工作,但在用户完成后继续打勾时感觉很傻。所以我还插入了一些代码(" WM_DELETE_WINDOW"等),允许用户关闭输入窗口并提前提交答案,同时关闭计时器。在此之后,这使程序冻结,我不知道如何解决它。这是我使用的代码的简化版本(问题完全相同):

import threading
from tkinter import *

def backgroundWindow():
    global root
    root = Tk()
    root.title("Happening at the same time")
    root.mainloop()

threadWindow = threading.Thread(target=backgroundWindow)
threadWindow.start()

print() # The threading did NOT work without this print statement (?)

def destroyer():
    global root
    newRoot.destroy()
    root.destroy()

newRoot = Tk()
newRoot.title("Simultaneous Window")
newRoot.protocol("WM_DELETE_WINDOW", destroyer)

newRoot.mainloop()

# program freezes up here (but does not finish)

print("Now the program continues")  # does NOT get printed

我也很困惑为什么第二个窗口在中间看不到看似随机的print语句时出现错误。我试着谷歌搜索所有这些,但没有成功;这是一个非常小众的问题。

编辑(我混淆了自己):

我知道有人已经回答了这个问题,而且效果很好 - 当关闭其中一个窗口时,两个窗口都会自动关闭 - 但是由于我对课程的了解很朦胧,我无法让它与我的倒计时程序一起工作,我使用了一个带有一个窗口的类(这是我设法让网格方法工作的唯一方法)。

原则是一样的,所以我错误地认为我可以解决它,但是关于运行涉及另一个类的类的事情让我感到难过。两个窗口的内容混乱(例如,一个窗口的bg颜色无意中应用于另一个窗口),当我关闭其中一个窗口时,另一个窗口仍然打开。有没有人有任何关于运行类和非类,与类的提示?这是没有线程的代码:

def conundrumClock():
    global threading, playerAnswer, playerNumber, tkinter
def clock():
    global threading, rootA
    def countdown(time): # (I copied this from a previous answer by Bryan Oakley)
        global threading
        if time == -1:
            rootA.destroy()
        else:
            if time == 0:
                label.configure(text="TIME UP!")
            else:
                label.configure(text="Time remaining: %d seconds" % time)

            rootA.after(1000, countdown, time-1)

    rootA = Tk() 
    rootA.title("COUNTDOWN CLOCK")
    label = Label(rootA, width=30)
    label.pack(padx=20, pady=20)
    countdown(15)
    rootA.mainloop() # I am aiming to get rid of this

    class AnswerEntry(tk.Tk):

        def __init__(self):
            tk.Tk.__init__(self)

            self.anagram = Label(self, text="Your anagram solution", bg="light cyan", font=(None, 15), width=18, height=2, anchor=E) 
            self.numerics = Label(self, text="Player number (1 or 2)", bg="light cyan", font=(None, 14), width=19, height=2, padx=4, anchor=E)
            self.anagram.grid(row=1, sticky=E, pady=1)
            self.numerics.grid(row=2, padx=(7,0), pady=5)

            self.solution = tk.Text(self, height=2, width=17,font=(None,15))
            self.number = tk.Text(self, height=2, width=17, font=(None, 15))
            self.solution.grid(row=1, column=1)
            self.number.grid(row=2, column=1) 

            self.button1 = tk.Button(self, text=" SUBMIT YOUR NAME ", bg="cyan", fg="black", width=20, height=1, font=(None, 11), anchor=W, command=self.answer)

            self.button2 = tk.Button(self, text="SUBMIT PLAYER NUMBER ", bg="cyan", fg="black", font=(None, 11), padx=3, command=self.the_number)
            self.button1.grid(row=100, column=1, sticky=E)
            self.button2.grid(row=100, column=0, sticky=W)

        def answer(self):
            global playerAnswer
            playerAnswer = self.solution.get('1.0', END)
        def the_number(self):
            global playerNumber
            playerNumber = self.number.get('1.0', END)

root = AnswerEntry()
root.title("REMEMBER TO PRESS THE BUTTONS!")
root.geometry("500x150")
root["bg"] = "aquamarine"
center(root) # (this is a function I got to center the window from a previous answer) 

第一个窗口显示一个计时器(倒计时),第二个窗口显示一个框,您可以在其中输入anagram的解决方案和您的玩家编号。下面的按钮允许玩家提交'这两件事(我在按钮命令程序中使用了' self。(text_entry_information).get' thingy)。 我没有试图让这些窗口同时运行,因为我做错了,这会让问题混乱。

再一次,任何帮助将不胜感激!

2 个答案:

答案 0 :(得分:3)

您不能超过1 mainloop()。即使您使用线程也不会,因为它会导致问题。但是你可以拥有多个tk.Toplevel窗口,它的工作方式相同。 下面是一个例子:

import tkinter as tk

class Demo1:
    def __init__(self, master):
        self.master = master
        self.master.title('Happening at the same time')
        self.newWindow = tk.Toplevel(self.master)
        self.app = Demo2(self.newWindow)

class Demo2:
    def __init__(self, master):
        self.master = master
        self.master.title("Simultaneous Window")

def main(): 
    root = tk.Tk()
    app = Demo1(root)
    root.mainloop()

if __name__ == '__main__':
    main()

如您所见,我只有一个mainloop()

答案 1 :(得分:1)

尝试更频繁地导入tkinter,因为它对命名空间更好。这个答案是为了解决你的编辑。

import tkinter as tk

def clock(root):
    global rootA
    def countdown(time): 
        if time == -1:
            destroyer() #invokes the destroy function 
        else:
            if time == 0:
                label.configure(text="TIME UP!")
            else:
                label.configure(text="Time remaining: %d seconds" % time)

            rootA.after(1000, countdown, time-1)

    rootA = tk.Toplevel(root) #Made it a tk.Toplevel instead of a new root
    rootA.protocol("WM_DELETE_WINDOW", destroyer) #set the window close protocol
    rootA.title("COUNTDOWN CLOCK")
    label = tk.Label(rootA, width=30)
    label.pack(padx=20, pady=20)
    countdown(15)

class AnswerEntry(tk.Tk):

    def __init__(self):
        super().__init__() #I changed tk.Tk.__init__(self) to this
        self.title("REMEMBER TO PRESS THE BUTTONS!") #I placed them here since they are the same thing
        self.geometry("500x150")
        self["bg"] = "aquamarine"
        self.protocol("WM_DELETE_WINDOW", destroyer)
        self.anagram = tk.Label(self, text="Your anagram solution", bg="light cyan", font=(None, 15), width=18, height=2, anchor=tk.E) 
        self.numerics = tk.Label(self, text="Player number (1 or 2)", bg="light cyan", font=(None, 14), width=19, height=2, padx=4, anchor=tk.E)
        self.anagram.grid(row=1, sticky=tk.E, pady=1)#Changed all your 'W' and 'E'... to 'tk.W' and 'tk.E'
        self.numerics.grid(row=2, padx=(7,0), pady=5)

        self.solution = tk.Text(self, height=2, width=17,font=(None,15))
        self.number = tk.Text(self, height=2, width=17, font=(None, 15))
        self.solution.grid(row=1, column=1)
        self.number.grid(row=2, column=1) 

        self.button1 = tk.Button(self, text=" SUBMIT YOUR NAME ", bg="cyan", fg="black", width=20, height=1, font=(None, 11), anchor=tk.W, command=self.answer)

        self.button2 = tk.Button(self, text="SUBMIT PLAYER NUMBER ", bg="cyan", fg="black", font=(None, 11), padx=3, command=self.the_number)
        self.button1.grid(row=100, column=1, sticky=tk.E)
        self.button2.grid(row=100, column=0, sticky=tk.W)

    def answer(self):
        global playerAnswer
        playerAnswer = self.solution.get('1.0', tk.END)
    def the_number(self):
        global playerNumber
        playerNumber = self.number.get('1.0', tk.END)

def destroyer():
    rootA.destroy() #When the window closes
    root.destroy()

if __name__ == '__main__':
    root = AnswerEntry()
    clock(root)
    root.mainloop()

下次请写一个新问题,以便有更多人可以参与。由于您的编辑是一个完全不同的问题。