我想要向您介紹能想像到的開始 GUI 編程的最簡單方法,就是使用 Scriptics 的 TK 和 Tkinter 封裝器。我們將與 developerWorks 中的 “Python 中的 curses 編程” 提到的 curses 庫進行很多比較。除了 curses 實現文本控制臺而 TK 實現 GUI 這一差別之外,這兩個庫有著驚人相似的接口。在使用任何一個庫之前,需要基本了解窗口和事件循環,并參考可用的窗口小部件。(好,好的參考和適量的練習。)
如同關于 curses 的文章,本文僅討論 Tkinter 本身的特性。既然很多 Python 發行版都帶有 Tkinter,因此可能無需下載支持庫或其它 Python 模塊。本文后面的 參考資料 指向幾個更高級別的用戶接口窗口小部件的集合,但是您可以用 Tkinter 本身做許多事,包括構造自己的高級窗口小部件。學習基本 Tkinter 模塊將為您引入 TK 的思維方式,即使您繼續使用更高級的窗口小部件集合,這種思維方式仍十分重要。
TK 簡要描述
TK 是與 TCL 語言關系最密切、且被廣泛使用的圖形庫,TCL 語言和 TK 都由 John Ousterhout 開發。雖然 TK 于 1991 年作為 X11 庫出現,但實際上它從那時起就被移植到每一種流行的 GUI。(它與 Python 逐漸擁有“標準”GUI 的情形相似。)現在,大多數流行語言和很多小型語言都有 TK 綁定(Tkinter 模塊)。
在開始之前,我必須承認:我不是瘦小枯干的 TK 編程專家。事實上,我的大部分 TK 編程經驗大約從我寫這篇文章三天前才開始。那三天并非沒有挑戰,但是最后我覺得很好地掌握了 Tkinter。我在這里要說的是:TK 和 Tkinter 封裝器設計得都非常好,便于用戶操作,并且只是關于 GUI 編程最簡單的介紹。
從測試應用程序開始
我們將使用 Txt2Html,這個在以前很多專欄(請參閱 參考資料 )中使用的文件格式轉換程序,的封裝器作為測試應用程序。雖然可以用幾種方式運行 Txt2Html,但這里的封裝器卻要從命令行運行 Txt2Html。該應用程序以批處理進程的形式運行,并帶有指出要執行的轉換各方面特性的命令行自變量。(以后,最好為用戶提供交互式選擇屏幕選項,以在執行實際轉換之前引導用戶逐步選擇不同的轉換選項并提供所選選項的可視反饋。)
tk_txt2html 基于帶有下拉菜單和嵌套子菜單的頂部菜單。旁邊有詳細的實現說明,它看起來與在 “Python 中的 Curses 編程” 中討論的 curses 版本很相象。雖然 TK 用較少的代碼可以實現更多的功能,但很明顯,tk_txt2html 和 curses_txt2html 很相似。例如,在 TK 中,象菜單這樣的特性可以依靠內置的 Tkinter 類實現,而無需從頭編寫。
除了設置配置選項之外,TK 封裝器還包括一個與 TK Text 窗口小部件一起構建的滾動幫助框(一個帶有 Message 窗口小部件的“關于”框)和一個進行 TK 動態幾何管理的歷史窗口。與大多數交互式應用程序一樣,封裝器用 TK 的 Entry 窗口小部件接受某些用戶輸入。
在進一步討論代碼之前,讓我們看一下實際運行中的應用程序。
學習基本知識
實際上,Tkinter 程序只需做三件事:
最小的 [Tkinter] 程序
import Tkinter # import the Tkinter module root = Tkinter.Tk() # create a root window root.mainloop() # create an event loop
這是一個完全有效的 Tkinter 程序(不要介意它沒有實際用處,因為它甚至不管理 "hello world")。該程序唯一需要做的是創建一些容納其根窗口的窗口小部件。這樣增強之后,無需程序員進一步干涉,該程序的 root .mainloop() 方法調用就可以處理所有用戶交互。
main() 函數
現在,我們看一下 tk_txt2html.py 更現實的 main() 函數。請注意,我更喜歡使用 John Grayson 的 import Tkinter 語句,而不是 from Tkinter import (請參閱 參考資料 中所列的他的書籍)。這不是因為我擔心名稱空間的干擾( from ... import 語句的通常警告),而是因為我想明確使用 Tkinter 類;我不想冒險將它們與我自己的函數和類相混淆)。建議您也這樣做,至少在開始時這樣做。
tk_txt2html main() 函數
def main(): global root, history_frame, info_line root = Tkinter.Tk() root.title('Txt2Html TK Shell') init_vars() #-- Create the menu frame, and menus to the menu frame menu_frame = Tkinter.Frame(root) menu_frame.pack(fill=Tkinter.X, side=Tkinter.TOP) menu_frame.tk_menuBar(file_menu(), action_menu(), help_menu()) #-- Create the history frame (to be filled in during runtime) history_frame = Tkinter.Frame(root) history_frame.pack(fill=Tkinter.X, side=Tkinter.BOTTOM, pady=2) #-- Create the info frame and fill with initial contents info_frame = Tkinter.Frame(root) info_frame.pack(fill=Tkinter.X, side=Tkinter.BOTTOM) # first put the column labels in a sub-frame LEFT, Label = Tkinter.LEFT, Tkinter.Label # shortcut names label_line = Tkinter.Frame(info_frame, relief=Tkinter.RAISED, borderwidth=1) label_line.pack(side=Tkinter.TOP, padx=2, pady=1) Label(label_line, text="Run #", width=5).pack(side=LEFT) Label(label_line, text="Source:", width=20).pack(side=LEFT) Label(label_line, text="Target:", width=20).pack(side=LEFT) Label(label_line, text="Type:", width=20).pack(side=LEFT) Label(label_line, text="Proxy Mode:", width=20).pack(side=LEFT) # then put the "next run" information in a sub-frame info_line = Tkinter.Frame(info_frame) info_line.pack(side=Tkinter.TOP, padx=2, pady=1) update_specs() #-- Finally, let's actually do all that stuff created above root.mainloop()
在這個簡單的 main() 函數中有幾件事要注意:
??? 每一個窗口小部件都有一個父代。每當創建窗口小部件時,傳遞給實例創建的第一個自變量是新的窗口小部件的父代。
??? 如果有其它窗口小部件創建自變量,將通過名稱傳遞它們。Python 的這一特性給我們以指定選項或允許它們取缺省值的極大靈活性。
??? 有幾個窗口小部件實例 (Frame) 是全局變量??梢酝ㄟ^在函數間傳遞變量來使它們成為本地變量,以便維護代碼范圍的理論純潔性,但是與它的實際用處相比過于麻煩。另外,使這些基本的 UI 元素全局化強調了這樣一個事實:它們可以在整個函數中使用。但是,要確保對自己的全局變量使用良好的命名規范。(事先給您一個警告,Python 人員看起來討厭匈牙利符號。)
??? 創建完窗口小部件之后,我們調用一個幾何圖形管理器來讓 TK 知道在哪里放置窗口小部件。TK 在計算細節信息時有許多魔力,特別是當調整窗口大小或動態添加窗口小部件時更是如此。但是在任何情況下,都需要讓 TK 知道使用哪套咒語。
應用幾何圖形管理器
TK 提供三個幾何圖形管理器: .pack() 、 .grid() 和 .place() 。雖然 .place() 可用于精細(換句話說,非常復雜)的控制,但 tk_txt2html 只使用頭兩個。大多數時候,您將使用 .pack() 。
當然,可以不帶自變量來調用 .pack() 方法。但是如果那樣做,窗口小部件可能會在顯示屏幕的某處結束,您可能也想為 .pack() 提供一些提示。這些提示中最重要的是 side 自變量??赡艿闹凳?LEFT、RIGHT、TOP 和 BOTTOM(請注意這些是 Tkinter 名稱空間中的變量)。
.pack() 的許多魔力來自可以將窗口小部件嵌套這一事實。特別的,除了作為其它窗口小部件的容器(它有時顯示不同類型的邊界)之外,Frame 窗口小部件幾乎不作什么。這樣,就可以很方便地在所希望的方向排列幾個框架,然后在每個框架中添加其它窗口小部件。按照調用框架(以及其它窗口小部件)的 .pack() 方法的順序來排列它們。因此,如果兩個窗口小部件都請求 side=TOP ,則滿足先進入的請求。
tk_txt2html 還偶爾使用 .grid() 。grid 幾何圖形管理器用可視的坐標線覆蓋父代窗口小部件。當窗口小部件調用 .grid(row=3, column=4) 時,它請求其父代將它放在第三行第四列上。通過查看父代的所有子代的請求來計算父代的總行數和總列數。
別忘了對自己的窗口小部件應用幾何圖形管理器,以免在顯示屏幕上看不到它們時后悔莫及。
菜單
Tkinter 能輕易生成菜單。雖然我們在這里使用十分簡單的示例,但是如果愿意,還可以用不同的字體、圖形、復選框和各種別致的子代窗口小部件來填充菜單。在我們的示例中,tk_txt2html 的菜單全部用我們在上面所見的行創建。
menu_frame.tk_menuBar(file_menu(), action_menu(), help_menu())
這行本身可能有些神秘。大多數必須完成的工作位于名為 *_menu() 的函數中。讓我們看一下最簡單的示例。
創建下拉菜單
def help_menu(): help_btn = Tkinter.Menubutton(menu_frame, text='Help', underline=0) help_btn.pack(side=Tkinter.LEFT, padx="2m") help_btn.menu = Tkinter.Menu(help_btn) help_btn.menu.add_command(label="How To", underline=0, command=HowTo) help_btn.menu.add_command(label="About", underline=0, command=About) help_btn['menu'] = help_btn.menu return help_btn
下拉菜單是將 Menu 小窗口部件作為子代的 Menubutton 小窗口部件。 .pack() (或 .grid() 等)將 Menubutton 排列在適當的位置。Menu 小窗口部件用 .add_command() 方法添加項。(請注意上面為 Menubutton 的目錄所作的奇怪分配。不要問為什么,跟著我這樣做并在您自己的代碼中也這樣做即可。)
獲得用戶輸入
下面將看到的示例演示 Label 小窗口部件 widget 如何顯示輸入(有關 Text 和 Message 小窗口部件的某些示例的完整資源,請參閱 參考資料 )。字段輸入的基本小窗口部件是 Entry。它易于使用,但是如果以前曾使用過 Python 的 raw_input() 或 curses 的 .getstr() ,您將發現技巧略有不同。TK 的 Entry 小窗口部件不返回可分配的值。相反,它獲取自變量來填充字段對象。例如,下面的函數允許用戶指定輸入文件。
接受用戶字段輸入
def GetSource(): get_window = Tkinter.Toplevel(root) get_window.title('Source File?') Tkinter.Entry(get_window, width=30, textvariable=source).pack() Tkinter.Button(get_window, text="Change", command=lambda: update_specs()).pack()
這里有幾件事要注意。我們為這個輸入創建了一個新的 Toplevel 小窗口部件和對話框,并且通過創建一個帶有 textvariable 自變量的 Entry 小窗口部件指定了輸入字段。但是等一下,還有件事!
textvariable 自變量沒有指定簡單的字符串變量。相反,它引用一個 StringVar 對象。在我們的示例中,從 main() 調用的 init_vars() 函數包含三行。
source = Tkinter.StringVar() source.set('txt2html.txt')
這創建了一個適用于用戶輸入的對象并為其分配了初始值。每次在與之相鏈接的 Entry 小窗口部件中進行更改時都立即修改該對象。每次在 Entry 小窗口部件中擊鍵、而不是讀取終止時,都進行 raw_input() 樣式的更改。
要想獲得戶輸入的值,我們使用 StringVar 實例的 .get() 方法。例如:
source_string = source.get()
結束語
此處所略述的技巧以及我們在完整的應用程序源代碼中使用的技巧應該足以使您開始進行 Tkinter 編程了。略微實踐之后您就會發現它不難掌握。有一個好處是:可以通過 Python 以外的很多語言訪問 TK 庫,因此您使用 Python 的 Tkinter 模塊學到的大多數知識可以應用到其它語言。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
