Как я обрабатываю окно близкое событие в Tkinter?

Многие объяснения уже присутствуют, чтобы объяснить, как это происходит и как это исправить, но вы также должны следовать рекомендациям, чтобы избежать NullPointerException вообще.

См. также: A хороший список лучших практик

Я бы добавил, очень важно, хорошо использовать модификатор final. Использование "окончательной" модификатор, когда это применимо в Java

Сводка:

  1. Используйте модификатор final для обеспечения хорошей инициализации.
  2. Избегайте возврата null в методы, например, при возврате пустых коллекций.
  3. Использовать аннотации @NotNull и @Nullable
  4. Быстрое завершение работы и использование утверждений, чтобы избежать распространения нулевых объектов через все приложение, когда они не должен быть пустым.
  5. Сначала используйте значения с известным объектом: if("knownObject".equals(unknownObject)
  6. Предпочитают valueOf() поверх toString ().
  7. Используйте null safe StringUtils StringUtils.isEmpty(null).

108
задан nbro 23 March 2015 в 02:44
поделиться

2 ответа

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()
149
ответ дан nbro 24 November 2019 в 03:32
поделиться

я хотел бы благодарить ответ Apostolos для того, чтобы обратить мое внимание на это. Вот намного более подробный пример для Python 3 в 2019 году с более четким описанием и примером кода.

<час>

Остерегаются того, которое destroy() (или не наличие пользовательского окна заключительный обработчик вообще) уничтожит окно и все его рабочие обратные вызовы немедленно, когда пользователь закроет его.

Это может быть плохо для Вас, в зависимости от Вашего текущего действия Tkinter, и особенно при использовании tkinter.after (периодические обратные вызовы). Вы могли бы использовать обратный вызов, который обрабатывает некоторые данные и пишет в диск... в этом случае, Вы, очевидно, хотите, чтобы запись данных закончилась без того, чтобы быть резко уничтоженным.

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

(Примечание: Я обычно разрабатываю графический интерфейсы пользователя как приятно инкапсулировавшие классы и разделяю рабочие потоки, и я определенно не использую "глобальный" (я использую переменные экземпляра класса вместо этого), но это предназначено, чтобы быть простым, упрощенным примером, чтобы продемонстрировать, как Tk резко уничтожает Ваши периодические обратные вызовы, когда пользователь закроет окно...)

from tkinter import *
import time

# Try setting this to False and look at the printed numbers (1 to 10)
# during the work-loop, if you close the window while the periodic_call
# worker is busy working (printing). It will abruptly end the numbers,
# and kill the periodic callback! That's why you should design most
# applications with a safe closing callback as described in this demo.
safe_closing = True

# ---------

busy_processing = False
close_requested = False

def close_window():
    global close_requested
    close_requested = True
    print("User requested close at:", time.time(), "Was busy processing:", busy_processing)

root = Tk()
if safe_closing:
    root.protocol("WM_DELETE_WINDOW", close_window)
lbl = Label(root)
lbl.pack()

def periodic_call():
    global busy_processing

    if not close_requested:
        busy_processing = True
        for i in range(10):
            print((i+1), "of 10")
            time.sleep(0.2)
            lbl["text"] = str(time.time()) # Will error if force-closed.
            root.update() # Force redrawing since we change label multiple times in a row.
        busy_processing = False
        root.after(500, periodic_call)
    else:
        print("Destroying GUI at:", time.time())
        try: # "destroy()" can throw, so you should wrap it like this.
            root.destroy()
        except:
            # NOTE: In most code, you'll wanna force a close here via
            # "exit" if the window failed to destroy. Just ensure that
            # you have no code after your `mainloop()` call (at the
            # bottom of this file), since the exit call will cause the
            # process to terminate immediately without running any more
            # code. Of course, you should NEVER have code after your
            # `mainloop()` call in well-designed code anyway...
            # exit(0)
            pass

root.after_idle(periodic_call)
root.mainloop()

, Этот код покажет Вам, что WM_DELETE_WINDOW обработчик работает даже, в то время как наше пользовательское periodic_call() занято посреди работы/циклов!

Мы используем немного симпатичные преувеличенный .after() значения: 500 миллисекунд. Это просто предназначено для создания очень легким для Вас видеть различие между закрытием, в то время как периодический вызов выполняем, или нет... Если Вы закроетесь, в то время как числа обновляют, Вы будете видеть, что WM_DELETE_WINDOW произошел , в то время как Ваш периодический вызов "был выполняем, обработав: Верный". Если Вы закрываетесь, в то время как числа приостанавливаются (подразумевать, что периодический обратный вызов не обрабатывает в тот момент), Вы видите, что завершение произошло, в то время как это "не занято".

В реальном использовании, Ваш .after() использовал бы что-то как 30-100 миллисекунд, чтобы иметь быстро реагирующий GUI. Это - просто демонстрация, чтобы помочь Вам понять, как защитить себя от значения по умолчанию Tk, "немедленно прерывают всю работу когда заключительное" поведение.

, Таким образом: Сделайте WM_DELETE_WINDOW, обработчик установил флаг и затем проверяет, что флаг периодически и вручную .destroy() окно, когда это безопасно (когда Ваше приложение сделано со всей работой).

пз: можно также использовать WM_DELETE_WINDOW для [1 116], спрашивают пользователь, если они ДЕЙСТВИТЕЛЬНО хотят закрыть окно; и если они отвечают не, Вы не устанавливаете флаг. Это очень просто. Вы просто показываете messagebox в Вашем WM_DELETE_WINDOW и устанавливаете флаг на основе ответа пользователя.

0
ответ дан 24 November 2019 в 03:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: