我有一个带有 Tkinter 的 Python GUI。在一堂课中,我有一些文本小部件。这些有一些default bindings其中一些我想最好用
bind_class()
替换。除了那些带有大写热键的热键,例如Ctrl-E/A/B/F
(即Ctrl-Shift-e/a/b/f
),我设法做到了。这些我只能通过分别在每个小部件上应用 bind()
方法来更改。我想知道为什么会这样,如果有更聪明的方法来使用bind_class()
来做到这一点,否则它似乎对上述默认绑定没有影响。
请考虑以下代码。单击每个文本小部件并尝试所有四个热键 (
Ctrl-b/B/y/Y
) 后,可以看到它们都按预期在两个小部件中工作,除了 Ctrl-B
(即 Ctrl-Shift-b
),它只有在绑定时才有效使用 bind()
. 到小部件
我现在看到这个问题:对于带有大写 Key 的内置函数,优先顺序显然如下:
bind()
> 带有 return 'break'
的内置函数可能 > bind_class()
。这意味着,如果 bind_class()
中引用的函数末尾没有 return 'break'
,那么 bind()
将永远不会工作,并且只会调用内置函数。同时,带有小写键的内置热键(例如Ctrl-a/e/b/f
)在某种程度上具有较低的优先级,甚至bind_class()
甚至可以优先于它们。
所以我的问题是:
bind()
和bind_class()
的预期功能吗?Ctrl-B
这样的内置函数,一次只对所有小部件使用bind_class()
,而不是一个接一个地对它们进行bind()
?欢迎任何有见地的评论。谢谢!
from tkinter import *
class Test:
def __init__(self):
self.window = Toplevel(root)
self.window.lift()
self.t1 = Text(self.window)
self.t1.insert(1.0, 'Initial text in Text widget #1')
#self.t1.bind('<Control-B>', self.func)
self.t1.pack()
self.t2 = Text(self.window)
self.t2.insert(1.0, 'Initial text in Text widget #2')
self.t2.bind('<Control-B>', self.func)
self.t2.pack()
self.t1.bind_class('Text', '<Control-b>', self.func)
self.t1.bind_class('Text', '<Control-B>', self.func) # This line has no effect.
self.t1.bind_class('Text', '<Control-y>', self.func)
self.t1.bind_class('Text', '<Control-Y>', self.func)
def func(self, event):
obj = root.focus_get()
obj.insert(END, '\n'+str(event))
return 'break'
root = Tk()
t1 = Test()
root.lower()
root.mainloop()
优先顺序不会改变,在所有情况下对于所有小部件都是相同的,并且始终由小部件的绑定标签定义。所以,回答你的前几个问题:不,你的结论不正确,是的,这是按设计工作的。
这种情况下的问题是,至少在某些平台上,
<Control-Shift-Key-B>
与虚拟事件<<SelectPrevChar>>
相关联。当您按下 control+B 时,事件会在传递给小部件之前转换为 <<SelectPrevChar>>
虚拟事件。类绑定是针对该事件而不是原始按键。换句话说,小部件永远不会看到<Control-Shift-Key-B>
,它只会看到<<SelectPrevChar>>
.
您可以通过以下两种方式之一解决此问题:
要删除虚拟事件并向原始事件序列添加显式绑定,您可以这样做:
self.window.event_delete("<<SelectPrevChar>>")
self.t1.bind_class("Text", "<Control-Shift-Key-B>", self.func)
要更改虚拟事件的类绑定,您可以在虚拟事件上使用
bind_class
:
self.t1.bind_class("Text", "<<SelectPrevChar>>", self.func)
要查看所有虚拟事件的列表以及它们绑定到的键,您可以运行这段代码,它使用不带参数的
event_info
命令来获取虚拟事件列表,然后使用相同的方法获取有关每个事件的信息。
for event in self.window.event_info():
info = self.window.event_info(event)
print(f"{event:<20s} => {', '.join(info)}")