我想要向您介紹能想像到的開(kāi)始 GUI 編程的最簡(jiǎn)單方法,就是使用 Scriptics 的 TK 和 Tkinter 封裝器。我們將與 developerWorks 中的 “Python 中的 curses 編程” 提到的 curses 庫(kù)進(jìn)行很多比較。除了 curses 實(shí)現(xiàn)文本控制臺(tái)而 TK 實(shí)現(xiàn) GUI 這一差別之外,這兩個(gè)庫(kù)有著驚人相似的接口。在使用任何一個(gè)庫(kù)之前,需要基本了解窗口和事件循環(huán),并參考可用的窗口小部件。(好,好的參考和適量的練習(xí)。)
如同關(guān)于 curses 的文章,本文僅討論 Tkinter 本身的特性。既然很多 Python 發(fā)行版都帶有 Tkinter,因此可能無(wú)需下載支持庫(kù)或其它 Python 模塊。本文后面的 參考資料 指向幾個(gè)更高級(jí)別的用戶(hù)接口窗口小部件的集合,但是您可以用 Tkinter 本身做許多事,包括構(gòu)造自己的高級(jí)窗口小部件。學(xué)習(xí)基本 Tkinter 模塊將為您引入 TK 的思維方式,即使您繼續(xù)使用更高級(jí)的窗口小部件集合,這種思維方式仍十分重要。
TK 簡(jiǎn)要描述
TK 是與 TCL 語(yǔ)言關(guān)系最密切、且被廣泛使用的圖形庫(kù),TCL 語(yǔ)言和 TK 都由 John Ousterhout 開(kāi)發(fā)。雖然 TK 于 1991 年作為 X11 庫(kù)出現(xiàn),但實(shí)際上它從那時(shí)起就被移植到每一種流行的 GUI。(它與 Python 逐漸擁有“標(biāo)準(zhǔn)”GUI 的情形相似。)現(xiàn)在,大多數(shù)流行語(yǔ)言和很多小型語(yǔ)言都有 TK 綁定(Tkinter 模塊)。
在開(kāi)始之前,我必須承認(rèn):我不是瘦小枯干的 TK 編程專(zhuān)家。事實(shí)上,我的大部分 TK 編程經(jīng)驗(yàn)大約從我寫(xiě)這篇文章三天前才開(kāi)始。那三天并非沒(méi)有挑戰(zhàn),但是最后我覺(jué)得很好地掌握了 Tkinter。我在這里要說(shuō)的是:TK 和 Tkinter 封裝器設(shè)計(jì)得都非常好,便于用戶(hù)操作,并且只是關(guān)于 GUI 編程最簡(jiǎn)單的介紹。
從測(cè)試應(yīng)用程序開(kāi)始
我們將使用 Txt2Html,這個(gè)在以前很多專(zhuān)欄(請(qǐng)參閱 參考資料 )中使用的文件格式轉(zhuǎn)換程序,的封裝器作為測(cè)試應(yīng)用程序。雖然可以用幾種方式運(yùn)行 Txt2Html,但這里的封裝器卻要從命令行運(yùn)行 Txt2Html。該應(yīng)用程序以批處理進(jìn)程的形式運(yùn)行,并帶有指出要執(zhí)行的轉(zhuǎn)換各方面特性的命令行自變量。(以后,最好為用戶(hù)提供交互式選擇屏幕選項(xiàng),以在執(zhí)行實(shí)際轉(zhuǎn)換之前引導(dǎo)用戶(hù)逐步選擇不同的轉(zhuǎn)換選項(xiàng)并提供所選選項(xiàng)的可視反饋。)
tk_txt2html 基于帶有下拉菜單和嵌套子菜單的頂部菜單。旁邊有詳細(xì)的實(shí)現(xiàn)說(shuō)明,它看起來(lái)與在 “Python 中的 Curses 編程” 中討論的 curses 版本很相象。雖然 TK 用較少的代碼可以實(shí)現(xiàn)更多的功能,但很明顯,tk_txt2html 和 curses_txt2html 很相似。例如,在 TK 中,象菜單這樣的特性可以依靠?jī)?nèi)置的 Tkinter 類(lèi)實(shí)現(xiàn),而無(wú)需從頭編寫(xiě)。
除了設(shè)置配置選項(xiàng)之外,TK 封裝器還包括一個(gè)與 TK Text 窗口小部件一起構(gòu)建的滾動(dòng)幫助框(一個(gè)帶有 Message 窗口小部件的“關(guān)于”框)和一個(gè)進(jìn)行 TK 動(dòng)態(tài)幾何管理的歷史窗口。與大多數(shù)交互式應(yīng)用程序一樣,封裝器用 TK 的 Entry 窗口小部件接受某些用戶(hù)輸入。
在進(jìn)一步討論代碼之前,讓我們看一下實(shí)際運(yùn)行中的應(yīng)用程序。
學(xué)習(xí)基本知識(shí)
實(shí)際上,Tkinter 程序只需做三件事:
最小的 [Tkinter] 程序
import Tkinter # import the Tkinter module root = Tkinter.Tk() # create a root window root.mainloop() # create an event loop
這是一個(gè)完全有效的 Tkinter 程序(不要介意它沒(méi)有實(shí)際用處,因?yàn)樗踔敛还芾?"hello world")。該程序唯一需要做的是創(chuàng)建一些容納其根窗口的窗口小部件。這樣增強(qiáng)之后,無(wú)需程序員進(jìn)一步干涉,該程序的 root .mainloop() 方法調(diào)用就可以處理所有用戶(hù)交互。
main() 函數(shù)
現(xiàn)在,我們看一下 tk_txt2html.py 更現(xiàn)實(shí)的 main() 函數(shù)。請(qǐng)注意,我更喜歡使用 John Grayson 的 import Tkinter 語(yǔ)句,而不是 from Tkinter import (請(qǐng)參閱 參考資料 中所列的他的書(shū)籍)。這不是因?yàn)槲覔?dān)心名稱(chēng)空間的干擾( from ... import 語(yǔ)句的通常警告),而是因?yàn)槲蚁朊鞔_使用 Tkinter 類(lèi);我不想冒險(xiǎn)將它們與我自己的函數(shù)和類(lèi)相混淆)。建議您也這樣做,至少在開(kāi)始時(shí)這樣做。
tk_txt2html main() 函數(shù)
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()
在這個(gè)簡(jiǎn)單的 main() 函數(shù)中有幾件事要注意:
??? 每一個(gè)窗口小部件都有一個(gè)父代。每當(dāng)創(chuàng)建窗口小部件時(shí),傳遞給實(shí)例創(chuàng)建的第一個(gè)自變量是新的窗口小部件的父代。
??? 如果有其它窗口小部件創(chuàng)建自變量,將通過(guò)名稱(chēng)傳遞它們。Python 的這一特性給我們以指定選項(xiàng)或允許它們?nèi)∪笔≈档臉O大靈活性。
??? 有幾個(gè)窗口小部件實(shí)例 (Frame) 是全局變量??梢酝ㄟ^(guò)在函數(shù)間傳遞變量來(lái)使它們成為本地變量,以便維護(hù)代碼范圍的理論純潔性,但是與它的實(shí)際用處相比過(guò)于麻煩。另外,使這些基本的 UI 元素全局化強(qiáng)調(diào)了這樣一個(gè)事實(shí):它們可以在整個(gè)函數(shù)中使用。但是,要確保對(duì)自己的全局變量使用良好的命名規(guī)范。(事先給您一個(gè)警告,Python 人員看起來(lái)討厭匈牙利符號(hào)。)
??? 創(chuàng)建完窗口小部件之后,我們調(diào)用一個(gè)幾何圖形管理器來(lái)讓 TK 知道在哪里放置窗口小部件。TK 在計(jì)算細(xì)節(jié)信息時(shí)有許多魔力,特別是當(dāng)調(diào)整窗口大小或動(dòng)態(tài)添加窗口小部件時(shí)更是如此。但是在任何情況下,都需要讓 TK 知道使用哪套咒語(yǔ)。
應(yīng)用幾何圖形管理器
TK 提供三個(gè)幾何圖形管理器: .pack() 、 .grid() 和 .place() 。雖然 .place() 可用于精細(xì)(換句話(huà)說(shuō),非常復(fù)雜)的控制,但 tk_txt2html 只使用頭兩個(gè)。大多數(shù)時(shí)候,您將使用 .pack() 。
當(dāng)然,可以不帶自變量來(lái)調(diào)用 .pack() 方法。但是如果那樣做,窗口小部件可能會(huì)在顯示屏幕的某處結(jié)束,您可能也想為 .pack() 提供一些提示。這些提示中最重要的是 side 自變量??赡艿闹凳?LEFT、RIGHT、TOP 和 BOTTOM(請(qǐng)注意這些是 Tkinter 名稱(chēng)空間中的變量)。
.pack() 的許多魔力來(lái)自可以將窗口小部件嵌套這一事實(shí)。特別的,除了作為其它窗口小部件的容器(它有時(shí)顯示不同類(lèi)型的邊界)之外,F(xiàn)rame 窗口小部件幾乎不作什么。這樣,就可以很方便地在所希望的方向排列幾個(gè)框架,然后在每個(gè)框架中添加其它窗口小部件。按照調(diào)用框架(以及其它窗口小部件)的 .pack() 方法的順序來(lái)排列它們。因此,如果兩個(gè)窗口小部件都請(qǐng)求 side=TOP ,則滿(mǎn)足先進(jìn)入的請(qǐng)求。
tk_txt2html 還偶爾使用 .grid() 。grid 幾何圖形管理器用可視的坐標(biāo)線(xiàn)覆蓋父代窗口小部件。當(dāng)窗口小部件調(diào)用 .grid(row=3, column=4) 時(shí),它請(qǐng)求其父代將它放在第三行第四列上。通過(guò)查看父代的所有子代的請(qǐng)求來(lái)計(jì)算父代的總行數(shù)和總列數(shù)。
別忘了對(duì)自己的窗口小部件應(yīng)用幾何圖形管理器,以免在顯示屏幕上看不到它們時(shí)后悔莫及。
菜單
Tkinter 能輕易生成菜單。雖然我們?cè)谶@里使用十分簡(jiǎn)單的示例,但是如果愿意,還可以用不同的字體、圖形、復(fù)選框和各種別致的子代窗口小部件來(lái)填充菜單。在我們的示例中,tk_txt2html 的菜單全部用我們?cè)谏厦嫠?jiàn)的行創(chuàng)建。
menu_frame.tk_menuBar(file_menu(), action_menu(), help_menu())
這行本身可能有些神秘。大多數(shù)必須完成的工作位于名為 *_menu() 的函數(shù)中。讓我們看一下最簡(jiǎn)單的示例。
創(chuàng)建下拉菜單
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 排列在適當(dāng)?shù)奈恢谩enu 小窗口部件用 .add_command() 方法添加項(xiàng)。(請(qǐng)注意上面為 Menubutton 的目錄所作的奇怪分配。不要問(wèn)為什么,跟著我這樣做并在您自己的代碼中也這樣做即可。)
獲得用戶(hù)輸入
下面將看到的示例演示 Label 小窗口部件 widget 如何顯示輸入(有關(guān) Text 和 Message 小窗口部件的某些示例的完整資源,請(qǐng)參閱 參考資料 )。字段輸入的基本小窗口部件是 Entry。它易于使用,但是如果以前曾使用過(guò) Python 的 raw_input() 或 curses 的 .getstr() ,您將發(fā)現(xiàn)技巧略有不同。TK 的 Entry 小窗口部件不返回可分配的值。相反,它獲取自變量來(lái)填充字段對(duì)象。例如,下面的函數(shù)允許用戶(hù)指定輸入文件。
接受用戶(hù)字段輸入
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()
這里有幾件事要注意。我們?yōu)檫@個(gè)輸入創(chuàng)建了一個(gè)新的 Toplevel 小窗口部件和對(duì)話(huà)框,并且通過(guò)創(chuàng)建一個(gè)帶有 textvariable 自變量的 Entry 小窗口部件指定了輸入字段。但是等一下,還有件事!
textvariable 自變量沒(méi)有指定簡(jiǎn)單的字符串變量。相反,它引用一個(gè) StringVar 對(duì)象。在我們的示例中,從 main() 調(diào)用的 init_vars() 函數(shù)包含三行。
source = Tkinter.StringVar() source.set('txt2html.txt')
這創(chuàng)建了一個(gè)適用于用戶(hù)輸入的對(duì)象并為其分配了初始值。每次在與之相鏈接的 Entry 小窗口部件中進(jìn)行更改時(shí)都立即修改該對(duì)象。每次在 Entry 小窗口部件中擊鍵、而不是讀取終止時(shí),都進(jìn)行 raw_input() 樣式的更改。
要想獲得戶(hù)輸入的值,我們使用 StringVar 實(shí)例的 .get() 方法。例如:
source_string = source.get()
結(jié)束語(yǔ)
此處所略述的技巧以及我們?cè)谕暾膽?yīng)用程序源代碼中使用的技巧應(yīng)該足以使您開(kāi)始進(jìn)行 Tkinter 編程了。略微實(shí)踐之后您就會(huì)發(fā)現(xiàn)它不難掌握。有一個(gè)好處是:可以通過(guò) Python 以外的很多語(yǔ)言訪(fǎng)問(wèn) TK 庫(kù),因此您使用 Python 的 Tkinter 模塊學(xué)到的大多數(shù)知識(shí)可以應(yīng)用到其它語(yǔ)言。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

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