Как мне обработать событие закрытия окна (пользователь нажимает кнопку «X») в программе Python Tkinter?
131
Tkinter поддерживает механизм, называемый обработчиками протокола . Здесь термин протокол относится к взаимодействию между приложением и оконным менеджером. Вызывается наиболее часто используемый протокол WM_DELETE_WINDOW
, который используется для определения того, что происходит, когда пользователь явно закрывает окно с помощью диспетчера окон.
Вы можете использовать этот protocol
метод для установки обработчика для этого протокола (виджет должен быть виджетом Tk
или Toplevel
):
Вот вам конкретный пример:
import tkinter as tk
from tkinter import messagebox
root = tk.Tk()
def on_closing():
if messagebox.askokcancel("Quit", "Do you want to quit?"):
root.destroy()
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()
Tkinter
не было окна сообщений подмодуля. Я использовалimport tkMessageBox as messagebox
Мэтт показал одну классическую модификацию кнопки закрытия.
Другой - сделать так, чтобы кнопка закрытия сворачивала окно.
Вы можете воспроизвести это поведение, имея окно программы сразу способом
быть в протокольном втором аргументе метода.
Вот рабочий пример, протестированный в Windows 7 и 10:
В этом примере мы даем пользователю два новых варианта выхода:
классический File → Exit, а также Escкнопку.
источник
В зависимости от активности Tkinter, и особенно при использовании Tkinter.after, остановка этого действия с помощью
destroy()
- даже с использованием протокола (), кнопки и т. Д. - нарушит это действие (ошибка "во время выполнения"), а не просто прекратит его. , Лучшее решение почти в каждом случае - использовать флаг. Вот простой и глупый пример того, как его использовать (хотя я уверен, что большинству из вас это не нужно! :)Это красиво завершает графическую активность. Вам нужно только проверить
running
в нужном месте (ах).источник
Я хотел бы поблагодарить Апостола за ответ, который обратил на это мое внимание. Вот гораздо более подробный пример для Python 3 в 2019 году с более четким описанием и примером кода.
Остерегайтесь того факта, что
destroy()
(или отсутствие специального обработчика закрытия окна) мгновенно уничтожит окно и все его запущенные обратные вызовы, когда пользователь закроет его.Это может быть плохо для вас, в зависимости от вашей текущей активности Tkinter, и особенно при использовании
tkinter.after
(периодических обратных вызовов). Возможно, вы используете обратный вызов, который обрабатывает некоторые данные и записывает на диск ... в этом случае вы, очевидно, хотите, чтобы запись данных завершилась без резкого прерывания.Лучшее решение для этого - использовать флаг. Поэтому, когда пользователь запрашивает закрытие окна, вы отмечаете это как флаг, а затем реагируете на него.
(Примечание: я обычно проектирую графические интерфейсы пользователя как красиво инкапсулированные классы и отдельные рабочие потоки, и я определенно не использую «глобальные» (вместо этого я использую переменные экземпляра класса), но это должен быть простой упрощенный пример для демонстрации как Tk внезапно убивает ваши периодические обратные вызовы, когда пользователь закрывает окно ...)
Этот код покажет вам, что
WM_DELETE_WINDOW
обработчик работает, даже когда наш кастомperiodic_call()
занят посреди работы / циклов!Мы используем довольно завышенные
.after()
значения: 500 миллисекунд. Это просто сделано для того, чтобы вам было очень легко увидеть разницу между закрытием, когда периодический вызов занят, или нет ... Если вы закроете, пока номера обновляются, вы увидите, что этоWM_DELETE_WINDOW
произошло, когда ваш периодический вызов "был занятая обработка: True ". Если вы закроете, когда номера приостановлены (это означает, что периодический обратный вызов в этот момент не обрабатывается), вы увидите, что закрытие произошло, пока он «не занят».В реальной жизни вам
.after()
потребуется примерно 30–100 миллисекунд, чтобы иметь гибкий графический интерфейс. Это просто демонстрация, которая поможет вам понять, как защитить себя от поведения Tk по умолчанию «мгновенно прерывать всю работу при закрытии».В итоге: сделайте так, чтобы
WM_DELETE_WINDOW
обработчик установил флаг, а затем периодически проверяйте этот флаг и вручную.destroy()
окно, когда это безопасно (когда ваше приложение завершило всю работу).PS: Вы также можете использовать,
WM_DELETE_WINDOW
чтобы спросить пользователя, ДЕЙСТВИТЕЛЬНО ли он хочет закрыть окно; и если они ответят «нет», вы не ставите флаг. Все очень просто. Вы просто показываете окно сообщения в своемWM_DELETE_WINDOW
и устанавливаете флаг на основе ответа пользователя.источник
Попробуйте простую версию:
Или, если вы хотите добавить больше команд:
источник
источник