Tk 具備一般常用的視窗元件,因此用 Tk 來開發小工具絕對夠用
↑
M V C
↓ ↓
Encrypt EncryptController
若是桌機平台的大型軟體也可用 Tk 來開發,基本上 Python 有哪一種桌機平台的版本,就可連帶用 Tk ,但是如手機、平板、穿戴式或其他平台等等,需要看 Tk 有否支援。
這邊先來要認識有哪些視窗元件,每個圖形介面程式庫提供的元件 (component) 都差不多,不過用的名稱,也就是類別的識別字 (identifier) 都不太一樣,以下是 Tk 的元件列表
類別 | 說明 |
---|---|
Button | 按鈕。 |
Canvas | 長方形區域。 |
Checkbutton | 核取按鈕。 |
Entry | 文字輸入欄。 |
Frame | 視窗。 |
Label | 文字標籤。 |
LabelFrame | 文字標籤視窗。 |
Listbox | 列表選單。 |
Menu | 選單列的下拉式選單。 |
MenuButton | 選單的選項。 |
Message | 類似 Label ,可多行。 |
OptionMenu | 下拉式的選項選單。 |
PaneWindow | 類似 Frame ,可包含其他視窗元件。 |
Radiobutton | 單選按鈕。 |
Scale | 拉桿。 |
Scrollbar | 捲軸。 |
Spinbox | 微調器 |
Text | 文字方塊。 |
Toplevel | 新增視窗。 |
關於 GUI 元件的英文或中文名稱,基本上各程式庫或作者的英文、中文用詞都不太一樣,,基本上這沒有什麼一致或權威的用詞,因此讀者只好自己習慣,倒是本書的用詞會保持一致的。
我們預計做出如下的 GUI
基本上「輸入:」、「輸出:」跟「訊息欄」都是文字標籤,也就是 Label ,「輸入:」及「輸出:」後面的凹陷下去的視窗元件是文字輸入欄,文字輸入欄的型態為 Entry ,下面「新建」、「載入」、「存檔」、「編碼」、「解碼」、「清除」、「拷貝」等七個是按鈕,按鈕的型態為 Button 。
雖說這裡只用到三種共十二個視窗元件,倒是接下來要用類別 (class) 要設計 GUI ,用類別的原因很簡單,就是把跟 GUI 相關的程式部分都打包在類別中, V 的部分用 EncryptView 類別,而 C 的部分用 EncryptController 類別,主要目的是讓整體程式碼比較好組織及維護。
然而一下子跳進 GUI 類別設計有點繁雜,這裡先列出步驟
- 定義繼承 (inherit) 自 Frame 的類別,例如 HelloView 。
- 在 __init__() 方法中呼叫 Frame 的 __init__() ,然後進行相關設定,最後呼叫 createWidgets() 方法。
- createWidgets() 方法為自訂建立視窗元件的方法。
- 執行部分先建立 Tk 應用程式物件 root ,然後建立 HelloView 的物件,並以 root 當作 master 的參數,最後呼叫 mainloop() 方法。
以下先將上個單元的 tk_demo3.py 用類別改寫以資說明
from tkinter import *
# Hello 的 View 類別
class HelloView(Frame):
# 設定初值
def __init__(self, master=None):
Frame.__init__(self, master)
self.winfo_toplevel().title("Hello World!")
self.grid()
self.createWidgets()
# 建立視窗元件
def createWidgets(self):
self.text = Label(self)
self.text["text"] = "Hello World!"
self.text["width"] = "30"
self.text["height"] = "5"
self.text["bg"] = "black"
self.text["fg"] = "white"
self.text.grid(row=0, column=0)
# GUI 執行部分
if __name__ == '__main__':
root = Tk()
app = HelloView(master=root)
root.mainloop()
#《程式語言教學誌》的範例程式
# http://kaiching.org/
# 檔名:hello_view.py
# 功能:示範 Python 的 Tk 應用程式
# 作者:張凱慶 */
首先,步驟 1 定義繼承自 Frame 的類別,繼承的寫法就是在類別名稱之後加上小括弧,小括弧中放父類別 (superclass) 的識別字
class HelloView(Frame):
因此 HelloView 就是一種 Frame 類別,所有的視窗元件就是要加入到 HelloView 上。
步驟 2 關於 __init__() 方法的定義
def __init__(self, master=None):
Frame.__init__(self, master)
self.winfo_toplevel().title("Hello World!")
self.grid()
self.createWidgets()
注意這裡要先呼叫 Frame 的 __init__()
Frame.__init__(self, master)
這是因為 HelloView 要發揮 Frame 的完整功能,就要先呼叫 Frame 的 __init__() ,另外 master 需要以 Tk 應用程式物件當參數,這在底下建立 HelloView 型態的變數 app 代入
app = HelloView(master=root)
下面依序做必要的設定,像是 winfo_toplevel() 是呼叫上一層的 Tk 應用程式物件,這是因為 Frame 沒有 title() 方法設定視窗標題
self.winfo_toplevel().title("Hello World!")
self.grid()
self.createWidgets()
然後呼叫 grid() ,表示 HelloView 要用格子版面管理員,最後呼叫 createWidgets() ,這用來建立所有在 HelloView 上的視窗元件,這也是步驟 3 的部分,其定義如下
def createWidgets(self):
self.text = Label(self)
self.text["text"] = "Hello World!"
self.text["width"] = "30"
self.text["height"] = "5"
self.text["bg"] = "black"
self.text["fg"] = "white"
self.text.grid(row=0, column=0)
HelloView 的 text 屬性為文字標籤 Label ,因此往後要操作這個文字標籤都可透過 text ,然後著接下來 text 的每一個屬性都是透過字典的 key:value 方式來設定,這是因為類別的屬性預設可用字典的方式存取。
最後執行部分也就是步驟 4 ,先建立 Tk 應用程式物件 root ,再建立 HelloView 的物件 app ,並以 root 當 HelloView() 的參數,最後 root 再呼叫 mainloop() 方法
if __name__ == '__main__':
root = Tk()
app = HelloView(master=root)
root.mainloop()
執行後會得到如下的視窗
按照同樣的步驟設計 EncryptView 類別,完整程式如下
from tkinter import *
from tkinter.ttk import *
# Encrypt 的 View 類別
class EncryptView(Frame):
# 設定初值
def __init__(self, master=None):
Frame.__init__(self, master)
self.winfo_toplevel().title("編密碼小工具")
self.grid()
self.createWidgets()
# 建立所有視窗元件
def createWidgets(self):
self.it = Label(self)
self.it["text"] = "輸入:"
self.it.grid(row=0, column=0,
sticky=N+E)
self.ifd = Entry(self)
self.ifd["width"] = 60
self.ifd.grid(row=0, column=1,
columnspan=6,
sticky=N+W)
self.ot = Label(self)
self.ot["text"] = "輸出:"
self.ot.grid(row=1, column=0,
sticky=N+E)
self.ofd = Entry(self)
self.ofd["width"] = 60
self.ofd.grid(row=1, column=1,
columnspan=6,
sticky=N+W)
self.nb = Button(self)
self.nb["text"] = "新建"
self.nb.grid(row=2, column=0,
sticky=N+W)
self.lb = Button(self)
self.lb["text"] = "載入"
self.lb.grid(row=2, column=1,
sticky=N+W)
self.sb = Button(self)
self.sb["text"] = "存檔"
self.sb.grid(row=2, column=2,
sticky=N+W)
self.eb = Button(self)
self.eb["text"] = "編碼"
self.eb.grid(row=2, column=3,
sticky=N+W)
self.db = Button(self)
self.db["text"] = "解碼"
self.db.grid(row=2, column=4,
sticky=N+W)
self.cb = Button(self)
self.cb["text"] = "清除"
self.cb.grid(row=2, column=5,
sticky=N+W)
self.cb2 = Button(self)
self.cb2["text"] = "拷貝"
self.cb2.grid(row=2, column=6,
sticky=N+W)
self.dt = Label(self)
m = "訊息欄"
self.dt["text"] = m
self.dt.grid(row=3, column=0,
columnspan=7,
sticky=N)
# GUI 執行部分
if __name__ == '__main__':
root = Tk()
app = EncryptView(master=root)
root.mainloop()
#《程式語言教學誌》的範例程式
# http://kaiching.org/
# 檔名:encrypt_view.py
# 功能:示範 Python 的 Tk 應用程式
# 作者:張凱慶 */
這邊多引入 tkinter.ttk ,這是因為會用到 tkinter.ttk 的樣式
from tkinter.ttk import *
此外,部分跨多行的視窗元件在呼叫 grid() 方法時多做了一些設定
self.ifd.grid(row=0, column=1,
columnspan=6,
sticky=N+W)
columnspan 為跨多少行,而 sticky 設定視窗元件在格子中的對齊方式。
程式執行結果如下
EncryptView 類別是純粹的 V ,因此按按鈕並不會有動作,在下一單元介紹 C 及加入 command 屬性的設定,這樣按按鈕才會有反應。
中英文術語對照 | |
---|---|
元件 | component |
類別 | class |
識別字 | identifier |
繼承 | inherit |
父類別 | superclass |
重點整理 |
---|
1. Tk 的視窗元件包括各種按鈕、文字輸入欄、文字標籤、拉桿、捲軸、文字方塊等等。 |
2. EncryptView 類別預計要有 3 個文字標籤、 2 的文字輸入欄及 7 個按鈕。 |
3. 利用類別設計 Tk 的 GUI ,首先定義繼承自 Frame 的類別,然後在 __init__() 方法中呼叫 Frame 的 __init__() ,接著定義 createWidgets() 方法逐一建立視窗中的元件。 |
問題與討論 |
---|
1. Tk 有哪些視窗元件?常用軟體如記事本、瀏覽器等等,用了哪些視窗元件呢? |
2. 利用 Tk 的 GUI 類別設計步驟為何?又為什麼要改用類別設計? |
練習 |
---|
1. 承接上一個單元的 tk_demo3.py ,寫一個新程式 hello_view2.py ,改成用 HelloView 類別設計 GUI。 |
2. 承接上一個單元的 game_demo.py 寫一個新程式 game_view.py ,改成用 GameView 類別設計 GUI 。 |
相關教學影片
- Tk 的各種視窗元件 ⇨ YouTube 頁面連結
- 用類別設計 Tk 應用程式的步驟 ⇨ YouTube 頁面連結
- EncryptView 與 EncryptController ⇨ YouTube 頁面連結