2016年11月24日 星期四

[轉貼]Python 的內建 GUI 模組 Tkinter 測試 (一) : 建立視窗

Python 的內建 GUI 模組 Tkinter 測試 (一) : 建立視窗

最近因為玩樹苺派的關係, 接觸到 Python 內建的 GUI 開發模組 Tkinter (意思是 Tk Interface), 初步覺得比用 Java 的 Swing 還要來得容易, 因此就來學看看唄!
Tk 原先是為 Tcl 語言所開發的 GUI 套件, 因為是 Tcl 的第一個擴充, 所以現在都合起來稱呼為 Tcl/Tk. Tcl 是一種以 string-based 的跨平台工具命令式直譯語言 (Tool command language), 繼承了 LISP/C/Shell 等語言的優點, 並具有語法簡單, 容易擴展與可靈活嵌入其他語言的特點, 而且全面支持 unicode.
而 Tkinter 是 Python 內建的標準模組, 內部嵌入了 Tcl/Tk GUI 套件, 用來在 Python 中建構 GUI 圖形介面程式, 它具有如下優點 :

  1. 簡單易學 :
    比 Python 其他 GUI 要容易, 甚至於我覺得比學 Java Swing 還容易.
  2. 程式碼精簡 :
    以很短的程式碼便能產生強大功能的 GUI 程式.
  3. 跨平台 :
    同樣的程式可以在 Linux/Windows/Mac 等系統上執行.
不過在 Python 2 中的模組名稱 Tkinter 到 Python 3 版後已被改為小寫的 tkinter, 使用時要注意所用之 Python 版本, 匯入時注意該用首字大寫與否. 不過 Python 2 下以 Tkinter 所寫的 GUI 程式可以利用 2to3 程式轉成 Python 3 版的程式, 詳見 :
# https://docs.python.org/2/library/tkinter.html

在 Python 中使用 Tkinter 需先匯入模組 : 

import Tkinter

from Tkinter import *
但書裡說不建議使用後面這種方式, 因為此方法是匯入 Tkinter 所有函數與屬性, 這樣會占用較多記憶體, 而且容易讓命名空間混淆, 並使得 debug 難度增加. 不過, 這種方式使用起來較方便.
比較常見的匯入方式是幫 Tkinter 取個別名 :
import Tkinter as tk
注意, 在 Python 3 要用小寫的 tkinter.
這樣就能使用 tk 這個別名來呼叫此模組內的函數 :
tk.函數名稱()
匯入模組就可以呼叫 Tk() 函數建立一個視窗實體 :
import Tkinter as tk
win=tk.Tk()
如果沒有取別名, 就要用 Tkinter.Tk() :
import Tkinter
win=Tkinter.Tk()
然後呼叫此視窗實體之 mainloop() 函數將此視窗加入事件監視迴圈, 這樣會產生 GUI 視窗了.
win.mainloop()
真是太簡單了, 建立一個空視窗只要三行程式碼!
如果用 Java Swing 來產生一個空視窗, 至少需要 10 行以上的程式碼 :
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class JFrame1 {
  JFrame f;
  public static void main(String argv[]) {
    new JFrame1();
    }
  public JFrame1() {
    f=new JFrame("JFrame 1");
    f.setBounds(0,0,400,300);
    f.setVisible(true);
    }
  }
這就是為什麼 Tkinter 非常適合用來快速開發 GUI 程式的原因.
在 IDLE 介面輸入此三行程式, 馬上就建立一個視窗了 :
Python 2.7.6 (default, Nov 10 2013, 19:24:18) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import Tkinter as tk
>>> win=tk.Tk()
>>> win.mainloop()

注意, 視窗描繪是在加入事件監視函數 mainloop() 後才發生的唷!
此空視窗右上角有標準視窗的縮小, 放大, 以及關閉按鈕, 還可以拖曳調整視窗大小. 預設標題 (title) 是 "tk", 我們可以呼叫 title 函數來加以設定 :
>>> import Tkinter as tk
win=tk.Tk()
win.title("My First Tk GUI")
win.mainloop()

如果不想讓使用者調整視窗大小, 可以呼叫 resizable(0, 0) 來禁止 :
import Tkinter as tk
win=tk.Tk()
win.title("My First Tk GUI")
win.resizable(0,0)
win.mainloop()

這樣不僅無法拖曳縮放視窗, 右上角的放大按鈕也被禁能了, 只能縮小與關閉. 此 resizable() 函數事實上是設定視窗左上角座標, 如果設為 0,0 表示無法縮放.
Tkinter 提供了下列 21 種 GUI 元件 (稱為 widget 或 control)  :

  1. Label
  2. Button
  3. Radiobutton
  4. Checkbutton
  5. Entry
  6. Frame
  7. LabelFrame
  8. Listbox
  9. Text
  10. Message
  11. PanedWindow
  12. Scrollbar
  13. Scale
  14. Spinbox
  15. Menu
  16. OptionMenu
  17. Menubutton
  18. Canvas
  19. Image
  20. Bitmap
  21. Toplevel
下面來測試其中的 Button 與 Label 元件, 把下列程式用記事本存成 test.py :
import Tkinter as tk
win=tk.Tk()     #建立視窗容器物件
win.title("Tk GUI")
label=tk.Label(win, text="Hello World!")   #建立標籤物件
label.pack()       #顯示元件
button=tk.Button(win, text="OK")
button.pack()     #顯示元件
win.mainloop()

然後在命令提示字元視窗執行 :
D:\Python\test>python test.py

可見在呼叫 pack() 函數做版面管理的話, Tkinter 會將元件由上而下水平置中順序排版.
注意, 在建立 GUI 元件時, 第一個參數必須為其容器物件 (此處為 win 變數所代表之視窗), 格式如下 :
元件變數=元件名稱(容器物件變數, [元件選項])
其他為元件之選項參數, 例如標籤文字 (text), 大小(size), 邊框 (border), 前景顏色 (foreground), 或背景顏色 (background), 每一種元件可能有不同之選項, 可以在建立元件時直接設定, 也可以在建立之後呼叫 configure() 函數或其別名 config() 來設定.
其次, 所建立的元件必須利用版面管理員 (geometry manager) 於視窗容器中定位, 這樣它才會在視窗中顯現, 而此 pack() 函數就是一個版面管理員, 它會由上而下擺放元件.
如果是使用 from Tkinter import * 匯入方式, 上面程式的寫法要改為 :
from Tkinter import *
win=Tk()
win.title("Tk GUI")
label=Label(win, text="Hello World!")
button=Button(win, text="OK")
label.pack()
button.pack()
win.mainloop()
ㄟ, 這個寫法好像比較簡單哩! 不用寫 tk.
除了由上而下的 pack() 版面管理員外, 也可以呼叫 grid() 函數使用網格版面來管理元件 :
from Tkinter import *
win=Tk()
win.title("Tk GUI")
label=Label(win, text="Hello World!")
button=Button(win, text="OK")
label.grid(column=0,row=0)
button.grid(column=1,row=0)
win.mainloop()

在呼叫 grid() 時, 我們將 label 放在 0 行 0 列, button 放在 1 行 0 列, 亦即這是一個 1x2 (1 列 2 行) 的網格, 因此元件是左右各排一個.
目前這個按鈕按下去不會有反應, 因為我們還沒有幫它設定事件處理函數. 在下面範例中, 我定義了一個按鈕事件處理函數 clickOK() 來處理 OK 鍵被按事件, 並設定了一個全域變數 count 來記錄 OK 鍵被按了幾次, 當 OK 被按時, count 會增量, 並且透過呼叫元件的 configure() 或 config() 函數來更改標籤元件的文字內容 (text 屬性) :
from Tkinter import *
win=Tk()
win.title("Tk GUI")
label=Label(win, text="Hello World!")
count=0
def clickOK():
    global count
    count=count + 1
    label.configure(text="Click OK " + str(count) + " times")
button=Button(win, text="OK", command=clickOK)
label.pack()
button.pack()
win.mainloop()

注意, 事件處理函數與 GUI 元件之間是透過 command 參數來綁定的. 另外上面也用到了 Python 內建函數 str() 來將數值類型的 count 轉成字串類型.
上面範例中的按鈕看起來似乎不是那麼好看, 所以 Tkinter 後來又推出了加強版的 ttk 模組來美化元件的外觀, 這 ttk 意思是 Themed Tkinter, 亦即主題化版本, 參考 :
# https://docs.python.org/2/library/ttk.html

此 ttk 模組包含了 17 種元件, 其中的 11 種是 Tkinter 原本已經有的 :

  1. Label
  2. Button
  3. Radiobutton
  4. Checkbutton
  5. Entry
  6. Frame
  7. Labelframe
  8. Menubutton
  9. Scale
  10. Scrollbar
  11. Panedwindow

另外 6 個是 ttk 推出的新元件 :

  1. Combobox
  2. Notebook
  3. Progressbar
  4. Separator
  5. Sizegrip
  6. Treeview

Ttk 的使用方法與 Tkinter 完全一樣, 使用前須先匯入此模組 :

from Tkinter import ttk

from ttk import *
將上面範例用 ttk 改寫如下 :
from Tkinter import *
from ttk import *
win=Tk()
win.title("ttk GUI")
label=Label(win, text="Hello World!")
count=0
def clickOK():
    global count
    count=count + 1
    label.config(text="Click OK " + str(count) + " times")
button=Button(win, text="OK", command=clickOK)
label.pack()
button.pack()
win.mainloop()

可見按鈕真的變漂亮了!
Tk 模組有一個測試函數 _test(), 會顯示一個內建的測試視窗 :
Python 2.7.8 (default, Jun 30 2014, 16:03:49) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> from Tkinter import *
Tkinter._test()

我這是在 Python 2.7.6 上測試的, Tk 的版本是 8.5, 如果在 Python 3 上測試, Tk 版本應該是 8.6 版以上. 8.6 版在功能上比起 8.5 有大幅改進.
呼叫 Tcl().eval() 函數可以查詢 Tk 版本 :
>>> Tkinter.Tcl().eval('info patchlevel')
'8.5.15'
用 Tkinter 模組寫好的 Python 程式可以用 py2exe 模組轉成 Windows 執行檔, 參考 :
# 將python轉成執行檔(py2exe)
# http://www.py2exe.org/index.cgi/Tutorial
# Tk : Dialog window
其他參考 :
# 傻貓布落格 : Tkinter
# TutorialsPoint
# An Introduction to Tkinter
# Thinking in Tkinter

網頁來源:http://yhhuang1966.blogspot.tw/2016/05/python-gui-tkinter.html

沒有留言: