GUI programming with wxPython 之 XRC

Stella981
• 阅读 851

一、概念

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文件。

    

        My Frame

        

            <object

                2

                3

                5

                5

                <object

                    

                        

                    

                

                <object

                    

                

                <object

                    

                        

                    

                

                <object

                    

                

                <object

                    0,0

                

                <object

                    

                        

                    

                

            

        

    

这段代码看起来很复杂,但是如果熟悉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,通常用下面的方式,

从上面这几行代码中,我们可以得到如下信息:

1. XRC用 表示要创建的对象。

2. XRC用所用的是C++的类名wxButton,而不是wxPython的类名wx.Button。

3. XRC用name表示对象的名称,对应于python代码中的id。

4. XRC用层次关系表示对象之间的父子关系。

下面这段代码显示了XRC文件的层次关系。

    

        Test Frame

        

            

                

            

        

    

注意,所有的结构都包含在 中,表示这个文件是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.wxpython.org/XRCTutorial

希望能对也为这个内容困惑的朋友有帮助。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这