Python 入門指南

單元 24 - C 的部分與設定 command

~~學習進度表~~

V 的部分已完成,現在進入到 C 的部分

EncryptView

M   V   C
↓      ↓
      Encrypt   EncryptController

這裡繼續回頭討論一下 V 的部分,其實絕大多數的 GUI 程式庫 (library) 在 V 的部分會改採 XML 或類似 XML 的方式設計,例如知名的 Qt 採 QML , JavaFX 採 FXML , Python 另一個知名的第三方程式庫 Kivy 用 KV language ,並且大都有圖形化的設計工具 (designer) ,用圖形化的設計工具製作 V 的部分就簡單多了,那問題來了, Tk 有沒有這方面的工具呢?

答案是有的,不過那是第三方團隊開發的工具,例如下圖為 Pygubu 的設計工具

Pygubu 為開放原始碼的軟體,其在 GitHub 的網址為 https://github.com/alejandroautalan/pygubu ,另一個知名第三方開發工具為 Page ,下載網址為 http://page.sourceforge.net/

Pygubu 就會產生副檔名 .ui 的 XML 檔案,以下為用文字編輯器 Atom 開啟上圖製作的 view.ui

至於如何下載安裝及在自己寫的 Python 程式載入使用 .ui 檔案,請參考其 GitHub 網頁的說明。

那為什麼我們不用 Pygubu 這樣的設計工具呢?原因其一為 Python 官方沒有納入,往後版本不見得會隨 Python 的版本同步更新,其二為需要額外介紹 Pygubu 的介面以及載入使用,這會額外加入其他的篇章,另外 Pygubu 直接用格子版面管理員,這雖然也是我們採用的版面管理員,可是網路上大量的 Tk 範例不見得會用格子版面管理員,因此有需要認識其他兩種版面管理員。

下面開始介紹 C 的部分,有新建開啟儲存編碼解碼清除拷貝等七個按鈕,這裡,先不急著實作七個按鈕的方法,因為每個方法都還牽涉到一些其他概念,我們改成先寫一個發展中的版本,讓每個按鈕都有功能,卻是相對簡化許多的功能。

簡單說,就是先讓按鈕做簡單的事情就好,那要什麼樣的事情呢?由於 self.dt 是提示訊息欄,本來按下按鈕就要顯示訊息到提示訊息欄,那我們就先讓按下按鈕顯示這是什麼按鈕就好了,這邊先提供 EncryptController 的開發中版本

from tkinter import Tk
from encrypt_view import EncryptView

# Encrypt 的 Controller 類別
class EncryptController:
  # 設定初值
  def __init__(self):
    self.app = EncryptView(master=Tk())
    self.app.nb["command"] = self.nm
    self.app.lb["command"] = self.lm
    self.app.sb["command"] = self.sm
    self.app.eb["command"] = self.em
    self.app.db["command"] = self.dm
    self.app.cb["command"] = self.cm
    self.app.cb2["command"] = self.cm2
    self.app.mainloop()

  # 按下新建按鈕的事件
  def nm(self):
    self.app.dt["text"] = "觸發新建按鈕的事件。"

  # 按下載入按鈕的事件
  def lm(self):
    self.app.dt["text"] = "觸發載入按鈕的事件。"

  # 按下儲存按鈕的事件
  def sm(self):
    self.app.dt["text"] = "觸發儲存按鈕的事件。"

  # 按下編碼按鈕的事件
  def em(self):
    self.app.dt["text"] = "觸發編碼按鈕的事件。"

  # 按下解碼按鈕的事件
  def dm(self):
    self.app.dt["text"] = "觸發解碼按鈕的事件。"

  # 按下清除按鈕的事件
  def cm(self):
    self.app.dt["text"] = "觸發清除按鈕的事件。"

  # 按下拷貝按鈕的事件
  def cm2(self):
    self.app.dt["text"] = "觸發拷貝按鈕的事件。"

# GUI 執行部分
if __name__ == '__main__':
  app = EncryptController()

#《程式語言教學誌》的範例程式
# https://kaiching.org/
# 檔名:encrypt_controller1.py
# 功能:示範 Python 的 Tk 應用程式
# 作者:張凱慶 */

請將 encrypt_controller1.py 放到跟 encrypt_view 相同的資料夾中。

先看到 import 的部分,這篇只引入兩個必要的類別 (class) ,分別是 TkEncryptView

from tkinter import Tk
from encrypt_view import EncryptView

這是因為視窗運作需要 Tk 應用程式物件,也需要替 EncryptController 設定 EncryptView 型態的屬性。

下面繼續看到 __init__() 方法的定義

# 設定初值
def __init__(self):
  self.app = EncryptView(master=Tk())
  self.app.nb["command"] = self.nm
  self.app.lb["command"] = self.lm
  self.app.sb["command"] = self.sm
  self.app.eb["command"] = self.em
  self.app.db["command"] = self.dm
  self.app.cb["command"] = self.cm
  self.app.cb2["command"] = self.cm2
  self.app.mainloop()

這裡屬性 appEncryptView 型態的物件,因此 app 就是一個 Frame

self.app = EncryptView(master=Tk())

然後透過 app 設定視窗元件的屬性 command ,例如這裡將 nbcommand 設定為 self.nm

self.app.nb["command"] = self.nm

self.nmEncryptController 的方法之一,其定義如下

def nm(self):
  self.app.dt["text"] = "觸發新建按鈕的事件。"

注意這樣的分別是視窗元件在 V 中設定,視窗元件相關事件連動的方法 (method) 則在 C 中設定。

其他的按鈕也依序設定, __init__() 方法最後呼叫 mainloop() ,好維持視窗顯示

self.app.mainloop()

之前的單元 mainloop() 方法都是由 Tk 應用程式物件 root 呼叫,這裡由繼承FrameEncryptView 型態的屬性 app 呼叫,其實兩者都可呼叫,因為 app 已經用 Tk 應用程式物件當參數建立。

來執行看看囉!以下是點擊編碼按鈕的結果

繼續點擊清除按鈕

結果都 ok !接下來,我們要開始整合 Encrypt 類別了。

中英文術語對照
類別class
設計工具designer
程式庫library
方法method
重點整理
1. Tk 的視窗元件由設定 command ,來指定與元件連動的方法。
2. 我們先寫一個發展中的版本,設定按下按鈕後在訊息欄顯示按鈕名稱。
問題與討論
1. 為什麼每個按鈕都要有專屬方法?可以幾個按鈕共用一個方法,或是某個按鈕不跟方法連結嗎?
2. 除了顯示按鈕名稱,還有什麼簡單事情是可以先給按鈕做的?
練習
1. 承接上一個單元的 hello_view2.py ,寫一個新程式 hello_controller.py ,替 HelloView 寫一個 HelloController 類別,當使用者按下按鈕後顯示 "按鈕被按"
2. 承接上一個單元的 game_view.py ,寫一個新程式 game_controller.py ,替 GameView 寫一個 GameController 類別,當使用者按下按鈕後顯示 "按鈕被按"

上一頁 單元 23 - Tk 的視窗元件與 V 的部分
回 Python 入門指南首頁
下一頁 單元 25 - 整合 Encrypt 類別
回 Python 教材首頁
回程式語言教材首頁