前面我们已经学习了Tkinter 的好几个组件。绘制单行文本我们当然知道要使用 Label 组件,多行选项我们使用 Listbox 组件,输入框用 Entry 组件,Button 就是按钮,Radiobutton 和 Checkbutton 分别对应单选和多选的情况,然后呢,有多个组件,我们还可以使用 Frame 和 LabelFrame 把它们构成一个框架,之后我们还学习了两个会滚的组件:Scrollbar 和 Scale,Scrollbar 就是滚动条,Scale 就是提供一个范围,让用户在这个范围内选择一个确切的值。
今天我们继续学习一个新的组件:Text 组件。这个组件主要是用于显示和处理多行文本,那么在 tkinter 的所有组件中,Text 组件显得异常强大和灵活,适用于多种任务。虽然说这个组件的主要任务是用于显示多行文本,但是它常常被用于当做简单的文本处理器、文本编辑器或者网页浏览器来使用,例如我们的IDLE的输入框,就是Text组价构成的。
我们举个例子来演示一下:
import tkinter as tk
root = tk.Tk()
text1 = tk.Text(root,width=30,height=2)
text1.pack()
text1.insert("insert",'I love \n')
text1.insert('end','XMY')
root.mainloop()
运行一下:
我们可以对这个文本内容进行任意的操作。
Text 组价不仅支持插入和编辑文本,还支持插入 image 对象,或者前面介绍的组件,例如说你可以插入一个按钮
import tkinter as tk
root = tk.Tk()
text1 = tk.Text(root,width=30,height=4)
text1.pack()
text1.insert("insert",'I love \n')
text1.insert('end','XMY\n')
def show():
print("让你点你就点呀~")
button1 = tk.Button(text1,text="点我点我~",command=show)
text1.window_create("insert",window=button1)
root.mainloop()
接下来插入图片试一下:
import tkinter as tk
root = tk.Tk()
text1 = tk.Text(root,width=30,height=30)
text1.pack()
text1.insert("insert",'I love \n')
text1.insert('end','XMY\n')
photo = tk.PhotoImage(file=r'D:\Code\Python\Tkinter\1.gif')
def show():
text1.image_create('end',image=photo)
button1 = tk.Button(text1,text="点我点我~",command=show)
text1.window_create("insert",window=button1)
root.mainloop()
看到这里,你是不是觉得Text组件很强大呢?其实更牛的东西还在后头。我们接着来:
Indexes 用法
Indexes(索引)是用来指向 Text 组件中文本的位置 ,跟 Python 的序列索引一样,Text 组件索引也是对应实际字符之间的位置。
Tkinter 提供一系列不同的索引类型:
- "line.column"(行/列)
- "line.end"(某一行的末尾)
- "insert"
- "current"
- "end"
- user-defined marks
- user-defined tags("tag.first","tag.last")
- selection(SEL_FIRST,SEL_LAST)
- window coordinate("@x,y")
- embedded object name(window,images)
- expressions
下边我们逐个给大家讲解:
"line.column"
由于我们的Text组件是支持多行文本的,所以我们现在从一个一维空间变成了二维空间,我们就可以用行和列来定位一个位置。
"%d.%d" % (line, column)
行/列 是最基础的索引方式,它们将索引位置的行号和列号以字符串的形式表示出来(中间以 "." 分隔,例如 "1.0")。需要注意的是,行号以 1 开始,列号则以 0 开始。
指定超出现有文本的最后一行的行号,或超出一行中列数的列号都不会引发错误。对于这样的指定,Tkinter 解释为已有内容的末尾的下一个位置。
需要注意的是,使用 行/列 的索引方式看起来像是浮点值。其实不只像而已,你在需要指定索引的时候使用浮点值代替也是可以的:
import tkinter as tk
root = tk.Tk()
text = tk.Text(root, width = 30, heigh = 3)
text.pack()
text.insert("insert", "I love Python")
print(text.get("1.2", 1.6))
root.mainloop()
使用 index() 方法可以将所有支持的“索引”格式转换为“行/列”格式的索引号。
"line.end"
行号加上字符串 ".end" 的格式表示为该行最后一个字符的位置:
import tkinter as tk
root = tk.Tk()
text = tk.Text(root, width = 30, heigh = 3)
text.pack()
"""
text.insert("insert", "I love Python")
print(text.get("1.2", 1.6))
"""
text.insert("insert", "I love Python")
print(text.get("1.2", "1.end"))
root.mainloop()
"insert"
对应插入光标的位置。
"current"(不常用)
对应与鼠标坐标最接近的位置。不过,如果你紧按鼠标任何一个按钮,它会直到你松开它才响应。
"end"
对应 Text 组件的文本缓冲区最后一个字符的下一个位置。
user-defined marks
user-defined marks 是对 Text 组件中位置的命名。"insert" 和 "current" 是两个预先命名好的 marks,除此之外你可以自定义 marks(请参考下方【Marks 用法】)。
User-defined tags
User-defined tags 代表可以分配给 Text 组件的特殊事件绑定和风格(请参考下节课的【Tags 用法】)。
你可以使用 "tag.first"(使用 tag 的文本的第一个字符之前)和 "tag.last"(使用 tag 的文本的最后一个字符之后)语法表示标签的范围。
"%s.first" % tagname
"%s.last" % tagname
如果查无此 tag,那么 Tkinter 会抛出一个TclError 异常。
selection(SEL_FIRST,SEL_LAST)
selection 是一个名为 SEL(或 "sel")的特殊 tag,表示当前被选中的范围,你可以使用 SEL_FIRST 到 SEL_LAST 来表示这个范围。如果没有选中的内容,那么 Tkinter 会抛出一个TclError 异常。
window coordinate("@x,y")
你还可以使用窗口坐标作为索引。例如在一个事件绑定中,你可以使用以下代码找到最接近鼠标位置的字符:
"@%d,%d" % (event.x, event.y)
embedded object name(window,images)
embedded object name 用于指向在 Text 组件中嵌入的 window 和 image 对象。要引用一个 window,只要简单地将一个 Tkinter 组件实例作为索引即可。引用一个嵌入的 image,只需使用相应的 PhotoImage 和 BitmapImage 对象。
expressions
expressions 用于修改任何格式的索引,用字符串的形式实现修改索引的表达式。
具体表达式实现如下:
表达式 | 含义 |
"+ count chars" | 1. 将索引向前(->)移动 count 个字符 2. 可以越过换行符,但不能超过 END 的位置 |
"- count chars" | 1. 将索引向后(<-)移动 count 个字符 2. 可以越过换行符,但不能超过 "1.0" 的位置 |
"+ count lines" | 1. 将索引向前(->)移动 count 行 2. 索引会尽量保持与移动前在同一列上,但如果移动后的那一行字符太少,将移动到该行的末尾 |
"- count lines" | 1. 将索引向后(<-)移动 count 行 2. 索引会尽量保持与移动前在同一列上,但如果移动后的那一行字符太少,将移动到该行的末尾 |
" linestart" | 1. 将索引移动到当前索引所在行的起始位置 2. 注意,使用该表达式前边必须有一个空格隔开 |
" lineend" | 1. 将索引移动到当前索引所在行的末尾 2. 注意,使用该表达式前边必须有一个空格隔开 |
" wordstart" | 1. 将索引移动到当前索引指向的单词的开头 2. 单词的定义是一系列字母、数字、下划线或任何非空白字符的组合 3. 注意,使用该表达式前边必须有一个空格隔开 |
" wordend" | 1. 将索引移动到当前索引指向的单词的末尾 2. 单词的定义是一系列字母、数字、下划线或任何非空白字符的组合 3. 注意,使用该表达式前边必须有一个空格隔开 |
TIPS:只要结果不产生歧义,关键字可以被缩写,空格也可以省略。例如:"+ 5 chars" 可以简写成 "+5c"
在实现中,为了确保表达式为普通字符串,你可以使用 str 或格式化操作来创建一个表达式字符串。下边例子演示了如何删除插入光标前边的一个字符:
def backspace(event):
event.widget.delete("%s-1c" % "insert", "insert")
Marks 用法
Marks(标记)通常是嵌入到 Text 组件文本中的不可见对象。事实上 Marks 是指定字符间的位置,并跟随相应的字符一起移动。Marks 有 “insert”,“current” 和 user-defined marks(用户自定义的 Marks)。其中,“insert” 和 “current” 是 Tkinter 预定义的特殊 Marks,它们不能够被删除。
“insert” 用于指定当前插入光标的位置,Tkinter 会在该位置绘制一个闪烁的光标(因此并不是所有的 Marks 都不可见)。
** “current”** 用于指定与鼠标坐标最接近的位置。不过,如果你紧按鼠标任何一个按钮,它会直到你松开它才响应。
你还可以自定义任意数量的 Marks,Marks 的名字是由普通字符串组成,可以是除了空白字符外的任何字符(为了避免歧义,你应该起一个有意义的名字)。使用 mark_set() 方法创建和移动 Marks。
如果你在一个 Mark 标记的位置之前插入或删除文本,那么 Mark 跟着一并移动。删除 Marks 你需要 使用 mark_unset() 方法,删除 Mark 周围的文本并不会删除 Mark 本身。
如果有做相关练习的,应该会被 Mark 的很多特性所疑惑,在准备这个内容的时候也很是迷惑,找了不知多少文档…最后总结为下边几个例子讲解:
import tkinter as tk
root = tk.Tk()
text = tk.Text(root, width = 30, heigh = 3)
text.pack()
text.insert("insert", "I love Python")
text.mark_set('here','1.2')
text.insert('here',"I love XMY")
root.mainloop()
例2,如果 Mark 前边的内容发生改变,那么 Mark 的位置也会跟着移动(说白了就是 Mark 会“记住”它后边的那货~):
import tkinter as tk
root = tk.Tk()
text = tk.Text(root, width = 30, heigh = 3)
text.pack()
text.insert("insert", "I love Python")
text.mark_set("here", "1.2")
text.insert("here", "X")
text.insert("here", "M")
root.mainloop()
例3,如果 Mark 周围的文本被删除了,Mark 仍然还在噢(只是它后边的那货被删除了,所以它六神无主,只能初始化为 "1.0"):
import tkinter as tk
root = tk.Tk()
text = tk.Text(root, width = 30, heigh = 3)
text.pack()
text.insert("insert", "I love Python")
text.mark_set("here", "1.2")
text.insert("here", "插")
text.delete("1.0", "end")
text.insert("here", "入")
root.mainloop()
例4,只有 mark_unset() 方法可以解除 Mark 的封印:
import tkinter as tk
root = tk.Tk()
text = tk.Text(root, width = 30, heigh = 3)
text.pack()
text.insert("insert", "I love Python")
text.mark_set("here", "1.2")
text.insert("here", "插")
text.mark_unset("here")
text.delete("1.0", "end")
text.insert("here", "入")
root.mainloop()
由于Text 组件的功能实在太强大,内容较多,剩下的下节课继续讲解。