Python tkinter 当 Toplevel 对象被销毁时,对于 <Destroy> 的顶层绑定,绑定命令执行多次
Python tkinter When Toplevel object is destroyed, With toplevel binding for <Destroy> the bound command executes multiple times
在下面的代码中,当顶层window被销毁时,
bind 语句被执行多次。顶层中的每个子部件可能一次。当我将顶层更改为框架时,绑定命令只执行一次。
在这个例子中, quit() 或 raise SystemExit 被推迟到命令完成它的循环。为什么会这样?
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showinfo
class PlainFrame(tk.Frame):
def __init__(self,parent):
super().__init__(parent)
self.butts = []
for i in range(20) :
# button
self.butts.append(ttk.Button(self, text=f'Click Me {i}'))
self.butts[i]['command'] = self.button_clicked
self.butts[i].grid(column=0,row=i)
self.pack()
def button_clicked(self):
showinfo(title='Information',
message='Hello, Tkinter!')
class MainFrame(tk.Toplevel):
#class MainFrame(tk.Frame):
def __init__(self, container,*args,**kwargs):
super().__init__(container,kwargs)
options = {'padx': 5, 'pady': 5}
# label
self.label = ttk.Label(self, text='Hello, Tkinter!')
self.label.pack(**options)
self.quit_button = ttk.Button(self,text='Quit',
command = self._quit)
self.quit_button.pack()
self.frame = PlainFrame(self)
# add when frame
#self.pack()
def button_clicked(self):
showinfo(title='Information',
message='Hello, Tkinter!')
def _quit(self):
self.destroy()
class App(tk.Tk):
def __init__(self):
super().__init__()
# configure the root window
self.title('My Awesome App')
self.geometry('600x100')
def quitting(self,event):
print ('just passing through')
quit()
raise SystemExit
if __name__ == "__main__":
app = App()
frame = MainFrame(app)
# app.withdraw()
frame.bind('<Destroy>',app.quitting)
app.mainloop()
With toplevel binding for the bound command executes multiple times
是的,这就是 tkinter 设计的工作方式。
绑定到某物时,您不会绑定到小部件。相反,您绑定到 绑定标签 。每个小部件都有一组 绑定标签 。当一个小部件接收到一个事件时,tkinter 将检查它的每个绑定标签,以查看是否有针对给定事件绑定到它的函数。
那么,什么是widget绑定标签呢?每个小部件都有绑定标签“all”。每个小部件还获得一个以小部件本身命名的绑定标记。它获得第三个绑定标签,即小部件的名称 class(例如:“Button”、“Label”等)。第四个标记——给您带来麻烦的标记——是包含小部件的 window 的名称。顺序从最具体到最不具体:小部件、小部件 class、window、“全部”。
您可以通过打印出小部件的绑定标签来查看这一点。考虑以下代码:
import tkinter as tk
root = tk.Tk()
toplevel = tk.Toplevel(root)
label = tk.Label(toplevel)
print(f"binding tags for label: {label.bindtags()}")
当 运行 时,上面的代码产生这个输出:
binding tags for label: ('.!toplevel.!label', 'Label', '.!toplevel', 'all')
第一个字符串,.!toplevel.!label
是标签小部件的内部名称。 Label
是小部件 class,.!toplevel
是顶级小部件的名称,然后是字符串 all
.
当您点击标签时会发生什么?首先,tkinter 将检查标签 .!toplevel.!label
上的按钮是否有绑定。如果您绑定到小部件本身,它将有一个。接下来,它将检查小部件 class 上是否存在绑定。滚动条和按钮等小部件将绑定到小部件 class,但标签不会。接下来,tkinter 将查看事件的 window 本身是否有绑定。最后,它将查看特殊标签 all
.
上是否有绑定
您可以通过将绑定标签列表传递给 bindtags
命令来更改小部件的绑定标签。例如,如果您希望每个小部件都有一个框架的绑定标签,您可以将绑定标签设置为包含框架,然后每个小部件都会响应绑定到框架的事件。
您也可以使用相同的技术来删除绑定。例如,如果您想从 Text
小部件中删除所有默认绑定,您可以从其绑定标签列表中删除 Text
。执行此操作后,小部件将不会响应任何按键或按键释放。
关于绑定标签如何工作的规范描述在 tcl/tk 的 bindtags man page 中。
在下面的代码中,当顶层window被销毁时, bind 语句被执行多次。顶层中的每个子部件可能一次。当我将顶层更改为框架时,绑定命令只执行一次。 在这个例子中, quit() 或 raise SystemExit 被推迟到命令完成它的循环。为什么会这样?
import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showinfo
class PlainFrame(tk.Frame):
def __init__(self,parent):
super().__init__(parent)
self.butts = []
for i in range(20) :
# button
self.butts.append(ttk.Button(self, text=f'Click Me {i}'))
self.butts[i]['command'] = self.button_clicked
self.butts[i].grid(column=0,row=i)
self.pack()
def button_clicked(self):
showinfo(title='Information',
message='Hello, Tkinter!')
class MainFrame(tk.Toplevel):
#class MainFrame(tk.Frame):
def __init__(self, container,*args,**kwargs):
super().__init__(container,kwargs)
options = {'padx': 5, 'pady': 5}
# label
self.label = ttk.Label(self, text='Hello, Tkinter!')
self.label.pack(**options)
self.quit_button = ttk.Button(self,text='Quit',
command = self._quit)
self.quit_button.pack()
self.frame = PlainFrame(self)
# add when frame
#self.pack()
def button_clicked(self):
showinfo(title='Information',
message='Hello, Tkinter!')
def _quit(self):
self.destroy()
class App(tk.Tk):
def __init__(self):
super().__init__()
# configure the root window
self.title('My Awesome App')
self.geometry('600x100')
def quitting(self,event):
print ('just passing through')
quit()
raise SystemExit
if __name__ == "__main__":
app = App()
frame = MainFrame(app)
# app.withdraw()
frame.bind('<Destroy>',app.quitting)
app.mainloop()
With toplevel binding for the bound command executes multiple times
是的,这就是 tkinter 设计的工作方式。
绑定到某物时,您不会绑定到小部件。相反,您绑定到 绑定标签 。每个小部件都有一组 绑定标签 。当一个小部件接收到一个事件时,tkinter 将检查它的每个绑定标签,以查看是否有针对给定事件绑定到它的函数。
那么,什么是widget绑定标签呢?每个小部件都有绑定标签“all”。每个小部件还获得一个以小部件本身命名的绑定标记。它获得第三个绑定标签,即小部件的名称 class(例如:“Button”、“Label”等)。第四个标记——给您带来麻烦的标记——是包含小部件的 window 的名称。顺序从最具体到最不具体:小部件、小部件 class、window、“全部”。
您可以通过打印出小部件的绑定标签来查看这一点。考虑以下代码:
import tkinter as tk
root = tk.Tk()
toplevel = tk.Toplevel(root)
label = tk.Label(toplevel)
print(f"binding tags for label: {label.bindtags()}")
当 运行 时,上面的代码产生这个输出:
binding tags for label: ('.!toplevel.!label', 'Label', '.!toplevel', 'all')
第一个字符串,.!toplevel.!label
是标签小部件的内部名称。 Label
是小部件 class,.!toplevel
是顶级小部件的名称,然后是字符串 all
.
当您点击标签时会发生什么?首先,tkinter 将检查标签 .!toplevel.!label
上的按钮是否有绑定。如果您绑定到小部件本身,它将有一个。接下来,它将检查小部件 class 上是否存在绑定。滚动条和按钮等小部件将绑定到小部件 class,但标签不会。接下来,tkinter 将查看事件的 window 本身是否有绑定。最后,它将查看特殊标签 all
.
您可以通过将绑定标签列表传递给 bindtags
命令来更改小部件的绑定标签。例如,如果您希望每个小部件都有一个框架的绑定标签,您可以将绑定标签设置为包含框架,然后每个小部件都会响应绑定到框架的事件。
您也可以使用相同的技术来删除绑定。例如,如果您想从 Text
小部件中删除所有默认绑定,您可以从其绑定标签列表中删除 Text
。执行此操作后,小部件将不会响应任何按键或按键释放。
关于绑定标签如何工作的规范描述在 tcl/tk 的 bindtags man page 中。