Программно нажмите кнопку `X` на панели инструментов? - PullRequest
0 голосов
/ 08 ноября 2018

Я знаю, что могу перехватить нажатие кнопки X с помощью protocol("WM_DELETE_WINDOW", do_something), однако мне трудно понять, как активировать эту кнопку или, по крайней мере, протокол, который срабатывает при нажатии этой кнопки.

Вот ситуация. У меня есть 2 класса. Мой основной Tk класс и мой Menu класс. Когда я настраиваю команду на закрытие программы с помощью кнопки exit из меню, я хочу, чтобы эта кнопка выполняла ту же функцию, что и кнопка X в классе Tk.

Теперь я знаю, что могу просто вызвать контроллер, который был передан в класс меню, а затем вызвать метод, который я построил для обработки события закрытия, однако я пытаюсь создать этот класс меню таким образом, чтобы мне не нужно было сделать это из класса меню. Это позволит мне использовать класс меню в любом приложении, которое я создаю, практически без редактирования.

Мне не удалось найти сообщение или какую-либо документацию, в которой говорилось бы, как я могу программно активировать протокол "WM_DELETE_WINDOW".

Вот изображение, если неясно, чего я хочу. Просто я хочу, чтобы кнопка выхода делала то же, что и кнопка X.

enter image description here

Основной класс:

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 файле:

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)

Ответы [ 3 ]

0 голосов
/ 08 ноября 2018

Я не верю, что есть метод, который вызывает конкретный protocol, так как protocol, похоже, является определенным наблюдателем событий. Вот фрагмент из класса модуля 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()

Конечно, это не так универсально, поскольку вы должны явно знать обработчик в главном окне.

0 голосов
/ 08 ноября 2018

Думаю, что я принял ответ Брайана, мне удалось найти обходной путь, я думаю, что здесь все в порядке.

Если я передам метод, используемый для закрытия окна, своему классу меню, а затем проверим, пропущено ли что-то, я могу выбрать погоду или не использовать метод выхода, который я сделал, или 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)
0 голосов
/ 08 ноября 2018

Мне не удалось найти сообщение или какую-либо документацию, в которой говорилось бы, как я могу программно активировать протокол "WM_DELETE_WINDOW".

Ты не можешь. По определению, протокол WM_DELETE_WINDOW исходит от оконного менеджера.

Перехват обработчика протокола разработан, чтобы дать вам возможность переопределить его поведение. Он не предназначен для запуска какого-либо кода независимо от того, как приложение уничтожено. Если вы хотите запустить какой-то код, когда окно разрушено, будь то пользователь, щелкающий по элементу управления на рамке окна или каким-либо другим способом, правильный способ сделать это - привязать событие <Destroy> в корневом каталоге. окно.

Вы должны быть осторожны, так как любая привязка в корневом окне будет срабатывать для каждого виджета. Следовательно, ваша привязка должна выполняться только тогда, когда 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...