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

Python 以编程方式按下工具栏上的“X”按钮?,python,tkinter,protocols,Python,Tkinter,Protocols,我知道我可以用protocolWM_DELETE_窗口截获按下X按钮,做些什么,但是我很难弄清楚如何激活这个按钮,或者至少是按下这个按钮时触发的协议 情况是这样的。我有两门课。我的主要Tk课程和菜单课程。当我使用菜单中的退出按钮设置关闭程序的命令时,我希望这个按钮与Tk类上的X按钮做完全相同的事情 现在我知道我可以简单地调用传递给menu类的控制器,然后调用为处理close事件而构建的方法,但是我尝试以一种不需要从menu类执行此操作的方式构建这个menu类。这将允许我在我构建的任何应用程序上使

我知道我可以用protocolWM_DELETE_窗口截获按下X按钮,做些什么,但是我很难弄清楚如何激活这个按钮,或者至少是按下这个按钮时触发的协议

情况是这样的。我有两门课。我的主要Tk课程和菜单课程。当我使用菜单中的退出按钮设置关闭程序的命令时,我希望这个按钮与Tk类上的X按钮做完全相同的事情

现在我知道我可以简单地调用传递给menu类的控制器,然后调用为处理close事件而构建的方法,但是我尝试以一种不需要从menu类执行此操作的方式构建这个menu类。这将允许我在我构建的任何应用程序上使用菜单类,几乎不需要编辑

我还没有找到一篇文章或一些文档,告诉我如何以编程方式激活WM_DELETE_窗口协议

如果不清楚我想要什么,这里有一张图片。简单地说,我希望exit按钮与X按钮完全一样

主要类别:

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文件上的菜单类:

我还没有找到一篇文章或一些文档,告诉我如何通过编程激活WM_DELETE_窗口协议

你不能。根据定义,WM_DELETE_窗口协议来自窗口管理器

捕获协议处理程序旨在为您提供重写其行为的机会。无论应用程序如何被破坏,它都不是用来触发某些代码的方法。如果要在窗口被销毁时运行某些代码,无论是通过用户单击窗口框架上的控件还是通过其他方式,正确的方法是绑定到根窗口上的事件

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

下面的示例说明了该技术。有一个方法handle_close,在窗口被破坏时调用。无论您是通过单击窗框上的控件来关闭窗口,还是单击“关闭我”!按钮,代码仍在运行

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()
我还没有找到一篇文章或一些文档,告诉我如何通过编程激活WM_DELETE_窗口协议

你不能。根据定义,WM_DELETE_窗口协议来自窗口管理器

捕获协议处理程序旨在为您提供重写其行为的机会。无论应用程序如何被破坏,它都不是用来触发某些代码的方法。如果要在窗口被销毁时运行某些代码,无论是通过用户单击窗口框架上的控件还是通过其他方式,正确的方法是绑定到根窗口上的事件

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

下面的示例说明了该技术。有一个方法handle_close,在窗口被破坏时调用。无论您是通过单击窗框上的控件来关闭窗口,还是单击“关闭我”!按钮,代码仍在运行

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()

我不相信有一种方法可以调用特定的协议,因为协议似乎是一个特定的事件监视。下面是模块类Tk的一个片段:

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

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()

当然,这并不是万能的,因为您必须明确知道主窗口中的处理程序。

我不相信有方法调用特定的协议,因为协议似乎是特定的事件监视。下面是模块类Tk的一个片段:

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

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()

当然,这并不是万能的,因为您必须明确知道主窗口中的处理程序。

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

如果我传递了用于处理菜单类窗口关闭的方法,然后检查是否传递了某些内容,那么我可以决定是否使用我创建的exit方法 或self.controller.destroy,并使用if语句

这是我的解决办法

主文件:

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)

虽然我已经接受了布莱恩的回答,但我还是设法找到了一个我认为在这里很好的解决办法

如果我将用于处理窗口关闭的方法传递给我的菜单类,然后检查是否传递了某些内容,那么我就可以决定是否使用我创建的exit方法或使用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)

数字,应该只是等待你的答案,而不是做我自己的挖掘没有结果。有趣。我已经试过PIP_MENU.startself.bind,self.handle_关闭,但是现在它可以工作了…所以你确定我不能伪X按钮吗?我无法用代码编写像self.controller.close_窗口这样的东西,并执行与X按钮完全相同的操作?@Mike SMT:我99%肯定。“x”按钮所做的就是破坏窗口,你只需打电话就可以做到。摧毁你自己。@Mike SMT:是的。Tkinter无法控制窗口的边框或这些边框上的控件。边界和控制是由操作系统提供的,生活在tkinter管理的空间之外。数字,应该只是等待你的答案,而不是我自己挖掘,没有结果。有趣。我已经试过PIP_MENU.startself.bind,self.handle_关闭,但是现在它可以工作了…所以你确定我不能伪X按钮吗?我无法用代码编写像self.controller.close_窗口这样的东西,并执行与X按钮完全相同的操作?@Mike SMT:我99%肯定。“x”按钮所做的就是破坏窗口,你只需打电话就可以做到。摧毁你自己。@Mike SMT:是的。Tkinter无法控制窗口的边框或这些边框上的控件。边界和控件由操作系统提供,位于tkinter管理的空间之外。