一、概念 XRC(XML Resource)的设计来源于wxWidgets,它的想法很简单,就是将界面设计的工作从程序中独立出来。具体的做法是,创建单独的XML文件,负责界面设计,程序运行的时候载入,生成界面。这样做的好处是显而易见的。首先,将繁琐的外观设计代码从程序中去掉,程序更清晰易读。其次,XRC文件独立于程序,程序运行时才调用,因此可以随意更换外观。这种思想并不是wxWidgets的原创,MFC中的RC已经有了,类似的还有HTML和CSS的关系。wxPython从wxWidgets继承而来,当然也保留了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程序。可以看到上面的代码中,除了Bind和OnSubmit,其他的代码都是和界面设计有关的,这些代码或者类似的代码出现于几乎所有的GUI程序中。下面是用XRC重新实现的代码。 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() 看起来是不是比上面的清晰多了,当然,程序要跑起来,还需要下面的部分。下面的代码属于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> 这段代码看起来很复杂,但是如果熟悉XML的话,应该很容易看明白它的结构。最关键的是,我们不用亲手写这些代码,很多工具,像XRCed,wxGlade都可以自动生成这些代码,你所要做的只是点几下鼠标。 三、创建XRC文件 虽然我们不用亲自写XRC文件,弄清楚它的原理还是必要的。在wxPython中,Button的构造函数是这样的。 wx.Button( parent, id, label='', pos=wx.DefaultPosition, size=wx.DefaultSize, size=wx.DefaultSize, style=0, validator=wx.DefaultValidator, name='button') 实际使用的时候,通常没有这么多参数, button = wx.Button(panel, wx.ID_ANY, 'Submit') 但是在XRC文件中,要创建一个Button,通常用下面的方式, <object name="button"> <label>Submit</label> </object> 从上面这几行代码中,我们可以得到如下信息: 1. XRC用<object> </object>表示要创建的对象。 2. XRC用所用的是C++的类名wxButton,而不是wxPython的类名wx.Button。 3. XRC用name表示对象的名称,对应于python代码中的id。 4. XRC用层次关系表示对象之间的父子关系。 下面这段代码显示了XRC文件的层次关系。 <?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> 注意,所有的结构都包含在<resource> </resource>中,表示这个文件是XML Resource文件。 到目前为止,我们对XRC文件的结构已经有了初步的认识,接下来要了解的是在Python程序中如何用到它们。 四、使用XRC文件 如何使用XRC文件是wxPython+XRC框架的关键一步。在前面的代码中,我们注意到这样两行, import wx from wx import xrc 这里要强调的是xrc模块必须单独被导入,所以,第二行是必须的。 class MyApp(wx.App): def OnInit(self): self.res = xrc.XmlResource('gui.xrc') self.init_frame() return True 在上面这几行代码中,在上面的代码中,Self.res存储xrc文件的内容。xrc.XmlResource是导入XRC文件,创建xrc对象的关键。 self.init_frame()是用户自己定义的函数。你不一定要定义self.init_frame()函数,这样做的好处只是把初始化框架的工作单独放在一起,看起来清晰。你也可以在OnInit函数中完成所有Frame的初始化工作。下面我们来看init_frame()函数中做了什么, self.frame = self.res.LoadFrame(None, 'mainFrame') LoadFrame()函数返回mainFrame的引用,将顶层Frame对象载入到python程序中,第一个参数是parent,第二个参数是ID,即XRC文件中为Frame取的名字。这个函数还初始化所有的children Frame,这些工作并没有显示的表现出来,由XRC自动完成。 当你想从XRC文件中提取信息时,有两种方式可以选择,一种是XRCID(XRC_name),这个函数可以获得对象的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函数返回对象的引用。但是并没有create新对象,仅仅是获取他们的引用,创建对象的工作已经在上面的LoadFrame()过程中完成了。另一个需要强调的是,调用XRCCTRL时,不一定提供直接的parent,可以使用更上一层的parent,XRCCTRL会用FindWindowById递归的往下找。也就是说,如果你迷路了,报上爷爷的名字,警察叔叔也能把你送到家。 到目前为止,构造界面的工作算是完成了。如果你熟悉GUI编程的话,马上会想到,接下来的工作应该是将Gui对象,事件和事件处理函数联系起来。这就是接下来要讲的事件绑定。 五、事件绑定 先看一段代码, self.frame.Bind( wx.EVT_BUTTON, self.OnSubmit, id=xrc.XRCID('button') ) 这是一个简单的绑定的例子,将按钮事件,事件函数和按钮ID绑定起来。前两个参数不用多解释,注意第三个参数中的XRCID函数,这个函数通过对象的名称ID返回对象的数字ID。这个数字ID是你在创建对象的时候wxPython用wx.NewID()生成的。 讲到这里有必要了解一下Bind()函数,下面是Bind()函数的定义, Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY) 最后一个参数我们忽略不计。第四个参数就是我们刚才的例子中的第三个参数,你也许会奇怪,为什么跳过了默认的第三个参数直接读取第四个参数,程序还能正常?在这种情形下,它的确就是正常。看了Bind()函数的定义之后,你也许还能想出另一种调用方法,像下面这样, self.button = xrc.XRCCTRL(self.panel, 'button') self.frame.Bind(wx.EVT_BUTTON, self.OnSubmit, self.button) 这当然也是正确的。但我们通常都用前一种方法。为什么呢?后一种不是更直观吗?真正的原因是,XRCCTRL只能返回wxWindow的继承类的引用,也就是有GetID方法的类。但很多情况下,我们要绑定的对象并不是wxWindow的继承类,像我们经常用到的Bind是将event handler和menu绑定起来,wxMenuItem类不是由wxWindow继承而来,因此,用XRCCTRL得不到它的引用,这种情况下,必须用XRCID来获得对象的ID,并将ID传给Bind作为参数。所以,为了简单,为了统一,我们就选择ID来作为Bind的参数。 关于利用wxPython+XRC的框架进行GUI编程,到这里就讲完了,这些东西是我从网上收集来的,半翻译半笔记最后写成现在这样。主要参考了下面这篇文章 http://wiki./XRCTutorial 希望能对也为这个内容困惑的朋友有帮助。 |
|