24

I have a program that I want to be like the Python shell and change color of certain words when they are typed. Any help?

WarpPrime
  • 215
  • 5
  • 20
Dan Alexander
  • 2,004
  • 6
  • 24
  • 34

4 Answers4

31

The main idea is to apply tags to the parts of text you want to customise. You can create your tags using the method tag_configure, with a specific style, and then you just need to apply this tag to the part of text you want to change using the method tag_add. You can also remove the tags using the method tag_remove.

The following is an example that uses tag_configure, tag_add and tag_remove methods.

#!/usr/bin/env python3

import tkinter as tk
from tkinter.font import Font

class Pad(tk.Frame):

    def __init__(self, parent, *args, **kwargs):
        tk.Frame.__init__(self, parent, *args, **kwargs)

        self.toolbar = tk.Frame(self, bg="#eee")
        self.toolbar.pack(side="top", fill="x")

        self.bold_btn = tk.Button(self.toolbar, text="Bold", command=self.make_bold)
        self.bold_btn.pack(side="left")

        self.clear_btn = tk.Button(self.toolbar, text="Clear", command=self.clear)
        self.clear_btn.pack(side="left")

        # Creates a bold font
        self.bold_font = Font(family="Helvetica", size=14, weight="bold")

        self.text = tk.Text(self)
        self.text.insert("end", "Select part of text and then click 'Bold'...")
        self.text.focus()
        self.text.pack(fill="both", expand=True)

        # configuring a tag called BOLD
        self.text.tag_configure("BOLD", font=self.bold_font)

    def make_bold(self):
        # tk.TclError exception is raised if not text is selected
        try:
            self.text.tag_add("BOLD", "sel.first", "sel.last")        
        except tk.TclError:
            pass

    def clear(self):
        self.text.tag_remove("BOLD",  "1.0", 'end')


def demo():
    root = tk.Tk()
    Pad(root).pack(expand=1, fill="both")
    root.mainloop()


if __name__ == "__main__":
    demo()

If you don't know what sel.first and sel.last are, check out this post or this reference.

Community
  • 1
  • 1
nbro
  • 15,395
  • 32
  • 113
  • 196
  • not helpful at all - I cant seem to specify a font color for the text –  Jun 27 '20 at 22:31
  • With that information alone we can't help you. Maybe ask a new question and provide an example that we can run and check that what you say is true. – nbro Jun 27 '20 at 22:32
  • It will work only after selecting the text and clicking `Bold` button and will not automatically highlight as soon as the user types a specific word. But the OP wants automatic highlighting. Is it possible to do using tkinter? –  Oct 04 '20 at 09:38
  • I want to do this in label, not text. I got this : 'Label' object has no attribute 'tag_configure' how can i do it in label? – DIGMASTER97 Jul 16 '21 at 07:57
  • 1
    @DIGMASTER97: you can't do it in a label. A label only has a single foreground and single background color. – Bryan Oakley Jul 20 '21 at 14:54
  • so if i have like a 100 words to have their own styling or another 100 to have a common styling will if have to define these tags that many times..? – Ajay Singh Rana Sep 08 '21 at 12:29
15

I have made a chat client. I highlighted certain parts of the conversation using a custom quite easy to use Text widget that allows you to apply tags using regular expressions. It was based on the following post: How to highlight text in a tkinter Text widget.

Here you have an example of use:

# "text" is a Tkinter Text

# configuring a tag with a certain style (font color)
text.tag_configure("red", foreground="red")

# apply the tag "red" 
text.highlight_pattern("word", "red")
Community
  • 1
  • 1
Rnhmjoj
  • 863
  • 1
  • 10
  • 18
  • 1
    a problem `AttributeError: 'Text' object has no attribute 'highlight_pattern'` – Shihab Jun 02 '21 at 10:06
  • 1
    I want to do this in label, not text. I got this : 'Label' object has no attribute 'tag_configure' how can i do it in label? – DIGMASTER97 Jul 16 '21 at 07:57
12

Have a look at this example:

from tkinter import *

root = Tk()

text = Text(root)
text.insert(INSERT, "Hello, world!\n")
text.insert(END, "This is a phrase.\n")
text.insert(END, "Bye bye...")
text.pack(expand=1, fill=BOTH)

# adding a tag to a part of text specifying the indices
text.tag_add("start", "1.8", "1.13")
text.tag_config("start", background="black", foreground="yellow")

root.mainloop()
nbro
  • 15,395
  • 32
  • 113
  • 196
Adem Öztaş
  • 20,457
  • 4
  • 34
  • 42
  • 3
    tag_add(tagname, startindex[,endindex] ...) This method tags either the position defined by startindex, or a range delimited by the positions startindex and endindex. – Adem Öztaş Feb 09 '13 at 09:21
  • 1
    the thing is with the 1.8 and 1.13 is where the text is i want it to change color when ever that text comes up – Dan Alexander Jun 08 '13 at 06:54
0

I was able to change the color of the text for every match of a regex using the custom tkinter widget Text to get an event similiar to a 'text_changed':

import tkinter as tk

class CustomText(tk.Text):

def __init__(self, *args, **kwargs):
    """A text widget that report on internal widget commands"""
    tk.Text.__init__(self, *args, **kwargs)

    # create a proxy for the underlying widget
    self._orig = self._w + "_orig"
    self.tk.call("rename", self._w, self._orig)
    self.tk.createcommand(self._w, self._proxy)

def _proxy(self, command, *args):
    cmd = (self._orig, command) + args
    result = self.tk.call(cmd)
    if command in ("insert", "delete", "replace"):
        self.event_generate("<<TextModified>>")
    return result

And then, use it like that:

scr = CustomText(w)
scr.tag_configure('red', foreground = 'red')
scr.tag_configure('purple', foreground = '#a820a1')
scr.bind('<<TextModified>>', self.__textchanged__)

def __textchanged__(self, evt):
    for tag in evt.widget.tag_names():
        evt.widget.tag_remove(tag, '1.0', 'end')
    lines = evt.widget.get('1.0', 'end-1c').split('\n')
    for i, line in enumerate(lines):
        self.__applytag__(i, line, 'red', 'while|if', evt,widget) # your tags here
        self.__applytag__(i, line, 'purple', 'True', evt.widget)  # with a regex

@staticmethod
def __applytag__ (line, text, tag, regex, widget):
    indexes = [(m.start(), m.end()) for m in re.finditer(regex, text)]
    for x in indexes:
        widget.tag_add(tag, f'{line+1}.{x[0]}', f'{line+1}.{x[1]}')