使用xrc的一点心得
chrisyan
|
1#
chrisyan 发表于 2006-11-19 17:23
使用xrc的一点心得
首先向大家说明,我学python才一个多月,写了一个程序主要是为了练习python和wxPython,如果有什么不对的地方还请大家指出。我也没怎么查是否有人写过类似的说明,就当给大家一个参考。我原来写过c++/gtk/gtkmm的程序,觉得wxPython的图形程序写起来还是很容易的,不过偶得一个同事说我的python程序写 的比较像C++程序,现在正在深入的研究python中。
我要讲的是如何在程序中使用wxPython库,其中包括各种widget的获得,动态的切换widget,也有很多资料的,我就把我使用过程中觉得需要注意的总结一下,其中也说明了整个gui的构建框架 我把程序分成了三个文件 一个 main.py 一个 ui.py 一个 common.py Main.py 是这样写的 import ui if __name__ == '__main__': application = ui.Application(0) application.MainLoop() else: print "Can't import Buildipsl main module,it shoud be run alone!" 也就是在主程序中运行起来图形界面,进入事件循环,比较简单。 Ui.py程序比较长,大概有1000行,就不贴在这了,我主要说明的就是这个部分。 Common.py是一些常用的公共函数,定义的一些存放数据的底层的类,ui界面的数据交互最终都是通过和底层类对象数据交换来实现的,这样层次比较分明,也容易独立修改。 下面看一下ui.py文件中的Application类,也就是main.py中调用的那个类 class Application(wx.App): def OnInit(self): self.conf = common.HandleConfig('fic.conf') self.connection_pool = common.ConnectionPool() self.resource = xrc.XmlResource('../resource/IPSLBuild.xrc') self.frame = MainFrame(None, self.resource,self.conf,self.connection_pool) self.frame.Show() return True 这个类在构造函数中实列化了一个’配置文件’类对象,一个’连接缓冲池’类对象,并且初始化了一个xrc资源文件类对象,传入MainFrame这个主窗体类。 MainFrame类 ,主窗体的实现类: 先看一下前几行 class MainFrame(wx.Frame): def __init__(self, parent, resource,setting,pool): self.res = resource self.setting = setting self.connection_pool=pool self.PostCreate(self.res.LoadFrame(parent, 'MainWindow')) self.PostCreate(self.res.LoadFrame(parent, 'MainWindow')) 这一行利用xrc资源对象,把在xml文件中定义的窗体load进来,设置本窗体类。 主窗体load进来了,接着就load窗体上的widget 一般的widget都可以使用像下面这样的函数: self.text_ctrl_build_dir = xrc.XRCCTRL(self,'text_ctrl_build_dir') 也就是使用xrc,XRCCTRL,第一个参数是parent,这里是self,也就是我们load进来的主窗体,后面加的是包含在主窗体里的widget的XML id 值,这样widget就进来了,由前面的self.text_ctrl_build_dir来引用着。使用和用代码构建出来的widget没什么区别。 但是,这里有例外,xrc.XRCCTRL只能使用在由wxWindow继承出来的widget上,如果widget不是由wxWindow 继承而来的,就获得不到对象引用,比如menu 和 toolbar。Menuitem和toolbar button要使用下面的方式: Menubar: self.menubar = self.res.LoadMenuBar('MainWindow_MenuBar') 这里的menubar在XML文件中是个顶层的widget,也就是说这个menubar不是包含在主窗体里的,结构上是和主窗体平级的 一个小提示: XML文件中顶层的widget都是用LoadXXXX 来获得,非顶层的才使用XRCCTRL,并且XRCCTRL中第一个参数要是以LoadXXXX获得的widget做为直接或间接的父widget. 比如一个panel,如果在XML文件的最顶层,就要使用LoadPanel,如果在一个Frame中,那就要使用XRCCTRL(Frame的引用,’panel xml id’) Menubar获得了,就把menubar设置在主窗体上 self.SetMenuBar(self.menubar) 获得子菜单用 self.menu_add = self.menubar.FindItemById(xrc.XRCID('menu_item_node_add')) toolbar可以在主窗体上获得 self.toolbar = xrc.XRCCTRL(self,'MainWdinow_ToolBar') 在使用上,我获得toolbar button主要是要改变其可用和不可用的状态: self.toolbar.EnableTool(xrc.XRCID("tool_button_node_add"),True) 想要toolbar button不能使用就用False 回调函数我下面再说. 到现在获得widget大家应该都没什么疑惑了吧,再说回调函数 回调函数一般是这样 self.config_panel.Bind(wx.EVT_BUTTON,self.OnConfSearch,id=xrc.XRCID('button_search_node_conf')) self.Bind(wx.EVT_MENU, self.OnAdd,id=xrc.XRCID('menu_item_node_add')) self.Bind(wx.EVT_TOOL, self.OnAdd,id=xrc.XRCID('tool_button_node_add')) 第一个是正常的Buttton 第二个是menuitem 第三个是toolbar button 其实都是一样的 父widget.Bind(事件类型,函数名,xml id) 再次说一下上面的menuitem 和toolbar button,其实如果不要像我这样要设置他们的可用和不可用的状态,不用获得他们的对象引用,可以直接绑定的,因为他们用的是xml id来查找的,没有bind在他们对象的引用上. 下面说一个我使用中的需求: 我窗体左边有一个treeview,需要在点左边不同的treeview item的时候,右边可以根据不同的情况变化. 我不想给右边通过代码来实现,因为可能有如下问题 1. 右边可能是个比较复杂的panel,代码量比较大,而且手写代码不容易后来的修改 2. 每次切换都要destory和Create一堆东西,资源的占用比较大 我喜欢完全的界面代码分离,好处多多,所以还是想用xrc的方式来做. 下面是实现步骤 右面我们要操作的实际上是一个splitter window的panel,叫splitter_right_panel 定义一个BoxSizer,用来向里面添东西 self.splitter_right_box_for_guest_panel=wx.BoxSizer(wx.VERTICAL) 初始化的时候先放上一个空panel self.splitter_right_box_for_guest_panel.Add(wx.Panel(self),1, wx.EXPAND, 0) 设置布局 self.splitter_right_panel.SetAutoLayout(True) 设置容器 self.splitter_right_panel.SetSizer(self.splitter_right_box_for_guest_panel) 调整widget间的布局关系 self.splitter_right_box_for_guest_panel.Fit(self.splitter_right_panel) 把两个要加的panel都变成右边splitter_right_panel的子widget self.config_panel.Reparent(self.splitter_right_panel) self.connection_panel.Reparent(self.splitter_right_panel 这一步比较重要,一定要让panel的父widget是他的直接panel,如果是主窗体,显示有问题. 还有这两个panel是xml文件中顶层的panel,要使用LoadPanel加载进来. 初始化基本上就完成了,在切换中的代码是写在treeview中的select changed回调函数中. self.main_frame.splitter_right_box_for_guest_panel.Detach(0) self.main_frame.config_panel.Hide() self.main_frame.connection_panel.Hide() if ……: self.main_frame.config_panel.Show() self.main_frame.splitter_right_box_for_guest_panel.Add(self.main_frame.config_panel,1,wx.EXPAND,0) else: self.main_frame.connection_panel.Show() self.main_frame.splitter_right_box_for_guest_panel.Add(self.main_frame.connection_panel,1,wx.EXPAND,0) 1. 任何时候都先把BoxSizer中的panel分离 2. 两个面板都hide 3. 根据判断加载不同的panel,show出来 这里用到的是Detach(),不要用Destory,panel对象是在xrc 资源文件中的,destory有问题,而且我们就是不想让它被destory,这样最开始得到的panel内部的widget的对象的引用一直都是可用的. 基本的原理我就说完了,写了两个common文件的类在这,是实现ssh连接的,我测试是可用的,还没写好, python异常我还没看,不好意思 使用的是paramiko模块,不过我用起来发现有问题,put和get函数有问题,没办法我自己写的put和get. 远程执行命令的时候不等执行完就退出了,命令在后台执行,这个不是我想要的,不过肯定有方法,只是我还不知道.我还没写完,让大家见笑了,呵呵 class Connection: def __init__(self,ip,account,pwd): self.trans = paramiko.Transport((ip, 22)) self.trans.connect(username=account, password=pwd) def put(self,localfile,remotefile): sftp=paramiko.SFTPClient.from_transport(self.trans) data = open(localfile, 'r').read() print 'remotefile is %s' % remotefile sftp.open(remotefile, 'w').write(data) def get(self,remotefile,localfile): sftp=paramiko.SFTPClient.from_transport(self.trans) data = sftp.open(remotefile, 'r').read() open(localfile, 'w').write(data) def execmd(self,cmd): chan = self.trans.open_session() chan.exec_command(cmd) def active(self): return self.trans.is_active() def __del__(self): self.trans.close() class ConnectionPool: def __init__(self): self.pool={} self.filelist=['bg','pnp','sub','fp','ep'] self.genscript='fic.pexpect' def add_connection(self,name,ip,account,pwd): if self.pool.has_key(name): return False try: con=Connection(ip,account,pwd) except Exception: return False self.pool[name]=con return True def del_connection(self,name): if not self.pool.has_key(name): return False else: del self.pool[name] def connected(self,name): if self.pool.has_key(name): return True else: return False def gen_config(self,name,path): if not self.pool.has_key(name): return False try: self.pool[name].execmd(r'rm -rf /tmp/%s' % self.genscript) self.pool[name].put(get_path(self.genscript),'/tmp/%s' % self.genscript) self.pool[name].execmd('python /tmp/%s' % self.genscript) self.pool[name].execmd(r'rm -rf /tmp/%s' % self.genscript) time.sleep(600) for i in self.filelist: print os.path.join(path,i) self.pool[name].get("/tmp/fic/%s" % i,os.path.join(path,i)) except: return False return True |