Как закрыть окно toplevel в tkinter
Перейти к содержимому

Как закрыть окно toplevel в tkinter

  • автор:

Окна

По умолчанию приложение Tkinter имеет одно главное окно, которое представляет класс tkinter.Tk . Запуск приложение приводит к запуску главного окно, в рамках которого помещаются все виджеты. Закрытие главного окна приводит к завершению работы приложения. Однако в рамках главного окна также можно запускать вторичные, неглавные окна. Например, октроем новое окно по нажатию на кнопку:

from tkinter import * from tkinter import ttk root = Tk() root.title("METANIT.COM") root.geometry("250x200") def click(): window = Tk() window.title("Новое окно") window.geometry("250x200") button = ttk.Button(text="Создать окно", command=click) button.pack(anchor=CENTER, expand=1) root.mainloop()

Здесь по нажатию на кнопку создается новый объект window, у него устанавливается заголовок и размеры.

Создание окон в Tkinter и Python

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

Как и главное окно, вторичные окна могут иметь виджеты. Например, определим на новом окне метку:

from tkinter import * from tkinter import ttk root = Tk() root.title("METANIT.COM") root.geometry("250x200") def click(): window = Tk() window.title("Новое окно") window.geometry("250x200") label=ttk.Label(window, text="Принципиально новое окно") label.pack(anchor=CENTER, expand=1) button = ttk.Button(text="Создать окно", command=click) button.pack(anchor=CENTER, expand=1) root.mainloop()

Единственное не надо забывать у добавляемых виджетов устанавливать окно в качестве родительского контейнера

Создание окон Tk в Tkinter и Python

Удаление окна

Для удаления окна применяется меnод destroy()

from tkinter import * from tkinter import ttk root = Tk() root.title("METANIT.COM") root.geometry("250x200") def click(): window = Tk() window.title("Новое окно") window.geometry("250x200") close_button = ttk.Button(window, text="Закрыть окно", command=lambda: window.destroy()) close_button.pack(anchor="center", expand=1) open_button = ttk.Button(text="Создать окно", command=click) open_button.pack(anchor="center", expand=1) root.mainloop()

В данном случае в новом окне по нажатию на кнопку close_button срабатывает метод window.destroy() , который закрывает окно и по сути аналогичен нажатию на крестик в верхнем правом углу окна.

Определение окна в объектно-ориентированном стиле

В примере выше новое окно, его параметры и вложенные виджеты определялись внутри функции, однако это приводит к разбуханию кода функции. И гораздо проще вынести определение окна в отдельный класс:

from tkinter import * from tkinter import ttk class Window(Tk): def __init__(self): super().__init__() # конфигурация окна self.title("Новое окно") self.geometry("250x200") # определение кнопки self.button = ttk.Button(self, text="закрыть") self.button["command"] = self.button_clicked self.button.pack(anchor="center", expand=1) def button_clicked(self): self.destroy() root = Tk() root.title("METANIT.COM") root.geometry("250x200") def click(): window = Window() open_button = ttk.Button(text="Создать окно", command=click) open_button.pack(anchor="center", expand=1) root.mainloop()

Здесь определение окна вынесено в отдельный класс Window, который наследуется от класса tkinter.Tk. Благодаря этому мы можем вынести весь код определения окна в отдельную структурную единицу — класс, что позволит упростить управление кодом.

Окно поверх других окон

Для создания диалогового окна, которое располагается поверх главного окна, применяется класс Toplevel :

from tkinter import * from tkinter import ttk root = Tk() root.title("METANIT.COM") root.geometry("250x200") def dismiss(window): window.grab_release() window.destroy() def click(): window = Toplevel() window.title("Новое окно") window.geometry("250x200") window.protocol("WM_DELETE_WINDOW", lambda: dismiss(window)) # перехватываем нажатие на крестик close_button = ttk.Button(window, text="Закрыть окно", command=lambda: dismiss(window)) close_button.pack(anchor="center", expand=1) window.grab_set() # захватываем пользовательский ввод open_button = ttk.Button(text="Создать окно", command=click) open_button.pack(anchor="center", expand=1) root.mainloop()

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

window.grab_set()

В функции dismiss() , которая закрывает окно, освобождаем ввод с помощью метода grab_release()

window.grab_release()

Всплывающие окна / tkinter 11

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

Но есть в Tkinter и другой класс, который используется для создания дополнительных окон верхнего уровня. Он называется Toplevel . Этот класс можно использовать для отображения любого типа окна: от диалоговых до мастер форм.

Начнем с создания простого окна, которое открывается по нажатию кнопки в основном. Дополнительное окно будет включать кнопку, которая закрывает его и возвращает фокус в основное:

Открытие дополнительного окна

Класс виджета Toplevel создает новое окно верхнего уровня, которое выступает в качестве родительского контейнера по аналогии с экземпляром Tk . В отличие от класса Tk можно создавать неограниченное количество окон верхнего уровня:

 
import tkinter as tk

class About(tk.Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.label = tk.Label(self, text="Это всплывающее окно")
self.button = tk.Button(self, text="Закрыть", command=self.destroy)

self.label.pack(padx=20, pady=20)
self.button.pack(pady=5, ipadx=2, ipady=2)

class App(tk.Tk):
def __init__(self):
super().__init__()
self.btn = tk.Button(self, text="Открыть новое окно",
command=self.open_window)
self.btn.pack(padx=50, pady=20)

def open_window(self):
about = About(self)
about.grab_set()

if __name__ == "__main__":
app = App()
app.mainloop()

Как работают всплывающие окна

Определяем подкласс Toplevel , который будет представлять собой кастомное окно. Его отношение с родительским будет определено в методе __init__ . Виджеты добавляются в это окно привычным образом:

 
class Window(tk.Toplevel):
def __init__(self, parent):
super().__init__(parent)

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

 
def open_window(self):
window = Window(self)
window.grab_set()

Обработка закрытия окна

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

Будем заново использовать класс App из прошлого примера, но изменим класс Window , чтобы он показывал диалоговое окно для подтверждения перед закрытием:

Обработка закрытия окна

В Tkinter можно определить, когда окно готовится закрыться с помощью функции-обработчика для протокола WM_DELETE_WINDOW . Его можно запустить, нажав на иконку «x» в верхней панели в большинстве настольных программ.

 
import tkinter as tk
import tkinter.messagebox as mb

class Window(tk.Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.protocol("WM_DELETE_WINDOW", self.confirm_delete)
self.label = tk.Label(self, text="Это всплывающее окно")
self.button = tk.Button(self, text="Закрыть", command=self.destroy)
self.label.pack(padx=20, pady=20)
self.button.pack(pady=5, ipadx=2, ipady=2)

def confirm_delete(self):
message = "Вы уверены, что хотите закрыть это окно?"
if mb.askyesno(message=message, parent=self):
self.destroy()

class App(tk.Tk):
def __init__(self):
super().__init__()
self.btn = tk.Button(self, text="Открыть новое окно",
command=self.open_about)
self.btn.pack(padx=50, pady=20)

def open_about(self):
window = Window(self)
window.grab_set()

if __name__ == "__main__":
app = App()
app.mainloop()

Этот метод-обработчик показывает диалоговое окно для подтверждения удаления окна. В более сложных программах логика обычно включает дополнительную валидацию.

Как работает проверка закрытия окна

Метод bind() используется для регистрации обработчиков событий виджетов, а метод protocol делает то же самое для протоколов менеджеров окна.

Обработчик WM_DELETE_WINDOW вызывается, когда окно верхнего уровня должно уже закрываться, и по умолчанию Tk уничтожает окно, для которого оно было получено. Поскольку это поведение перезаписывается с помощью обработчика confirm_delete , нужно явно уничтожить окно при подтверждении.

Еще один полезный протокол — WM_TAKE_FOCUS . Он вызывается, когда окно получает фокус.

Стоит запомнить, что для сохранения фокуса в дополнительном окне при открытом диалоговом нужно передать ссылку экземпляру верхнего уровня в параметре parent диалоговой функции:

 
if mb.askyesno(message=message, parent=self):
self.destroy()

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

Передача переменных между окнами

Два разных окна иногда нуждаются в том, чтобы передавать информацию прямо во время работы программы. Ее можно сохранять на диск и читать в нужном окне, но в определенных случаях удобнее обрабатывать ее в памяти и передавать в качестве переменных.

Основное окно будет включать три кнопки-переключателя для выбора типа пользователя, а второе — форму для заполнения его данных:

Передача переменных между окнами

Для сохранения пользовательских данных создаем namedtuple с полями, которые представляют собой экземпляр каждого пользователя. Эта функция из модуля collections получает имя типа и последовательность имен полей, а возвращает подкласс кортежа для создания простого объекта с заданными полями:

 
import tkinter as tk
from collections import namedtuple

User = namedtuple("User", ["username", "password", "user_type"])

class UserForm(tk.Toplevel):
def __init__(self, parent, user_type):
super().__init__(parent)
self.username = tk.StringVar()
self.password = tk.StringVar()
self.user_type = user_type

label = tk.Label(self, text="Создать пользователя " + user_type.lower())
entry_name = tk.Entry(self, textvariable=self.username)
entry_pass = tk.Entry(self, textvariable=self.password, show="*")
btn = tk.Button(self, text="Submit", command=self.destroy)

label.grid(row=0, columnspan=2)
tk.Label(self, text="Логин:").grid(row=1, column=0)
tk.Label(self, text="Пароль:").grid(row=2, column=0)
entry_name.grid(row=1, column=1)
entry_pass.grid(row=2, column=1)
btn.grid(row=3, columnspan=2)

def open(self):
self.grab_set()
self.wait_window()
username = self.username.get()
password = self.password.get()
return User(username, password, self.user_type)

class App(tk.Tk):
def __init__(self):
super().__init__()
user_types = ("Админ", "Менеджер", "Клиент")
self.user_type = tk.StringVar()
self.user_type.set(user_types[0])

label = tk.Label(self, text="Пожалуйста, выберите роль пользователя")
radios = [tk.Radiobutton(self, text=t, value=t,
variable=self.user_type) for t in user_types]
btn = tk.Button(self, text="Создать", command=self.open_window)

label.pack(padx=10, pady=10)
for radio in radios:
radio.pack(padx=10, anchor=tk.W)
btn.pack(pady=10)

def open_window(self):
window = UserForm(self, self.user_type.get())
user = window.open()
print(user)

if __name__ == "__main__":
app = App()
app.mainloop()

Когда поток выполнения возвращается в основное окно, пользовательские данные выводятся в консоль.

Как работает передача данных между окнами

Большая часть кода в этом примере рассматривалась и ранее, а основное отличие — в методе open() класса UserForm , куда перемещен вызов grab_set() . Однако именно метод wait_windows() отвечает за остановку исполнения и гарантирует, что данные не вернутся, пока форма не будет изменена:

Как совершить закрытие окна(Tkinter)?

В данном коде присутствует функция add. Что надо прописать чтобы при выполнении этой функции совершалось закрытие данного окна и открытие следующего или замена данного окна другим?(все комментарии стоят перед кнопкой/подписью/местом ввода текста)

from subprocess import call import tkinter as tk from tkinter import ttk class Main(tk.Frame): def __init__(self,root): super().__init__(root,background="royal blue") self.pack(fill=tk.BOTH, expand=1) #кнопка "Добавить книги в ЧБ" btn_add=ttk.Button(self, text='Добавить книги в ЧБ', command=add) btn_add.place(x=56, y=70) #кнопка "Посмотреть ЧБ" btn_watch=ttk.Button(self, text='Просмотреть ЧБ',command=watch) btn_watch.place(x=185, y=70) #кнопка "Уведомления" btn_notif=ttk.Button(self, text='Уведомления',command=notif) btn_notif.place(x=291, y=70) #кнопка "Сохранить" btn_save=ttk.Button(self, text='Сохранить',command=save) btn_save.place(x=800, y=415) #надпись "ФИО" lb_fio=ttk.Label(text='ФИО',foreground='black',background='royal blue') lb_fio.place(x=26,y=140) #надпись "Класс" lb_class=ttk.Label(text='Класс',foreground='black',background='royal blue') lb_class.place(x=220,y=140) #надпись "Литера" lb_lit=ttk.Label(text='Литера',foreground='black',background='royal blue') lb_lit.place(x=310,y=140) #место ввода "ФИО" en_fio=ttk.Entry(self,width=25) en_fio.place_configure(x=56, y=140) #место ввода "Класс" en_class=ttk.Entry(self,width=3) en_class.place_configure(x=257, y=140) #место ввода "Литера" en_lit=ttk.Entry(self,width=3) en_lit.place_configure(x=355, y=140) def add(): call(['python','H:\project\lib\lar3\cods\gl.py']) def notif(): pass def watch(): pass def save(): pass if __name__ == '__main__': root=tk.Tk() app=Main(root) app.pack() root.title('Электронный формуляр') root.geometry("900x450+300+200") root.resizable(False, False) root.mainloop()
  • Вопрос задан более трёх лет назад
  • 1443 просмотра

Есть ли возможность закрыть главное окна при закрытии TopLevel окна через крестик?

dimonchik2013

А как можно прописать в чтобы при нажатии на крестик воспроизводился скрипт? Это же кнопка не мной создаётся.

dimonchik2013

Dimonchik @dimonchik2013
oneLEAM, события нет?
oneLEAM @oneLEAM Автор вопроса
Dimonchik, какого события?

dimonchik2013

Dimonchik @dimonchik2013

DELETE_WINDOW или что там происходит при

Ваш ответ на вопрос

Войдите, чтобы написать ответ

tkinter

  • Tkinter
  • +1 ещё

Как заставить готовый exe-файл обращаться к файлам по фиксированной директории?

  • 1 подписчик
  • 27 окт.
  • 18 просмотров

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *