Text 元件功能如下 :
- 可輸入多行文字
- 可在文字中嵌入圖片
- 提供文字格式化功能 (字型, 顏色, 大小, 樣式)
- 可與捲軸元件 Scrollbar 相結合為文字編輯器
Text 元件甚至可做為簡易型的網頁瀏覽器用.
本系列之前的文章參考 :
1. Text 元件的用法 :
呼叫 tkinter 模組的 Text() 函數即可建立一個文字區塊並傳回一個 Text 物件 :
tk.Text(父容器物件 [, options])
其中父容器物件可以是 Tk 視窗物件, Frame, 或 TopLevel 等容器物件. options 是可選參數群, 注意, Text 元件在 ttk 子模組沒有提供, 大概是因為此元件外觀沒有甚麼需要美化之處. 常用的可選參數群 options 與物件如下表 :
Text 元件參數 | 說明 |
width | 寬度 (單位=字元數) |
height | 高度 (單位=字元數) |
bg/background | 背景色, 可用標準顏色名稱字串例如 'blue' 或 "#0000ff" |
fg/foreground | 前景色 (文字顏色), 可用標準顏色名稱字串例如 'blue' 或 "#0000ff" |
font | 字型與尺寸 (px), 粗體 (bold) 或斜體 (italic), 底線或刪除線等 |
padx | 元件與容器的水平間距 (px) |
pady | 元件與容器的垂直間距 (px) |
state | 可用狀態, NORMAL=可編輯, DISABLED=不可編輯 |
wrap | 換行方式, CHAR (預設)=字元, WORD=字, NONE=不換行 |
relief | 外框型式, sunken (預設), flat, groove, raised, ridge, solid |
xscrollcommand | 綁定之水平捲軸之 set 方法 |
yscrollcommand | 綁定之垂直捲軸之 set 方法 |
這些參數除了可以在建立 Text 物件時透過傳入參數群 options 設定外, 也可以在建立物件後用 [] 運算子或呼叫 config() 方法去設定或更改. 注意, 不能用 . 運算子存取這些參數, 例如應該用 text["bg"]="ivory", 不能用 text.bg="ivory", 因為它們不是物件的屬性.
參數 xscrollcommand 與 yscrollcommand 是用來綁定 (呼叫) Scrollbar 元件的 set() 方法的, 這樣就可以為 Text 元件加上捲軸了. 不過這種綁定是雙向的, 在 Scrollbar 元件上還必須設定其 command 參數來呼叫 Text 元件的 xview() 或 yview() 方法, 這樣當捲動 Scrollbar 時 Text 元件的內容才會同步捲動. 這些參數都可用 Text 物件的 [] 運算子存取, 但設定則必須用 config() 方法.
建立 Text 物件後可用下列常用方法進行操作 :
Text 元件常用方法 | 說明 |
insert(type, str) | 以指定型式 type=INSERT/END 將字串 str 插入原內容後面 |
config(**option) | 設定物件屬性 options |
index(pos) | 傳回位置字串 pos 之 "列.行" 格式索引位置 (str) |
get(from, to) | 取得字元索引 from 到 to 之間的字元 (不含 to) |
delete(from, to) | 刪除字元索引 from 到 to 之間的字元 (不含 to) |
注意, 以上參數或方法中的大寫常數前面必須加上簡名 tk 才能存取, 例如 tk.NORM, tk.INSERT 等. 若使用 from tkinter import * 匯入則不必加 tk, 但不建議此種匯入法.
insert(type, str) 用來將字串插入目前內容的最後面, 由於 Text 元件無法在建立物件時設定內容初始值 (因為它沒有像 Entry 元件那樣有 text 屬性), 其內容必須在建立物件之後呼叫 insert() 方法加以設定. type=tk.INSERT 用來將字串插入到原內容的最後面, 而 tk.END 則是在插入後終結方塊內容. 其實用 tk.END 後再繼續用 tk,INSERT 還是可以的. 除了使用 tkinter 常數外, type 參數也可以用位置字串 "insert" 與 "end".
config(**options) 方法用來在建立 Text() 物件後進行參數設定, 因此並不需要在呼叫 Text() 就傳入全部要設定之參數, 可以不傳入任何選用參數 (即先套用預設值), 再呼叫 config() 來修改.
index(pos) 方法用來將位置字串轉換成 "列.行" 的索引格式, 傳回值是字串. Text 元件的內容字串基本上是使用 "列.行" 的矩陣索引格式來定位, 其中列從 1 起算, 行從 0 起算, 因此左上角第一個字元的索引位置是 "1.0", 第二列的第三行的位置是 "2.2", 除此之外 tinker 模組也定義了一些特定的位置字串, 其中較常用的如下表 :
常用的位置字串 | 說明 |
"1.0" | 文字區塊內容的開頭字元 (1 列 0 行) 索引 |
"3.6" | 文字區塊內容的 3 列 6 行的字元索引 |
"1.end" | 文字區塊內容第 1 列末尾字元的索引 |
"insert" | 文字區塊內容的插入位置 (預設是末尾字元) 索引 |
"end" | 文字區塊內容的末尾字元索引 |
"sel.first" | 文字區塊內容中被選取區域的開頭字元索引 |
"sel.last" | 文字區塊內容中被選取區域的末尾字元索引 |
注意, 內容為全數字的位置字串也可以用浮點數表示, 例如 "1.0" 可用 1.0 表示. 位置字串 pos 的詳細用法參考 :
get(from, to) 方法會傳回指定索引 from 到 to 的字串, 要取得 Text 元件之內容只能透過此方法, 因為 Text 元件並沒有像 Entry 元件那樣有 textvariable 參數將內容綁定到 StringVar 物件上. 取得 Text 元件的全部內容可用方法如下 :
- get(1.0, "end")
- get("1.0", "end")
- get("1.0", tk.END)
- get(1.0, tk.END)
delete(from, to) 方法可以刪除指定字元索引 from 到 to 之間的字元, 因此若要刪除 Text 元件的全部內容, 下面四種方式均可 :
- delete(1.0, "end")
- delete("1.0", "end")
- delete("1.0", tk.END)
- delete(1.0, tk.END)
但如果要刪除第 1 列一整列, 可用下面兩種做法 :
- delete("1.0", "1.end")
- delete("1.0", "2.0")
第一種做法的 "1.end" 表示第一列最後一個字元的下一個字元 (即跳行字元 \n) 的位置索引, 所以只刪除第一列的內容, 但不刪除跳行字元, 會留下一個空白列. 第二種作法 "2.0" 表示第二列的開頭, 所以會把整第一列包含跳行字元都刪除. 這些用法在開發文字編輯器時很有用, 參考 :
測試 1-1 : 建立文字區塊並插入內容 [看原始碼]
import tkinter as tk
win=tk.Tk() # 建立根視窗容器
win.title("tkinter GUI 測試") # 設定視窗標題
win.geometry("400x200") # 設定視窗大小
text=tk.Text(win) # 建立 Text 物件
text.insert(tk.INSERT, "Hello World!\n") # 插入字串於原內容之後
text.insert(tk.INSERT, "width=" + str(text["width"]) + \n") # 預設寬度
text.insert(tk.INSERT, "height=" + text["height"] + \n") # 預設高度
text.insert(tk.INSERT, "font=" + str(text["font"]) + "\n") # 預設字型
text.insert(tk.INSERT, "padx=" + str(text["padx"]) + "\n") # 預設水平間距
text.insert(tk.INSERT, "pady=" + str(text["pady"]) + "\n") # 預設垂直間距
text.insert(tk.INSERT, "bg=" + str(text["bg"]) + "\n") # 預設背景色
text.insert(tk.INSERT, "fg=" + str(text["fg"]) + "\n") # 預設前景色
text.insert(tk.END, "你是在說哈囉嗎?") # 插入字串於原內容之後
text.pack()
win.mainloop()
此例先建立 Text 物件然後插入內容, 順便檢視 width, height, 與 font 參數的預設值, 結果如下 :
可見 Text 元件的預設參數 width 為 80 個字元, height 為 24 個字元, font 為 TkFixedFont, 水平與垂直間距都是 1px, 背景與前景都是 Windows 預設值 (bg=白色, fg=黑色).
下面範例是在建立物件後用 [] 運算子或 config() 方法去設定元件參數 :
測試 1-2 : 建立文字區塊時傳入參數與呼叫 config() 設定參數 [看原始碼]
import tkinter as tk
win=tk.Tk()
win.title("tkinter GUI 測試")
win.geometry("400x300")
text=tk.Text(win, bg="#00ffff", fg="blue", width=30, height=10) # 建立物件時傳入參數
text.config(padx=5, pady=5, font="Helvetic 12 bold italic") # 呼叫 config() 方法設定參數
text.insert(tk.INSERT, "Hello World!\n")
text.insert(tk.INSERT, "width=" + str(text["width"]) + "\n")
text.insert(tk.INSERT, "height=" + str(text["height"]) + "\n")
text.insert(tk.INSERT, "font=" + str(text["font"]) + "\n")
text.insert(tk.INSERT, "padx=" + str(text["padx"]) + "\n")
text.insert(tk.INSERT, "pady=" + str(text["pady"]) + "\n")
text.insert(tk.INSERT, "bg=" + str(text["bg"]) + "\n")
text.insert(tk.INSERT, "fg=" + str(text["fg"]) + "\n")
text.insert(tk.INSERT, "你是在說哈囉嗎?")
text.pack()
win.mainloop()
此例分別在建立物件時傳入參數以及之後呼叫 config() 方法來設定文字區塊的屬性, 兩種做法均可. 注意, bg 參數可用顏色名稱 (例如 "ivory") 或顏色碼 (例如 "#00ff00"), font 參數可以使用字串形式, 也可以使用元組形式, 即 font=("Helvetic", 12, "bold", "italic"), 效果是一樣的, 結果如下 :
下面範例利用 delete() 方法來刪除全部或部分內容 :
測試 1-3 : 呼叫 delete() 方法刪除全部或部份內容 [看原始碼]
import tkinter as tk
from tkinter import ttk
win=tk.Tk()
win.title("tkinter GUI 測試")
win.geometry("400x300")
text=tk.Text(win, width=30, height=10, bg="aqua")
text.config(padx=5, pady=5, font="Helvetic 12")
text.insert(tk.INSERT, "Hello World!\n")
text.insert(tk.INSERT, "width=" + str(text["width"]) + "\n")
text.insert(tk.INSERT, "height=" + str(text["height"]) + "\n")
text.insert(tk.INSERT, "font=" + str(text["font"]) + "\n")
text.insert(tk.INSERT, "padx=" + str(text["padx"]) + "\n")
text.insert(tk.INSERT, "pady=" + str(text["pady"]) + "\n")
text.insert(tk.INSERT, "bg=" + str(text["bg"]) + "\n")
text.insert(tk.INSERT, "fg=" + str(text["fg"]) + "\n")
text.insert(tk.END, "你是在說哈囉嗎?")
text.pack()
def delete_all():
text.delete(1.0, "end") # 刪除全部內容
def delete_row_1():
text.delete(1.0, 2.0) # 刪除第一列 (含跳行)
btn1=ttk.Button(win, text="刪除全部", command=delete_all)
btn1.pack(side=tk.LEFT)
btn2=ttk.Button(win, text="刪除第一列", command=delete_row_1)
btn2.pack(side=tk.LEFT)
win.mainloop()
此例在文字方塊下添加兩個按鈕, 用 command 參數分別綁定 delete_all() 與 delete_row_one() 來刪除全部內如與第一列, 結果如下 :
下面的範例用來測試 state 參數 :
測試 1-4 : 設定 state 參數以啟用或禁用 Text 元件 [看原始碼]
import tkinter as tk
from tkinter import ttk
win=tk.Tk()
win.title("tkinter GUI 測試")
win.geometry("400x300")
text=tk.Text(win, width=30, height=10, bg="aqua")
text.config(padx=5, pady=5, font="Helvetic 12")
text.insert(tk.INSERT, "Hello World!\n")
text.pack()
def disable_widget():
text.config(state=tk.DISABLED, bg='gray', fg='yellow') # 將 Text 元件禁用
def normal_widget():
text.config(state=tk.NORMAL, bg='aqua', fg='black') # 將 Text 元件啟用
btn1=ttk.Button(win, text="Disabled", command=disable_widget)
btn1.pack(side=tk.LEFT)
btn2=ttk.Button(win, text="Normal", command=normal_widget)
btn2.pack(side=tk.LEFT)
win.mainloop()
此處我們使用 config() 方法來設定 state, bg, 與 sg 參數, 另一種方式是直接用 [] 運算子更改元件之屬性, 例如上面的 normal_widget() 可改寫為 :
def normal_widget():
text["state"]=tk.NORMAL
text["bg"]='aqua'
text["fg"]='black'
效果是一樣的.
Text 元件沒有像 Entry 元件那樣具有 textvariable 參數可將其內容綁定到 StringVar 物件上, 欲取得其內容必須利用其 get() 方法, 下列範例利用 index("sel.first") 與 index("sel.last") 分別取得選取區域的起始與結束索引, 再將這兩個索引傳入 get() 方法中即可取得所選取的文本內容 :
測試 1-5 : 呼叫 get() 方法擷取 Text 元件的內容 [看原始碼]
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox as msgbox
win=tk.Tk()
win.title("tkinter GUI 測試")
win.geometry("400x300")
text=tk.Text(win, width=30, height=10, bg="aqua")
text.config(padx=5, pady=5, font="Helvetic 12")
text.insert("insert", "Hello World!\n") # type 參數使用字串
text.insert("end", "你是在說哈囉嗎?") # type 參數使用字串
text.pack()
def get_all_content():
content=text.get(1.0, "end")
msgbox.showinfo("Info", content)
def get_marked_content():
try:
start=text.index("sel.first") # 取得選取區域的開頭索引
stop=text.index("sel.last") # 取得選取區域的結尾索引
content=text.get(start, stop) # 取得選取區域之內容
msgbox.showinfo("Info", content)
except:
msgbox.showinfo("Info", "請選取內容")
btn1=ttk.Button(win, text="顯示全部內容", command=get_all_content)
btn1.pack(side=tk.LEFT)
btn2=ttk.Button(win, text="顯示選擇內容", command=get_marked_content)
btn2.pack(side=tk.LEFT)
win.mainloop()
注意, 呼叫 index() 與 get() 方法時有可能出現例外 (例如沒有選取導致無法傳回索引), 因此必須放在 try-except 區塊中以處理可能發生的例外. 結果如下 :
下面範例則是測試 index() 方法的傳回值 :
測試 1-6 : 呼叫 index() 方法取得選取字串的索引位置 [看原始碼]
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox as msgbox
win=tk.Tk()
win.title("tkinter GUI 測試")
win.geometry("400x300")
text=tk.Text(win, width=30, height=10, bg="aqua")
text.config(padx=5, pady=5, font="Helvetic 12")
text.insert("insert", "Hello World!\n")
text.insert("insert", "你是在說哈囉嗎?")
text.pack()
def get_all_content():
content=text.get(1.0, "end")
msgbox.showinfo("Info", content)
def get_marked_index():
try:
start=text.index("sel.first")
stop=text.index("sel.last")
content="起始字元索引=" + start + " 結束字元索引=" + stop
msgbox.showinfo("Info", content)
except:
msgbox.showinfo("Info", "請選取內容")
btn1=ttk.Button(win, text="顯示選擇內容起訖索引", command=get_marked_index)
btn1.pack()
win.mainloop()
此例利用 index() 將位置字串 "sel.first" 與 "sel.last" 轉成 "列.行" 索引格式傳回, 可以得知被選取的文字起訖索引, 結果如下 :
"說" 的索引為 "2.3" 沒錯, "囉" 是 "2.5", 此處結束字元索引卻是 "2.6", 因為位置字串 "sel.last" 代表的是被選取文本末尾字元的下一個字元索引.
2. Scrollbar 捲軸元件用法 :
上面範例中所建立的 Text 元件當內容很長時, 必須使用向下鍵才能看到下面的內容, tkinter 提供 了 Scrollbar 捲軸元件可以跟 Text 元件綁定在一起, 這樣即可使用滑鼠捲動來檢視內容. Scrollbar 在 tk 與 ttk 都有 (但外觀其實沒甚麼差別), 通常用來跟 Canvas, Text 與 Listbox 搭配作為這些元件的捲軸, 另外, 水平捲軸也可以在 Entry 元件上.
呼叫 tk 的 Scrollbar() 函數即可建立一個捲軸物件 :
tk.Scrollbar(父容器物件 [, options])
其中父容器物件可以是 Tk 視窗物件, Frame, 或 TopLevel 等容器物件. options 是可選參數群 :
Scrollbar 元件常用參數 | 說明 |
command | 當捲軸捲動時呼叫之函式名稱 (不含括號) |
orient | 捲軸方向=tk.HORIZONTAL/tk.VERTICAL (預設) |
width | 捲軸寬度 (單位 px) |
bg | 滑桿與箭頭的背景色 |
其中 command 用來綁定其它元件的相對應外觀函式, 例如垂直捲軸要綁定 Text 或 Listbox 元件的 yview() 函式; 而水平捲軸則是要綁定 xview() 函式.
Scrollbar 元件的方法如下表 :
Scrollbar 元件的方法 | 說明 |
get() | 傳回表示捲軸位置的元組 (a, b), a=左/上, b=右/下 |
set(first, last) | 將捲軸綁定到另一元件之 xscrollcommand 或 yscrollcommand 參數 |
其中 get() 方法較少用, 而 set() 方法最重要, 用來將捲軸綁定到 Canvas, Text 或 Listbox 元件上, 這兩個元件都有 xscrollcommand 與 yscrollcommand 這兩個參數, 只要將這兩個參數指定為呼叫 Scrllbar 元件的 set 方法, 並將 Scrollbar 的 command 參數設定為呼叫 Canvas, Text 或 Listbox 的 xview 或 yview 即完成雙向綁定, 其配對如下表所示 :
Text/Listbox/Canvas 元件 | Scrollbar 元件 | |
垂直捲軸 | yscrollcommand=scollbar.set | command=text.yview |
水平捲軸 | xscrollcommand=scollbar.set | command=text.xview |
不過對於 Text 元件而言, 因為內容會自動換行 (使用 wrap 參數可設定是以 CHAR/WORD 為單位), 所以幾乎用不到到垂直捲軸. 注意, 由於 Text 元件與 Scrollbar 元件互相用
參考 :
例如 :
測試 2-1 : 綁定右方捲軸的 Text 元件 [看原始碼]
import tkinter as tk
from tkinter import ttk
win=tk.Tk()
win.title("tkinter GUI 測試")
win.geometry("500x300")
scrollbar=tk.Scrollbar(win)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y) #捲軸靠右填滿 Y 軸
text=tk.Text(win, height=10, bg="aqua")
text.config(padx=5, pady=5, font="Helvetic 12")
text.config(yscrollcommand=scrollbar.set) #Text 綁定捲軸
scrollbar.config(command=text.yview) #捲軸綁定 Text
str="Hello World!\n你是在說哈囉嗎?" #基本內容字串
strlist=[] #放內容的空串列
for i in range(10): #用迴圈複製字串串列
strlist.append(str)
text.insert("insert", "\n".join(strlist)) #將字串串列內容用跳行字元串接
text.pack(side=tk.LEFT, fill=tk.Y) #Text 靠左填滿 Y 軸 (要在捲軸之後 pack)
win.mainloop()
此例建立了一個 Text 與 Scrollbar 元件, 使用 pack() 排版時要注意將 fill 參數設為 Y 以填滿 Y 軸, side 參數兩者要配合, 此處 Text 設為 LEFT 在左, 則 Scrollbar 就要設為 RIGHT 在右. 注意, Scrollbar 要比 Text 先放進視窗容器內, 否則會看不到捲軸. 最重要的是 Text 與 Scrollbar 彼此透過 yscrollcommand 與 command 參數互相綁定, 結果如下 :
只要將 Text 與 Scrollbar 的位置交換, 捲軸就會換成左邊了, 例如 :
測試 2-2 : 綁定左方捲軸的 Text 元件 [看原始碼]
import tkinter as tk
from tkinter import ttk
win=tk.Tk()
win.title("tkinter GUI 測試")
win.geometry("500x300")
scrollbar=tk.Scrollbar(win)
scrollbar.pack(side=tk.LEFT, fill=tk.Y)
text=tk.Text(win, height=10, bg="aqua")
text.config(padx=5, pady=5, font="Helvetic 12")
text.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=text.yview)
str="Hello World!\n你是在說哈囉嗎?"
strlist=[]
for i in range(10):
strlist.append(str)
text.insert("insert", "\n".join(strlist))
text.pack(side=tk.RIGHT, fill=tk.Y)
win.mainloop()
此例程式與上例的差異只是 side 參數值交換而已, 結果如下 :
測試 2-3 : 綁定上方水平捲軸的 Text 元件 [看原始碼]
import tkinter as tk
from tkinter import ttk
win=tk.Tk()
win.title("tkinter GUI 測試")
win.geometry("500x300")
scrollbar=ttk.Scrollbar(win, orient=tk.HORIZONTAL) #建立水平捲軸
scrollbar.pack(side=tk.TOP, fill=tk.X)
text=tk.Text(win, height=10, bg="aqua")
text.config(padx=5, pady=5, font="Helvetic 12", wrap=tk.NONE) #不換行
text.config(xscrollcommand=scrollbar.set) #Text 綁定水平捲軸
scrollbar.config(command=text.xview) #Scrollbar 綁定 Text 的水平方向
str="Hello World!你是在說哈囉嗎?"
strlist=[]
for i in range(10):
strlist.append(str)
text.insert("insert", "".join(strlist)) #將字串串列串接為長字串
text.pack(side=tk.BOTTOM, fill=tk.X)
win.mainloop()
此例的 Scrollbar 元件用 orient 參數指定為水平捲軸, 並在呼叫 pack() 時指定 side 參數為上方 (TOP), fill 參數指定 X 軸填滿. Text 元件則用 side 參數指定放在下方 (BOTTOM) 與 X 軸填滿. 因為是水平捲軸, 因此 Text 與 Scrollbar 互相綁定時 Text 要改用 xscrollcommand 參數去連結 Scrollbar 的 set() 方法, 而 Scrollbar 的 command 參數則是要綁定 Text 的 xview() 方法, 結果如下 :
注意此例的捲軸改用了 ttk 版的 Scrollbar 元件, 但其實與 tk 的外觀並無差異.
參考 :
2 則留言 :
謝謝您這系列tkinter的文章, 很有幫助, 我之前在找text scroll bar的部份, 最後用了網路上搜尋到的ScrolledText也不錯用~
感謝您回應, 我也來測試看看.
張貼留言