Python Tkinter - 用退出按钮关闭子窗口

2 投票
2 回答
17290 浏览
提问于 2025-04-17 20:55

我有一个树莓派,连接了一个Piface扩展板。我做了一个图形界面(GUI),可以控制Piface板上的LED灯。界面上有一个按钮,点击后会打开一个新窗口,在这个窗口里再有一个按钮,按下这个按钮就会启动和停止一段小代码,让LED灯像《骑士神车》那样不停地上下闪烁,这个效果是通过在一个线程里使用循环实现的。

在这个新窗口里,我还添加了一个退出按钮。我想加一段代码,让我点击退出按钮时,可以关闭这个新窗口,然后返回到主窗口。我查了很多例子,但就是搞不清楚应该放什么代码,放在哪里。我试过用'quit',但那样会把整个程序都关闭。

看了很多例子,我觉得我可能不是很正确地创建新窗口,所以如果有更好的方法,请随时告诉我。

那么,有没有更好的方法呢?任何建议都非常感谢。

提前谢谢大家。

这是代码的一部分……

   def new_window(self):
        print('New Window')
        self.newWindow = tk.Toplevel(self.master)
        self.app = App2(self.newWindow)
        self.newWindow.grab_set()   # I added this line to stop opening multiple new windows

class App2:


    def __init__(self, master):

            frame = Frame(master)
            frame.pack()
            Label(frame, text='Turn LED ON').grid(row=0, column=0)
            Label(frame, text='Turn LED OFF').grid(row=0, column=1)

            self.button0 = Button(frame, text='Knight Rider OFF', command=self.convert0)
            self.button0.grid(row=2, column=0)
            self.LED0 = Label(frame, image=logo2)
            self.LED0.grid(row=2, column=1)

            self.button9 = Button(frame, text='Exit', command=self.close_window)
            self.button9.grid(row=3, column=0)


    def convert0(self, tog=[0]):

        tog[0] = not tog[0]
        if tog[0]:
            print('Knight Rider ON')
            self.button0.config(text='Knight Rider ON')
            t=threading.Thread(target=self.LED)
            t.start()
            self.signal = True    #added to stop thread
            self.LED0.config(image = logo)
        else:
            print('Knight Rider OFF')
            self.button0.config(text='Knight Rider OFF')
            self.signal = False   #added to stop thread
            self.LED0.config(image = logo2)

    def LED(self):
            while self.signal:   #added to stop thread

                a=0

                while self.signal:   #added to stop thread
                        pfio.digital_write(a,1) #turn on
                        sleep(0.05)
                        pfio.digital_write(a,0) #turn off
                        sleep(0.05)
                        a=a+1

                        if a==7:
                                break

                while self.signal:   #added to stop thread

                        pfio.digital_write(a,1) #turn on
                        sleep(0.05)
                        pfio.digital_write(a,0) #turn off
                        sleep(0.05)
                        a=a-1

                        if a==0:
                                break

    def close_window(self):
        print('Close Child window')
        #self.newWindow.destroy()   Not sure what to put here?

2 个回答

1

在Tk中,窗口是通过destroy方法来关闭的。所以如果你有一个对话框(toplevel),想要把它关掉,就调用它的destroy()方法。或者你也可以选择withdraw,这样对象仍然存在,但不再显示在屏幕上。如果想要重新显示它,就用deiconify()来恢复这个对话框。不过,通常我们更常用destroy来关闭窗口。下面是一个简单的例子,展示了如何创建和关闭一个子对话框:

import sys
from Tkinter import *

class App(Frame):
    def __init__(self, parent = None):
        Frame.__init__(self, parent)
        self.grid()

        self.button = Button(self, text = "Create Dialog", command=self.CreateDialog)
        self.button.grid()

    def CreateDialog(self):
        dialog = Toplevel(self)
        dialog.wm_title("Dialog window")
        dialog.wm_transient(self)
        dialog.wm_protocol("WM_DELETE_WINDOW", lambda: self.onDeleteChild(dialog))
        button = Button(dialog, text="Close", command=lambda: self.onDeleteChild(dialog))
        button.grid()

    def onDeleteChild(self, w):
        w.destroy()


def main():
    app = App()
    app.mainloop()

if __name__ == "__main__":
    sys.exit(main())

你还可以考虑在代码中使用定时器来控制LED循环,而不是用while循环。可以看看这个回答,它使用了Tk的after函数来在一定时间后运行代码。如果在处理函数中重新安排另一个after调用,就可以让某个函数在固定的时间间隔内运行,这样就不会阻塞事件处理,也不需要额外的线程。

5

如果你把 new_window 放到你的 App2 里,那就没问题了。

self.newWindow.destroy() 

这个操作会关闭窗口。这样做是正确的。窗口关闭后,里面的所有小部件也会被销毁。

quit() 会停止 mainloop()。在这种情况下,程序会在最后一行结束,同时也会销毁所有东西。

你肯定要使用 destroy

class App2:

    newWindow = None

    def close_window(self):
        print('Close Child window')
        if self.newWindow:
            try: self.newWindow.destroy()   
            except (): pass # fill in the error here
            self.newWindow = None

   def new_window(self):
        print('New Window')
        self.close_window()
        self.newWindow = tk.Toplevel(self.master)
        self.app = App2(self.newWindow)
        self.newWindow.grab_set()

    @classmethod
    def start_app(cls):
        window = tk.Tk(self.master)
        app = App2(window)
        return app

你不应该在线程中访问 Tkinter。可以看看其他的选择

我刚开始学习 Tkinter 时,也对 quitdestroy 感到困惑。

撰写回答