Text Widget¶
Manual page: text(3tk)
This widget is useful for displaying text that the user should be able to edit.
The text can contain multiple lines. Use Entry
if the text is just 1
line, or Label
if you don’t want the user to be able to edit the
text.
Note
This widget is not a Ttk widget because there is no Ttk text widget. However, that’s not a problem because usually text widgets are configured to have custom colors anyway instead of using the Ttk theme’s colors. For example, my editor lets the user change the colors of the text widget by selecting a color theme from a menu. Only the text widget colors change, and Ttk widgets are not affected by that.
Hello World!¶
import teek
window = teek.Window()
text = teek.Text(window)
text.pack()
text.insert(text.start, 'Hello World!')
window.on_delete_window.connect(teek.quit)
teek.run()
This program displays a window with a Hello World!
text in it displayed
using a monospace font. It lets you edit the text.
There is more example code in examples/text.py.
TextIndex Objects¶
In the above example, we used Text.insert()
, and we told it to insert
the hello world to the beginning of the text widget by passing it
text.start
. Here text.start
was a text index
namedtuple
:
>>> window = teek.Window()
>>> text = teek.Text(window)
>>> text.start
TextIndex(line=1, column=0)
Note
Line numbers start at 1, and columns start at 0. Tk does this too, and teek doesn’t hide it because it is very common to call the first line “line 1”…
Traceback (most recent call last):
File "my_file.py", line 1, in <module>
first line of code
...
…but it’s just as common to start column numbering at 0, just like string indexing in general.
Avoid working with 0-based line numbers if you can. That way your teek code will probably be simpler and less confusing.
The text indices can do everything that namedtuples can do.
>>> text.start.line # do this if you need only line or column
1
>>> text.start.column
0
>>> line, column = text.start # do this if you need both
>>> line
1
>>> column
0
In general, they behave like tuples:
>>> for lol in text.start:
... print(lol)
...
1
0
>>> list(text.start)
[1, 0]
There is also text.end
, which is the text index of the end of the text
widget:
>>> text.end
TextIndex(line=1, column=0)
>>> text.end == text.start # there's nothing in this text widget yet
True
>>> text.insert(text.start, 'hello')
>>> text.end
TextIndex(line=1, column=5)
>>> text.end == text.start
False
>>> text.insert(text.end, '\nsome text') # now there are 2 lines of text
>>> text.end
TextIndex(line=2, column=9)
>>> text.get(text.start, text.end)
'hello\nsome text'
The indexes may be out of bounds, but that does not create errors:
>>> text.TextIndex(1000, 1000)
TextIndex(line=1000, column=1000)
>>> text.get(text.start, text.TextIndex(1000, 1000))
'hello\nsome text'
>>> text.TextIndex(1000, 1000).between_start_end()
TextIndex(line=2, column=9)
The text index class can be accessed as some_text_widget.TextIndex
:
>>> text.TextIndex(1, 0)
TextIndex(line=1, column=0)
>>> isinstance('lol', text.TextIndex)
False
The TextIndex
classes are also valid type specifications.
Text indices have the following attributes and methods:
-
some_text_index.
forward
(chars=0, indices=0, lines=0)¶ Return a new text index after this index (or before, if the arguments are negative, or the same index if all arguments are zero). All arguments must be integers, and given as keyword arguments. Search for
+ count
in text(3tk) for an explanation of what each argument does.>>> text.get(text.start, text.end) 'hello\nsome text' >>> text.start.forward(chars=3) TextIndex(line=1, column=3) >>> text.start.forward(chars=7) # goes over a newline character TextIndex(line=2, column=1)
The returned indexes are always converted to be between
start
andend
withbetween_start_end()
:>>> text.start.forward(chars=1000) # won't go past end of text widget TextIndex(line=2, column=9)
-
some_text_index.
back
(chars=0, indices=0, lines=0)¶ Like
forward()
, but goes back instead of forward.
-
some_text_index.
linestart
()¶ -
some_text_index.
lineend
()¶ -
some_text_index.
wordstart
()¶ -
some_text_index.
wordend
()¶ These return new text indices. Search for e.g.
linestart
in text(3tk) for details.
-
some_text_index.
between_start_end
()¶ If the text index is before
start
or afterend
, this returnsstart
orend
, respectively. Otherwise the text index is returned as is.>>> text.TextIndex(1000, 1000) TextIndex(line=1000, column=1000) >>> text.TextIndex(1000, 1000).between_start_end() TextIndex(line=2, column=9) >>> text.end TextIndex(line=2, column=9)
Tip
Text indices are usually namedtuples, but methods that take text indices as
arguments (e.g. insert()
) can also take regular
(line, column)
tuples.
Marks¶
If you have a text widget that contains hello world
, the position just
before w
changes if you change hello
to something else, or if the user
edits the text widget’s content:
>>> text.replace(text.start, text.end, "hello world")
>>> text.get(text.start, text.end)
'hello world'
>>> before_w = text.TextIndex(1, 6)
>>> before_w
TextIndex(line=1, column=6)
>>> text.get(before_w, text.end)
'world'
>>> text.replace(text.start, text.start.forward(chars=5), 'hi')
>>> text.get(text.start, text.end) # hello was replaced with hi
'hi world'
>>> text.get(before_w, text.end) # but before_w didn't update!
'ld'
>>> before_w
TextIndex(line=1, column=6)
We can solve this problem by adding a mark:
>>> text.replace(text.start, text.end, "hello world")
>>> text.marks['before_w'] = text.TextIndex(1, 6)
>>> text.get(text.marks['before_w'], text.end)
'world'
>>> text.replace(text.start, text.start.forward(chars=5), 'hi')
>>> text.get(text.marks['before_w'], text.end)
'world'
>>> text.get(text.start, text.end)
'hi world'
Marks move with the text as the text before them is changed.
Text.marks
is a dictionary-like object with mark name strings as keys
and index objects as values. There is also a special
'insert'
mark that represents the cursor position:
# move cursor to new_cursor_pos
text.marks['insert'] = new_cursor_pos
There are more details about marks in the MARKS
section of
text(3tk).
Tags¶
You can use tags to change things like color of some of the text without
changing all of it. For example, this code displays a Hello World
with a
red Hello
and a green World
:
import teek
window = teek.Window("Text Widget Demo")
text = teek.Text(window)
text.pack(fill='both', expand=True)
text.insert(text.start, "hello world")
hello_tag = text.get_tag('hello_tag')
hello_tag['foreground'] = teek.Color('red')
hello_tag.add(text.start, text.start.forward(chars=len('hello')))
world_tag = text.get_tag('world_tag')
world_tag['foreground'] = teek.Color('green')
world_tag.add(text.end.back(chars=len('world')), text.end)
window.on_delete_window.connect(teek.quit)
teek.run()
Each tag has a name which is mostly useful for debugging. If you want to create
a tag, call get_tag()
with whatever name you want; a new tag is
created if a tag with the given name doesn’t exist yet.
>>> text.get_tag('hello_tag')
<Text widget tag 'hello_tag'>
Tag objects behave like config
objects, so you can e.g. change
their options with their dictionary-like behaviour. See TAGS
in
text(3tk) for a list of supported options. As usual, the options are
given without a leading -
, like 'foreground'
instead of
'-foreground'
.
There is a special tag named 'sel'
that represents the currently selected
text.
Tag objects have these attributes and methods. Search for pathName tag
in
text(3tk) for more information about them.
-
some_tag.
name
¶ The Tk name of the tag, as a string.
-
some_tag.
delete
()¶ Remove this tag from everywhere in the text widget, and forget all configuration options the tag has been given.
-
some_tag.
remove
(index1=None, index2=None)¶ Remove this tag from the text widget between the given indices.
index1
defaults tostart
, andindex2
defaults toend
.
-
some_tag.
to_tcl
()¶ Returns
some_tag.name
. See Python to Tcl conversion.
-
some_tag.
ranges
()¶ Returns a list of
(start_index, end_index)
pairs that describe where the tag has been added. The indexes are index objects.
-
some_tag.
prevrange
(index1, index2=None)¶ -
some_tag.
nextrange
(index1, index2=None)¶ These return the previous or next
(start_index, end_index)
pair. Seeranges()
and text(3tk).
-
some_tag.
bind
(sequence, func, *, event=False)¶
-
some_tag.
lower
(other_tag=None)¶ -
some_tag.
raise_
(other_tag=None)¶ These call
tag lower
andtag raise
documented in text(3tk). The raise method is calledraise_
instead ofraise
becauseraise
is not a valid method name in Python.For example, if you have code like this…
import teek text = teek.Text(teek.Window()) text.pack() red_tag = text.get_tag('red') blue_tag = text.get_tag('blue') red_tag['foreground'] = 'red' blue_tag['foreground'] = 'blue' text.insert(text.start, 'asdasdasd', [red_tag, blue_tag])
…then the asdasdasd text has two tags that specify different foreground colors. If you want red asdasdasd, the red tag should be above the blue tag, so you need to do
red_tag.raise_(blue_tag)
orblue_tag.lower(red_tag)
.
Text Widget Methods and Attributes¶
-
class
teek.
Text
(parent, **kwargs)[source]¶ This is the text widget.
Manual page: text(3tk)
-
start
¶ -
end
¶ TextIndex objects that represents the start and end of the text.
Tip
Use
textwidget.end.line
to count the number of lines of text in the text widget.Note that
end
changes when the text widget’s content changes:>>> window = teek.Window() >>> text = teek.Text(window) >>> text.end TextIndex(line=1, column=0) >>> old_end = text.end >>> text.insert(text.end, 'hello') >>> text.end TextIndex(line=1, column=5) >>> old_end TextIndex(line=1, column=0) >>> text.get(old_end, text.end) 'hello'
Tk has a concept of an invisible newline character at the end of the widget. In pure Tcl or in tkinter, getting the text from beginning to
end
returns the text in the widget plus a\n
, which is why you almost always need to doend - 1 char
instead of justend
. Teek doesn’t do that because 99% of the time it’s not useful and 1% of the time it’s confusing to people reading the code, sotext.get(text.start, text.end)
doesn’t return anything that is not visible in the text widget.
-
marks
¶ A dictionary-like object with mark names as keys and index objects as values. See Marks.
-
xview
(*args)¶ -
yview
(*args)¶ These call
pathName xview
andpathName yview
as documented in text(3tk). Pass string arguments to these methods to invoke the subcommands. For example,text_widget.yview('moveto', 1)
scrolls to end vertically.If no arguments are given, these methods return a two-tuple of floats (see the manual page); otherwise, None is returned.
-
get
(index1=None, index2=None)[source]¶ Return text in the widget.
If the indexes are not given, they default to the beginning and end of the text widget, respectively.
Return all tags as tag objects.
See
pathName tag names
in text(3tk) for more details.
-