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