-2

I'm wondering if it's possible to bind a tag, such as 'keyword', to a string, such as 'print' to my Text widget in Tkinter (Python).

I'd like to know this because I'm not sure how I can use regular expressions to connect to my Text widget, and it seems extremely over-complicated, so I was wondering if there was any normal way.


My current code (SyntaxHighlighting.py):

global lang
lang = 'py'

def highlighter_bind(obj):
    # Bind a highlighting language to a text widget within obj.
    # Notice that obj must be a Frame with a .root property being
    # the root it is binded to, and a .text widget being where I
    # can highlight the text.
    #
    # The lang variable must be specified as a string that
    # contains the file extension of whatever you want to
    # highlight.
    #
    # Supported languages:
    #  - py

    def command(self):
        # Sub command for highlighting, it's what will
        # be "binded" to <Key>.

        if lang == 'py':
            print 'imported language py'

            # This is to get all the contents from the Text widget (property).
            t = o.text.get('1.0', END).split('\n')[0:len(o.text.get('1.0', END).split('\n')) - 1]

            # Now loop through the text and find + add a tag to each of the line's contents, assuming that there is something to be found.
            for i in range(0, len(t)):
                # We use the try / except because search.group will return an error if it's a NoneType.
                try:
                    # Now we need to search through the line and see if we can find print.
                    if search('print ', t[i]).group(0):
                        # Now, WHERE did we find print? I get confused there,
                        # because I have no idea where to find the index(es) of
                        # the string I found within a line. I use a regex to find the
                        # string, but how do I find where?
                        print "Ok!" # Temporary, just stating that I found it. It works.
                except:
                    pass
        else:
            print 'unrecognized language:', lang

   o.root.bind('<Key>', command)

Ideal code were it to actually work:

...
def command(self):
    if lang == 'py':
        obj.text.tag_match("print", 'keyword')
        # And even better, using regex to match..
        obj.text.tag_match("(if|elif|else)", 'keyword')
        obj.text.tag_match("(not|and|or)", 'keyword')
        obj.text.tag_match("(\+|-|*|/|**|%%)", 'keyword') # %% is to put a modulus in.
Aqua the SeaWing
  • 333
  • 4
  • 16
  • Meh, not really. I'm not sure what I should put. All I have now is a `re.search()`, but I have no idea how to get **where** in the string my matches would be, nor how to convert it to a Tkinter index. – Aqua the SeaWing Jan 14 '15 at 00:07
  • Without code, it is diffuclt to help. What about using [StringVar](http://effbot.org/tkinterbook/variable.htm) ? – Marcin Jan 14 '15 at 00:09
  • I could do that but it'd be really handy to know of a `re` function that will return indexes of where my string _is_ within my string. But I do have my code so that it uses a `for` loop to search each line, which is easy (`for i in text.get('0.0', END).split('\n'): # ...`) – Aqua the SeaWing Jan 14 '15 at 00:12
  • Just edit you question and add this code, example input and output. Its not clear what you are really trying to do, and how tkinter fits in your problem. – Marcin Jan 14 '15 at 00:14
  • 1
    "bind a tag to a string" makes absolutely no sense. It's a bit like asking if you can paint a song -- the words just don't go together. Likewise for connecting a regular expression to a widget. What do you expect to happen when you bind to a string? Do you mean that you want something to happen when you type a whole word or an abbreviation, like a macro? What do you mean by connecting a regular expression to a text widget, and how does that relate to bindings, tags and strings? There's probably an easy solution to your problem, but your problem is very unclear. – Bryan Oakley Jan 14 '15 at 02:49
  • The text widget has a search function that can search for regular expressions. Is that what you're asking about? – Bryan Oakley Jan 14 '15 at 02:50
  • @Marcin: text widgets don't support StringVars. – Bryan Oakley Jan 14 '15 at 02:50
  • @BryanOakley Thanks. Forgot about this. – Marcin Jan 14 '15 at 03:35
  • 1
    Ah, so you're doing syntax highlighting? Have you looked at the answers to this question: http://stackoverflow.com/q/3781670/7432 ? – Bryan Oakley Jan 14 '15 at 12:45

1 Answers1

1

The way to apply highlighting according to a regular expression or fixed string is to use the text widget search method. Your method of searching the raw text and then trying to determine the index is not the right approach.

Here is an example of applying the tag "keyword" to all instances of the word "print":

import Tkinter as tk

class Example(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.text = tk.Text(self, wrap="none")
        xsb = tk.Scrollbar(self, orient="horizontal", command=self.text.xview)
        ysb = tk.Scrollbar(self, orient="vertical", command=self.text.yview)
        self.text.configure(yscrollcommand=ysb.set, xscrollcommand=xsb.set)
        ysb.grid(row=0, column=1, sticky="ns")
        xsb.grid(row=1, column=0, sticky="ew")
        self.text.grid(row=0, column=0, sticky="nsew")
        self.grid_rowconfigure(0, weight=1)
        self.grid_columnconfigure(0, weight=1)

        self.text.tag_configure("keyword", foreground="#b22222")

        # this probably isn't what you want to do in production, 
        # but it's good enough for illustrative purposes
        self.text.bind("<Any-KeyRelease>", self.highlight)
        self.text.bind("<Any-ButtonRelease>", self.highlight)

    def highlight(self, event=None):
        self.text.tag_remove("keyword", "1.0", "end")

        count = tk.IntVar()
        self.text.mark_set("matchStart", "1.0")
        self.text.mark_set("matchEnd", "1.0")

        while True:
            index = self.text.search("print", "matchEnd","end", count=count)
            if index == "": break # no match was found

            self.text.mark_set("matchStart", index)
            self.text.mark_set("matchEnd", "%s+%sc" % (index, count.get()))
            self.text.tag_add("keyword", "matchStart", "matchEnd")

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()

For an example that subclasses the text widget and adds a method for highlighting any regular expression, see this answer to the question How to highlight text in a tkinter Text widget

Community
  • 1
  • 1
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Sorry for never getting around to accepting this answer! D: It helped a lot, though - my text editor's going great thanks to you. :) – Aqua the SeaWing Feb 23 '15 at 22:50