Я пытаюсь выполнить функцию обратного вызова с событием закрытия: self.toplevel_1.bind (”, lambda: self.close_windows (3)) но Destroy применяется ко всем виджетам-потомкам Toplevel.
Поэтому в функции bind я должен найти Toplevel, чтобы ссылаться только на него, а не на другие виджеты-потомки.
Я читал, что я должен использовать event.widget, но я не знаю, как это реализовать.
Большое спасибо за любую помощь.
from tkinter import *
class A (Frame):
def __init__(self,master, **kwargs):
super().__init__(master, **kwargs)
self.master = master
self._open_1 = False
self._open_2 = False
self.btn = Button(self, text='create winoows', command= self.create)
self.btn .pack()
def create(self):
if self._open_1 is False:
self.w1 = Toplevel(self.master)
self.w1 .title('window 1')
self.w1 .bind('<Destroy>',lambda f: self.close_windows(1))
if self._open_2 is False:
self.w2 = Toplevel(self.master)
self.w2 .title('window 2')
self.w2 .bind('<Destroy>',lambda f: self.close_windows(2))
self._open_1 = True
self._open_2 = True
def close_windows(self, number, event=None):
if number is 1:
event.widget.destroy()
self._open_1 = False
if number is 2: pass #......
root = Tk()
a = A(root)
a .pack()
root .mainloop()
Code language: HTML, XML (xml)
Следует отметить, что ‘WM_DELETE_WINDOW’ не будет иметь значения, поскольку окно не имеет оконного менеджера.
Как найти окно. Решение.
Не совсем понимаю, что вы пытаетесь сделать, но проблема в том, что параметр event вашего колбека никогда не назначается при вызове и поэтому имеет значение по умолчанию None.
Причина кроется здесь:
self.w1.bind('<Destroy>', lambda f: self.close_windows(1))
Code language: PHP (php)
Функция bind ожидает два параметра. Первый – это событие, а второй – функция, которая при вызове tk получает событие в качестве параметра.
В вашем случае эта функция является лямбдой, а параметр, который она получает (который вы назвали f), будет событием, о котором идет речь.
Но, как мы видим, вы вообще не используете f.
Просто передайте его в качестве второго параметра в функцию close_windows().
Переписывание этой строки (и замена f на более осмысленное имя) будет выглядеть следующим образом:
self.w1.bind('<Destroy>', lambda event: self.close_windows(1, event))
Code language: PHP (php)
Полный код
Полный код будет выглядеть следующим образом. В примере if number is 1 поменял на if number == 1, а if self._open_1 is False на if not self._open_1, что более правильно.
from tkinter import *
class A (Frame):
def __init__(self,master, **kwargs):
super().__init__(master, **kwargs)
self.master = master
self._open_1 = False
self._open_2 = False
self.btn = Button(self, text='create winoows', command= self.create)
self.btn.pack()
def create(self):
if not self._open_1:
self.w1 = Toplevel(self.master)
self.w1.title('window 1')
self.w1.bind('<Destroy>', lambda event: self.close_windows(1, event))
if not self._open_2:
self.w2 = Toplevel(self.master)
self.w2.title('window 2')
self.w2.bind('<Destroy>', lambda event: self.close_windows(2, event))
self._open_1 = True
self._open_2 = True
def close_windows(self, number, event=None):
if number == 1:
self._open_1 = False
event.widget.destroy()
print("DEBUG: Close 1", event)
if number == 2:
self._open_2 = False
event.widget.destroy()
print("DEBUG: Close 2", event)
root = Tk()
a = A(root)
a.pack()
root.mainloop()
Code language: HTML, XML (xml)
Бонус
Код может быть усовершенствован для использования списков окон и булевых значений вместо переменных _open_1, _open_2, w1, w2. Таким образом, он становится более универсальным, и вы можете легко заставить его создавать и обрабатывать любое количество окон.
Вот версия со списками:
from tkinter import Toplevel
from tkinter import *
class A (Frame):
def __init__(self, master, nwindows=2, **kwargs):
super().__init__(master, **kwargs)
self.master = master
# Подготовим список логических операций и список окон.
self._open = [False] * nwindows
self.windows = [None] * nwindows
self.btn = Button(self, text='create windows', command= self.create)
self.btn.pack()
def create(self):
# Зацикливание окон, которые еще не созданы
for i in range(len(self._open)):
if not self._open[i]:
w = Toplevel(self.master, width=300)
w.title(f'window {i+1}')
w.bind('<Destroy>',
lambda event, number=i: self.close_windows(number, event))
# Обновление списков
self.windows[i] = w
self._open[i] = True
def close_windows(self, number, event=None):
# В качестве меры предосторожности, хотя этого
# не должно происходить, проверяется, закрыто ли уже окно.
if not self._open[number]:
return
# Закрываем его и обновляем список
self._open[number] = False
event.widget.destroy()
print(f"DEBUG: Close {number}")
root = Tk()
a = A(root, nwindows=4)
a.pack()
root.mainloop()
Code language: HTML, XML (xml)
Обратите внимание, что в этой реализации вам не нужно будет использовать event.widget для закрытия нужного окна. Вы можете сделать self.windows[number].destroy().