在文本小部件中重新绑定“全选”
我正在使用文本小部件,但遇到了一个关于Tk使用的老式快捷键的问题。
比如:
全选: Ctrl + /
和 Ctrl + a
剪切: Ctrl + w
和 Ctrl + x
复制: Meta + w
和 Ctrl + c
粘贴: Ctrl + y
和 Ctrl + v
在Windows系统上,除了Ctrl+a,其他的快捷键都能正常工作。
1) 有没有办法重定向绑定,这样 .bind('<Control-a>')
就可以调用已经绑定的Ctrl + /?
2) 我尝试了“全选”的操作:
txt_text.bind('<Control-a>', self.ctext_selectall)
其中:
def ctext_selectall(self, callback):
"""Select all text in the text widget"""
self.txt_text.tag_add('sel', '1.0', 'end')
但是这个方法不奏效,因为Ctrl+a是默认的(光标会跳到开头)。它在其他一些未绑定的字母上可以正常工作。如果第一个问题的解决方案不可行,还有没有其他办法可以让这个功能正常?
2 个回答
你可以随意使用下面的代码,或者至少看看在 DiacriticalEntry
和 DiacriticalText
这两个类里是怎么实现 select_all
方法的。如果你决定直接使用这些类,替代你现在用的任何控件,你会发现一个好处,那就是用户可以更轻松地输入一些特定的字符,这些字符在其他情况下可能会比较难输入。
## {{{ http://code.activestate.com/recipes/576950/ (r3)
from tkinter import *
from tkinter.scrolledtext import ScrolledText
from unicodedata import lookup
import os
class Diacritical:
"""Mix-in class that adds keyboard bindings for accented characters, plus
other common functionality.
An inheriting class must define a select_all method that will respond
to Ctrl-A."""
accents = (('acute', "'"), ('grave', '`'), ('circumflex', '^'),
('tilde', '='), ('diaeresis', '"'), ('cedilla', ','),
('stroke', '/'), ('ring above', ';'))
def __init__(self):
# Fix some key bindings
self.bind("<Control-Key-a>", self.select_all)
# We will need Ctrl-/ for the "stroke", but it cannot be unbound, so
# let's prevent it from being passed to the standard handler
self.bind("<Control-Key-/>", lambda event: "break")
# Diacritical bindings
for a, k in self.accents:
# Little-known feature of Tk, it allows to bind an event to
# multiple keystrokes
self.bind("<Control-Key-%s><Key>" % k,
lambda event, a=a: self.insert_accented(event.char, a))
def insert_accented(self, c, accent):
if c.isalpha():
if c.isupper():
cap = 'capital'
else:
cap = 'small'
try:
c = lookup("latin %s letter %c with %s" % (cap, c, accent))
self.insert(INSERT, c)
# Prevent plain letter from being inserted too, tell Tk to
# stop handling this event
return "break"
except KeyError as e:
pass
class DiacriticalEntry(Entry, Diacritical):
"""Tkinter Entry widget with some extra key bindings for
entering typical Unicode characters - with umlauts, accents, etc."""
def __init__(self, master=None, **kwargs):
Entry.__init__(self, master, **kwargs)
Diacritical.__init__(self)
def select_all(self, event=None):
self.selection_range(0, END)
return "break"
class DiacriticalText(ScrolledText, Diacritical):
"""Tkinter ScrolledText widget with some extra key bindings for
entering typical Unicode characters - with umlauts, accents, etc."""
def __init__(self, master=None, **kwargs):
ScrolledText.__init__(self, master, **kwargs)
Diacritical.__init__(self)
def select_all(self, event=None):
self.tag_add(SEL, "1.0", "end-1c")
self.mark_set(INSERT, "1.0")
self.see(INSERT)
return "break"
def test():
frame = Frame()
frame.pack(fill=BOTH, expand=YES)
if os.name == "nt":
# Set default font for all widgets; use Windows typical default
frame.option_add("*font", "Tahoma 8")
# The editors
entry = DiacriticalEntry(frame)
entry.pack(fill=BOTH, expand=YES)
text = DiacriticalText(frame, width=76, height=25, wrap=WORD)
if os.name == "nt":
# But this looks better than the default set above
text.config(font="Arial 10")
text.pack(fill=BOTH, expand=YES)
text.focus()
frame.master.title("Diacritical Editor")
frame.mainloop()
if __name__ == "__main__":
test()
## end of http://code.activestate.com/recipes/576950/ }}}
默认的绑定是应用在小部件类上的。当你进行绑定时,它会影响到特定的小部件,而这个绑定是在类绑定之前发生的。所以,实际上你的绑定是先发生的,然后才是类绑定,这就让人觉得你的绑定没有起作用。
解决这个问题有两种方法。第一种,你的 ctext_selectall
可以返回字符串 "break",这样就可以阻止类绑定的触发。这应该足以解决你眼前的问题。
第二种解决方案是修改类绑定,让你想要的绑定适用于所有文本小部件。你可以通过 bind_class
方法来实现这一点。
下面是重新绑定类的一个例子:
def __init__(...):
self.root.bind_class("Text","<Control-a>", self.selectall)
def selectall(self, event):
event.widget.tag_add("sel","1.0","end")
effbot.org 有一篇不错的文章,标题是 事件和绑定。它详细讲解了类和小部件的绑定以及它们发生的顺序。
Tk 的绑定机制在所有图形用户界面工具包中算是最好的之一。一旦你理解了它的工作原理(其实非常简单),你会发现很容易增强或替换任何默认的绑定。