【问题标题】:Apply tags in a text widget在文本小部件中应用标签
【发布时间】:2011-10-05 09:16:11
【问题描述】:

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

我正在编写一个简单的编辑器,支持基本格式。 通过使用文本小部件(名为 text),我放置了一个标签“b”来设置该标签适用于粗体的文本。

当我将粗体应用于选择时,这不是问题:

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

当我只想在打字时打开/关闭粗体时,我遇到了两个问题。 要打开它,我发现的唯一方法是:

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

请注意,我必须插入两个空格。如果我插入一个,粗体不适用。如果我插入 '',我的光标会返回一个字符!

我的第二个问题是如何关闭它,当我在一个粗体区域内书写时 - 对此我没有丝毫想法......

感谢您的帮助!

【问题讨论】:

    标签: python tkinter


    【解决方案1】:

    您可能没有意识到,但您正在尝试做一些在 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()
    

    【讨论】:

    • 我终于下定决心,能够将这段代码插入我的,似乎工作!然而,一个小问题是移动光标将当前格式拖到字符上。我在OnKey的开头放了一个,你怎么看?
    • @alessandro:如果我不得不猜测,听起来您的代码正在为每个事件调用 OnKey,而不仅仅是按键。你应该解决这个问题,而不是为了解决这个问题而进行破解。
    • mmmm 我不这么认为:即使运行您的代码,它的行为也是如此!事实上,我第一次尝试 event.char 测试是在你的身上
    • @alessandro:好的,我明白你现在在说什么了。通过“移动光标”,您的意思是您正在使用箭头键。是的,在这种情况下,您需要明确检查非插入键,例如箭头键。
    • 最后一点帮助(或者我应该开始一个新问题......)我刚刚看到其他按键会产生问题(例如退格),所以我必须编写一种方法让它通过通过仅可打印的字符...认为“string.printable 中的 event.char”可以吗?
    【解决方案2】:

    谢谢你,Bryan,但你的方法在我看来太复杂了,而且我并不急于开始为缺少的东西编写绑定 - 粘贴 为例!

    我简单地写了以下内容,它似乎有效

            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')
    

    我唯一剩下的问题是我还没有找到在选择粗体时不插入额外空格的方法 - 但我可以忍受它......

    亚历山德罗

    【讨论】:

    • 因为我自己的答案(这个)刚刚得到了反对票,谁愿意评论为什么?亲爱的先生,我向您保证,它有效!
    • ... 没有答案,所以我会确认(在使用了很多天后)我的解决方案有效,所以我会接受它作为答案,并保留它。
    • 投反对票可能是因为 Bryan 付出了很多努力为您提供了一个全面的答案,然后您甚至没有投赞成票;然后你只需接受他给你的信息并回答你自己的问题。哎哟... +1 布莱恩。
    • 抱歉,我仍然是一个非常可怜的 Tkinter-er,坦率地说,我什至无法理解 Bryan 的回答......对不起大家!
    • @alessandro:我投了反对票,原因很简单,因为这不能回答问题。如果其他人与您有同样的问题,IMO 这个答案对他们没有帮助。如果他们接受你的建议,他们可能会解决他们的第一个问题,但现在他们有另一个问题,那就是你不知道如何摆脱的额外空间。
    猜你喜欢
    • 1970-01-01
    • 2011-08-23
    • 1970-01-01
    • 2016-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多