以编程方式按下工具栏上的“X”按钮?

Programmatically press the `X` button on the toobar?

我知道我可以使用 protocol("WM_DELETE_WINDOW", do_something) 拦截按下 X 按钮,但是我很难弄清楚如何激活此按钮或至少在按下此按钮时触发的协议.

情况是这样的。我有 2 classes。我的主要 Tk class 和我的 Menu class。当我设置命令以使用菜单中的 exit 按钮关闭程序时,我希望此按钮执行与 Tk [=39] 上的 X 按钮完全相同的操作=].

现在我知道我可以简单地调用传递给菜单的控制器 class 然后调用我构建的方法来处理关闭事件但是我正在尝试构建这个菜单 class这样我就不需要从菜单 class 中执行此操作。这将允许我在我构建的任何应用程序上使用菜单 class,几乎不需要编辑。

我找不到 post 或一些文档来告诉我如何以编程方式激活 "WM_DELETE_WINDOW" 协议。

如果不清楚我想要什么,这里有一张图片。只是我希望退出按钮与 X 按钮完全一样。

主要class:

import tkinter as tk
import PIP_MENU


class PIP(tk.Tk):
    def __init__(self):
        super().__init__()
        PIP_MENU.start(self)
        self.protocol("WM_DELETE_WINDOW", self.handle_close)

    def handle_close(self):
        print("Closing")
        self.quit()

if __name__ == '__main__':
    PIP().mainloop()

单独 .py 文件上的菜单 class:

import tkinter as tk

class Menu(tk.Menu):
    def __init__(self, controller):
        super().__init__()
        self.controller = controller
        controller.config(menu=self)
        file_menu = tk.Menu(self, tearoff=0)
        self.add_cascade(label="File", menu=file_menu)
        file_menu.add_command(label="Exit", command=self.handle_exit)

    def handle_exit(self):
        # What can I do here that will be handled by
        # protocol "WM_DELETE_WINDOW" of the main class?
        # All I can find is destroy() and quit()
        # But both of these do not get handled by "WM_DELETE_WINDOW".

def start(controller):
    Menu(controller)

I have not been able to find a post or some documentation that tells me how I can programmatically active the "WM_DELETE_WINDOW" protocol.

你不能。根据定义,WM_DELETE_WINDOW 协议来自 window 管理器。

捕获协议处理程序旨在让您有机会覆盖其行为。无论应用程序如何销毁,它都不是一种触发某些代码的方法。如果你想在 window 被销毁时 运行 一些代码,无论是通过用户点击 window 框架上的控件还是通过其他方式,正确的方法都是是绑定到根 window 上的 <Destroy> 事件。

您必须小心,因为每个小部件都会触发根 window 上的任何绑定。因此,当 event.widget 与根 window 相同时,您的绑定应该只 运行。

以下示例说明了该技术。有一个方法 handle_close 每当 window 被销毁时调用。无论是点击window框上的控件关闭window,还是点击"Close me!"按钮,代码还是运行s。

import tkinter as tk


class Example(tk.Tk):
    def __init__(self):
        super().__init__()
        self.bind("<Destroy>", self.handle_close)

        button = tk.Button(self, text="Close me!", command=self.destroy)
        button.pack()

    def handle_close(self, event):
        if event.widget == self:
            print("Closing")
        self.quit()

example = Example()
example.mainloop()

我不相信有调用特定 protocol 的方法,因为 protocol 似乎是特定事件监视。这是模块 class Tk:

的片段
class Tk(Misc, Wm):
    """Toplevel widget of Tk which represents mostly the main window
    of an application. It has an associated Tcl interpreter."""

    def _loadtk(self):

    ...

    self.protocol("WM_DELETE_WINDOW", self.destroy)

如您所见,默认情况下,模块本身将协议设置为 destroy()protocol()方法只是寻求替换默认函数(在没有函数的情况下,它只是删除函数):

def wm_protocol(self, name=None, func=None):
    """Bind function FUNC to command NAME for this widget.
    Return the function bound to NAME if None is given. NAME could be
    e.g. "WM_SAVE_YOURSELF" or "WM_DELETE_WINDOW"."""
    if callable(func):
        command = self._register(func)
    else:
        command = func
    return self.tk.call(
        'wm', 'protocol', self._w, name, command)
protocol = wm_protocol

但要实现您想要的效果,您应该能够通过以下方式引用相同的处理方法:

def handle_exit(self):
    self.controller.handle_close()

当然,这不是通用的,因为您必须明确知道主程序中的处理程序 window。

以为我已经接受了 Bryan 的回答,但我确实设法找到了一个我认为在这里很好的解决方法。

如果我将用于处理 window 关闭的方法传递到我的菜单 class,然后检查是否传递了某些内容,然后我可以决定天气或不使用我用 if 语句创建的退出方法或 self.controller.destroy()

这是我的解决方案。

主文件:

import tkinter as tk
from tkinter import messagebox
import PIP_MENU


class PIP(tk.Tk):
    def __init__(self):
        super().__init__()
        PIP_MENU.start(self, self.handle_close)
        self.protocol("WM_DELETE_WINDOW", self.handle_close)

    def handle_close(self):
        x = messagebox.askquestion("DERP", "Do you want to close without saving?")
        if x == "yes":
            self.destroy()

if __name__ == '__main__':
    PIP().mainloop()

菜单文件:

import tkinter as tk

class Menu(tk.Menu):
    def __init__(self, controller, exit_handler=None):
        super().__init__()
        self.controller = controller
        self.exit_handler = exit_handler
        controller.config(menu=self)
        file_menu = tk.Menu(self, tearoff=0)
        self.add_cascade(label="File", menu=file_menu)
        file_menu.add_command(label="Exit", command=self.handle_exit)

    def handle_exit(self):
        if self.exit_handler != None:
            self.exit_handler()
        else:
            self.controller.quit()


def start(controller, exit_handler=None):
    return Menu(controller, exit_handler)