1. 程式人生 > >《零基礎入門學習Python》第070講:GUI的終極選擇:Tkinter7

《零基礎入門學習Python》第070講:GUI的終極選擇:Tkinter7

上節課我們介紹了Text元件的Indexs 索引和 Marks 標記,它們主要是用於定位,Marks 可以看做是特殊的 Indexs,但是它們又不是完全相同的,比如在預設情況下,你在Marks指定的位置中插入資料,Marks 的位置會自動發生改變, 因為Marks 認它後面的“那個傢伙”,當 Marks 前面的資料被刪除時,Marks 並不會被刪除,它的位置只是相應的向前移動了,只有 mark_unset() 方法 才能夠刪除Marks,這節課我們接著來介紹 Tags 的用法。

Tags(標籤)通常用於改變 Text 元件中內容的樣式和功能。你可以修改文字的字型、尺寸和顏色。另外,Tags 還允許你將文字、嵌入的元件和圖片與鍵盤和滑鼠等事件相關聯。除了 user-defined tags(使用者自定義的 Tags),還有一個預定義的特殊 Tag:SEL。

SEL(或 "sel")用於表示對應的選中內容(如果有的話)。

你可以自定義任意數量的 Tags,Tags 的名字是由普通字串組成,可以是除了空白字元外的任何字元。另外,任何文字內容都支援多個 Tags 描述,任何 Tag 也可以用於描述多個不同的文字內容。

我們來舉個例子:

import tkinter as tk

root = tk.Tk()

text = tk.Text(root, width=40, height=5,)
text.pack()

text.insert("insert", 'I love Python.com')

text.tag_add("tag1", "1.7", "1.13", "1.15")
text.tag_config("tag1", background = "yellow", foreground = "red")

root.mainloop()

還有一點需要注意的是:如果你對同一個範圍內的文字加上多個 Tags,並且設定相同的選項,那麼新建立的 Tag 樣式會覆蓋比較舊的 Tag

import tkinter as tk

root = tk.Tk()

text = tk.Text(root, width=40, height=5)
text.pack()

text.insert("insert", 'I love Python.com')

text.tag_add("tag1", "1.7", "1.13", "1.15")
text.tag_add("tag2", "1.7", "1.13", "1.15")
text.tag_config("tag2", foreground = "green")
text.tag_config("tag1", background = "yellow", foreground = "red")

root.mainloop()

那麼新建立的 Tag2 會覆蓋比較舊的 Tag1 的相同選項, 注意,與下邊的呼叫順序沒有關係

你或許想控制 Tags 間的優先順序,這可以實現嗎?完全沒有問題!你可以使用 tag_raise() 和 tag_lower() 方法來提高和降低某個 Tag 的優先順序。

import tkinter as tk

root = tk.Tk()

text = tk.Text(root, width=40, height=5)
text.pack()

text.tag_config("tag1", background="yellow", foreground="red")
text.tag_config("tag2", foreground="green")

text.tag_lower("tag2")

text.insert("insert", "I love Python!", ("tag2", "tag1"))

root.mainloop()

另外 Tags 還支援事件繫結,使用的是 tag_bind() 的方法。

下邊例子中我們將文字("Python.com")與滑鼠事件進行繫結,當滑鼠進入該文字段的時候,滑鼠樣式切換為 "arrow" 形態,離開文字段的時候切換回 "xterm" 形態。當觸發滑鼠“左鍵點選操作”事件的時候,使用預設瀏覽器開啟Python的首頁(https://www.python.org/):

import tkinter as tk
import webbrowser

root = tk.Tk()

text = tk.Text(root, width=40, height=5)
text.pack()

text.insert("insert", "I love Python.com!")

text.tag_add("link", "1.7", "1.17")
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("https://www.python.org/")

text.tag_bind("link", "<Enter>", show_arrow_cursor)
text.tag_bind("link", "<Leave>", show_xterm_cursor)
text.tag_bind("link", "<Button-1>", click)

root.mainloop()

接下來給大家介紹幾個 Tags 使用上的技巧:

(一)判斷內容是否發生變化

通過校檢 Text 元件中文字的 MD5 摘要來判斷內容是否發生改變

import tkinter as tk
import hashlib

root = tk.Tk()

text = tk.Text(root, width=40, height=5)
text.pack()

text.insert("insert", "I love Python.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("風平浪靜")

tk.Button(root, text = "檢查", command = check).pack()

root.mainloop()

(二)查詢操作

使用 search() 方法可以搜尋 Text 元件中的內容。但是傳統的 search() 方法只查詢到一個,就返回,我們可以加入一個迴圈,查詢所有的。

import tkinter as tk
import hashlib

root = tk.Tk()

text = tk.Text(root, width=40, height=5)
text.pack()

text.insert("insert", "I love Python.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"
    
root.mainloop()

(三)恢復、撤銷操作

Text 元件還支援“恢復”和“撤銷”操作,這使得 Text 元件顯得相當高大上。

通過設定 undo 選項為 True 可以開啟 Text 元件的“撤銷”功能。然後用 edit_undo() 方法實現“撤銷”操作,用 edit_redo() 方法實現“恢復”操作。

import tkinter as tk
import hashlib

root = tk.Tk()

text = tk.Text(root, width=40, height=5, undo = True)
text.pack()

text.insert("insert", "I love Python.com!")

def show():
    text.edit_undo()
    

tk.Button(root, text = "撤銷", command = show).pack()
    
root.mainloop()

 Text 元件內部有一個棧專門用於記錄內容的每次變動,所以每次“撤銷”操作就是一次彈棧操作,“恢復”就是再次壓棧。

預設情況下,每一次完整的操作將會放入棧中。但怎麼樣算是一次完整的操作呢?Tkinter 覺得每次焦點切換、使用者按下 Enter 鍵、刪除\插入操作的轉換等之前的操作算是一次完整的操作。也就是說你連續輸入“I love Python” 的話,一次的“撤銷”操作就會將所有的內容刪除。

那我們能不能自定義呢?比如我希望插入一個字元就算一次完整的操作,然後每次點選“撤銷”就去掉一個字元。

當然可以!做法就是先將 autoseparators 選項設定為 False(因為這個選項是讓 Tkinter 在認為一次完整的操作結束後自動插入“分隔符”),然後繫結鍵盤事件,每次有輸入就用 edit_separator() 方法人為地插入一個“分隔符”:

import tkinter as tk
import hashlib

root = tk.Tk()

text = tk.Text(root, width=40, height=5, undo = True, autoseparators = False)
text.pack()

text.insert("insert", "I love Python.com!")

def callback(event):
    text.edit_separator()  #人為插入分隔符

text.bind('<Key>', callback)

def show():
    text.edit_undo()    

tk.Button(root, text = "撤銷", command = show).pack()
    
root.mainloop()