我有一个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()
也可以优先于它们。
我的问题是:
- 我上面的结论是正确的,还是有别的原因?
- 这是
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()
优先级顺序不会改变,在任何情况下对所有小部件都是相同的,并且总是由小部件的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)}")