GUI的最终选择 Tkinter(五):Text用法

Text组件

绘制单行文本使用Label组件,多行选使用Listbox,输入框使用Entry,按钮使用Button组件,还有Radiobutton和Checkbutton组件用于提供单选或多选的情况,多个组件可以使用Frame组件先搭建一个框架,这样组合起来显示好看点,最后还学习了Scrollbar和Scale,Scrollbar组件用于实现滚动条,而Scale则是让用户在一个范围内选择一个确定的值。

Text(文本)组件用于显示和处理多行文本。在Tkinter的所有组件中,Text组件显得异常强大和灵活,它适用于处理多种任务,虽然该组件的蛀牙牡蛎是显示多行文本,但它长城被用于作为简单的文本编辑器和网页浏览器使用。

当创建一个Text组件的时候,它里面是没有内容的,为了给其插入内容,可以利用insert()方法以及INSERT或END索引号:

1 from tkinter import *
2 
3 root = Tk()
4 text = Text(root,width=20,height=15)
5 text.pack()
6 text.insert(INSERT,"Python3 \n") #INSERT索引表示插入光标当前的位置
7 text.insert(END,"python算法")
8 mainloop()

执行结果:

Text组件不仅支持插入和编辑文本,它还支持插入image对象和windows组件。

 1 from tkinter import *
 2 
 3 root = Tk()
 4 text = Text(root,width=20,height=15)
 5 text.pack()
 6 text.insert(INSERT,"I love Python3") #INSERT索引表示插入光标当前的位置
 7 def show():
 8     print("被点了一下。。。")
 9 b1 = Button(text,text="点我",command=show)
10 text.window_create(INSERT,window=b1)
11 mainloop()

 执行结果:

下面代码将实现单击显示一张图片

 1 from tkinter import *
 2 
 3 root = Tk()
 4 text = Text(root,width=20,height=15)
 5 text.pack()
 6 photo = PhotoImage(file='bg.gif')
 7 def show():
 8     text.image_create(END,image=photo)
 9 b1 = Button(text,text="点我",command=show)
10 text.window_create(INSERT,window=b1)
11 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(SELFIRST.SELLAST)
  • window coordinate("@x,y")
  • embedded object name(window,images)
  • expressions

1. "line.column‘’

用行号和列号组成的字符串是常用的索引方法,它们将索引位置的行号和列号以字符串的形式表示出来(中间以“.”分隔,例如“1.0”)。需要注意的是:行号以1开始,列号则以0开始,还可以使用一下语法建索引:

“%d.%d”%(line,column)

指定超出现有文本的最后一行的行号,或超出一行中列数的列号都不会引发错误。对于这样的指定,Tkinter解释为已有内容的末尾的下一个位置。

需要注意的是,使用“行/列”的索引方式看起来像是浮点值,其实在需要指定索引的时候使用浮点值代替也是可以的:

 1 from tkinter import *
 2 
 3 root = Tk()
 4 text = Text(root,width=20,height=15)
 5 text.pack()
 6 def show():
 7     text.insert(INSERT, "i love python")
 8     print(text.get("1.2", 1.6))
 9 b1 = Button(text,text="点我",command=show)
10 text.window_create(INSERT,window=b1)

执行结果:

2. “line.end”

行号加上字符串“.end”的格式表示该行最后一个字符串的位置:

from tkinter import *

root = Tk()
text = Text(root,width=20,height=15)
text.pack()
def show():
    text.insert(INSERT, "i love python")
    print(text.get("1.2", "1.end"))
b1 = Button(text,text="点我",command=show)
text.window_create(INSERT,window=b1)

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”%tagnam 

8. selection(SELFIRST,SELLAST)

selection是一个名为SEL(或“sel”)的特殊tag,表示当前被选中的范围,可以使用SELFIRST和SELLAST来表示这个范围如果没有选中的内容,那么Tkinter会抛出一个TclError异常。

9. windows coordinate("@x.y")

可以使用串口坐标作为索引。例如在一个时间绑定中,你可以使用以下代码找到最接近的鼠标字符:

“@%d,%d”%(event.x,event.y)

 

10. embedded object name(window,images)

embedden object name 用于指向在Text组件中嵌入的window和image对象。要引用一个window,只要简单地讲一个Tkinter组件实例作为索引即可。引用一个嵌入的image,只需要使用相应的PhotoImage和BitmapImage对象。

11. expressions

expressions用于修改任何格式的索引,用字符串的形式实现修改索引的表达式,具体表达式实现如表:

表达式 含义
"+count chars" 将索引向前(->)移动count个字符。可以越过换行符,但不能超过END的位置
"-count chars" 将索引向后(<-)移动count个字符。可以越过换行符,但不能超过"1.0"的位置
"+count lines" 将索引向前(->)移动count行,索引会尽量保持与移动前在同一列上,但如果移动后的那一行字符太少,将移动到该行的末尾。
"-count lines" 将索引向后(,-)移动count行,索引会尽量保持与移动前在同一列上,但如果移动后的那一行字符太少,将移动到该行的末尾。
"linestart" 将索引移动到当前索引所在行的起始位置。注意:使用的该表达式前面必须用一个空格隔开
"lineend" 将索引移动到当前索引所在行的末尾位置。注意:使用的该表达式前面必须用一个空格隔开
"wordstart" 将索引移动到当前索引指向的单词的开头。单词的定义是一系列字母、数字、下划线或任何为空白字符的组合。注意:使用该表达式前面必须用一个空格隔开
"wordend" 将索引移动到当前索引指向的单词的末尾。单词的定义是一系列字母、数字、下划线或任何为空白字符的组合。注意:使用该表达式前面必须用一个空格隔开

提示:只要结果不产生歧义,关键字可以被缩写,空格可以是省略的,例如:“+ 5chars”可以缩写为“+5c”

在实现中,为了确保表达式为普通字符串,你可以使用str或格式化操作来创建一个表达式字符串。来看下面列子,删除插入光标前面的一个字符。

def backspace(event):
    event.widgt.delete("%s-1c"%INSERT,INSERT)

Marks用法

Marks通常是指嵌入到Text组件文本中的不可见对象,简单的说就是指定字符串间的位置,它会跟着相应的字符一起移动。Marks有INSERT,CURRENT和user-defined marks(用户自定义的Marks)。其中,INSERT和CURRENT是Tkinter预定义的特殊Marks,它不能够被删除。

INSERT(或insert)用于指定当前插入的光标的而为之,Tkinter会在该位置绘制一个闪烁的光标(因此并不是所有的Marks都不可见)。

CURRENT(或current)用于指定与与鼠标坐标最接近的位置。不过,如果你紧按鼠标任何一个按钮,它会知道你松开才响应。

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

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

其实Marks事实上就是索引,用于表示位置:

1 from tkinter import *
2 root = Tk()
3 text = Text(root,width=30,height=10)
4 text.pack()
5 text.insert(INSERT,'I love python')
6 text.mark_set("here","1.2")
7 text.insert('here',"")
8 mainloop()

执行结果:

再如,如果Marks前边的内容发生改变,那么Marks的位置也会跟着移动(实际上,就是Mark会记住它后面的内容)

1 from tkinter import *
2 root = Tk()
3 text = Text(root,width=30,height=10)
4 text.pack()
5 text.insert(INSERT,'I love python')
6 text.mark_set("here","1.2")
7 text.insert('here',"")
8 text.insert('here',"")
9 mainloop()

执行结果:

再如,如果Marks周围的文本被删了,Mark仍然在:

 1 from tkinter import *
 2 root = Tk()
 3 text = Text(root,width=30,height=10)
 4 text.pack()
 5 text.insert(INSERT,'I love python')
 6 text.mark_set("here","1.2")
 7 text.insert('here',"")
 8 text.delete('1.0',END)
 9 text.insert('here',"")
10 mainloop()

执行结果:

再如,只有mark_unset()方法可以解除Mark的封印;

 1 from tkinter import *
 2 root = Tk()
 3 text = Text(root,width=30,height=10)
 4 text.pack()
 5 text.insert(INSERT,'I love python')
 6 text.mark_set("here","1.2")
 7 text.insert('here',"")
 8 text.mark_unset("here")
 9 text.delete('1.0',END)
10 text.insert('here',"")
11 mainloop()

错误信息:

---------------------------------------------------------------------------
TclError                                  Traceback (most recent call last)
<ipython-input-7-2c32c5656dea> in <module>()
      8 text.mark_unset("here")
      9 text.delete('1.0',END)
---> 10 text.insert('here',"")
     11 mainloop()

c:\python36\lib\tkinter\__init__.py in insert(self, index, chars, *args)
   3264         """Insert CHARS before the characters at INDEX. An additional
   3265         tag can be given in ARGS. Additional CHARS and tags can follow in ARGS."""
-> 3266         self.tk.call((self._w, 'insert', index, chars) + args)
   3267     def mark_gravity(self, markName, direction=None):
   3268         """Change the gravity of a mark MARKNAME to DIRECTION (LEFT or RIGHT).

TclError: bad text index "here"

 

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

Tags用法

 Tags(标签)通常用于改变Text组件中内容的样式和功能。可以用来修改文本的字体,尺寸和颜色。另外,Tags还允许将文本,嵌入的组件和图片与键盘鼠标等事件相关联。除了user-defined tags(用户自定义的Tags),还有一个预定义的特殊Tags:SEL.

SEL(或sel)用于表示对象的选中内容(如果有的话)

可以自定义任意数量的Tags,Tags的名字是有普通字符串组成,可以是除了空白字符以外的额任意字符。另外,任何文本内容都支持多个Tags描述,任何Tags也可以用于描述多个不同的文本内容。

为指定文本添加Tags可以使用tag_add()方法:

 

1 from tkinter import *
2 root = Tk()
3 text = Text(root,width=30,height=10)
4 text.pack()
5 text.insert(INSERT,'I love python3.6')
6 text.tag_add("tag1","1.7","1.12","1.14")
7 text.tag_config("tag1",background="yellow",foreground="red")
8 mainloop()

 

执行结果:

如上,使用tag_config()方法可以设置Tags的样式。下表列举了tag_config()可以使用的选项。

如上,使用 tag_config() 方法可以设置 Tags 的样式。下面罗列了 tag_config() 方法可以使用的选项:

选项含义
background

①指定该Tag所描述的内容的背景颜色

②注意:bg并不是该选项的缩写,在这里bg被解释成bgstipple选项的缩写

bgstipple

①指定一个位图作为背景,并使用background选项指定的颜色填充

②只有设定了background选项,该选项才会生效

③默认的标准位图有:'error', 'gray75', 'gray50', 'gray25', 'gray12', 'hourglass', 'info', 'questhead', 'question'和'warning'

borderwidth

①指定文本框的宽度

②默认值是0

③只有设定了relief选项该选项才会生效

④注意:该选项不能使用bd缩写

fgstipple

①指定一个位图作为前景色

②默认的标准位图有:'error', 'gray75', 'gray50', 'gray25', 'gray12', 'hourglass', 'info', 'questhead', 'question'和'warning'

font ①指定该Tag所描述的内容使用的字体
foreground

①指定该Tag所描述的内容使用的前景色

②注意:fg并不是该选项的缩写,在这里fg被解释为fgstipple的缩写

justify

①控制文本的对齐方式

②默认是LEFT(左对齐),还可以选择RIGHT(右对齐)和CENTER(居中)

③注意:需要将Tag指向该行的第一个字符,该选项才能生效

Imargin1

①设置Tag指向的文本块第一行的缩进

②默认值是0

③注意:需要将Tag指向该行的第一个字符或整个文本块,该选项才能生效

Imargin2

①设置Tag指向的文本块除了第一行其他行的缩进

②默认值是0

③注意:需要将Tag指向整个文本块,该选项才能生效

offset

①设置Tag指向的文本相对于基线的偏移距离

②可以控制文本相对于基线是升高(正数值)或者降低(负数值)

③默认值是0

overstrike

①在Tag指定的文本范围画一条删除线

②默认值是False

relief

①指定Tag对应范围的文本的边框样式

②可以使用的值有:SUNKEN,RAISED,GROOVE,RIDGE或FLAT

③默认值是FLAT(没有边框)

margin

①设置Tag指向的文本块右侧的缩进

②默认值是0

spacing1

①设置Tag所描述的文本块中每一行与上方的文本间隔

②注意:自动换行不算

③默认值是0

spacing2

①设置Tag所描述的文本块中自动换行的各行间的空白间隔

②注意:换行符("\n")不算

③默认值是0

spacing3

①设置Tag所描述的文本块中每一行与下方的文本间隔

②注意:自动换行不算

③默认值是0

tabs

①定制Tag所描述的文本块中Tab按键的功能

②默认Tab被定义为8个字符的宽度

③你还可以定制多个制表位:tabs=('3c', '5c', '12c')表示前三个Tab的宽度分别为3cm,5cm,12cm,接着的Tab按照最后两个的差值计算,即:19cm,26cm,33cm

④你应该注意到,它上边'c'的含义是“厘米”而不是“字符”,还可以选择的单位有"i"(英寸),"m"(毫米),"p"(DPI,大约是'1i'等于'72p')

⑤如果是一个整型值,则单位是像素

underline

①该选项设置为True的话,则Tag所描述的范围内的文本将被画上下划线

②默认值是False

wrap

①设置当一行文本的长度超过width选项设置的宽度时,是否自动换行。

②该选项的值可以是:NONE(不自动换行),CHAR(按字符自动换行)和WORD(按单词自动换行)

如果对于一个范围内容的文本加上多个Tags,并且相同的选项,那么新创建的Tag样式会覆盖比较旧的Tag:

 

1 from tkinter import *
2 root = Tk()
3 text = Text(root,width=30,height=10)
4 text.pack()
5 text.tag_config("tag1",background="yellow",foreground="red")
6 text.tag_config("tag2",foreground="blue")
7 #tag2中foreground将覆盖tag1中的foreground,tag2中没有background,所以用tag1的
8 text.insert(INSERT,'I love python3.6',("tag1","tag2"))
9 mainloop()

执行结果:

不过,这里可以使用tagraise()和tag_lower()方法来提高和降低某个Tag的优先级:

1 from tkinter import *
2 root = Tk()
3 text = Text(root,width=30,height=10)
4 text.pack()
5 text.tag_config("tag1",background="yellow",foreground="red")
6 text.tag_config("tag2",foreground="blue")
7 text.tag_lower("tag1")
8 text.insert(INSERT,'I love python3.6',("tag1","tag2"))
9 mainloop()

执行结果:

Tag还可以支持时间的绑定,绑定时间使用的是tag_bind()方法。下面举个例子:让文本(“python3.6”)与鼠标事件进行绑定,当鼠标进入该文本段的时候,鼠标样式切换“arrow”状态,离开文本段的时候切换回“xterm”形态。当触发鼠标“左键单击操作”时间的时候,使用默认浏览器打开绑定的链接:

 1 from tkinter import *
 2 root = Tk()
 3 text = Text(root,width=30,height=10)
 4 text.pack()
 5 text.insert(INSERT,'I love python3.6')
 6 text.tag_add("link","1.7","1.16")
 7 text.tag_config("link",foreground="blue",underline=True)
 8 def show_hand_cursor(event):
 9     text.config(cursor="arrow")
10 def show_arrow_cursor(event):
11     text.config(cursor="xterm")
12 def click(event):
13     webbrowser.open("http://www.baidu.com")
14 text.tag_bind("link","<Enter>",show_hand_cursor)
15 text.tag_bind("link","<Leave>",show_arrow_cursor)
16 text.tag_bind("link","<Button-1>",click)
17 mainloop()

执行结果:

 下面给大家介绍几个比较实用的Text组件

第一个是判断内容是否发生变化,例如做一个记事本,当用户关闭的时候,程序需要检查内容是否有改动,如果有,需要提醒用户保存。下面的例子中,可以通过小燕Text组件中文本的MD5摘要来判断内筒是否发生改变。

 1 from tkinter import *
 2 import hashlib
 3 root = Tk()
 4 text = Text(root,width=20,height=5)
 5 text.pack()
 6 text.insert(INSERT,"I love Python3.x")
 7 contents = text.get(1.0,END)
 8 def getSig(contents):
 9     m = hashlib.md5(contents.encode())
10     return m.digest()
11 sig = getSig(contents) 
12 def check():
13     contents = text.get(1.0,END)
14     if sig != getSig(contents):
15         print("内容没有保存")
16     else:
17         print("文件没有改动")
18 Button(root,text="检查",command=check).pack()
19 mainloop()

 执行结果:

第二个是查找操作,是使用search()方法可以搜索Text组件中的内容,可以提供一个确切的目标进行搜索(默认),也可以使用Tcl格式的正则表达式进行搜索需要设置regexp选项为True(:):

 1 from tkinter import*
 2 root = Tk()
 3 text = Text(root,width=30,height=5)
 4 text.pack()
 5 text.insert(INSERT,"I love python3.6")
 6 #将任何格式的索引号统一为元组(行,列)的格式输出
 7 def getIndex(text,index):
 8     return tuple(map(int,str.split(text.index(index),".")))
 9 start = 1.0
10 while True:
11     pos = text.search("o",start,stopindex=END)
12     if not pos:
13         break
14     print("找到了,位置是:",getIndex(text,pos))
15     start = pos + "+1c" #将start指向下一个字符
16 mainloop()

执行结果:

找到了,位置是: (1, 3)
找到了,位置是: (1, 11)
这里第一是找出“o"的位置,然后以"o"为起点,继续找第二个位置,直到找完
注意:这里如果忽略stopindex选项,表示知道文本的末尾结束搜索。设置backwards选项为True,则是修改搜索的方向(变为向后搜索,那么start遍历应该设置为END,stopindex选项设置为1.0,最后+1c设置为-1c )
最后,Text组件还支持“恢复”和“撤销”操作,这使得Text组件显得相当高大上。通过设置undo选项为True,可以开启Text组件的撤销功能,然后利用editundo可以开启实现撤销操作,用editredo()方法可以实现恢复操作。
 1 from tkinter import *
 2 import hashlib
 3 root = Tk()
 4 text = Text(root,width=20,height=5)
 5 text.pack()
 6 text.insert(INSERT,"I love Python3.x")
 7 def show():
 8     text.edit_undo()
 9 Button(root,text="撤销",command=show).pack()
10 mainloop()
执行结果:

点击一次撤销后,插入进去的数据已经没有 了

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

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

但是我们可以自定义,做法就是先将autoseparators选项设置为False(因为这个选项是让Tkinter在人为一次完成的操作结束后自动插入“分隔符”,然后绑定键盘事件,每次有输入就用edit_separator()方法人为插入一个“分隔符”:

 1 from tkinter import *
 2 import hashlib
 3 root = Tk()
 4 text = Text(root,width=20,height=5,autoseparators=False,undo=True,maxundo=10) 5 text.pack()
 6 def callback():
 7     text.edit_separator()
 8 text.bind("<Key>",callback)
 9 text.insert(INSERT,"I love Python3.x")
10 def show():
11     text.edit_undo()
12 Button(root,text="撤销",command=show).pack()
13 mainloop()

 

 


posted @ 2018-11-14 22:17  阳光宝贝-沐沐  阅读(28600)  评论(1编辑  收藏  举报