在文本小部件中应用标签

0 投票
2 回答
1662 浏览
提问于 2025-04-17 03:42

我需要一些关于标签的帮助!

我正在写一个简单的编辑器,支持基本的文本格式化。通过使用一个叫做 text 的文本组件,我可以用一个标签 'b' 来设置文本,使其变成粗体。

当我对选中的文本应用粗体时,这没什么问题:

text.tag_add('b', SEL_FIRST,SEL_LAST)

但是,当我在输入时想要随时开启或关闭粗体时,我遇到了两个问题。要开启粗体,我找到的唯一方法是:

text.insert(INSERT, '  ', 'b' )
text.mark_set("insert", INSERT+'-1c')

注意,我必须插入两个空格。如果我只插入一个空格,粗体就不会生效。如果我插入 '', 光标会退回一个字符!

我的第二个问题是,当我在一个已经是粗体的区域内写字时,怎么关闭粗体——对此我完全没有头绪...

谢谢任何帮助!

2 个回答

-1

谢谢你,Bryan,但我觉得你的方法有点复杂,我也不太想去写那些缺失的绑定,比如说paste这个功能!

我简单地写了下面的代码,结果似乎可以正常工作。

        l=text.tag_names('insert')
        if l==() or l[0]!='b':   # select bold
          text.insert(INSERT, '  ', 'b' )
          text.mark_set('insert', 'insert-1c')
        else:                    # deselect bold
          text.insert(INSERT, ' ' )
          text.tag_remove ('b','insert-1c') 
          text.mark_set('insert', 'insert-1c')

我现在唯一的问题是,当我选择加粗时,还是会多插入一个空格,但我能接受这个问题...

alessandro

3

你可能没有意识到,但你正在尝试在Tkinter中做一件非常困难的事情。虽然文本小部件的标签是一个强大的概念,但在创建像所见即所得编辑器这样的东西时,它们有一些缺点。

你需要稍微改变一下思路。与其插入空格,我认为更好的解决方案是添加一些绑定,每次插入字符时应用(或移除)你的标签。这也有自己的一些问题,但只要你足够关注细节,就能克服它们。我们可以通过自定义键绑定来实现这一点。

当你在文本小部件中插入一个字符时,这个操作是通过文本小部件类上的<Key>事件绑定来处理的。所以,我们可以绑定<Key>来添加标签。然而,如果我们在小部件上添加<Key>的绑定,这个绑定会在类绑定之前触发,这意味着我们的代码会在字符插入之前执行,而不是之后。这样我们就会尝试修改一个实际上还没有插入的小部件。

解决这个问题的一种方法是绑定到键的释放事件,而不是按下事件,因为字符是在按下时插入的。不过,想象一下用户按住一个键的场景——会输入多个字符,但你可能只会收到一次键抬起事件。所以这个解决方案并不是特别好。

另一种解决方案是以某种方式安排我们的自定义绑定在默认绑定之后发生。为此,我们需要做两件事:1)调整小部件的“绑定标签”,在类标签后面添加一个额外的标签,2)为这个新绑定标签添加一个绑定。

这种方法也有缺点。并不是因为绑定标签,而是因为除了<Key>之外,你还需要处理很多其他事件(例如,控制键加V来粘贴并不由<Key>绑定处理,所以你需要为粘贴添加一个特殊情况)。

尽管如此,这个解决方案可能对你来说已经足够好,或者至少能帮助你更好地理解问题,而理解问题往往是找到解决方案的最大障碍。

在下面的例子中,我有一个文本小部件,它有一个额外的绑定标签,名为“CustomText”,我们将其放在标准“Text”绑定标签之后。我在这个标签上为<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()

撰写回答