【Tkinter】Python標準庫中的GUI框架入門——window和widget

本博客高度參考了這篇文章,可以認爲是該文章的部分譯文,強烈推薦。
對原作者和網站提供如此高質量的教程表示感謝!

圖形用戶界面(Graphical User Interface,簡稱GUI),是指採用圖形方式顯示計算機的操作界面。相較於命令行,這種方式上手難度低,更加容易爲普通用戶所接受。

Python中有許多GUI框架,但Tkinter是唯一一種被寫入標準庫中的。Tkinter是跨平臺(cross-platform)的,這意味着相同的代碼可以在Windows、Linux和macOS上運行,而且GUI中的可視化元素(按鈕、文本框等)可以根據其所在的操作系統進行自動渲染。

當然,Tkinter也有其不足之處。其中一點就是,它顯得“過時”了——許多可視化元素的樣式看起來像上個世紀的個人電腦中的一樣。如果你追求新潮、酷炫,那麼Tkinter多半沒法滿足你的要求;如果只是想實現某些功能而不怎麼注重外觀,那麼輕量級的Tkinter足以讓你快速、便捷地構建自己的應用。

1. 窗口(window)

在一個GUI中,最基礎最底層的元素是窗口(window)。如果一個把一個完整的GUI比作一幅畫,那麼window就是畫布,所有的操作都在這張畫布上進行。所以,在編寫GUI之前,需要創建這樣一張畫布——用編程的語言來說,就是要先實例化這樣一個window。

首先,打開Python的交互式窗口,導入tkinter(在所有的交互代碼中,均省略了’>>>’):

import tkinter as tk

然後,實例化一個window

window = tk.Tk()

這時,操作系統就會彈出一個新的窗口,大概長這個樣子:
在這裏插入圖片描述
至此,畫布就準備好了,因爲現在什麼都沒做,所以畫布是空白的。我們可以向這張畫布上寫一些字符:

greeting = tk.Label(text='Hello Tkinter!')
greeting.pack()

最終彈窗的結果是這樣的:
在這裏插入圖片描述
第一行代碼的意思是,實例化一個Label,在實例化的過程中,通過text指定了該變量要顯示的內容。

第二行代碼的意思是,將該實例固定到畫布上。

LabelTkinter中的一個類,單詞翻譯成中文的意思是“說明性短語”,在GUI領域,這個類被叫做“控件”(widget),通過向窗口中添加不同的控件,並對它們進行組合,就形成了各式各樣的GUI了。

Windows操作系統之所以這樣命名,就是因爲它本質上就是許多window的組合,當然這些組合是非常複雜的。這些window爲用戶屏蔽了看上去很枯燥的命令操作,從而提升了用戶體驗,但代價也是很巨大的,就是速度變慢。這種變慢對於普通用戶來說可能並沒有什麼感覺,但是對於服務器而言,卻很可能是致命的,這也是爲什麼絕大部分服務器都採用沒有GUI的Linux操作系統的原因之一。

2. 控件(widget)

如果說窗口是畫布,那麼控件就是不同的顏料了。它們也是組成一個GUI所必不可少的東西,是用戶與程序進行交互的基礎。在Tkinter中,每一種控件都由一個類來定義,一些常見的控件及其說明如下:

控件 說明
Label 用於在屏幕上打印文本
Button 按鈕,可以包含文字,並在單擊時進行相應的操作
Entry 只允許單行文本輸入的控件
Text 允許多行文本輸入的控件
Frame 一個矩形區域,用於對控件分組或爲其提供填充

當然,還有許多其他的控件,但上面這些確實是最基礎的,熟練使用這些控件,就可以開發一些有趣的東西,並對Tkinter的運用有相當程度的理解。

2.1 使用Label控件

Label控件可以用來展示文本或者圖片。這些文本(圖片)是不能夠被用戶編輯的,上面你已經看到了一個展示文本的例子,而實際上我們還可以對展示的文本進行更多的操作:

greeting = tk.Label(
    text='Hello Tkinter!',
    foreground="white",
    background="black"
	)
greeting.pack()

很直觀,我們改變了前景色和背景色,彈窗如下:
在這裏插入圖片描述
關於顏色,可以查看顏色手冊。當然也可以使用十六進制的RGB顏色值,這更加靈活。

Tkinter還提供了縮寫,如果你覺得background這個單詞太長了,可以用簡寫bg代替它,同理,可以用fg代替foreground

通過指定寬度和高度,來控制控件的大小:

greeting = tk.Label(
    text='Hello Tkinter!',
    fg="white",
    bg="black",
    width=10,
    height=10
	)
greeting.pack()

窗口如下:
在這裏插入圖片描述
這裏就存在一個問題了:寬度和高度都指定的是“10”,怎麼渲染出來的不是一個正方形呢?這是因爲在Tkinter中,寬度和高度都是以文本單元(text unit)來度量的。具體來說,1單位的寬度由系統中的字符“0”或者數字0的寬度來決定,1單位的高度由系統中的字符“0”或者數字0的高度來決定。

採用這種設置而不是用英寸、釐米、像素等度量,也是爲了保證程序在跨平臺運行時顯示一致。以字符來度量意味着控件大小與用戶機器上的默認字體是相關的,這樣就保證了文本能夠正確地嵌入到Label和Button中。

2.2 使用Button控件

顧名思義,Button控件就是用來展示一個按鈕的,點擊該按鈕,可以實現一個操作。

事實上,我們可以將Button理解爲一個可以點擊的Label。用於創建Label的關鍵字參數也適用於Button控件:

button = tk.Button(
    text="Click me!",
    width=25,
    height=5,
    bg="blue",
    fg="yellow",
)

彈窗是這樣的:
在這裏插入圖片描述

2.3 使用Entry控件

當你需要收集用戶輸入的一些信息,比如用戶名、密碼等,這時候就可以使用Entry控件了。它顯示爲一個小的文本框,用戶可以在其中輸入一些文本。創建和設計Entry控件的工作原理與上面提到的LabelButton非常類似:

entry = tk.Entry(fg="yellow", bg="blue", width=50)

彈窗如下:
在這裏插入圖片描述
上面已經說過,Entry是一個單行輸入的文本框,那麼一個重要的功能就是如何使用它從用戶那裏獲取信息。對於一個Entry實例,最常用的方法有三個:

  • .get():獲取Entry實例中的文本
  • .delete():刪除Entry實例中的文本
  • .insert():向Entry實例中插入文本

下面通過具體的例子來演示這些方法的作用。

首先,創建一個GUI,其窗口顯示的內容如下:
在這裏插入圖片描述
生成這個窗口的腳本爲:

import tkinter as tk


window = tk.Tk()
label = tk.Label(text='name')
entry = tk.Entry()
label.pack()
entry.pack()

需要注意的一點是,兩個控件都是居中對齊的,這也是.pack()方法的特性之一,後文會對這個特性進行說明。

單擊一下輸入框,就可以在裏面輸入內容了,假設我們輸入“Real Python”。爲了驗證各個方法的作用,我們在交互式窗口接着輸入:

entry.get()

則會打印出“Real Python”的字符。

.delete()方法用於刪除字符,其使用與對待Python中的字符串的使用方式很相似,需要傳入一個參數告訴Python刪除的字符的位置:

entry.delete(0)

即刪除了第一個字符,這時彈窗爲:
在這裏插入圖片描述
如果想一次性刪除好幾個字符,還需要傳入第二個整數,用於告訴Python刪除字符的終止位置:

entry.delete(0, 4)

這時,彈窗爲:
在這裏插入圖片描述
第二個數字爲終止字符的位置,該位置的字符不會被刪除,上面的代碼實際上只刪除了位置爲0、1、2、3的字符。

如果想一次性刪除全部,可以用如下的方法實現:

entry.delete(0, tk.END)

這時,彈窗裏面的Entry控件又爲空了:
在這裏插入圖片描述
與之相反,你可以通過.insert()方法來向控件中添加字符:

entry.insert(0, "Python")

第一個整數告訴Python從哪裏開始插入字符。如果Entry控件中沒有任何字符,那麼新字符總是插入到控件的開始位置上,與你傳入的第一個參數無關。

這時的窗口又變成了這樣:
在這裏插入圖片描述
再繼續往裏面插值:

entry.insert(0, "Real ")

會發現,窗口變成了第一次輸入後的樣子:
在這裏插入圖片描述

2.4 使用Text控件

可以將Text控件理解爲一個可以輸入很多行文本的Entry。事實上,它的作用就是如此。

Entry控件類似,Text控件也有三種主要的方法:

  • .get():獲取Text實例中的文本
  • .delete():刪除Text實例中的文本
  • .insert():向Text實例中插入文本

需要注意的是,儘管方法名稱完全一致,但由於多行文本的原因,其使用方法略有不同。

首先,我們重新創建一個帶Text控件的GUI:

window = tk.Tk()
text_box = tk.Text()
text_box.pack()

GUI應該具有如下樣式:

在這裏插入圖片描述

單擊該Text中的任意位置,然後就可以輸入字符了,這裏我輸入了“Hello World”,佔用了兩行。

Entry控件相類似,可以採用.get()方法來提取Text控件中的內容。但需要注意的是,Text控件的.get()方法至少需要一個參數。如果只傳入一個參數,那麼你將得到該位置上的字符;如果想要得到數個字符,則需要傳入開始位置參數和終止位置參數。由於Text控件中的內容可能包含多行,因此,每個參數都必須包含兩部分內容:字符的行號字符在該行中的位置

所以,位置參數的格式是<line>.<char>。爲了直觀的理解,嘗試一下下面的代碼:

text_box.get("1.0")
# 'H'

text_box.get("1.0", "1.5")
# 'Hello'

text_box.get("1.0", tk.END)
# 'Hello\nWorld\n'

注意到,換行符也是一個字符。

在理解了.get()方法之後,相應的.delete()方法和.insert()方法也是通過類似的方式進行調用的。具體不再贅述,只需要記住,\n也是一個字符。

2.5 使用Frame控件

一個好的GUI不但有很多控件,它們之間還得通過合適的方式組合起來纔行,Frame控件就可以滿足這個要求。

可以把Frame控件理解爲其他控件的容器,在實例化其他控件時,通過master參數來指定該控件屬於哪個Frame。具體的,看以下代碼:

import tkinter as tk

window = tk.Tk()

frame_a = tk.Frame()
frame_b = tk.Frame()

label_a = tk.Label(master=frame_a, text="I'm in Frame A")
label_a.pack()

label_b = tk.Label(master=frame_b, text="I'm in Frame B")
label_b.pack()

frame_a.pack()
frame_b.pack()

window.mainloop()

彈窗如下:
在這裏插入圖片描述
代碼的理解也是比較直觀的:

  • 5、6行實例化了兩個Frame控件;
  • 8、9行實例化了一個Label控件,並通過master參數,指定了該控件包含於frame_a中;
  • 11、12行操作類似;
  • 14、15行將兩個Frame控件pack到window上;
  • 17行啓動了window的主循環,以保證窗口處於打開狀態。

接下來,如果我改變14和15行的順序,那麼彈窗是什麼樣的呢?

...
frame_b.pack()
frame_a.pack()
...

彈窗如下:
在這裏插入圖片描述
通過前後兩次實驗的對比,可以這樣理解Frame控件的工作原理:其他控件在實例化時,通過master參數指定了其所屬的Frame,然後將其pack到該Frame上。最後,將Frame實例pack到主window上,就顯示出來各個控件了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章