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()