Python 入門指南 V2.00 - 單元 22 - GUI 的基本概念




大部分物件導向程式語言在開發 GUI 的部份會採取 MVC 模式 (Model-View-Controller) , M 是我們的 Encrypt 類別 (class) , V 為介面,而 C 為 M 與 V 之間的控制器



依 MVC 模式,三個部分都應該是獨立開發的類別,這是說 M 是資料計算的核心類別, V 是介面類別, C 為控制 M 與 V 之間交流的類別,倒是這在我們的例子略顯複雜,並不見得能簡化 GUI 的開發,因此我們不打算採取三個獨立類別的開發方式。

標準模組庫 (standard library) 中的圖形介面模組 (module) 為 tkintertkinter 在國外已經發展的相當成熟,許多程式語言 (programming language) 也都直接內建 Tk 。


我們的 V 跟 C 都會寫在 EncryptGUI 類別中,首先看到 V 的部份,也就是我們的 GUI 。設計 GUI ,首先要認識使用哪一種版面管理 (layout management) , Tk 有三種幾何版面管理員 (geometry manager) ,如下表


版面管理員名稱說明
定位版面管理員自訂每個元件的座標。
包裹版面管理員由程式自動安排每個元件的位置。
格子版面管理員表格示安排每個元件,須指定元件所在的格數。

其次要認識有哪些視窗元件,每個圖形介面程式庫提供的元件 (component) 都差不多,不過用的名稱,也就是類別的識別字 (identifier) 都不太一樣,以下是 Tk 的元件列表


類別名稱中文用詞
Button按鈕
Canvas畫布
Checkbutton複選方塊
Entry文字欄位
Frame視窗
Label文字標籤
LabelFrame標籤視窗
ListBox選取清單
Menu選單
MenuButton選單元件
Message對話視窗
OptionMenu下拉式選單
PanedWindow面板
RadioButton單選方塊
Scale控制桿
Scrollbar捲軸
SpinBox旋鈕
Text文字方塊
Toplevel頂層視窗元件

關於 GUI 元件的英文或中文名稱,基本上各程式庫或作者的用詞都不太一樣,基本上這沒有什麼一致或權威的用詞,因此讀者只好自己習慣,倒是本書的用詞會保持一致的 ^_^

通常寫小程式就直接用包裹版面管理員 (pack manager) ,如下例


001from tkinter import *
002  
003root = Tk()
004text = Label(root, text="Tk's job!!",
005             width="30", height="5",
006             bg="black", fg="white")
007text.pack()
008root.mainloop()
009 
010# 檔名: tk_demo.py 
011# 作者: Kaiching Chang 
012# 時間: July, 2014

用 Tk 首先要 import tkinter ,這裡用 fromimport 之後星號表示所有名稱,這使我們在程式中可以直接使用 tkinter 中的名稱,不需要連帶寫出 tkinter


001from tkinter import *

然後建立 Tk 物件


003root = Tk()

這個視窗只有一個文字標籤, Label 型態的 text


004text = Label(root, text="Tk's job!!",
005             width="30", height="5",
006             bg="black", fg="white")

這裡用了幾個參數 (parameter) ,文字標籤的文字 text"Tk's job!!" ,高 height5 ,寬 width30 ,背景顏色 bg"black" (黑色),前景文字顏色為 "white" (白色)。


來執行看看囉!結果如下



至於定位版面管理員 (place manager) 須自行定義每個元件在視窗的座標,這有點麻煩,我們替 Encrypt 類別設計的 GUI 打算用格子版面管理員 (grid manager) ,預計做出如下的 GUI



GUI 的建置放在類別中,每個元件都用實體屬性 (instance attribute) 建立,不難,純 GUI 外觀程式,也就是 V 的部分如下


001from tkinter import *
002from tkinter.ttk import * 
003
004# Encrypt 的 GUI 類別
005class EncryptGUI(Frame):
006   # 設定初值
007   def __init__(self, master=None):
008      Frame.__init__(self, master)
009      self.grid()
010      self.createWidgets()
011
012   # 建立所有視窗元件 
013   def createWidgets(self):
014      self.it = Label(self)
015      self.it["text"] = "Input:"
016      self.it.grid(row=0, column=0)
017      self.ifd = Entry(self)
018      self.ifd["width"] = 60
019      self.ifd.grid(row=0, column=1,
020                    columnspan=6)
021
022      self.ot = Label(self)
023      self.ot["text"] = "Output:"
024      self.ot.grid(row=1, column=0)
025      self.ofd = Entry(self)
026      self.ofd["width"] = 60
027      self.ofd.grid(row=1, column=1,
028                    columnspan=6)
029
030      self.nb = Button(self)
031      self.nb["text"] = "New"
032      self.nb.grid(row=2, column=0)
033      self.lb = Button(self)
034      self.lb["text"] = "Load"
035      self.lb.grid(row=2, column=1)
036      self.sb = Button(self)
037      self.sb["text"] = "Save"
038      self.sb.grid(row=2, column=2)
039      self.eb = Button(self)
040      self.eb["text"] = "Encode"
041      self.eb.grid(row=2, column=3)
042      self.db = Button(self)
043      self.db["text"] = "Decode"
044      self.db.grid(row=2, column=4)
045      self.db["command"] = self.dm
046      self.cb = Button(self)
047      self.cb["text"] = "Clear"
048      self.cb.grid(row=2, column=5)
049      self.cb2 = Button(self)
050      self.cb2["text"] = "Copy"
051      self.cb2.grid(row=2, column=6)
052 
053      self.dt = Label(self)
054      m = "something happened"
055      self.dt["text"] = m
056      self.dt.grid(row=3, column=0,
057                   columnspan=7)
058
059# GUI 執行部分 
060if __name__ == '__main__':
061   root = Tk()
062   app = EncryptGUI(master=root)
063   app.mainloop()
064 
065# 檔名: encrypt_gui.py 
066# 作者: Kaiching Chang 
067# 時間: July, 2014

EncryptGUI 部分屬性與方法的命名是基於排版需求,因此採取非常簡短的命名方式,例如 itinput text 的頭字母縮寫詞。

EncryptGUI 類別直接繼承 (inherit) tkinter 中的 Frame ,繼承的寫法是在類別識別字後面加上小括弧,小括弧裡為所要繼承的類別名稱,由於 Frame 為沒有元件的視窗,因此我們可以在 EncryptGUI 上加入需要的視窗元件


005class EncryptGUI(Frame):

__init__() 方法 (method) 需要預設參數 master ,這裡先呼叫 Frame__init__()


006# 設定初值
007def __init__(self, master=None):
008   Frame.__init__(self, master)
009   self.grid()
010   self.createWidgets()

注意下面呼叫 grid() ,這是繼承自 Frame 的方法,表示我們在這個 GUI 中要使用格子版面管理,最後呼叫的 createWidgets() 則是我們待會用來加入視窗元件的方法。


接下來呼叫 createWidgets() ,這個方法建立三個 Label 、兩個 Entry 及七個 Button ,全都設定好相關的格子,其中


019self.ifd.grid(row=0, column=1,
020              columnspan=6)

這樣的設定是 self.ifd 在從上數下來第 0 列,從左往右第 1 行,然後水平擴展 6 格,注意,第幾列第幾行都是從 0 開始數。


另外看到屬性用中括弧加字串設定,這些字串就是字典型態的 key 值


015self.it["text"] = "Input:"

類別中定義的屬性會自動轉換成字典,因此可直接由 key-value 的方式設定屬性值。


目前按鈕都沒有動作,因為我們還沒設定相關事件 (event) 處理,這要設定 command 囉!


中英文術語對照


MVC 模式Model-View-Controller
類別class
標準模組庫standard library
模組module
程式語言programming language
版面管理layout management
幾何版面管理員geometry manager
元件component
識別字identifier
包裹版面管理員pack manager
參數parameter
定位版面管理員place manager
格子版面管理員grid manager
實體屬性instance attribute

重點整理


  1. MVC 為軟體工程把模型、介面及控制分開的發展模式, M 就是就是處理資料的類別, V 為使用者介面的類別, C 則是控制 M 與 V 交流的類別。
  2. 標準模組庫直接整合 Tk ,首先認識有三種版面管理,分別是定位版面管理員、包裹版面管理員、格子版面管理員,其次要認識有哪些視窗元件。
  3. 包裹版面管理員適合視窗元件少的小程式使用。
  4. 定位版面管理員須自行定義每個視窗元件在視窗中的座標。
  5. 格子版面管理員將視窗分成格子,然後將視窗元件放入指定的格子中。


問題與討論


  1. 什麼是 MVC 模式?為什麼開發軟體要採用 MVC 模式?
  2. 試著比較 Tk 的三種幾何版面管理員的差別,找出常用軟體如記事本、瀏覽器等等,適合套用哪一種管理員?

練習


  1. 仿造 encrypt_gui.py ,寫一個新程式 tk_demo2.py ,裡頭設置一個 LabelButton
  2. 承接上一個單元的猜數字遊戲,將新程式寫在 guessgame_gui.py 中,替猜數字遊戲設計圖形介面,裡頭至少需要一個建立新遊戲的 New 按鈕及猜測用的 Guess 按鈕,另外需要一個顯示結果的 Label

the end

沒有留言: