How to highlight text in a tkinter Text widget

33,400

It's the right widget to use for these purposes. The basic concept is, you assign properties to tags, and you apply tags to ranges of text in the widget. You can use the text widget's search command to find strings that match your pattern, which will return you enough information apply a tag to the range that matched.

For an example of how to apply tags to text, see my answer to the question Advanced Tkinter text box?. It's not exactly what you want to do but it shows the basic concept.

Following is an example of how you can extend the Text class to include a method for highlighting text that matches a pattern.

In this code the pattern must be a string, it cannot be a compiled regular expression. Also, the pattern must adhere to Tcl's syntax rules for regular expressions.

class CustomText(tk.Text):
    '''A text widget with a new method, highlight_pattern()

    example:

    text = CustomText()
    text.tag_configure("red", foreground="#ff0000")
    text.highlight_pattern("this should be red", "red")

    The highlight_pattern method is a simplified python
    version of the tcl code at http://wiki.tcl.tk/3246
    '''
    def __init__(self, *args, **kwargs):
        tk.Text.__init__(self, *args, **kwargs)

    def highlight_pattern(self, pattern, tag, start="1.0", end="end",
                          regexp=False):
        '''Apply the given tag to all text that matches the given pattern

        If 'regexp' is set to True, pattern will be treated as a regular
        expression according to Tcl's regular expression syntax.
        '''

        start = self.index(start)
        end = self.index(end)
        self.mark_set("matchStart", start)
        self.mark_set("matchEnd", start)
        self.mark_set("searchLimit", end)

        count = tk.IntVar()
        while True:
            index = self.search(pattern, "matchEnd","searchLimit",
                                count=count, regexp=regexp)
            if index == "": break
            if count.get() == 0: break # degenerate pattern which matches zero-length strings
            self.mark_set("matchStart", index)
            self.mark_set("matchEnd", "%s+%sc" % (index, count.get()))
            self.tag_add(tag, "matchStart", "matchEnd")
Share:
33,400
Admin
Author by

Admin

Updated on July 05, 2022

Comments

  • Admin
    Admin almost 2 years

    I want to know how to change the style of certain words and expressions based on certain patterns.

    I am using the Tkinter.Text widget and I am not sure how to do such a thing (the same idea of syntax highlighting in text editors). I am not sure even if this is the right widget to use for this purpose.

  • CodingCat
    CodingCat over 11 years
    Thanks, this has helped me immensely! Can you tell me how to change this so it accepts regular expressions as patterns, though? (When I try, I get TypeError: '_sre.SRE_Pattern' object has no attribute '__getitem__')
  • Russell Smith
    Russell Smith over 11 years
    @Lastalda: the text widget search method accepts a keyword argument named regexp. If you set this to True the pattern will be treated as a regular expression. I've updated my answer to include this functionality. Unfortunately tkinter-specific documentation on the search method is a bit sparse. If you read the official tk documentation it's explained a little better, though you have to do a small mental translation from tcl to python. See tcl.tk/man/tcl8.5/TkCmd/text.htm#M120
  • CodingCat
    CodingCat over 11 years
    Thanks for looking into it. But I still get the same error. :( Am I doing something wrong with the regexp? I use w.HighlightPattern(re.compile("R\d+"),"blue") and I get the error traceback File "C:\Python27\lib\lib-tk\Tkinter.py", line 3030, in search if pattern and pattern[0] == '-': args.append('--') TypeError: '_sre.SRE_Pattern' object has no attribute '__getitem__'
  • Russell Smith
    Russell Smith over 11 years
    @Lastalda: The pattern argument must be a string, not a compiled regular expression.
  • CodingCat
    CodingCat over 11 years
    Huh. I tried that first, then tried the compiled version when it didn't work. But now it does work. Anyway, it's working now. Thank you so much!
  • Russell Smith
    Russell Smith almost 8 years
    @tom_mai78101: I think you are incorrect. Did you actually try running the code? It highlights fine whether the pattern is a fixed string or a regular expression.count represents the number of characters that matched, and that's how many characters we want to highlight. If we match 10 characters, we want to highlight 10 characters. Why subtract 1?
  • Russell Smith
    Russell Smith almost 8 years
    @tom_mai78101: I think it's simply the case that your pattern is matching more than you realize. It's impossible for me to say since I don't know what your pattern actually resolves to.
  • tom_mai78101
    tom_mai78101 almost 8 years
    @BryanOakley The pattern I am matching is WedrBot. No spaces, nor anything added. This is what happens before adding -1: i.imgur.com/HjXsVKb.png and after adding -1: i.imgur.com/wYHVApY.png I am guessing it may be because I specified my regex to not include the ] character, so it can differentiate from tags for the IRC, sender, and names within the text messages. But that is my educated guess. What do you think?
  • Russell Smith
    Russell Smith almost 8 years
    @tom_mai78101: ake a close look at your pattern. If the pattern looks like WedrBot([^\>])|...) than that means it will match the string WedrBot] because the literal character ] is matched by the sub-pattern [^\>] which means "any character other than >". You are telling it to match the literal string WedrBot followed by any character other than that one, so it matches WedrBot]. Again, it's not the highlighting code, it's the fact that your pattern is matching more than you think it does. Of that I am 100% certain.
  • tom_mai78101
    tom_mai78101 almost 8 years
    @BryanOakley I took a look, and indeed it is like that. I'm currently looking up alternatives for this. Thanks, and sorry for saying you're incorrect.