您可能没有意识到,但您正在尝试做一些在 Tkinter 中非常困难的事情。虽然文本小部件标签是一个强大的概念,但在创建像所见即所得编辑器这样的东西时它们有一些弱点。
你需要稍微改变你的方法。而不是插入空格,我认为更好的解决方案是在每次插入字符时添加应用(或删除)标签的绑定。这有它自己的一系列问题,但是只要对细节给予足够的关注,你就可以克服它们。我们可以通过自定义键绑定来做到这一点。
当您将字符插入文本小部件时,这由文本小部件类上的<Key> 事件上的绑定处理。所以,我们可以绑定到<Key> 来添加标签。但是,如果我们在小部件上添加到<Key> 的绑定,这将触发类绑定before,这意味着我们的代码将在插入字符之前而不是之后执行。我们将尝试修改尚未实际插入小部件的内容。
解决这个问题的一种方法是绑定到一个键释放而不是按键,因为字符是在按键上插入的。然而,想想用户按住一个键的场景——将输入多个字符,但您可能只会得到一个按键事件。所以这个解决方案不是特别好。
另一种解决方案是以某种方式安排我们的自定义绑定发生在默认绑定之后。为此,我们需要做两件事:1) 调整小部件的“绑定标签”,使其在类标签之后有一个额外的标签,以及 2) 为这个新的绑定标签添加一个绑定。
这种方法也有缺点。不是因为绑定标签,而是因为除了<Key> 之外,您还需要处理更多事件(例如,<Key> 绑定不处理要粘贴的 control-v,因此您必须添加粘贴的特殊情况)。
话虽如此,这个解决方案对你来说可能已经足够好了,或者至少足以帮助你更好地理解问题,而理解问题往往是找到解决方案的最大障碍。
在下面的示例中,我有一个文本小部件,它有一个名为“CustomText”的附加绑定标签,我们将它放在标准“文本”绑定标签之后。我在这个标签上为<Key> 事件设置了一个绑定,在处理程序中我只是将适当的标签应用到刚刚插入的字符上。
您必须添加自己的代码来处理剪贴板粘贴,以及处理冲突标签的问题(例如两个标签各有自己的字体)。不过,希望这个例子能起到启发作用
import Tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.tag_vars = {
"underline": tk.IntVar(),
"red": tk.IntVar(),
}
self.text = MyText(self, width=40, height=8)
self.text.tag_configure("red", foreground="red")
self.text.tag_configure("underline", underline=True)
toolbar = tk.Frame(self)
self.underline = tk.Checkbutton(self, text="Underline",
onvalue = True, offvalue=False,
variable = self.tag_vars["underline"]
)
self.red = tk.Checkbutton(self, text="Red",
onvalue = True, offvalue=False,
variable = self.tag_vars["red"]
)
self.underline.pack(in_=toolbar, side="left")
self.red.pack(in_=toolbar, side="left")
toolbar.pack(side="top", fill="x")
self.text.pack(side="top", fill="both", expand=True)
class MyText(tk.Text):
def __init__(self, parent, *args, **kwargs):
tk.Text.__init__(self, *args, **kwargs)
self.parent = parent
# add a new bind tag, "CustomText" so we
# can have code run after the class binding
# has done it's work
bindtags = list(self.bindtags())
i = bindtags.index("Text")
bindtags.insert(i+1, "CustomText")
self.bindtags(tuple(bindtags))
# set a binding that will fire whenever a
# self-inserting key is pressed
self.bind_class("CustomText", "<Key>", self.OnKey)
def OnKey(self, event):
# we are assuming this is called whenever
# a character is inserted. Apply or remove
# each tag depending on the state of the checkbutton
for tag in self.parent.tag_vars.keys():
use_tag = self.parent.tag_vars[tag].get()
if use_tag:
self.tag_add(tag, "insert-1c", "insert")
else:
self.tag_remove(tag, "insert-1c", "insert")
if __name__ == "__main__":
app = SampleApp()
app.mainloop()