Saturday, 16 May 2015

Changing The Colour Of Selected Text In Tkinter Text Wiget In Python

I am working on a simple chatbot program using Tkinter in Python 3 and have finished all the back end.
I'm now working on making the front end look pretty.

The Problem
One of the things I wanted to do was change the colour of the chatbot's title in the text widget to make it easier to read. I thought it would be pretty easy.
My guess was that I could modify the colour from the .insert method. A little like this:

text.insert(END,'Bottity Bottity Bot Bot: '+ BotInput + '\n',foreground='red')
Fail!!!
Life isn't that easy.

The Solution
It turns out that it is quite a task to add a colour to words in a text widget in Tkinter. The best hack I could find was to modify a search and highlight function I found in Python in a Nutshell by Alex Martelli.


I had to make some changes which basically removed the search function and replaced it with a key word of the name of my chatbot, in this case: Bottity Bottity Bot Bot

Here are the results in a simple example:

###Changing the colour of selected text in Tkinter Text wiget###
from tkinter import *
from tkinter import ttk
root = Tk( )
# fill rest of root with a Text and put some text there
BotInput = "This usually links to a database dictionary of responses."
###This is were the magic happens###
# action-function for the Button: highlight all occurrences of string
def find( ):
# get string to look for (if empty, no searching)
s = "Bottity Bottity Bot Bot:"
if s:
# start from the beginning (and when we come to the end, stop)
idx = '1.0'
while 1:
# find next occurrence, exit loop if no more
idx = text.search(s, idx, nocase=1, stopindex=END)
if not idx: break
# index right after the end of the occurrence
lastidx = '%s+%dc' % (idx, len(s))
# tag the whole occurrence (start included, stop excluded)
text.tag_add('found', idx, lastidx)
# prepare to search for next occurrence
idx = lastidx
# use a red foreground for all the tagged occurrences
text.tag_config('found', foreground='red')
# This displays the entries from the Bot and the User in the text
def display_entry(*args):
UserInput = Comment.get()
text.insert(END,
'Bottity Bottity Bot Bot: '+ BotInput + '\n')
text.insert(END,
'User: ' +UserInput +'\n')
Comment.delete(0,END)
#Runs the function
find()
#Creat text wiget
text = Text(root)
text.pack()
#Entry field to add content to text wiget.
Comment_Lbl = ttk.Label(root, text="Comment here:")
Comment_Lbl.pack()
Comment = ttk.Entry(root)
Comment.pack()
Comment.bind("<Return>", display_entry)
Comment.focus()
Comment_Button = ttk.Button(root, text="Enter", command = lambda:display_entry(None))
Comment_Button.pack()
root.mainloop( )
As you can see, the find() function searches for my chatbot name and then changes it's foreground color in the text.tag_config('found', foreground='red') section. in tag_config you can also modify the font styles and the background.

P.S. I really like the lastidx = '%s+%dc' % (idx, len(s)) variable. This was such a clever bit of code that I spent an hour down the rabbit hole on String Formatting Operations.

No comments:

Post a Comment