在文本小部件中应用标签

发布于 2024-12-08 09:58:08 字数 469 浏览 0 评论 0原文

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

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

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

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

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

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

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

我的第二个问题是当我在粗体区域内书写时如何将其关闭 - 为此我一无所知......

感谢您的帮助!

I need some help tag-related!

I am writing a simple editor, supporting basic formatting.
By using a Text widget (named text), I put a tag 'b' to set the text where this tag applies to bold.

This is not a problem when I apply the bold to a selection:

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

I have instead two problems when I just want to switch on/off the bold while typing.
To switch it on the only way I found is this:

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

notice that I have to insert TWO spaces. If I insert one, the bold doesnt apply. If I insert '', my cursor goes back one char!

My second problem is how to switch it off, when I'm writing within a bolded region - and for this I havent the slightest idea...

Thanks for any help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

命硬 2024-12-15 09:58:08

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

你需要稍微改变一下你的方法。我认为更好的解决方案是添加每次插入字符时应用(或删除)标签的绑定,而不是插入空格。这有其自身的一系列问题,但只要足够关注细节,您就可以克服这些问题。我们可以通过自定义键绑定来做到这一点。

当您将字符插入文本小部件时,这是通过文本小部件类上的 事件上的绑定来处理的。因此,我们可以绑定到 来添加标签。但是,如果我们向小部件上的 添加绑定,这将在类绑定之前触发,这意味着我们的代码将在插入的字符之前而不是之后执行。我们将尝试修改尚未实际插入到小部件中的内容。

解决这个问题的一种方法是绑定到释放键而不是按键,因为字符是在按下时插入的。但是,考虑一下用户按下并按住某个键的场景 - 将输入多个字符,但您可能只会收到一个按键事件。所以这个解决方案并不是特别好。

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

这种方法也有缺点。不是因为绑定标签,而是因为除了 之外,您还需要处理更多事件(例如,用于粘贴的 control-v 不是由 < ;Key> 绑定,因此您必须添加特殊情况才能粘贴)。

话虽这么说,这个解决方案可能对您来说足够好,或者至少足以帮助您更好地理解问题,而理解问题往往是找到解决方案的最大障碍。

在下面的示例中,我有一个文本小部件,它有一个名为“CustomText”的附加绑定标签,我们将其放置在标准“Text”绑定标签之后。我为 事件对此标记进行了绑定,并在处理程序中将适当的标记应用于刚刚插入的字符。

您必须添加自己的代码来处理剪贴板粘贴,以及处理冲突标签的问题(例如两个标签各自具有自己的字体)。但希望这个例子能起到启发作用

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

You may not realize it, but you're trying to do something that's very hard in Tkinter. While the text widget tags are a powerful concept, they have some weaknesses when it comes to creating something like a wysywig editor.

You need to change your approach a bit. Instead of inserting spaces, I think the better solution is to add bindings that apply (or remove) your tags each time a character is inserted. This has it's own set of problems, but with enough attention to detail you can overcome them. We can do that though a custom key binding.

When you insert a character into a text widget, this is handled by a binding on the <Key> event on the text widget class. So, we could bind to <Key> to add the tag. However, if we add a binding to <Key> on the widget, this will fire before the class binding, meaning our code will execute before the character inserted rather than after. We'll be trying to modify something that hasn't actually been inserted into the widget yet.

One way to solve that is to bind to a key release rather than a key press, since the character is inserted on the press. However, think of the scenario where a user presses and holds a key down -- multiple characters will be entered but you may only get a single key-up event. So this solution isn't particularly good.

Another solution is to somehow arrange for our custom binding to happen after the default binding. To do that we need to do two things: 1) adjust the "bind tags" for the widget to have an additional tag after the class tag, and 2) add a binding to this new bind tag.

There are downsides to this approach too. Not because of the bind tags, but because there are a lot more events you need to handle besides <Key> (for example, control-v to paste isn't handled by the <Key> binding, so you'll have to add a special case for pasting).

That being said, this solution might be good enough for you, or at least good enough to help you better understand the problem, and understanding the problem is often the biggest obstacle to finding a solution.

In the following example I have a text widget that has an additional bindtag named "CustomText" that we place after the standard "Text" bind tag. I put a binding on this tag for the <Key> event, and in the handler I simply apply the appropriate tags to the just-inserted character.

You'll have to add your own code to handle a clipboard paste, and the problem of dealing with conflicting tags (such as two tags each with their own font). Hopefully, though, this example will act as inspiration

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()
╰◇生如夏花灿烂 2024-12-15 09:58:08

谢谢布莱恩,但在我看来,你的方法太复杂了,而且我不太急于开始为缺少的内容编写绑定 - 粘贴作为示例!

我只是写下了以下内容,它似乎有效

        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

Thank you Bryan, but your approach seems to me too much complicated, and I'm not so eager to start writing bindings for what's missing - paste as an example!

I simply wrote down the following, and it seems to work

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

My only remaining problem is that I didnt find yet a way not to insert an additional space when selecting bold - but I can live with it...

alessandro

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文