Язык программирования Python

Как найти окно, вызвавшее событие в tkinter в Python?

Я пытаюсь выполнить функцию обратного вызова с событием закрытия: 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().

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

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