一、概念 XRC(XML Resource)的設(shè)計來源于wxWidgets,它的想法很簡單,就是將界面設(shè)計的工作從程序中獨立出來。具體的做法是,創(chuàng)建單獨的XML文件,負責(zé)界面設(shè)計,程序運行的時候載入,生成界面。這樣做的好處是顯而易見的。首先,將繁瑣的外觀設(shè)計代碼從程序中去掉,程序更清晰易讀。其次,XRC文件獨立于程序,程序運行時才調(diào)用,因此可以隨意更換外觀。這種思想并不是wxWidgets的原創(chuàng),MFC中的RC已經(jīng)有了,類似的還有HTML和CSS的關(guān)系。wxPython從wxWidgets繼承而來,當(dāng)然也保留了XRC。 這里有幾點要補充的。一是wxPython的XRC文件中用到的類名稱仍然是wxWidgets中的類名稱,換句話說,wxPython和wxWidgets可以共用XRC文件,第二點要補充的是XRC文件可以編譯成二進制文件XRS,或者編譯成C++代碼。 二、例子 先來看一個例子。 import wx class MyFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self, parent=None, id=wx.ID_ANY, title='My Frame') panel = wx.Panel(self) label1 = wx.StaticText(panel, wx.ID_ANY, 'First name:') label2 = wx.StaticText(panel, wx.ID_ANY, 'Last name:') self.text1 = wx.TextCtrl(panel, wx.ID_ANY) self.text2 = wx.TextCtrl(panel, wx.ID_ANY) button = wx.Button(panel, wx.ID_ANY, 'Submit') sizer = wx.FlexGridSizer(rows=2, cols=2, vgap=5, hgap=5) self.Bind(wx.EVT_BUTTON, self.OnSubmit, button) sizer.Add(label1) sizer.Add(self.text1) sizer.Add(label2) sizer.Add(self.text2) sizer.Add((0,0)) #filler for the grid cell sizer.Add(button) panel.SetSizer(sizer) sizer.Fit(self) def OnSubmit(self, evt): wx.MessageBox('Your name is %s %s!' % (self.text1.GetValue(), self.text2.GetValue()), 'Feedback') class MyApp(wx.App): def OnInit(self): frame = MyFrame() self.SetTopWindow(frame) frame.Show() return True if __name__ == '__main__': app = MyApp(False) app.MainLoop() 這是一個簡單的wxPython程序??梢钥吹缴厦娴拇a中,除了Bind和OnSubmit,其他的代碼都是和界面設(shè)計有關(guān)的,這些代碼或者類似的代碼出現(xiàn)于幾乎所有的GUI程序中。下面是用XRC重新實現(xiàn)的代碼。 import wx from wx import xrc class MyApp(wx.App): def OnInit(self): self.res = xrc.XmlResource('gui.xrc') self.init_frame() return True def init_frame(self): self.frame = self.res.LoadFrame(None, 'mainFrame') self.panel = xrc.XRCCTRL(self.frame, 'panel') self.text1 = xrc.XRCCTRL(self.panel, 'text1') self.text2 = xrc.XRCCTRL(self.panel, 'text2') self.frame.Bind(wx.EVT_BUTTON, self.OnSubmit, id=xrc.XRCID('button')) self.frame.Show() def OnSubmit(self, evt): wx.MessageBox('Your name is %s %s!' % (self.text1.GetValue(), self.text2.GetValue()), 'Feedback') if __name__ == '__main__': app = MyApp(False) app.MainLoop() 看起來是不是比上面的清晰多了,當(dāng)然,程序要跑起來,還需要下面的部分。下面的代碼屬于XRC文件。 下面的代碼屬于XRC文件。 <?xml version="1.0" encoding="utf-8"?> <resource> <object name="mainFrame"> <title>My Frame</title> <object name="panel"> <object <cols>2</cols> <rows>3</rows> <vgap>5</vgap> <hgap>5</hgap> <object <object name="label1"> <label>First name:</label> </object> </object> <object <object name="text1"/> </object> <object <object name="label2"> <label>Last name:</label> </object> </object> <object <object name="text2"/> </object> <object <size>0,0</size> </object> <object <object name="button"> <label>Submit</label> </object> </object> </object> </object> </object> </resource> 這段代碼看起來很復(fù)雜,但是如果熟悉XML的話,應(yīng)該很容易看明白它的結(jié)構(gòu)。最關(guān)鍵的是,我們不用親手寫這些代碼,很多工具,像XRCed,wxGlade都可以自動生成這些代碼,你所要做的只是點幾下鼠標。 三、創(chuàng)建XRC文件 雖然我們不用親自寫XRC文件,弄清楚它的原理還是必要的。在wxPython中,Button的構(gòu)造函數(shù)是這樣的。 wx.Button( parent, id, label='', pos=wx.DefaultPosition, size=wx.DefaultSize, size=wx.DefaultSize, style=0, validator=wx.DefaultValidator, name='button') 實際使用的時候,通常沒有這么多參數(shù), button = wx.Button(panel, wx.ID_ANY, 'Submit') 但是在XRC文件中,要創(chuàng)建一個Button,通常用下面的方式, <object name="button"> <label>Submit</label> </object> 從上面這幾行代碼中,我們可以得到如下信息: 1. XRC用<object> </object>表示要創(chuàng)建的對象。 2. XRC用所用的是C++的類名wxButton,而不是wxPython的類名wx.Button。 3. XRC用name表示對象的名稱,對應(yīng)于python代碼中的id。 4. XRC用層次關(guān)系表示對象之間的父子關(guān)系。 下面這段代碼顯示了XRC文件的層次關(guān)系。 <?xml version="1.0" encoding="utf-8"?> <resource> <object name="mainFrame"> <title>Test Frame</title> <object name="panel"> <object name="button"> <label>Submit</label> </object> </object> </object> </resource> 注意,所有的結(jié)構(gòu)都包含在<resource> </resource>中,表示這個文件是XML Resource文件。 到目前為止,我們對XRC文件的結(jié)構(gòu)已經(jīng)有了初步的認識,接下來要了解的是在Python程序中如何用到它們。 四、使用XRC文件 如何使用XRC文件是wxPython+XRC框架的關(guān)鍵一步。在前面的代碼中,我們注意到這樣兩行, import wx from wx import xrc 這里要強調(diào)的是xrc模塊必須單獨被導(dǎo)入,所以,第二行是必須的。 class MyApp(wx.App): def OnInit(self): self.res = xrc.XmlResource('gui.xrc') self.init_frame() return True 在上面這幾行代碼中,在上面的代碼中,Self.res存儲xrc文件的內(nèi)容。xrc.XmlResource是導(dǎo)入XRC文件,創(chuàng)建xrc對象的關(guān)鍵。 self.init_frame()是用戶自己定義的函數(shù)。你不一定要定義self.init_frame()函數(shù),這樣做的好處只是把初始化框架的工作單獨放在一起,看起來清晰。你也可以在OnInit函數(shù)中完成所有Frame的初始化工作。下面我們來看init_frame()函數(shù)中做了什么, self.frame = self.res.LoadFrame(None, 'mainFrame') LoadFrame()函數(shù)返回mainFrame的引用,將頂層Frame對象載入到python程序中,第一個參數(shù)是parent,第二個參數(shù)是ID,即XRC文件中為Frame取的名字。這個函數(shù)還初始化所有的children Frame,這些工作并沒有顯示的表現(xiàn)出來,由XRC自動完成。 當(dāng)你想從XRC文件中提取信息時,有兩種方式可以選擇,一種是XRCID(XRC_name),這個函數(shù)可以獲得對象的ID;另一種是XRCCTRL(XRC_name),可以獲得對象的引用。 self.panel = xrc.XRCCTRL(self.frame, 'panel') self.text1 = xrc.XRCCTRL(self.panel, 'text1') self.text2 = xrc.XRCCTRL(self.panel, 'text2') 對XRCCTRL需要多說兩句,XRCCTRL函數(shù)返回對象的引用。但是并沒有create新對象,僅僅是獲取他們的引用,創(chuàng)建對象的工作已經(jīng)在上面的LoadFrame()過程中完成了。另一個需要強調(diào)的是,調(diào)用XRCCTRL時,不一定提供直接的parent,可以使用更上一層的parent,XRCCTRL會用FindWindowById遞歸的往下找。也就是說,如果你迷路了,報上爺爺?shù)拿?,警察叔叔也能把你送到家? 到目前為止,構(gòu)造界面的工作算是完成了。如果你熟悉GUI編程的話,馬上會想到,接下來的工作應(yīng)該是將Gui對象,事件和事件處理函數(shù)聯(lián)系起來。這就是接下來要講的事件綁定。 五、事件綁定 先看一段代碼, self.frame.Bind( wx.EVT_BUTTON, self.OnSubmit, id=xrc.XRCID('button') ) 這是一個簡單的綁定的例子,將按鈕事件,事件函數(shù)和按鈕ID綁定起來。前兩個參數(shù)不用多解釋,注意第三個參數(shù)中的XRCID函數(shù),這個函數(shù)通過對象的名稱ID返回對象的數(shù)字ID。這個數(shù)字ID是你在創(chuàng)建對象的時候wxPython用wx.NewID()生成的。 講到這里有必要了解一下Bind()函數(shù),下面是Bind()函數(shù)的定義, Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY) 最后一個參數(shù)我們忽略不計。第四個參數(shù)就是我們剛才的例子中的第三個參數(shù),你也許會奇怪,為什么跳過了默認的第三個參數(shù)直接讀取第四個參數(shù),程序還能正常?在這種情形下,它的確就是正常??戳薆ind()函數(shù)的定義之后,你也許還能想出另一種調(diào)用方法,像下面這樣, self.button = xrc.XRCCTRL(self.panel, 'button') self.frame.Bind(wx.EVT_BUTTON, self.OnSubmit, self.button) 這當(dāng)然也是正確的。但我們通常都用前一種方法。為什么呢?后一種不是更直觀嗎?真正的原因是,XRCCTRL只能返回wxWindow的繼承類的引用,也就是有GetID方法的類。但很多情況下,我們要綁定的對象并不是wxWindow的繼承類,像我們經(jīng)常用到的Bind是將event handler和menu綁定起來,wxMenuItem類不是由wxWindow繼承而來,因此,用XRCCTRL得不到它的引用,這種情況下,必須用XRCID來獲得對象的ID,并將ID傳給Bind作為參數(shù)。所以,為了簡單,為了統(tǒng)一,我們就選擇ID來作為Bind的參數(shù)。 關(guān)于利用wxPython+XRC的框架進行GUI編程,到這里就講完了,這些東西是我從網(wǎng)上收集來的,半翻譯半筆記最后寫成現(xiàn)在這樣。主要參考了下面這篇文章 http://wiki./XRCTutorial 希望能對也為這個內(nèi)容困惑的朋友有幫助。 |
|