You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何限制Tkinter按钮点击仅同时打开1个Toplevel窗口

问题描述

使用Tkinter开发带日期选择功能的Python应用时,交互逻辑为点击主窗口按钮弹出日期选择子窗口,选完日期后将值回传到主界面。但现有实现存在缺陷:多次点击按钮会生成大量重复的Toplevel子窗口,效果如下图:
重复生成Toplevel窗口的问题示例

现有问题代码如下:

import tkinter as tk

class App(tk.Tk):
    """ Allows user to pick a date """
    def __init__(self):
        super().__init__()

        self.label_a01 = tk.Label(text = 'Selected date is:')

        self.var_selected_date = tk.StringVar(value = "Noting selected")
        self.label_selected_date = tk.Label(textvariable = self.var_selected_date)

        self.button_a01 = tk.Button(self,
            text="Select the first day", command = self.select_a_date)
        self.button_a02 = tk.Button(self,
            text="Select the last day", command = self.select_a_date)

        self.label_a01.pack()
        self.label_selected_date.pack()
        self.button_a01.pack(pady = 5)
        self.button_a02.pack(pady = 5)

    def select_a_date(self):
        self.window01 = tk.Toplevel(self)
        self.label_b01 = tk.Label(self.window01, text = 'Please select a date from the calendar')
        self.button_b01 = tk.Button(self.window01, text="01 Jan 2022", command = lambda: self.update_date_to_mainframe(self.button_a01, "01 Jan 2022"))
        self.button_b02 = tk.Button(self.window01, text="31 Dec 2022", command = lambda: self.update_date_to_mainframe(self.button_a02, "31 Dec 2022"))

        self.label_b01.pack(pady = 5)
        self.button_b01.pack(pady = 5)
        self.button_b02.pack(pady = 5)

    def update_date_to_mainframe(self, widget, text_to_update):
        widget.config(text = text_to_update)

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

if __name__ == '__main__':
    main()
解决方案

核心逻辑是维护子窗口的引用,新建窗口前先做存在性校验,避免重复创建,具体实现步骤:

  • 在主窗口初始化阶段,新增一个实例变量存储日期选择子窗口的引用,初始值设为None
  • 触发打开子窗口的方法时,先校验引用对应的窗口是否存在:如果已存在,直接将已打开的窗口提升到最上层、获取焦点,终止后续新建窗口的逻辑
  • 为子窗口绑定关闭事件(包括点击右上角关闭按钮、选择完日期主动关闭窗口两种场景),窗口关闭后将引用重置为None,保证后续可以正常重新唤起窗口
修正后可运行代码
import tkinter as tk

class App(tk.Tk):
    """ Allows user to pick a date """
    def __init__(self):
        super().__init__()
        # 初始化子窗口引用,初始为None
        self.date_pick_window = None

        self.label_a01 = tk.Label(text = 'Selected date is:')

        self.var_selected_date = tk.StringVar(value = "Nothing selected")
        self.label_selected_date = tk.Label(textvariable = self.var_selected_date)

        self.button_a01 = tk.Button(self,
            text="Select the first day", command = self.select_a_date)
        self.button_a02 = tk.Button(self,
            text="Select the last day", command = self.select_a_date)

        self.label_a01.pack()
        self.label_selected_date.pack()
        self.button_a01.pack(pady = 5)
        self.button_a02.pack(pady = 5)

    def select_a_date(self):
        # 先判断子窗口是否已经存在
        if self.date_pick_window is not None and self.date_pick_window.winfo_exists():
            # 窗口已存在,提至最前并获取焦点,不新建
            self.date_pick_window.lift()
            self.date_pick_window.focus_force()
            return
        
        # 不存在才新建窗口
        self.date_pick_window = tk.Toplevel(self)
        # 绑定窗口右上角关闭事件,关闭时重置引用
        self.date_pick_window.protocol("WM_DELETE_WINDOW", self._close_date_pick_window)

        self.label_b01 = tk.Label(self.date_pick_window, text = 'Please select a date from the calendar')
        self.button_b01 = tk.Button(self.date_pick_window, text="01 Jan 2022", 
            command = lambda: self.update_date_to_mainframe(self.button_a01, "01 Jan 2022"))
        self.button_b02 = tk.Button(self.date_pick_window, text="31 Dec 2022", 
            command = lambda: self.update_date_to_mainframe(self.button_a02, "31 Dec 2022"))

        self.label_b01.pack(pady = 5)
        self.button_b01.pack(pady = 5)
        self.button_b02.pack(pady = 5)

    def _close_date_pick_window(self):
        """关闭日期选择窗口并重置引用"""
        if self.date_pick_window is not None and self.date_pick_window.winfo_exists():
            self.date_pick_window.destroy()
        self.date_pick_window = None

    def update_date_to_mainframe(self, widget, text_to_update):
        widget.config(text = text_to_update)
        # 选完日期后自动关闭子窗口
        self._close_date_pick_window()
        # 同步更新选中日期展示
        self.var_selected_date.set(text_to_update)

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

if __name__ == '__main__':
    main()

说明:修正代码同时补全了原代码里选中日期后主界面展示值未更新、初始提示拼写错误(Noting改为Nothing)的小问题,选择完日期后会自动关闭选择窗口,交互更顺畅。

内容的提问来源于stack exchange,提问作者jakelime

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 18:18:25