2020年9月12日星期六

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

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


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

輸入框是跟程序打交道的途徑,比如程序要求你輸入賬號和密碼。那麼它就要提供兩個輸入框,並且接收密碼的輸入框還會用星號* 將實際的內容給隱藏起來。
我們學了還幾個tkinter 的組件之後,你自然就會發現,其實,很多方法和選項,它們之間都是通用的,這些選項對於不同的組件來說,名字一樣,內容也一樣。比如說,在輸入框中,用代碼增加和刪除內容,也就是使用insert() 和delete() 方法。

創建一個輸入框

我們來嘗試一下:
  1. import tkinter as tk
  2. root = tk.Tk()
  3. e = tk.Entry(root)
  4. e.pack(padx = 20, pady = 20)
  5. root.mainloop()
運行一下:
就得到了一個輸入框,我們可以在裡面填寫任意的字符,當字符太長時,它會自動滾動。
我們這裡試一下:
e.delete(0, END)(這就是清空輸入框)
e.insert(0, "默認文本...")
  1. import tkinter as tk
  2. root = tk.Tk()
  3. e = tk.Entry(root)
  4. e.pack(padx = 20, pady = 20)
  5. e.delete(0, "end") #清空输入框
  6. e.insert(0, "默认文本...") #在 索引0 位置写入字符
  7. root.mainloop()
運行一下:

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

我們可以使用Entry 組件的get() 方法,和之前的方法也是一樣的。
你也可以將一個tkinter 的變量(通常是一個StringVar 變量)掛鉤到textvariable 選項,然後通過變量的get() 方法來獲取。
下面這個例子,就要實現如圖的效果,按獲取信息按鈕時,會清空輸入框,然後打印信息。
  
當我們按下獲取信息按鈕時:
我們一起來實現吧:
  1. import tkinter as tk
  2. root = tk.Tk()
  3. #生成两个 Label,显示作品和作者
  4. tk.Label(root, text = "作品:").grid(row = 0, column = 0)
  5. tk.Label(root, text = "作者:").grid(row = 1, column = 0)
  6. e1 = tk.Entry(root)
  7. e2 = tk.Entry(root)
  8. e1.grid(row = 0, column = 1, padx = 10, pady = 5)
  9. e2.grid(row = 1, column = 1, padx = 10, pady = 5)
  10. def show():
  11. print("作品:《%s》" %e1.get())
  12. print("作者:%s" %e2.get())
  13. e1.delete(0, "end")
  14. e2.delete(0, "end")
  15. tk.Button(root, text = "获取信息", width = 10, command = show)\
  16. .grid(row = 3, column = 0, sticky = "w", padx = 10, pady = 5)
  17. tk.Button(root, text = "退出", width = 10, command = root.quit)\
  18. .grid(row = 3, column = 1, sticky = "e", padx = 10, pady = 5) #退出就直接调用窗口的 quit() 方法
  19. 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.將該選項設置為"*",則是密碼輸入框(你這是為什麼字符,就以什麼字符代替)
我們直接在上面的代碼上修改:
  1. import tkinter as tk
  2. root = tk.Tk()
  3. tk.Label(root, text = "账号:").grid(row = 0, column = 0)
  4. tk.Label(root, text = "密码:").grid(row = 1, column = 0)
  5. v1 = tk.StringVar()
  6. v2 = tk.StringVar()
  7. e1 = tk.Entry(root, textvariable = v1)
  8. e2 = tk.Entry(root, textvariable = v2, show = "*")
  9. e1.grid(row = 0, column = 1, padx = 10, pady = 5)
  10. e2.grid(row = 1, column = 1, padx = 10, pady = 5)
  11. def show():
  12. print("账号:%s" %v1.get())
  13. print("密码:%s" %v2.get())
  14. e1.delete(0, "end")
  15. e2.delete(0, "end")
  16. tk.Button(root, text = "登录", width = 10, command = show)\
  17. .grid(row = 3, column = 0, sticky = "w", padx = 10, pady = 5)
  18. tk.Button(root, text = "退出", width = 10, command = root.quit)\
  19. .grid(row = 3, column = 1, sticky = "e", padx = 10, pady = 5) #退出就直接调用窗口的 quit() 方法
  20. 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 鍵將焦點轉移到第二個輸入框的時候,驗證功能被成功觸發:
  1. import tkinter as tk
  2. master = tk.Tk()
  3. def test():
  4. if e1.get() == "来自江南的你":
  5. print("正确!")
  6. return True
  7. else:
  8. print("错误!")
  9. e1.delete(0, "end")
  10. return False
  11. v = tk.StringVar()
  12. e1 = tk.Entry(master, textvariable=v, validate="focusout", validatecommand=test)#focusout 就是焦点离开时验证
  13. e2 = tk.Entry(master)
  14. e1.pack(padx=10, pady=10)
  15. e2.pack(padx=10, pady=10)
  16. master.mainloop()
當輸入信息不是“來自江南的你” 的時候,就會打印錯誤,然後清空輸入框。
然後,invalidcommand選項指定的函數只有在validatecommand的返回值為False的時候才被調用。
下邊的例子中,在第一個輸入框輸入“來自江南的我”,並通過Tab 鍵將焦點轉移到第二個輸入框,validatecommand 指定的驗證函數被觸發並返回False,接著invalidcommand 被觸發:
  1. import tkinter as tk
  2. master = tk.Tk()
  3. v = tk.StringVar()
  4. def test1():
  5. if v.get() == "来自江南的你":
  6. print("正确!")
  7. return True
  8. else:
  9. print("错误!")
  10. e1.delete(0, "end")
  11. return False
  12. def test2():
  13. print("我被调用了......")
  14. return True
  15. e1 = tk.Entry(master, textvariable=v, validate="focusout", validatecommand=test1, invalidcommand=test2)
  16. e2 = tk.Entry(master)
  17. e1.pack(padx=10, pady=10)
  18. e2.pack(padx=10, pady=10)
  19. 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()方法將驗證函數包裝起來:
  1. import tkinter as tk
  2. master = tk.Tk()
  3. v = tk.StringVar()
  4. def test(content, reason, name):
  5. if content == "来自江南的你":
  6. print("正确!")
  7. print(content, reason, name)
  8. return True
  9. else:
  10. print("错误!")
  11. print(content, reason, name)
  12. return False
  13. testCMD = master.register(test) #冷却,就是调用 register() 方法将验证函数包装起来
  14. e1 = tk.Entry(master, textvariable=v, validate="focusout", validatecommand=(testCMD, '%P', '%v', '%W'))
  15. e2 = tk.Entry(master)
  16. e1.pack(padx=10, pady=10)
  17. e2.pack(padx=10, pady=10)
  18. master.mainloop()
運行一下:

有了以上的內容,我們就可以來設計一個計算器了。
  1. import tkinter as tk
  2. master = tk.Tk()
  3. frame = tk.Frame(master)
  4. frame.pack(padx = 10, pady = 10)
  5. v1 = tk.StringVar()
  6. v2 = tk.StringVar()
  7. v3 = tk.StringVar()
  8. def test(content):
  9. if content.isdigit():
  10. return True
  11. else:
  12. return False
  13. testCMD = master.register(test) #冷却,就是调用 register() 方法将验证函数包装起来
  14. e1 = tk.Entry(frame, width = 10, textvariable=v1, validate="key", validatecommand=(testCMD, '%P')).grid(row = 0, column = 0)
  15. tk.Label(frame, text = "+").grid(row = 0, column = 1)
  16. e2 = tk.Entry(frame, width = 10, textvariable=v2, validate="key", validatecommand=(testCMD, '%P')).grid(row = 0, column = 2)
  17. tk.Label(frame, text = "=").grid(row = 0, column = 3)
  18. e3 = tk.Entry(frame, width = 10, textvariable=v3, state = "readonly").grid(row = 0, column = 4)
  19. def calc():
  20. result = int(v1.get() )+ int(v2.get())
  21. v3.set(str(result))
  22. tk.Button(frame, text = "计算结果", command = calc).grid(row = 1, column = 2, pady = 10)
  23. master.mainloop()

針對網友提出的計算器程序存在的Bug,現對代碼進行修改:

(修改日期:2019年1月7日09:34:40)

  1. import tkinter as tk
  2. master = tk.Tk()
  3. frame = tk.Frame(master)
  4. frame.pack(padx = 10, pady = 10)
  5. v1 = tk.StringVar()
  6. v2 = tk.StringVar()
  7. v3 = tk.StringVar()
  8. #针对输入框不能完全清空的问题,修改 test() 函数代码如下:
  9. #为什么不能清空,可以查看下方评论
  10. def test(content):
  11. if content.isdigit() or content == "":
  12. return True
  13. else:
  14. return False
  15. testCMD = master.register(test) #冷却,就是调用 register() 方法将验证函数包装起来
  16. e1 = tk.Entry(frame, width = 10, textvariable=v1, validate="key", validatecommand=(testCMD, '%P')).grid(row = 0, column = 0)
  17. tk.Label(frame, text = "+").grid(row = 0, column = 1)
  18. e2 = tk.Entry(frame, width = 10, textvariable=v2, validate="key", validatecommand=(testCMD, '%P')).grid(row = 0, column = 2)
  19. tk.Label(frame, text = "=").grid(row = 0, column = 3)
  20. e3 = tk.Entry(frame, width = 10, textvariable=v3, state = "readonly").grid(row = 0, column = 4)
  21. #针对输入框为空时按下计算按钮会报错的问题,修改 calc() 函数代码如下:
  22. def calc():
  23. if v1.get() == "" or v2.get() == "":
  24. result = ""
  25. v3.set(str(result))
  26. else:
  27. result = int(v1.get() )+ int(v2.get())
  28. v3.set(str(result))
  29. tk.Button(frame, text = "计算结果", command = calc).grid(row = 1, column = 2, pady = 10)
  30. master.mainloop()

0 留言:

發佈留言