7、文本Text

Text 组件用于显示文本文档,包含纯文本或格式化文本(使用不同字体,嵌入图片,显示链接,甚至是带 CSS 格式的 HTML 等)。因此,它常常也被用于作为简单的文本编辑器和网页浏览器使用。

import tkinter as tk

root = tk.Tk()

# 创建了一个宽60字符、高10行的Text控件
text = tk.Text(root,width=60,height=30)
text.pack()

# 创建一个Button,每次点击给Text尾部添加一句话
btn = tk.Button(root,text='click to add text',command=lambda :text.insert('end','hello\n'))
btn.pack()

root.mainloop()

image-20250414170346685

属性

属性 含义
autoseparators 1. 指定实现“撤销”操作的时候是否自动插入一个“分隔符”(用于分隔操作记录); 2. 默认值是 True
background 1. 设置 Text 组件的背景颜色 ;2. 注意:通过使用 Tags 可以使 Text 组件中的文本支持多种背景颜色显示(请参考上方【Tags 用法】)
bg 跟 background 一样
borderwidth 1. 设置 Entry 的边框宽度 ;2. 默认值是 1 像素
bd 跟 borderwidth 一样
cursor 1. 指定当鼠标在 Text 组件上飘过的时候的鼠标样式 ;2. 默认值由系统指定
exportselection 1. 指定选中的文本是否可以被复制到剪贴板 ;2. 默认值是 True ;3. 可以修改为 False 表示不允许复制文本
font 1. 设置 Text 组件中文本的默认字体 ;2. 注意:通过使用 Tags 可以使 Text 组件中的文本支持多种字体显示
foreground 1. 设置 Text 组件中文本的颜色 ;2. 注意:通过使用 Tags 可以使 Text 组件中的文本支持多种颜色显示
fg 跟 foreground 一样
height 1. 设置 Text 组件的高度; 2. 注意:单位是行数,不是像素噢
highlightbackground 1. 指定当 Text 组件没有获得焦点的时候高亮边框的颜色 ;2. 默认值由系统指定
highlightcolor 1. 指定当 Text 组件获得焦点的时候高亮边框的颜色 ;2. 默认值由系统指定
highlightthickness 1. 指定高亮边框的宽度 ;2. 默认值是 0
insertbackground 1. 设置插入光标的颜色 ;2. 默认是 BLACK(或 "black")
insertborderwidth 1. 设置插入光标的边框宽度 ;2. 默认值是 0 3;3.你得设置 insertwidth 选项为比较大的数值才能看出来
insertofftime 1. 该选项控制光标的闪烁频率(灭); 2. 单位是毫秒
insertontime 1. 该选项控制光标的闪烁频率(亮); 2. 单位是毫秒
insertwidth 1. 指定光标的宽度 ;2. 默认值是 2 像素
maxundo 1. 设置允许“撤销”操作的最大次数 ;2. 默认值是 0 ;3. 设置为 -1 表示不限制
padx 1. 指定水平方向上的额外间距(内容和边框间) ;2. 默认值是 1
pady 1. 指定垂直方向上的额外间距(内容和边框间) ;2. 默认值是 1
relief 1. 指定边框样式 ;2. 默认值是 "sunken" ;3. 其他可以选择的值是 "flat","raised","groove" 和 "ridge"
selectbackground 1. 指定被选中文本的背景颜色 ;2. 默认值由系统指定
selectborderwidth 1. 指定被选中文本的边框宽度 ;2. 默认值是 0
selectforeground 1. 指定被选中文本的字体颜色 ;2. 默认值由系统指定
setgrid 1. 指定一个布尔类型的值,确定是否启用网格控制 ;2. 默认值是 False
spacing1 1. 指定 Text 组件的文本块中每一行与上方的空白间隔; 2. 注意:自动换行不算 ;3. 默认值是 0
spacing2 1. 指定 Text 组件的文本块中自动换行的各行间的空白间隔 ;2. 注意:换行符('\n')不算 ;3. 默认值是 0
spacing3 1. 指定 Text 组件的文本中每一行与下方的空白间隔 ;2. 注意:自动换行不算; 3. 默认值是 0
state 1. 默认情况下 Text 组件响应键盘和鼠标事件("normal") ;2. 如果将该选项的值设置为 "disabled",那么上述响应就不会发生,并且你无法修改里边的内容
tabs 1. 定制 Tag 所描述的文本块中 Tab 按键的功能; 2. 默认 Tab 被定义为 8 个字符的宽度 ;3.你还可以定义多个制表位:tabs=('3c', '5c', '12c') 表示前 3 个 Tab 宽度分别为 3厘米,5厘米,12厘米,接着的 Tab 按照最后两个的差值计算,即:19厘米,26厘米,33厘米;4.你应该注意到了,它上边 'c' 的含义是“厘米”而不是“字符”,还可以选择的单位有 'i'(英寸),'m'(毫米)和 'p'(DPI,大约是 '1i' 等于 '72p');5.如果是一个整型值,则单位是像素
takefocus 1. 指定使用 Tab 键可以将焦点移动到 Text 组件中 ;2. 默认是开启的,可以将该选项设置为 False 避免焦点在此 Text 组件中
undo 1. 该选项设置为 True 开启“撤销”功能 ;2. 该选项设置为 False 关闭“撤销”功能 ;3. 默认值是 False
width 1. 设置 Text 组件的宽度 ;2. 注意:单位是字符数,因此 Text 组件的实际宽度还取决于字体的大小
wrap 1. 设置当一行文本的长度超过 width 选项设置的宽度时,是否自动换行 ;2. 该选项的值可以是:"none"(不自动换行),"char"(按字符自动换行)和 "word"(按单词自动换行)
xscrollcommand 1. 与 scrollbar(滚动条)组件相关联(水平方向)
yscrollcommand 1. 与 scrollbar(滚动条)组件相关联(垂直方向)

加载控件或图片

在 Text 组件中插入对象,可以使用 window_create()image_create() 方法来插入一个控件或者一个图片

import tkinter as tk
from PIL import Image,ImageTk

root = tk.Tk()

# 创建Text
text = tk.Text(root,width=30,height=10)
text.pack()

# 在 Text 中插入一个按钮控件
test_btn = tk.Button(text,text='test_btn')
text.window_create('insert',window=test_btn)

# 在 Text 中插入一张图片
# 这个方法传入 file 参数的话只支持 gif 图片
img1 = tk.PhotoImage(file='./123.gif')
text.image_create('insert',image=img1)

# 如果需要插入其他格式图片,则需要使用PIL库中的ImageTk方法进行转换
img2 = ImageTk.PhotoImage(Image.open('./123.jpg'))
text.image_create('insert',image=img2)

root.mainloop()

只读

将 state 选项从默认的 "normal" 修改为 "disabled",使得 Text 组件中的内容为“只读”形式。不过需要注意的是,当你需要进行任何修改的时候,记得将 state 选项改回 "normal",否则 insert()delete() 方法都会失效。

text['state'] = 'disabled'

搜索

使用search()方法,可以可以搜索 Text 组件中的内容

search()方法的 backwards 属性表示是否向后搜索

pos + '+1c'表示pos后一个字符,同理pos - '-1c'表示pos前一个字符

import tkinter as tk
from PIL import Image,ImageTk

root = tk.Tk()

# 创建Text
text = tk.Text(root,width=30,height=10)
text.pack()
text.insert('insert','hello\nworld')

lable = tk.Label(root)
lable.pack()

search_val = ''
search_start = 1.0
pos = ''

# 搜索函数
def search():
    global search_val,search_start,pos
    # 获取搜索框内的内容
    val = search_entry.get()
    # 判断搜索框内容如果变了的话就重新从头搜索
    if search_val != val:
        search_start = 1.0
        pos = None
        search_val = val
    # 在Text控件中搜索,返回索引
    pos = text.search(val,index=search_start,stopindex='end')
    # 如果没有搜索到,则显示 not found,并退出函数
    if not pos:
        lable['text'] = 'not found'
        search_val = ''
        return
    # 如果搜索到了,就显示索引
    lable['text'] = pos
    # 并且将下回搜索的开始位置设置到本次搜索结果后一个字符
    search_start = pos + '+1c'

# 搜索框与搜索按钮
search_entry = tk.Entry(root)
search_entry.pack()
search_btn = tk.Button(root,text='Search',command=search)
search_btn.pack()


root.mainloop()

image-20250414180715090

恢复和撤销操作

通过设置 undo 选项为 True 可以开启 Text 组件的“撤销”功能。然后用 edit_undo() 方法实现“撤销”操作,用 edit_redo() 方法实现“恢复”操作。

这是因为 Text 组件内部有一个栈专门用于记录内容的每次变动,所以每次“撤销”操作就是一次弹栈操作,“恢复”就是再次压栈。

import tkinter as tk

root = tk.Tk()

# 此处开启Text控件的撤销操作
text = tk.Text(root,undo=True)
text.pack()

undo_btn = tk.Button(root,text='撤销',command=lambda :text.edit_undo())
redo_btn = tk.Button(root,text='恢复',command=lambda :text.edit_redo())
undo_btn.pack()
redo_btn.pack()

root.mainloop()

默认情况下,每一次完整的操作将会放入栈中。但怎么样算是一次完整的操作呢?Tkinter 觉得每次焦点切换、用户按下 Enter 键、删除、插入操作的转换等之前的操作算是一次完整的操作。也就是说你连续输入I love Python的话,一次的“撤销”操作就会将所有的内容删除。

如果不想这样,那么做法就是先将 autoseparators 选项设置为 False(因为这个选项是让 Tkinter 在认为一次完整的操作结束后自动插入“分隔符”),然后绑定键盘事件,每次有输入就用 edit_separator() 方法人为地插入一个“分隔符”:

import tkinter as tk
 
root = tk.Tk()
 
text = tk.Text(root, width=40, height=5, autoseparators=False, undo=True, maxundo=10)
text.pack()
 
def callback(event):
    text.edit_separator()
 
text.bind('<Key>', callback)
 
def move():
    text.edit_undo()
 
tk.Button(root, text = "撤销", command = move).pack()
 
root.mainloop()

常用方法

方法 功能
delete(起始位置,[,终止位置]) 删除指定区域文本,起始位置 index = 1.0(特别注意),也可以传入一个 window 对象或者一个 image 对象
get(起始位置,[,终止位置]) 获取指定区域文本
insert(位置,[,字符串]...) 将文本插入到指定位置
see(位置) 在指定位置是否可见文本,返回布尔值
index(标记) 返回标记所在的行和列
mark_names() 返回所有标记名称
mark_set(标记,位置) 在指定位置设置标记
mark_unset(标记) 去除标记

indexes 索引

Text 控件的操作,基本上都与索引有关,Tkinter 提供一系列不同的索引类型:

index()

index() 方法用于将所有支持的“索引”格式,即line.column(行、列)的格式

import tkinter as tk
from PIL import Image,ImageTk

root = tk.Tk()

# 创建Text
text = tk.Text(root,width=30,height=10)
text.pack()
text.insert('insert','hello\n')

lable = tk.Label(root,text=f'insert: {text.index('insert')},end: {text.index('end')}')
lable.pack()

root.mainloop()

image-20250414174019466

此时的2.0表示索引insert也就是光标在第2行第0列;3.0表示索引end也就是文档结束在第3行第0列

Marks

Marks(标记)通常是嵌入到 Text 组件文本中的不可见对象。事实上 Marks 是指定字符间的位置,并跟随相应的字符一起移动。

insertcurrent 是 Tkinter 预定义的特殊 Marks,它们不能够被删除。

你还可以自定义任意数量的 Marks,Marks 的名字是由普通字符串组成,可以是除了空白字符外的任何字符(为了避免歧义,你应该起一个有意义的名字)。使用 mark_set() 方法创建和移动 Marks。

如果你在一个 Mark 标记的位置之前插入或删除文本,那么 Mark 跟着一并移动。删除 Marks 你需要使用 mark_unset() 方法,删除 Mark 周围的文本并不会删除 Mark 本身

import tkinter as tk

root = tk.Tk()

text = tk.Text(root)
text.pack()

text.insert("1.0","HelloWorld")

text.mark_set("my_mark","1.2") # 在 e 和 l 中间插入一个 mark,命名为 my_mark

text.insert("my_mark","123") # my_mark的位置插入123

root.mainloop()

image-20250415100622459

默认插入内容到 Mark,是插入到它的左侧(就是说插入一个字符的话,Mark 向后移动了一个字符的位置)。那能不能插入到 Mark 的右侧呢?其实是可以的,通过 mark_gravity() 方法就可以实现。

text.mark_gravity("my_mark", "left")  #默认是 "right"

Tags

gs(标签)通常用于改变 Text 组件中内容的样式和功能。你可以修改文本的字体、尺寸和颜色。另外,Tags 还允许你将文本、嵌入的组件和图片与键盘和鼠标等事件相关联。

sel

除了 user-defined tags(用户自定义的 Tags),还有一个预定义的特殊 Tag:sel用于表示对应的选中内容(如果有的话)。

例如实现获取选中内容:

import tkinter as tk

root = tk.Tk()

text = tk.Text(root)
text.pack()

lable = tk.Label(root)
lable.pack()

def get_select(e):
    w = text.get(text.index('sel.first'),text.index('sel.last'))
    lable['text'] = w

text.bind('<ButtonRelease-1>',get_select)

root.mainloop()

image-20250415102957576

自定义Tags

你可以自定义任意数量的 Tags,Tags 的名字是由普通字符串组成,可以是除了空白字符外的任何字符。另外,任何文本内容都支持多个 Tags 描述,任何 Tag 也可以用于描述多个不同的文本内容。为指定文本添加 Tags 可以使用 tag_add() 方法:

import tkinter as tk

root = tk.Tk()

text = tk.Text(root)
text.pack()

text.insert('1.0','HelloWorld')
text.tag_add('my_tag_1','1.1') # 设置my_tag_1到第一行第二个字符
text.tag_config('my_tag_1',background='yellow',foreground='red') # 设置my_tag_1字符背景颜色和字体颜色

text.tag_add('my_tag_2','1.3','1.6') # 设置my_tag_2到第一行的第(四-七)的字符
text.tag_config('my_tag_2',background='black',foreground='white') # 设置my_tag_1字符背景颜色和字体颜色


root.mainloop()

image-20250415103803993

使用 tag_config() 方法可以设置 Tags 的样式,具体参数如下:

选项 含义
background 1. 指定该 Tag 所描述的内容的背景颜色; 2. 注意:bg 并不是该选项的缩写,在这里 bg 被解释为 bgstipple 选项的缩写
bgstipple 1. 指定一个位图作为背景,并使用 background 选项指定的颜色填充; 2. 只有设置了 background 选项该选项才会生效; 3.默认的标准位图有:'error', 'gray75', 'gray50', 'gray25', 'gray12', 'hourglass', 'info', 'questhead', 'question' 和 'warning'
borderwidth 1. 指定文本框的宽度 ;2. 默认值是 0 ;3. 只有设置了 relief 选项该选项才会生效 ;4. 注意:该选项不能使用 bd 缩写
fgstipple 1. 指定一个位图作为前景色 ;2. 默认的标准位图有:'error', 'gray75', 'gray50', 'gray25', 'gray12', 'hourglass', 'info', 'questhead', 'question' 和 'warning'
font 指定该 Tag 所描述的内容使用的字体
foreground 1. 指定该 Tag 所描述的内容的前景色 ;2. 注意:fg 并不是该选项的缩写,在这里 fg 被解释为 fgstipple 选项的缩写
justify 1. 控制文本的对齐方式 ;2. 默认是 "left"(左对齐),还可以选择 "right"(右对齐)和 "center"(居中) ;3. 注意:需要将 Tag 指向该行的第一个字符,该选项才能生效
lmargin1 1. 设置 Tag 指向的文本块第一行的缩进; 2. 默认值是 0 ;3. 注意:需要将 Tag 指向该文本块的第一个字符或整个文本块,该选项才能生效
lmargin2 1. 设置 Tag 指向的文本块除了第一行其他行的缩进 ;2. 默认值是 0 ;3. 注意:需要将 Tag 指向整个文本块,该选项才能生效
offset 1. 设置 Tag 指向的文本相对于基线的偏移距离;2. 可以控制文本相对于基线是升高(正数值)或者降低(负数值); 3. 默认值是 0
overstrike 1. 在 Tag 指定的文本范围画一条删除线 ;2. 默认值是 False
relief 1. 指定 Tag 对应范围的文本的边框样式 ;2. 可以使用的值有:"sunken", "raised", "groove", "rifge" 或 "flat" ;3. 默认值是 "flat"(没有边框)
rmargin 1. 设置 Tag 指向的文本块右侧的缩进; 2. 默认值是 0
spacing1 1. 设置 Tag 所描述的文本块中每一行与上方的空白间隔; 2. 注意:自动换行不算 ;3. 默认值是 0
spacing2 1. 设置 Tag 所描述的文本块中自动换行的各行间的空白间隔 ;2. 注意:换行符('\n')不算 ;3. 默认值是 0
spacing3 1. 设置 Tag 所描述的文本块中每一行与下方的空白间隔 ; 2. 注意:自动换行不算; 3. 默认值是 0
tabs 1. 定制 Tag 所描述的文本块中 Tab 按键的功能 ;2. 默认 Tab 被定义为 8 个字符的宽度 ;3.你还可以定义多个制表位:tabs=('3c', '5c', '12c') 表示前 3 个 Tab 宽度分别为 3厘米,5厘米,12厘米,接着的 Tab 按照最后两个的差值计算,即:19厘米,26厘米,33厘米 ;4.你应该注意到了,它上边 'c' 的含义是“厘米”而不是“字符”,还可以选择的单位有 'i'(英寸),'m'(毫米)和 'p'(DPI,大约是 '1i' 等于 '72p');5.如果是一个整型值,则单位是像素
underline 1. 该选项设置为 True 的话,则 Tag 所描述的范围内文本将被画上下划线 ;2. 默认值是 False
wrap 1. 设置当一行文本的长度超过 width 选项设置的宽度时,是否自动换行; 2. 该选项的值可以是:"none"(不自动换行),"char"(默认)(按字符自动换行)和 "word"(按单词自动换行)

Tags 还支持事件绑定,使用的是 tag_bind() 的方法。

下边例子中我们将文本("Python.com")与鼠标事件进行绑定,当鼠标进入该文本段的时候,鼠标样式切换为 "arrow" 形态,离开文本段的时候切换回 "xterm" 形态。当触发鼠标“左键点击操作”事件的时候,使用默认浏览器打开Python的首页(https://www.python.org/):

import tkinter as tk
import webbrowser
 
root = tk.Tk()
 
text = tk.Text(root, width=40, height=5)
text.pack()
 
text.insert("insert", "I love Python.com!")
 
text.tag_add("link", "1.7", "1.17")
text.tag_config("link", foreground = "blue", underline = True)
 
def show_arrow_cursor(event):
    text.config(cursor = "arrow")
 
def show_xterm_cursor(event):
    text.config(cursor = "xterm")
 
def click(event):
    webbrowser.open("https://www.python.org/")
 
text.tag_bind("link", "<Enter>", show_arrow_cursor)
text.tag_bind("link", "<Leave>", show_xterm_cursor)
text.tag_bind("link", "<Button-1>", click)
 
root.mainloop()