本文转自:金庆专栏的 XRC初步,金庆也是整理别人的,但是我就偷懒直接转金庆的资料了,一并表示感谢!
转自:云雀 的 XRC初步(上) 和 XRC初步(下)
XRC初步(上)
wxPython 中定义用户界面布局可以采用两种方法。传统的方法是将界面布局代码直接写在源文件之中,这种方法不需要学习XML语法,概念上较为简单,避免了同时维护多个文件的麻烦;同时,对IDE的自动完成的支持较好。另一种方法则是采用XML资源文件,即XRC(XML Resource)。XRC的基本出发点是界面布局和程序逻辑的分离,即将界面布局代码采用XML的方式放在分离的文件之中;在程序中不涉及控件的创建和布局,只需要加载相应的资源并处理事件绑定即可。在wxPython中采用XRC分离界面布局和程序逻辑主要有以下几个方面的好处:
1. 提高了程序的可维护性
2. 界面布局可由专业人员设计
3. 更好的支持GUI布局设计工具
4. 作为wxWidgets的规范,XRC资源可通用于C++、Python等语言
示例
使用XRC建立用户界面必须建立两个分离的文件:XML资源文件(.xrc)和python源文件。XRC文件包含了用户界面布局信息,如下例:
#design layout in a separate XML file
<?
xml version="1.0" encoding="utf-8"
?>
<
resource
>
<
object
class
="wxFrame"
name
="mainFrame"
>
<
title
>
My Frame
</
title
>
<
object
class
="wxPanel"
name
="panel"
>
<
object
class
="wxFlexGridSizer"
>
<
cols
>
2
</
cols
>
<
rows
>
3
</
rows
>
<
vgap
>
5
</
vgap
>
<
hgap
>
5
</
hgap
>
<
object
class
="sizeritem"
>
<
object
class
="wxStaticText"
name
="label1"
>
<
label
>
First name:
</
label
>
</
object
>
</
object
>
<
object
class
="sizeritem"
>
<
object
class
="wxTextCtrl"
name
="text1"
/>
</
object
>
<
object
class
="sizeritem"
>
<
object
class
="wxStaticText"
name
="label2"
>
<
label
>
Last name:
</
label
>
</
object
>
</
object
>
<
object
class
="sizeritem"
>
<
object
class
="wxTextCtrl"
name
="text2"
/>
</
object
>
<
object
class
="spacer"
>
<
size
>
0,0
</
size
>
</
object
>
<
object
class
="sizeritem"
>
<
object
class
="wxButton"
name
="button"
>
<
label
>
Submit
</
label
>
</
object
>
</
object
>
</
object
>
</
object
>
</
object
>
</
resource
>
Python源文件中则包括加载资源文件和事件绑定代码,同时可以使用xrc.XRCCTRL方法从名字获取控件,使用xrc.XRCID方法获取控件ID:
#
logic by itself in module
import
wx
from
wx
import
xrc
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
class
MyApp(wx.App):
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
def
OnInit(self):
self.res
=
xrc.XmlResource(
'
gui.xrc
'
)
assert
self.res
self.init_frame()
return
True
def
init_frame(self):
self.frame
=
self.res.LoadFrame(None,
'
mainFrame
'
)
assert
self.frame
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
'
)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
if
__name__
==
'
__main__
'
:
app
=
MyApp(False)
app.MainLoop()
创建XRC文件
首先我们看看XRC的组成。XRC文件由一系列预定义的标签组成,你可以注意到这些预定义的标签与控件创建时的关键字参数相似。例如,在wxPython中创建按键的代码如下:
button
=
wx.Button(parent
=
panel, id
=
wx.ID_ANY, label
=
'
Submit
'
)
与之相应的XRC代码则是这个样子:
<
object
class
="wxButton"
name
="button"
>
<
label
>
Submit
</
label
>
</
object
>
从例示中可以注意到几个XRC的关键概念:
1. 每个控件与一个XML节点相对应
2. 控件间的层次关系与XML节点间层次关系相对应,放置控件的容器与相应的XML节点的父节点相对应
3. 节点class属性值对应控件的C++类名
4. 节点name属性值为控件在XRC文件中的唯一标识,可通过该值自python源文件中获取控件
5. label等其余参数在XRC文件中作为节点的子节点出现
有了这些基本概念,现在我们可以使用XRC文件来布局我们的用户界面了。存在大量的工具以可视化的方式来帮助我们完成这个繁琐的工作。wxPython 自带的XRCED以一种半可视化的方式(可视化的设定控件属性并预览,但不支持直接拖放控件)支持用户界面设计;开源的wxglade则支持直接的控件拖放,并可生成C++、python、perl、lisp和XRC的源文件;wxDesigner、dialogBlock,wxDesigner,BOA 等工具同样值得一试。
XRC初步(下)
python源文件中的处理
完成了XRC界面布局文件的编写,下一步必须在python源文件中做相应的处理。与XRC处理相关的wxPython代码段主要包括三个方面:资源文件的加载,控件的获取和事件绑定代码:
1.导入与XRC处理相关的xrc模块:
import
wx.xrc as xrc
2.加载资源文件:
self.res
=
xrc.XmlResource(
'
XRCfilename.xrc
'
)
3.获取控件。此过程不需要显式的创建控件,通过设计XRC文件时相应的name属性值获取即可:
#
加载Frame,第一个参数为父窗口,当前为顶层窗口,故为None
#
第二个参数为设计XRC文件时Frame的name属性值
self.frame
=
self.res.LoadFrame(None,
'
mainFrame
'
)
#
获取控件,第一个参数为控件的父窗口
#
第二个参数为设计XRC文件时控件的name属性值
self.panel
=
xrc.XRCCTRL(self.frame,
'
panel
'
)
self.text1
=
xrc.XRCCTRL(self.panel,
'
text1
'
)
#
获取panel中的控件时不必显式获取panel本身,直接传入self.frame即可递归向下查找
self.text2
=
xrc.XRCCTRL(self.frame,
'
text2
'
)
#
获取控件ID,参数为设计XRC文件时控件的name属性值
self.id
=
xrc.XRCID(
'
button
'
)
4.绑定事件处理有两种方式:
a. 绑定按键事件:
self.button
=
xrc.XRCCTRL(self.panel,
'
button
'
)
self.frame.Bind(wx.EVT_BUTTON, self.OnSubmit, self.button)
b. 因为xrc.XRCCTRL仅能返回wx.Window的派生类,而wxMenuItem不是派生自wx.Window,不能采用xrc.XRCCTRL 获取菜单项,因此事件必须委托给Frame绑定。派生自wx.Window的按键等控件也可以采用本方法绑定:
self.frame.Bind(wx.EVT_BUTTON, self.OnSubmit, id
=
xrc.XRCID(
'
button
'
))
动态创建
加载Frame窗体时将自动加载其子控件和子窗体,那么,你如何控制子窗体和子控件的加载呢?为此,你必须将子窗体定义为和窗体节体平行的顶级节点 (Frames、Dialogs、Panels、Toolbars、Menus和MenuBars可以作为顶级节点),并通过程序控制加载。如下例:
dynamic.xrc文件:
<?
xml version="1.0" encoding="utf-8"
?>
<
resource
>
<
object
class
="wxFrame"
name
="mainFrame"
>
<
title
>
Primary Frame
</
title
>
<
object
class
="wxPanel"
name
="mainPanel"
>
<
object
class
="wxButton"
name
="button"
>
<
label
>
New Frame
</
label
>
</
object
>
</
object
>
</
object
>
<
object
class
="wxFrame"
name
="nextFrame"
>
<
title
>
Secondary Frame
</
title
>
<
object
class
="wxPanel"
name
="nextPanel"
>
<
object
class
="wxStaticText"
name
="label"
>
<
label
>
This is a dynamically created frame.
</
label
>
</
object
>
</
object
>
</
object
>
</
resource
>
Python源文件:
import
wx
from
wx
import
xrc
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
class
MyApp(wx.App):
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
def
OnInit(self):
self.res
=
xrc.XmlResource(
'
dynamic.xrc
'
)
self.init_frame()
return
True
def
init_frame(self):
self.frame
=
self.res.LoadFrame(None,
'
mainFrame
'
)
self.panel
=
xrc.XRCCTRL(self.frame,
'
mainPanel
'
)
self.button
=
xrc.XRCCTRL(self.panel,
'
button
'
)
self.frame.Bind(wx.EVT_BUTTON, self.OnNewFrame, self.button)
self.frame.Show()
def
OnNewFrame(self, evt):
self.frame2
=
self.res.LoadFrame(None,
'
nextFrame
'
)
self.frame2.Show()
![](https://i-blog.csdnimg.cn/blog_migrate/6810355c2f78c12e91b7997a8e8c583a.gif)
if
__name__
==
'
__main__
'
:
app
=
MyApp(False)
app.MainLoop()
动态添加子控件时必须首先调用父窗口的GetSizer方法,创建控件后添加到Sizer,并调用Sizer的Fit方法为控件留出显示空间:
MainWindow
=
res.LoadFrame(None,
"
frame_1
"
)
my_sizer
=
MainWindow.GetSizer()
my_text
=
wx.TextCtrl(MainWindow,
-
1
,
"
Test
"
, size
=
(
325
,
200
))
my_sizer.Add(my_text,
1
, wx.EXPAND
|
wx.ADJUST_MINSIZE, 0)
my_sizer.Fit(MainWindow)