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

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

今天我們來學習 Entry 元件,也就是我們平時所說的 輸入框。

輸入框是跟程式打交道的途徑,比如 程式要求你輸入 賬號 和 密碼。那麼它就要提供兩個輸入框,並且接收密碼的輸入框還會用 星號 * 將實際的內容給隱藏起來。

我們學了還幾個 tkinter 的元件之後,你自然就會發現,其實,很多方法和選項,它們之間都是通用的,這些選項對於不同的元件來說,名字一樣,內容也一樣。比如說,在輸入框中,用程式碼增加和刪除內容,也就是使用 insert() 和 delete() 方法。

建立一個輸入框

我們來嘗試一下:

import tkinter as tk

root = tk.Tk()

e = tk.Entry(root)
e.pack(padx = 20, pady = 20)

root.mainloop()

執行一下:

就得到了一個輸入框,我們可以在裡面填寫任意的字元,當字元太長時,它會自動滾動。

我們這裡試一下 :

e.delete(0, END) (這就是清空輸入框)

e.insert(0, "預設文字...")

import tkinter as tk

root = tk.Tk()

e = tk.Entry(root)
e.pack(padx = 20, pady = 20)

e.delete(0, "end") #清空輸入框
e.insert(0, "預設文字...") #在 索引0 位置寫入字元

root.mainloop()

執行一下:

那我們如何獲取輸入框中的內容呢?

我們可以使用 Entry 元件的 get() 方法,和之前的方法也是一樣的。

你也可以將一個 tkinter 的變數(通常是一個 StringVar 變數)掛鉤到 textvariable 選項,然後通過變數的 get() 方法來獲取。

下面這個例子 ,就要實現如圖的效果,按 獲取資訊 按鈕時,會清空輸入框,然後列印資訊。

  

當我們按下 獲取資訊 按鈕時:

我們一起來實現吧:

import tkinter as tk

root = tk.Tk()

#生成兩個 Label,顯示作品和作者
tk.Label(root, text = "作品:").grid(row = 0, column = 0)
tk.Label(root, text = "作者:").grid(row = 1, column = 0)

e1 = tk.Entry(root)
e2 = tk.Entry(root)
e1.grid(row = 0, column = 1, padx = 10, pady = 5)
e2.grid(row = 1, column = 1, padx = 10, pady = 5)

def show():
        print("作品:《%s》" %e1.get())
        print("作者:%s" %e2.get())
        e1.delete(0, "end")
        e2.delete(0, "end")

tk.Button(root, text = "獲取資訊", width = 10, command = show)\
                .grid(row = 3, column = 0, sticky = "w", padx = 10, pady = 5)
tk.Button(root, text = "退出", width = 10, command = root.quit)\
                .grid(row = 3, column = 1, sticky = "e", padx = 10, pady = 5)  #退出就直接呼叫視窗的 quit() 方法 

root.mainloop()

我們首先生成兩個 Label ,來自於 root 視窗,顯示作品和作者,關於佈局,我們傳統的做法是用兩個 Frame 把它包圍起來,現在教你一個新的方法,tkinter 總共提供了三種不同的 佈局元件的方法,一種就是我們熟悉的 pack,還有一種就是 grid (網格),就是使用表格的形式來管理你的元件。另一種就是 place(在後面的筆記中介紹)。

grid() 是允許你使用表格的形式來管理元件的位置,它有選項 row 表示 行,column 表示 列。(行數列數都是從0開始。)

然後是生成兩個輸入框。

最後是新增兩個按鈕。第一個是 “獲取資訊”,設定寬度 width ,command 為show()函式,另一個是“退出”按鈕,我們直接呼叫 quit() 方法即可。

關於按鈕的佈局,因為我們不僅要放在最後一行,還要分別靠左、靠右,這就需要設定 grid() 的 sticky 選項

sticky 用法

1. 控制組件在 grid 分配的空間中的位置

2. 可以使用 "n", "e", "s", "w" 以及它們的組合來定位(ewsn代表東西南北,上北下南左西右東)

3. 使用加號(+)表示拉長填充,例如 "n" + "s" 表示將元件垂直拉長填充網格,"n" + "s" + "w" + "e" 表示填充整個網格

4. 不指定該值則居中顯示

然後我們執行程式:

當我們按下 獲取資訊 時,

但是當我們按 退出 按鈕時,沒有任何反應,這是為什麼呢?

這是因為 我們使用的 IDLE 也是使用 tkinter 寫出來的,這裡會發生衝突。我們可以直接 右鍵使用Python直接執行 該程式,就會正常工作了。

接下來就是如何設計一個密碼輸入框(用星號*代替你實際輸入的內容)。

這個很簡單,我們只需要設定一個 show 選項就可以了。

show 用法

1. 設定輸入框如何顯示文字的內容
2. 如果該值非空,則輸入框會顯示指定字串代替真正的內容
3. 將該選項設定為 "*",則是密碼輸入框(你這是為什麼字元,就以什麼字元代替)

我們直接在上面的程式碼上修改:

import tkinter as tk

root = tk.Tk()

tk.Label(root, text = "賬號:").grid(row = 0, column = 0)
tk.Label(root, text = "密碼:").grid(row = 1, column = 0)

v1 = tk.StringVar()
v2 = tk.StringVar()

e1 = tk.Entry(root, textvariable = v1)
e2 = tk.Entry(root, textvariable = v2, show = "*")
e1.grid(row = 0, column = 1, padx = 10, pady = 5)
e2.grid(row = 1, column = 1, padx = 10, pady = 5)

def show():
        print("賬號:%s" %v1.get())
        print("密碼:%s" %v2.get())
        e1.delete(0, "end")
        e2.delete(0, "end")

tk.Button(root, text = "登入", width = 10, command = show)\
                .grid(row = 3, column = 0, sticky = "w", padx = 10, pady = 5)
tk.Button(root, text = "退出", width = 10, command = root.quit)\
                .grid(row = 3, column = 1, sticky = "e", padx = 10, pady = 5)  #退出就直接呼叫視窗的 quit() 方法 

root.mainloop()

這裡我們還使用了另一種獲取輸入框內容的方法:“你也可以將一個 tkinter 的變數(通常是一個 StringVar 變數)掛鉤到 textvariable 選項,然後通過變數的 get() 方法來獲取。”

執行一下:

基本的問題都解決了。

接下來我們試圖設計一個計算器,大家都知道,計算器不能夠輸入除了數字之外的任何字元,這又該如何限制呢?

這也是可以實現的,Entry 本身就自帶了 驗證功能。用於驗證輸入框裡的內容的合法性,比如要求輸入數字,你輸入了字母那就是非法。實現該功能,需要通過設定 validate、validatecommand 和 invalidcommand 選項

首先啟用驗證的“開關”是 validate 選項,該選項可以設定的值有:(焦點指的就是 輸入字元的游標

含義
'focus' 當 Entry 元件獲得或失去焦點的時候驗證
'focusin' 當 Entry 元件獲得焦點的時候驗證
'focusout' 當 Entry 元件失去焦點的時候驗證
'key' 當輸入框被編輯的時候驗證
'all' 當出現上邊任何一種情況的時候驗證
'none' 1. 關閉驗證功能
2. 預設設定該選項(即不啟用驗證)
3. 注意,是字串的 'none',而非 None

其次是為 validatecommand 選項指定一個驗證函式,該函式只能返回 True 或 False 表示驗證的結果。一般情況下驗證函式只需要知道輸入框的內容即可,可以通過 Entry 元件的 get() 方法獲得該字串。

下邊的例子中,在第一個輸入框輸入“來自江南的你” 並通過 Tab 鍵將焦點轉移到第二個輸入框的時候,驗證功能被成功觸發:

import tkinter  as tk

master = tk.Tk()

def test():
    if e1.get() == "來自江南的你":
        print("正確!")
        return True
    else:
        print("錯誤!")
        e1.delete(0, "end")
        return False

v = tk.StringVar()

e1 = tk.Entry(master, textvariable=v, validate="focusout", validatecommand=test)#focusout 就是焦點離開時驗證
e2 = tk.Entry(master)
e1.pack(padx=10, pady=10)
e2.pack(padx=10, pady=10)

master.mainloop()

當輸入資訊不是 “來自江南的你” 的時候,就會列印錯誤,然後清空輸入框。

然後,invalidcommand 選項指定的函式只有在 validatecommand 的返回值為 False 的時候才被呼叫。

下邊的例子中,在第一個輸入框輸入“來自江南的我”,並通過 Tab 鍵將焦點轉移到第二個輸入框,validatecommand 指定的驗證函式被觸發並返回 False,接著 invalidcommand 被觸發:

import tkinter as tk

master = tk.Tk()

v = tk.StringVar()

def test1():
    if v.get() == "來自江南的你":
        print("正確!")
        return True
    else:
        print("錯誤!")
        e1.delete(0, "end")
        return False

def test2():
    print("我被呼叫了......")
    return True

e1 = tk.Entry(master, textvariable=v, validate="focusout", validatecommand=test1, invalidcommand=test2)
e2 = tk.Entry(master)
e1.pack(padx=10, pady=10)
e2.pack(padx=10, pady=10)

master.mainloop()

最後,其實 Tkinter 還有隱藏技能,不過需要冷卻才能觸發

Tkinter 為驗證函式提供一些額外的選項:

額外選項 含義
'%d' 操作程式碼:0 表示刪除操作;1 表示插入操作;2 表示獲得、失去焦點或 textvariable 變數的值被修改
'%i' 1. 當用戶嘗試插入或刪除操作的時候,該選線表示插入或刪除的位置(索引號)
2. 如果是由於獲得、失去焦點或 textvariable 變數的值被修改而呼叫驗證函式,那麼該值是 -1
'%P' 1. 當輸入框的值允許改變的時候,該值有效
2. 該值為輸入框的最新文字內容
'%s' 該值為呼叫驗證函式前輸入框的文字內容
'%S' 1. 當插入或刪除操作觸發驗證函式的時候,該值有效
2. 該選項表示文字被插入和刪除的內容
'%v' 該元件當前的 validate 選項的值
'%V' 1. 呼叫驗證函式的原因
2. 該值是 'focusin','focusout','key' 或 'forced'(textvariable 選項指定的變數值被修改)中的一個
'%W' 該元件的名字,不過是 tk 變數在內部註冊的名字,為一串數字。

為了使用這些選項,你可以這樣寫:validatecommand=(f, s1, s2, ...)

其中,f 就是你“冷卻後”的驗證函式名,s1、s2、s3 這些是額外的選項,這些選項會作為引數依次傳給 f 函式。我們剛剛說了,使用隱藏技能前需要冷卻,其實就是呼叫 register() 方法將驗證函式包裝起來:

import tkinter as tk
master = tk.Tk()

v = tk.StringVar()

def test(content, reason, name):
    if content == "來自江南的你":
        print("正確!")
        print(content, reason, name)
        return True
    else:
        print("錯誤!")
        print(content, reason, name)
        return False

testCMD = master.register(test)  #冷卻,就是呼叫 register() 方法將驗證函式包裝起來
e1 = tk.Entry(master, textvariable=v, validate="focusout", validatecommand=(testCMD, '%P', '%v', '%W'))
e2 = tk.Entry(master)
e1.pack(padx=10, pady=10)
e2.pack(padx=10, pady=10)

master.mainloop()

執行一下:


有了以上的內容,我們就可以來設計一個計算器了。

import tkinter as tk
master = tk.Tk()

frame = tk.Frame(master)
frame.pack(padx = 10, pady = 10)

v1 = tk.StringVar()
v2 = tk.StringVar()
v3 = tk.StringVar()

def test(content):
        if content.isdigit():
                return True
        else:
                return False
        
testCMD = master.register(test)  #冷卻,就是呼叫 register() 方法將驗證函式包裝起來

e1 = tk.Entry(frame, width = 10, textvariable=v1, validate="key", validatecommand=(testCMD, '%P')).grid(row = 0, column = 0)
tk.Label(frame, text = "+").grid(row = 0, column = 1)
e2 = tk.Entry(frame, width = 10, textvariable=v2, validate="key", validatecommand=(testCMD, '%P')).grid(row = 0, column = 2)
tk.Label(frame, text = "=").grid(row = 0, column = 3)
e3 = tk.Entry(frame, width = 10, textvariable=v3, state = "readonly").grid(row = 0, column = 4)

def calc():
        result = int(v1.get() )+ int(v2.get())
        v3.set(str(result))

tk.Button(frame, text = "計算結果", command = calc).grid(row = 1, column = 2, pady = 10)

master.mainloop()