@yanglt7
2018-12-03T12:31:26.000000Z
字数 8153
阅读 655
Tkinter
截至目前,我们已经学了不少组件:绘制单行文本使用 Label 组件,多行选项使用 Listbox 组件,输入框使用 Entry 组件,按钮使用 Button 组件,还有 Radiobutton 和 Checkbutton 用于提供单选或多选的情况。多个组件可以用 Frame 组件先搭建一个框架。Scrollbar 组件用于实现滚动条,而 Scale 组件则是让用户在一定范围内选择一个确定的值。
Text(文本)组件用于显示和处理多行文本。在 Tkinter 的所有组件中,Text 组件显得异常强大和灵活,它适用于处理多种任务。虽然该组件的主要目的是显示多行文本,但它常常也被用于作为简单的文本编辑器和网页浏览器使用。
当创建一个 Text 组件的时候,它里面是没有内容的。为了给其插入内容,可以使用 insert() 方法以及 INSERT 或 END 索引号:
例 18
from tkinter import *
root = Tk()
text = Text(root, width=30, height=2) # 文本框的高度和宽度
text.pack()
# INSERT 索引表示插入光标当前的位置
text.insert(INSERT, "I love\n")
text.insert(END, "FishC.com!")
mainloop()
Text 组件不仅支持插入和编辑文本,它还支持插入 image 对象和 window 组件:
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love\n")
text.insert(END, "FishC.com!")
def show():
print("哟,我被点了一下~")
b1 = Button(text, text="点我点我", command=show)
text.window_create(INSERT, window=b1)
mainloop()
实现单击一下按钮显示一张图片的功能:
from tkinter import *
root = Tk()
text = Text(root, width=50, height=30)
text.pack()
photo = PhotoImage(file="bg.gif")
def show():
text.image_create(END, image=photo)
b1 = Button(text, text="点我点我", command=show)
text.window_create(INSERT, window=b1)
mainloop()
Indexes(索引)是用来指向 Text 组件中文本的位置,跟 Python 的序列索引一样,Text 组件索引也是对应实际字符之间的位置。
Tkinter 提供一系列不同的索引类型:
(1)"line.column"
用行号和列号组成的字符串方式,它们将索引位置的行号和列号以字符串的形式表示出来(中间以 "." 分隔,例如 "1.0")。需要注意的是,行号以 1 开始,列号则以 0 开始。还可以使用一下语法构建索引:
"%d.%d" % (line, column)
指定超出现有文本的最后一行的行号,或超出一行中列数的列号都不会引发错误。对于这样的指定,Tkinter 解释为已有内容的末尾的下一个位置。
需要注意的是,使用“行/列”的索引方式看起来像是浮点值。其实在需要指定索引的时候使用浮点值代替也是可以的:
例 19
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love FishC")
print(text.get("1.2", 1.6))
mainloop()
(2)"line.end"
行号加上字符串,".end" 的格式表示为该行最后一个字符的位置:
例 20
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love FishC")
print(text.get("1.2", 1.end))
mainloop()
(3)INSERT(或 "insert")
对应插入光标的位置。
(4)CURRENT(或 "current")
对于与鼠标坐标最接近的位置。不过,如果你紧按鼠标的任何一个按钮,会直到你松开它才响应。
(5)END(或 "end")
对于 Text 组件的文本缓冲区最后一个字符的下一个位置。
(6)user-defined marks
user-defined marks 是对 Text 组件中位置的命名。INSERT 和 CURRENT 是两个预先命名好的 marks,除此之外可以自定义 marks。
(7)user-defined tags
user-defined tags 代表可以分配给 Text 组件的特殊事件绑定和风格。
可以使用 "tag.first" (使用 tag 的文本的第一个字符之前)和 "tag.last"(使用 tag 的文本的最后一个字符之后)语法表示标签的范围:
"%s.first" % tagname
"%s.last" % tagname
(8)selection(SEL_FIRST, SEL_LAST)
selection 是一个名为 SEL(或 "sel")的特殊 tag,表示当前被选中的范围,可以使用 SEL_FIRST 到 SEL_LAST 来表示这个范围。如果没有选中的内容,那么 Tkinter 会抛出一个 TclError 异常。
(9)window coordinate("@x,y")
可以使用窗口坐标作为索引。例如在一个事件绑定中,可以使用一下代码找到最接近鼠标位置的字符:
"@%d, %d" % (event.x, event.y)
(10)embedded object name(window, images)
embedded object name 用于指向在 Text 组件中嵌入的 window 和 image 对象。要引用一个 window,只要简单地将一个 Tkinter 组件实例作为索引即可。引入一个嵌入的 image,只需使用相应的 PhotoImage 和 BitmapImage 对象。
(11)expressions
expressions 用于修改任何格式的索引,用字符串的形式实现修改索引的表达式。
在实现中,为了确保表达式为普通字符串,可以使用 str 或格式化操作来创建一个表达式字符串。
删除插入光标前面的一个字符:
def backspace(event):
event.widget.delete("%s-1c" % INSERT, INSERT)
Marks(标记)通常是嵌入到 Text 组件文本中的不可见对象。事实上,Marks 是指定字符间的位置,并跟随相应的字符一起移动。Marks 有 INSERT、CURRENT 和 user-defined marks(用户自定义的 Marks)。其中,INSERT 和 CURRENT 是 Tkinter 预定义的特殊 Marks,它们不能被删除。
INSERT(或 "insert")用于指定当前光标的位置,Tkinter 会在该位置绘制一个闪烁的光标(因此并不是所有的 Marks 都不可见)。
CURRENT(或 "current")用于指定与鼠标坐标最接近的位置。不过,如果你紧按鼠标任何一个按钮,它会直到你松开它才响应。
还可以自定义任意数量的 Marks,Marks 的名字是由普通字符串组成,可以是除了空白字符外的任何字符(为了避免歧义,应语义化命名)。使用 mark_set() 方法创建和移动 Marks。
如果在一个 Marks 标记的位置之前插入或删除文本,那么 Mark 跟着一并移动。删除 Marks 需要使用 mark_unset() 方法,删除 Mark 周围的文本并不会删除 Mark 本身。
例 21
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love FishC")
text.mark_set("here", "1.2")
text.insert("here", "插")
mainloop()
如果 Mark 前边的内容发生改变,那么 Mark 的位置也会跟着移动(即 Mark 会记住后面的位置)
text.insert(INSERT, "I love FishC")
text.mark_set("here", "1.2")
text.insert("here", "插")
text.insert("here", "入")
如果 Mark 周围的文本被删除了,Mark 仍然还在。
text.insert(INSERT, "I love FishC")
text.mark_set("here", "1.2")
text.insert("here", "插")
text.delete("1.0", END)
text.insert("here", "入")
只有 mark_unset() 方法可以解除 Mark 的封印:
text.insert(INSERT, "I love FishC")
text.mark_set("here", "1.2")
text.insert("here", "插")
text.mark_unset("here")
text.delete("1.0", END)
text.insert("here", "入")
默认插入内容到 Mark,是插入到它的左侧(就是说插入一个字符的话,Mark 向后移动了一个字符的位置)。可以通过 mark_gravity() 方法实现插入到 Mark 的右侧。
text.insert(INSERT, "I love FishC")
text.mark_set("here", "1.2")
text.mark_gravity("here", LEFT)
text.insert("here", "插")
text.insert("here", "入")
Tags(标签)通常用于改变 Text 组件中内容的样式和功能。可以用来修改文本的字体、尺寸和颜色。另外,Tags 还允许将文本、嵌入的组件和图片与键盘和鼠标等事件相关联。除了 user-defined tags(用户自定义的 Tags),还有一个预定义的特殊 Tag:SEL。
SEL(或 "sel")用于表示对应的选中内容(如果有的话)。
可以自定义任意数量的 Tags,Tags 的名字是由普通字符串组成,可以是除了空白字符外的任何字符。另外,任何文本内容都支持多个 Tags 描述,任何 Tags 也可以用于描述多个不同的文本内容。
例 22
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love FishC.com!")
text.tag_add("tag1", "1.7", "1.12", "1.14")
text.tag_config("tag1", background="yellow", foreground="green") # 这里不是 bg, fg
mainloop()
如上,使用 tag_config() 方法可以设置 Tags 样式。
如果对同一个范围内的文本加上多个 Tags,并且设置相同的选项,那么新创建的 Tag 样式会覆盖比较旧的 Tag:
text.tag_config("tag1", background="yellow", foreground="green") # 旧的 Tag
text.tag_config("tag2", foreground="red") # 新的 Tag
text.insert(INSERT, "I love FishC.com!", ("tag2", "tag1")) # 与调用顺序无关
可以使用 tag_raise() 和 tag_lower() 方法来提高和降低某个 Tag 的优先级:
text.tag_config("tag1", background="yellow", foreground="green")
text.tag_config("tag2", foreground="red")
text.tag_lower("tag2")
text.insert(INSERT, "I love FishC.com!", ("tag2", "tag1"))
Tag 还支持事件绑定,绑定事件使用的是 tag_bind() 的方法。
例 23
将文本("I love FishC.com!")与鼠标时间进行绑定,当鼠标进入该文本段的时候,鼠标样式切换为 "arrow" 形态,离开文本段的时候切换回 "xterm" 形态。当触发鼠标“左键单击操作”事件的时候,使用默认浏览器打开鱼C工作室的首页。
from tkinter import *
import webbrowser
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love FishC.com!")
text.tag_add("link", "1.7", "1.16")
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("http://www.fishc.com")
text.tag_bind("link", "<Enter>", show_arrow_cursor)
text.tag_bind("link", "<Leave>", show_xterm_cursor)
text.tag_bind("link", "<Button-1>", click) # 鼠标左键
mainloop()
实用技巧 1:判断内容是否发生变化,比如做一个记事本程序,当用户关闭的时候,程序应该检查内容是否有改动,如果有变化,应该提醒用户报存。
例 24
通过校验 Text 组件中文本的 MD5 摘要来判断内容是否发生改变。
from tkinter import *
import hashlib
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love FishC.com!")
contents = text.get("1.0", END)
def getSig(contents):
m = hashlib.md5(contents.encode())
return m.digest()
sig = getSig(contents)
def check():
contents = text.get("1.0", END)
if sig != getSig(contents):
print("警报:内容发生变动!")
else:
print("风平浪静~")
Button(root, text="检查", command=check).pack()
mainloop()
实用技巧 2:查找操作,使用 search() 方法可以搜索 Text 组件中的内容。可以提供一个确切的目标进行搜索(默认),也可以使用 Tcl 格式的正则表达式进行搜索(需设置 regexp 选项为 True)。
例 25
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5)
text.pack()
text.insert(INSERT, "I love FishC.com!")
# 将任何格式的索引号统一为元组(行,列)的格式输出
def getIndex(text, index):
return tuple(map(int, str.split(text.index(index), ".")))
start = "1.0"
while True:
pos = text.search("o", start, stopindex=END)
if not pos:
break
print("找到啦,位置是:", getIndex(text, pos))
start = pos + "+1c" # 将 start 指向下一个字符
mainloop()
如果忽略 stopindex 选项,表示直到文本的末尾结束搜索。设置 backwards 选项为 True,则是修改搜索的方向(变为向后搜索,那么 start 变量应该设置为 END,stopindex 选项设置为 1.0,最后,"+1c" 改为 "-1c")
最后,Text 组件还支持“恢复”和“撤销”操作。通过设置 undo 选项为 True 可以开启 Text组件的“撤销”功能,然后用 edit_undo() 方法实现“撤销”操作,用 edit_redo() 方法实现“恢复”操作。
这是因为 Text 组件内部有一个栈专门用于记录内容的每次变动,所以每次“撤销”操作就是一次弹栈操作,“恢复”就是再次压栈。
例 26
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5, undo=True)
text.pack()
text.insert(INSERT, "I love FishC.com!")
def show():
text.edit_undo()
Button(root, text="撤销", command=show).pack()
mainloop()
默认情况下,每一次完整的操作都会放入栈中。Tkinter 认为每次焦点切换、用户按下回车键、删除/插入操作的转换等之前的操作算是一次完整的操作。
自定义插入一个字符就算一次完整的操作,然后每次单击“撤销”就会去掉一个字符。做法是先将 autoseparators 选项设置为 False(因为这个选项是让 Tkinter 在认为一次完整的操作结束后自动插入“分隔符”),然后绑定键盘事件,每次有输入就用 edit_separator() 方法人为的插入一个“分隔符”。
from tkinter import *
root = Tk()
text = Text(root, width=30, height=5, undo=True, autoseparators=False)
text.pack()
text.insert(INSERT, "I love FishC.com!")
def callback(event):
text.edit_separator()
text.bind('<Key>', callback)
def show():
text.edit_undo()
Button(root, text="撤销", command=show).pack()
mainloop()
摘自《零基础入门学习Python》
参考链接:
An Introduction To Tkinter
Tkinter汇总
Python GUI编程(Tkinter)
Tkinter颜色方案举例
python tkinter可以使用的颜色
tkinter学习-菜单与画布
程序设计思想与方法--第五章
用Python中的tkinter模块作图