Tkinter中冲突的默认绑定和用户定义绑定之间的事件依赖的优先级变化



我有一个Python GUI与Tkinter。在一个类中,我有一些文本小部件。它们有一些默认绑定,其中一些我想用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()中引用的函数末尾没有return 'break',则bind_class()将永远不起作用,并且只会调用内置函数。同时,带有小写键的内置热键(例如Ctrl-a/e/b/f)具有较低的优先级,甚至bind_class()也可以优先于它们。

我的问题是:

  1. 我上面的结论是正确的,还是有别的原因?
  2. 这是bind()bind_class()的预期功能吗?
  3. 是否仍然有可能覆盖像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()

优先级顺序不会改变,在任何情况下对所有小部件都是相同的,并且总是由小部件的bindtags定义。所以,回答你的前几个问题:不,你的结论是不正确的,是的,这是按设计工作的。

这种情况下的问题是,至少在某些平台上,<Control-Shift-Key-B>与虚拟事件<<SelectPrevChar>>相关联(<Control-B><Control-Shift-Key-B>相同)。当您按control+B时,事件在传递给小部件之前被转换为<<SelectPrevChar>>虚拟事件。类绑定在该事件上,而不是在原始的按键上。换句话说,小部件从来没有看到<Control-Shift-Key-B>,它只看到<<SelectPrevChar>>

你可以用两种方法解决这个问题:

  • 你可以删除虚拟事件,在这种情况下,你的类绑定将工作
  • 可以更改虚拟事件的类绑定

要从虚拟事件中删除特定的原始事件,并向原始事件序列添加显式绑定,可以这样做:

self.window.event_delete("<<SelectPrevChar>>", "<Control-Shift-Key-B>")
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)}")