不管常規(guī)方法如何,我們先嘗試一個(gè)輕巧并且很實(shí)用的小例子。它會(huì)讓你體會(huì)到在Tk程序背后看起來(lái)的最初感覺(jué)。 設(shè)計(jì) 我們要是用的例子是一個(gè)簡(jiǎn)單的GUI工具,用來(lái)將一個(gè)單位為英尺的數(shù)字轉(zhuǎn)換為與其相等的米制單位數(shù)字。如果我們將其簡(jiǎn)單的描繪出來(lái),應(yīng)該看起來(lái)如下: 看起來(lái)我們需要一個(gè)短小的文本輸入部件,可以讓我們輸入一個(gè)英尺單位的數(shù)字,同時(shí)還有一個(gè)‘Calculate’按鈕,用它獲取輸入的英尺數(shù)字,執(zhí)行計(jì)算,然后把計(jì)算后的米制數(shù)字結(jié)果放在位于輸入文本框下面的位置。當(dāng)然,我們還需要三個(gè)靜態(tài)的標(biāo)簽(”Feet “,“is equivalent to”和"Meters")幫助我們指出如何使用界面。 下面是創(chuàng)建該程序的Python代碼: from tkinter import * from tkinter import ttk def calculate(*args): try: value = float(feet.get()) meters.set((0.3048 * value * 10000.0 + 0.5)/10000.0) except ValueError: pass root = Tk() root.title("Feet to Meters") mainframe = ttk.Frame(root, padding="3 3 12 12") mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) mainframe.columnconfigure(0, weight=1) mainframe.rowconfigure(0, weight=1) feet = StringVar() meters = StringVar() feet_entry = ttk.Entry(mainframe, width=7, textvariable=feet) feet_entry.grid(column=2, row=1, sticky=(W, E)) ttk.Label(mainframe, textvariable=meters).grid(column=2, row=2, sticky=(W, E)) ttk.Button(mainframe, text="Calculate", command=calculate).grid(column=3, row=3, sticky=W) ttk.Label(mainframe, text="feet").grid(column=3, row=1, sticky=W) ttk.Label(mainframe, text="is equivalent to").grid(column=1, row=2, sticky=E) ttk.Label(mainframe, text="meters").grid(column=3, row=2, sticky=W) for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5) feet_entry.focus() root.bind('<Return>', calculate) root.mainloop() 最終的用戶界面: 每一種語(yǔ)言,包括這部教程里的,都有多種可供選擇編寫風(fēng)格與習(xí)慣約定,幫助我們決定如何使用變量和函數(shù)命名,過(guò)程、函數(shù)化或面向?qū)ο箫L(fēng)格等等。 由于這本教程關(guān)注的是Tk,在這里將盡可能簡(jiǎn)單,一般會(huì)使用非常直接的代碼風(fēng)格,而不是專注于我們的代碼在過(guò)程、模塊、對(duì)象、類等方面??赡芏嗟氖牵銓?huì)看到在很多例子里會(huì)有相同的對(duì)象名、變量名等。 逐步解說(shuō) from tkinter import * from tkinter import ttk 這兩行告訴Python我們的程序需要兩個(gè)模塊。第一個(gè),'tkinter',是一個(gè)綁定Tk的標(biāo)準(zhǔn)模塊,當(dāng)它被加載時(shí)就會(huì)讓你的系統(tǒng)中存在的Tk庫(kù)被載入。第二個(gè),“ttk”,是Python與Tk8.5版本中新增部件的綁定。(按鈕,文本框之類的都叫做部件) 提示:需要注意的是,我們已經(jīng)從tkinter模塊中導(dǎo)入了全部,所以我們能調(diào)用tkinter函數(shù),而不需要前綴,前綴是標(biāo)準(zhǔn)調(diào)用方式。盡管如此,由于我們只導(dǎo)入了ttk本身,也就是說(shuō)我們需要在調(diào)用ttk內(nèi)部函數(shù)的時(shí)候添加前綴。比如,調(diào)用‘Entry(...)’函數(shù),將引用tkinter模塊你的函數(shù),而只有用'ttk.Entry(...)'才能引用ttk模塊內(nèi)函數(shù)。正如你將要看到的,有些函數(shù)在這兩個(gè)模塊里都被定義了,并且有些時(shí)候你可能兩者都需要。依據(jù)內(nèi)容的不同,在這里使用ttk調(diào)用設(shè)備,并且在教程中使用該風(fēng)格。 更新:如果你正在從老版本代碼遷移到新版本代碼,你會(huì)發(fā)現(xiàn)一件事情,就是Tkinter模塊的名字都變?yōu)樾懥?。例如,“tkinter”而不再是"Tkinter",這是從Python3.0開(kāi)始改變的。 root = Tk() root.title("Feet to Meters") mainframe = ttk.Frame(root, padding="3 3 12 12") mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) mainframe.columnconfigure(0, weight=1) mainframe.rowconfigure(0, weight=1) 僅供參考:是的,“Calculate”函數(shù)出現(xiàn)在之前。我們將在后面進(jìn)行描述,但是程序里在靠近開(kāi)始的時(shí)候要包括它,因?yàn)槠渌绦蛐枰谩?/b> 以上幾行代碼建立了主窗口,給定名稱為“Feet to Meters”。緊接著,創(chuàng)建了一個(gè)框架部件,用來(lái)控制我們用戶界面上所有控件,并把它們放在主窗口上。“columnconfigure”/“rowconfigure”僅僅是告訴Tk,如果主窗口改變大小,框架應(yīng)該擴(kuò)展到其余空間。 僅供參考:嚴(yán)格的說(shuō),我們可以將界面其他部分直接放如主窗口中,而不需要框架的干預(yù)。盡管如此,主窗口其本身并非是“方案”部件的一部分,所以它的背景色并不能與我們將要放入其中的方案部件相匹配,使用一個(gè)“方案”框架部件來(lái)控制內(nèi)容,保證背景色的正確。 feet = StringVar() meters = StringVar() feet_entry = ttk.Entry(mainframe, width=7, textvariable=feet) feet_entry.grid(column=2, row=1, sticky=(W, E)) ttk.Label(mainframe, textvariable=meters).grid(column=2, row=2, sticky=(W, E)) ttk.Button(mainframe, text="Calculate", command=calculate).grid(column=3, row=3, sticky=W) 以上代碼創(chuàng)建了我們程序的主要三個(gè)控件:一個(gè)用于輸入英尺數(shù)字的輸入框,一個(gè)放置米制結(jié)果數(shù)字的label標(biāo)簽,以及一個(gè)執(zhí)行計(jì)算從操作的按鈕。 對(duì)于這三個(gè)控件的每一個(gè)來(lái)說(shuō),我們需要做兩件事情:創(chuàng)建控件本身,和將它們放入界面中。我們創(chuàng)建的內(nèi)容窗口是作為Tk方案部件類的實(shí)例,這三個(gè)控件是這個(gè)實(shí)例的“孩子”,(這里應(yīng)該翻譯為子類)。在創(chuàng)建它們的同時(shí),我們給定它們確定的選項(xiàng)。例如,輸入框的寬度,按鈕上的文本等等。輸入框和標(biāo)簽都被拾取為一個(gè)神奇的“textvariable(文本變量)”,一會(huì)兒我們將看到是怎么做的。 如果這些控件被創(chuàng)建,它們不會(huì)自動(dòng)顯示在界面上,因?yàn)門k不知道相對(duì)于其他控件,你想把它們?nèi)绾畏胖谩_@就是“grid”部分做的事情。記住我們應(yīng)用程序的布局網(wǎng)格,我們放置控件在不同的列(1,2或者3)和行(1、2和3)。“sticky”選項(xiàng)說(shuō)明了通過(guò)使用羅盤是方位,控件將要如何在網(wǎng)格單元里排放,例如“w”(west)意思是定位控件到單元格的左邊,“we”(west-east)意思是定位控件從左邊到右邊(相當(dāng)于控件從左到右占滿)等等。 ttk.Label(mainframe, text="feet").grid(column=3, row=1, sticky=W) ttk.Label(mainframe, text="is equivalent to").grid(column=1, row=2, sticky=E) ttk.Label(mainframe, text="meters").grid(column=3, row=2, sticky=W) 上面三行代碼對(duì)我們界面中的三個(gè)靜態(tài)文本標(biāo)簽控件準(zhǔn)確的做了相同的事情:創(chuàng)建了它們,并放置在界面上合適的網(wǎng)格位置上。 for child in mainframe.winfo_children(): child.grid_configure(padx=5, pady=5) feet_entry.focus() root.bind('<Return>', calculate) 上面三行代碼讓我們的界面看起來(lái)更友好。 第一行,檢查框架內(nèi)的所有子控件,并且在每個(gè)控件周圍添加一小點(diǎn)距離,所以它們看起來(lái)沒(méi)那么擁擠。當(dāng)我們第一次放置控件到界面上時(shí),我們可以添加這些選項(xiàng)到每一個(gè)"grid"調(diào)用,但這僅僅是一種漂亮的快捷方式。 第二行,告訴Tk把注意力轉(zhuǎn)移到我們的輸入框上,也就是說(shuō),在開(kāi)始的時(shí)候,光標(biāo)會(huì)默認(rèn)在輸入框區(qū)域內(nèi),所以用戶在輸入的時(shí)候,不需要先單擊它。 第三行,告訴Tk如果用戶按下了回車鍵,就會(huì)調(diào)用計(jì)算程序段,作用和點(diǎn)擊計(jì)算按鈕時(shí)一樣。 def calculate(*args): try: value = float(feet.get()) meters.set((0.3048 * value * 10000.0 + 0.5)/10000.0) except ValueError: pass 這里我們定義計(jì)算程序,當(dāng)用戶點(diǎn)擊計(jì)算按鈕或者按下回車鍵的時(shí)候會(huì)被調(diào)用。它執(zhí)行從英尺到米的換算,從輸入框中獲取輸入的英尺數(shù)字,并把結(jié)果放在標(biāo)簽控件中。 說(shuō)什么?它看起來(lái)不像是我們用這些控件做了什么事。這就是我們?cè)趧?chuàng)建控件時(shí)定義的神奇的“textvariable”選項(xiàng)起的作用。我們?yōu)檩斎肟蚨x了全局變量“feet”作為textvariable,也就是說(shuō),無(wú)論什么時(shí)候輸入發(fā)生改變,Tk將自動(dòng)更新全局變量“feet”。同理,如果我們明確改變一個(gè)與控件相關(guān)的textvariable的值(就像我們對(duì)鏈接到標(biāo)簽控件的“Meters”所做的那樣),該控件將自動(dòng)被當(dāng)前變量的內(nèi)容更新。很靈活。 root.mainloop() 這最后一行告訴Tk讓它的事件循環(huán),這樣才能讓所有的事情運(yùn)行。 忘記了什么? 這里還有一些值得檢查而我們沒(méi)有在Tk程序中包括的。例如: ·我們沒(méi)有考慮當(dāng)事件改變時(shí)重繪界面 ·我們沒(méi)有考慮評(píng)價(jià)發(fā)送事件消息,捕獲監(jiān)測(cè),或者在每個(gè)空間上處理事件 ·在創(chuàng)建控件的時(shí)候,我們沒(méi)有提供更多的選項(xiàng);默認(rèn)看起來(lái)已經(jīng)關(guān)注了很多事情,并且我們僅僅改變了按鈕上的顯示文本。 ·我們沒(méi)有寫復(fù)雜的代碼來(lái)獲取和設(shè)定簡(jiǎn)單控件的值,我們僅僅把它們鏈接到變量 ·我們沒(méi)有考慮當(dāng)用戶關(guān)閉窗口或者改變窗口大小后會(huì)發(fā)生什么 ·我們沒(méi)有寫額外的代碼讓它能夠跨平臺(tái)工作
|
|