Tkinter04_Text

Tkinter04_Text

标签(空格分隔): Tkinter


文章目录

  • Tkinter04_Text
      • Text 组件
        • Indexes 用法
        • Marks 用法
        • Tags 用法

Text 组件

截至目前,我们已经学了不少组件:绘制单行文本使用 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 用法

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

(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 用于修改任何格式的索引,用字符串的形式实现修改索引的表达式。

  • 只要结果不产生歧义,关键字可以被缩写,空格也可以省略。例如,"+ 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(或 “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 用法

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", "", show_arrow_cursor)
text.tag_bind("link", "", show_xterm_cursor)
text.tag_bind("link", "", 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('', 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模块作图

你可能感兴趣的:(Tkinter)