The text widget can be used to display and edit arbitrary amounts of text, but it can also hold images and other widgets. It is a versatile widget with a large number of subcommands. We will not cover all functionality in this tutorial. Instead we limit ourselves to the basic functionality and some not so basic features.
Creating a text widget with a command like:
grid [text .t]
brings up a widget that is actually a reasonably capable text editor in its own right. You can type in text, remove, copy and paste text as you go along in the usual ways.
Besides interactive use you can use subcommands like insert to add text programmatically:
set infile [open "readme.txt"] set contents [read $infile] close $infile .t insert end $contents
Because the text that you want to display may not all fit on the screen, it is quite usual to add scrollbars to select what part is shown:
scrollbar .h -orient horizontal -command {.t xview} scrollbar .v -orient vertical -command {.t yview} text .t -xscrollcommand {.h set} -yscrollcommand {.v set} grid .t .v -sticky news grid .h - -sticky news
and we have a text widget and scrollbars that allow us to display and edit a large file.
One possible use of the text widget is displaying the output from an external program. In that case, you probably want to see the last lines of text by default as they are added to the widget. This can be achieved with the see command:
.t see end
The chan event procedure that reads the output from the external process would look something like:
proc displayOutput {channel} { if { [gets $channel line] >= 0} { .t insert end "$line\n" ;# Add a newline explicitly .t see end } else { ... } }
In the above examples ‘end’ is a special position in the text that is contained within the text widget. You can use any position (or index as it is called in the manual pages) you like:
Absolute positions:
‘1.0’ means the first line (1) and the first character on that line (0)
‘2.3’ means the second line and the fourth character
(For historical reasons the line count starts at 1 and the character count starts at 0.)
Special positions:
‘end’ signifies the position after the last character
‘2.end’ signifies the position after the last character on the second line
‘insert’ is the position where the characters that you type are inserted
You can add markers to indicate positions that you want the text widget to remember, like bookmarks:
.t mark set Here 2.3 ... .t see Here
would ensure that the character at position 2.3 becomes visible — it will not necessarily be visible on the first line in the widget!
Of course you will want to get the text from the text widget, once you are done editing. This is easy:
set contents [.t get 1.0 end]
will get you the complete contents of the text widget. You specify the beginning and end of the text you want to retrieve. So, if you are building a text editor, the procedure to save the contents would take a form like:
proc saveText {filename} { set outfile [open $filename w] puts $outfile [.t get 1.0 end] close $outfile }
You can do calculations with indices:
end - 1 chars
(or end-1c
) indicates the character before the
very end of the text, i.e., the last actual character
$index + 1 lines
(or $index + 1 l
) indicates the line after the
position contained in the variable index and the same character on the
line
This allows you to do some very sophisticated tasks in an easy way, like searching the text for particular words one by one (see below).
The text widget has a powerful mechanism for influencing the appearance of pieces of text: tags. Text can be given a particular colour or font via tags. Here is an example:
.t tag configure header -font "Helvetica 16 bold" .t insert 1.0 "The header\n\n" header
By setting the font for the tag ‘header’ to a different font family and font size than the default, any text with that tag will stand out.
Tags can be used for more things than just the appearance:
The option -elide can be used to make text visible and invisible.
You can add event bindings to a tag, which provides you with hypertext capabilities (see below)
Tags can be added like in the above example when you insert the text, but also later on:
.t insert 1.0 "The header\n\n" .t tag configure header -font "Helvetica 16 bold" .t tag add header 1.0 1.end
A tag has effect on one or more ranges of text and there can be more than one tag that has such an effect:
.t tag configure large -font "Helvetica 16" .t tag configure blue -foreground blue .t insert end "This text has a blue colour" blue .t tag add large 1.0 1.9
Of course, conflicts may occur — one tag imposing a green colour, the other a blue colour, for instance. This is resolved by the priority of the tag - see the manual page on the tag raise and tag lower subcommands.
To find where the tags are in the text you can use the tag nextrange, tag prevrange and tag ranges subcommands. They return the indices of the first character that has the tag and the first character that has not.
Last but not least, tags exist independently in the text widget: their properties remain defined even if there is no text fragment that uses them. So you can prepare all the tags you need before filling the text widget and you do not have to worry that they disappear inadvertently.
Another useful feature of the text widget is that you can search for text. The search subcommand has a number of options, like: search for an exact string, ignore the case, use glob patterns or regular expression patterns. In general it returns the position index of the first character of the substring that fits the search pattern.
The following commands bring the first occurrence of the string ‘scroll’ into view:
set index [.t search -exact "scroll" 1.0] .t see $index
You have to give the search subcommand a starting position, so in an editor application you can continue searching by increasing the index:
set next [.t search -exact "scroll" $index+1c]
If you want to highlight the substring in green, use:
.t tag configure found -foreground green .t tag add found $index $index+6c
or if you want to highlight the entire line:
.t tag configure found -background green .t tag add found "$index linestart" "$index lineend"
Another nice feature is to highlight the word that contains the substring:
.t tag configure found -background green .t tag add found "$index wordstart" "$index wordend"
That way computing the right positions is done entirely by the text widget itself.
As indicated before, you can define event bindings for tags. This means that you can enhance the text widget with hypertext behaviour. A short example of how to do that is given below:
proc showMessage {} { .t tag configure hidden -elide 0 } .t tag configure hidden -elide 1 .t tag configure hypertext -underline 1 .t tag bind hypertext <Button-1> {showMessage} .t insert end "Click " .t insert end "here" hypertext .t insert end "\nYoufound the hidden message!" hidden
This illustrates the principle: you can do virtually anything you want with this mechanism. It has in fact been used for a hypertext information system (see Wiki page http://wiki.tcl.tk/19649 for instance).
While it is easy to programmatically influence the appearance of the text or parts of the text, it is a bit trickier to make the text widget behave as a word processor would if you selected ‘bold text’. The widget must be instructed to add a tag to any text you type so that that text appears as bold.
One solution is to overload the bindings for key press events:
bind .text <KeyPress> {+insertChar %K} proc insertChar {char} { if { [string length $char] == 1 } { .text insert insert $char $::texttag return -code break } }
This technique is used in the program below.
proc mainWindow {} { global bold global italic global texttag # # Create the toolbar # set t [frame .toolbar] ttk::button $t.store -text Store -style Toolbutton \ -command {storeText} ttk::checkbutton $t.bold -text B -style Toolbutton \ -variable bold \ -command {toggleFont} ttk::checkbutton $t.italic -text I -style Toolbutton \ -variable italic \ -command {toggleFont} ttk::separator .sep text .text grid $t.store $t.bold $t.italic -padx 2 -sticky ns grid $t -sticky w grid .sep -sticky we grid .text # # Tags for the various fonts # .text tag configure bolditalic -font "Times 12 bold italic" .text tag configure bold -font "Times 12 bold" .text tag configure italic -font "Times 12 italic" .text tag configure normal -font "Times 12" .text tag add normal insert bind .text <KeyPress> {+insertChar %K} set texttag normal set italic 0 set bold 0 } # insertChar -- # Insert the typed character with the right tag # proc insertChar {char} { if { [string length $char] == 1 } { .text insert insert $char $::texttag return -code break } } # toggleFont -- # Make the text appear in bold or italic font or normal # # Side effects: # Next letters typed in the text widget have the selected font # proc toggleFont {} { global bold global italic global texttag if { $italic } { if { $bold } { set texttag bolditalic } else { set texttag italic } } else { if { $bold } { set texttag bold } else { set texttag normal } } } # storeText -- # Dump the contents # # Side effects: # Prints the contents of the text widget in a console # proc storeText {} { console show puts [.text dump 1.0 end] } # main -- # Start the program # # mainWindow
The text widget has many more options and subcommands than can be described in a tutorial like this. To give an impression:
Insert images and widgets in the text
Undo and redo facilities
Options for wrapping, margins, background colour and so on for the widget as a whole and for individual tags
Create peer widgets: the contents is the same (editing in one widget is reflected automatically in the other widget), but you can show different parts of the text