python - 当用户在模态窗口外单击时如何响铃?
问题描述
情况很简单。我有一个带有帮助 - 关于菜单的主窗口。单击此菜单项时,将打开一个模式窗口(假设它是一个关于窗口)。我禁用了self.grab_set()
主窗口(尽管当您单击主标题栏时模态窗口会闪烁)。到现在为止还挺好。
这是一个问题:当用户在主窗口的模态窗口之外单击时,我真的很喜欢响铃。
这是我能找到的关于grab_set() 的信息,实际上并没有那么多:
- [effbot] ...一个名为grab_set的方法,它确保没有鼠标或键盘事件被发送到错误的窗口。
- [effbot] 将此应用程序的所有事件路由到此小部件。
- [kite.com] 抓取将所有事件定向到应用程序中的 this 和后代小部件。
- [google book] grab_set() 确保所有应用程序的事件都发送到 w,直到相应的调用 grab_release([Me:] 或直到窗口被销毁?)
我不太清楚如何理解这一点:这是否意味着您可以在模态窗口内的主窗口上处理事件(比如敲响我的钟声)?所以我尝试了这样的事情:
self.bind('<Button-1>', self.bell)
Tkinter 回调中的异常:_tkinter.TclError: bad window path name
parent.bind('<Button-1>', self.bell)
没有任何反应
那么,如何像在许多其他应用程序中一样在主窗口的模态窗口外单击时发出铃声?
派生问题:
- 将grab_set用于模态窗口后,是否仍然可以从主窗口捕获事件?
- 有没有办法防止闪烁?
我真的很想了解这种神秘的grab_set()
方法。
剥离代码:
import tkinter as tk
class About(tk.Toplevel):
def __init__(self, parent):
tk.Toplevel.__init__(self, parent)
self.geometry('200x150')
#--- OK button
btn_ok = tk.Button(self, text='OK', command=self.destroy) # destroy with OK
btn_ok.pack(side=tk.TOP)
btn_ok.focus() # destroy with spacebar
#--- Make window modal
self.grab_set()
# self.wait_window() # is this necessary in this case?
# self.bind('<Button-1>', self.bell) ??? The question
class MenuBar(tk.Menu):
def __init__(self, parent):
tk.Menu.__init__(self)
helpmenu = tk.Menu(self, tearoff=0)
helpmenu.add_command(label='About', command=lambda: About(parent))
self.add_cascade(label='Help', menu=helpmenu)
class MainApp():
def __init__(self, parent):
parent.configure(background='#000000')
parent.geometry('800x600')
menubar = MenuBar(parent)
parent.configure(menu=menubar)
if __name__ == '__main__':
root = tk.Tk()
MainApp(root)
root.mainloop()
解决方案
当您设置一个抓取时,所有按钮点击都将转到带有抓取的窗口。您以捕获任何其他事件的方式捕获它们。在单击按钮的情况下,您可以通过将函数绑定到<1>
.
重要的是要知道根窗口或Toplevel
窗口上的绑定将应用于该窗口中的所有小部件。例如,self
即使您单击“确定”按钮,代码中的绑定也会触发。因此,回调可能只在与事件关联的小部件与顶层相同时才起作用。
例子:
class About(tk.Toplevel):
def __init__(self, parent):
...
self.bind("<1>", self.capture_click)
...
def capture_click(self, event):
if event.widget == self:
<your logic here>
在想知道用户是否点击了窗口外的情况下,可以使用事件对象的坐标与窗口进行比较,看看点击是在窗口内还是在窗口外。
def on_click(self, event):
if event.widget == self:
if (event.x < 0 or event.x > self.winfo_width() or
event.y < 0 or event.y > self.winfo_height()):
self.bell()
推荐阅读
- apache-flink - 有没有办法在执行流之前以编程方式检查 Flink 流作业是否从保存点开始?
- python - SciPy 警告消息:“检测到病态矩阵”
- django - TypeError: __init__() 得到了一个意外的关键字参数“选择”
- java - Java:优化的大规模价值存储替代方案
- augmented-reality - 如何使用 Reality Kit 在屏幕前设置实体?
- javascript - 将 -Infinity 转换为零
- python - 在 Python 中计算人脸的法线向量
- c# - 如何检查unity2d平台游戏的地面
- vue.js - Vuetify 取消选择 v-tabs
- python - Python AttributeError:“str”对象没有属性“items”