用wxpython开发一个简单的exe其实很简单的,但是在开发的过程中会遇到若干的坑、疑问、甚至bug,让人摸不清头脑!恰恰关于这方面的文档是少之又少,看来看去大家还是在官方的文档上加以引用说明,但是我们在开发的过程中遇到的问题,网上几乎找不到相关的解答。不知道是大家没遇到呢?还是遇到解决了不愿分享给大家?我本人是个自动化测试工程,在开发领域可以说是菜鸟一枚,只能把自己遇到的问题拿出来和大家分享!也希望大神们踩过的坑,解决的问题能分享出来,让我们这些小辈们能少踩坑~~好吧,进入今天的主题:wxpython分割窗研究(解决sashPosition=0无效的BUG)!

分割窗在应用的程序开发中是特别常见的,比如robotframework,以及我们python的IDE(PyCharm)的主界面都是分割窗的应用例子,图片如下:

上面就是3个分割窗,注意的是wxpython最多只支持2个分割窗,如果开发这种分割窗只能用嵌套了!分割子窗口1与2其实是嵌套在画板1上面的,下面我也介绍如何利用Sizer布局得到这样的分割窗。

有了上图直观的认识后,我也引用个官方的例子,然后从这上面拓展,官方例子如下:

#coding=utf-8
import wx
class Myframe(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None)
self.minpane=0
self.initpos=0
self.MakeMenuBar()
self.sp=wx.SplitterWindow(self)# 创建一个分割窗
self.p1=wx.Panel(self.sp,style=wx.SUNKEN_BORDER) #创建子面板
self.p2=wx.Panel(self.sp,style=wx.SUNKEN_BORDER)
self.p1.SetBackgroundColour("pink")
self.p2.SetBackgroundColour("blue")
self.p1.Hide() # 确保备用的子面板被隐藏
self.p2.Hide()
self.sp.Initialize(self.p1) #  初始化分割窗
self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGING,self.OnSashChanging,self.sp)
self.Bind(wx.EVT_SPLITTER_SASH_POS_CHANGED,self.OnSashChanged,self.sp)
def OnSplitV(self, evt): # 响应垂直分割请求
self.sp.SplitVertically(self.p1, self.p2, self.initpos)
def MakeMenuBar(self):
menu=wx.Menu()
item=menu.Append(-1,"Split horizontally")
self.Bind(wx.EVT_MENU,self.OnSplitH,item)
self.Bind(wx.EVT_UPDATE_UI,self.OnCheckCanSplit,item)
item=menu.Append(-1,"Split vertically")
self.Bind(wx.EVT_MENU,self.OnSplitV,item)
self.Bind(wx.EVT_UPDATE_UI,self.OnCheckCanSplit,item)
item=menu.Append(-1,"Unsplit")
self.Bind(wx.EVT_MENU,self.OnUnsplit,item)
self.Bind(wx.EVT_UPDATE_UI,self.OnCheckCanUnsplit,item)
menu.AppendSeparator()
item=menu.Append(-1,"Set initial sash position")
self.Bind(wx.EVT_MENU,self.OnSetPos,item)
item = menu.Append(-1, "Set minimum pane size")
self.Bind(wx.EVT_MENU,self.OnSetMin,item)
menu.AppendSeparator()
mbar=wx.MenuBar()
mbar.Append(menu,"Splitter")
self.SetMenuBar(mbar)
def OnSashChanging(self,evt):
print "OnSashChanging:",evt.GetSashPosition()
def OnSashChanged(self,evt):
print "OnSashChanged:", evt.GetSashPosition()
def OnSplitH(self,evt):# 响应水平分割请求
self.sp.SplitHorizontally(self.p1,self.p2,self.initpos)
def OnCheckCanSplit(self,evt):
evt.Enable(not self.sp.IsSplit())
def OnCheckCanUnsplit(self,evt):
evt.Enable(self.sp.IsSplit())
def OnUnsplit(self,evt):
self.sp.Unsplit()
def OnSetMin(self,evt):
minpane=wx.GetNumberFromUser("Enter the minimum pane size","","Minimum Pane Size",self.minpane,0,1000,self)
if minpane!=-1:
self.minpane=minpane
self.sp.SetMinimumPaneSize(self.minpane)
def OnSetPos(self,evt):
initpos=wx.GetNumberFromUser("Enter the initial sash position (to be used in the Split call)","","Initial Sash Position",self.initpos,-1000,1000,self)
if initpos!=-1:
pass
app = wx.PySimpleApp()
frame=Myframe()
frame.Show(True)
app.MainLoop()

程序是很简单的,但是涵盖了分割的基本方法,运行一下是没有问题的。菜单包含着以垂直或水平方式来分割窗口,针对上面的例子我想说明几点!

1.分割窗只能分割一次,对已分割的窗口再分割将会失败,要确定窗口当前是否被分割了,调用方法 IsSplit()。

2.如果你想不分割窗口或者重新分割窗口那么必须使用Unsplit(toRemove=None)。参数toRemove 是实际要移除的wx.Window对象,并且必须是这两个子窗口中的一个。如果 toRemove是None,那么底部或右部的窗口将被移除。

3.如果只有一个窗口分割调用Initialize(window)。如果2个窗口分割者调用SplitHorizontally (window1,window2,sashPosition=0)或 SplitVertically(window1, window2, sashPosition=0)。参数window1和window2是两个子窗口,参数sashPosition包含分割条的初始位置。对于水平分割(垂直分割)来说,window1被放置在window2的顶部(左边)。如果sashPosition是一个正数,它代表分割条距顶部的初始高度(分割条距左边框的初始宽度)。如果sashPosition是一个负数,它定义了分割条距底部的高度值(分割条距右边框的初始宽度)。如果sashPosition是0,那么这个分割条位于正中

那么我们的问题来了,如果我启动这个应用程序就想得到一个已经水平分割好的且分割条居中窗口,怎么办?毕竟我们实际的应用程序不太可能让你通过菜单来切换吧,而且布局都是固定的。可能有的人觉得我提出这个问题有点小白,看我啪啪的给你敲出几行代码来。最简单的代码如下:

#coding=utf-8
import wx
class Myframe(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None)
self.sp=wx.SplitterWindow(self)# 创建一个分割窗
self.p1=wx.Panel(self.sp,style=wx.SUNKEN_BORDER) #创建子面板
self.p2=wx.Panel(self.sp,style=wx.SUNKEN_BORDER)
self.p1.SetBackgroundColour("pink")
self.p2.SetBackgroundColour("blue")
self.p1.Hide() # 确保备用的子面板被隐藏
self.p2.Hide()
self.sp.SplitHorizontally(self.p1,self.p2,0) # 代表分割条居中
app = wx.PySimpleApp()
frame=Myframe()
frame.Show(True)
app.MainLoop()

先申明一下我的wx版本是2.8的,运行平台是64 bit的win8系统,运行结果让我大跌眼镜:

这是什么东西,我设置的sashPosition=0啊,说好的分割条居中呢,这也太不靠谱了吧,我反正是实在找不出这段代码写的有啥问题(知道哪里错的大哥一定要告诉我^ ^),我姑且认为这是版本的一个bug吧,我相信好多同学和我遇到一样的问题。当然如果我们给sashPosition设置参数比如sashPosition=100,让分割条距离顶部100个像素表现是正常的。我又不相信爱情了,明明我们在上面官方的例子里利用菜单分割是正常的,为什么我们在Frame的构造函数里面分割就不行了呢,想了想唯一的不同可能是菜单在水平分割的时候要调用Unsplit()方法取消分割,但是我们在构造函数里面也没有分割啊,不管怎么样我们在分割之前也调用这个看看效果如何,不过遗憾的是然并卵。在这个时候我突然想到我在给Panel设置背景图片时用到了wx.EVT_ERASE_BACKGROUND事件,我们本着曲线救国的思想来试下吧,修改后的程序如下:

#coding=utf-8
import wx
class Myframe(wx.Frame):
def __init__(self,flag=False):
wx.Frame.__init__(self,None)
self.sp=wx.SplitterWindow(self)# 创建一个分割窗
self.flag=flag
self.fisrt=0
self.p1=wx.Panel(self.sp,style=wx.SUNKEN_BORDER) #创建子面板
self.p2=wx.Panel(self.sp,style=wx.SUNKEN_BORDER)
self.p1.SetBackgroundColour("pink")
self.p2.SetBackgroundColour("blue")
self.p1.Hide() # 确保备用的子面板被隐藏
self.p2.Hide()
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBack)
self.sp.Unsplit()
self.sp.SplitHorizontally(self.p1, self.p2,)
def OnEraseBack(self,event):
if self.fisrt<2 or self.flag :
self.sp.SetSashPosition()
self.fisrt=self.fisrt+1
app = wx.PySimpleApp()
frame=Myframe(True)
frame.Show(True)
app.MainLoop()

我们怀着试一试的心态去试下,结果如下:

说明sashPosition=0奏效了。还有wx.EVT_ERASE_BACKGROUND当背景需要重新绘制时就会触发,应用程序启用的时候会调用2次。重要的是我们使用了self.sp.SetSashPosition()这个函数,它的作用是重新定义分割条的位置,这里我们解释下self.first和self.flag。我么考虑到动态分割的问题,比如我启动这个应用程序的时候它是均等分割的,但是我们改变这个框架大小的时候,我们有2种选择,一种就是不再动态分割,一种继续动态平均分割。

1.self.first<2是保证我们初始启动这个应用程序的时候能调用self.sp.SetSashPosition()这个函数,这时候分割条是在中间的。

2.self.flag如果我们设置为True那么当我们改变frame框架大小的时候self.sp.SetSashPosition()一直被调用,达到动态均分的目的,如果false则self.SetSashPosition()不再被调用,具体可以自己设置感受。

下面我们讨论3种我认为可能用到的场景。

第一种场景:我们实现一个如下布局的分割窗:

这个嵌套的分割窗特点是:水平分割窗均分,2个子垂直分割窗也均分。代码如下:

#coding=utf-8
import wx
import time
class Myframe(wx.Frame):
def __init__(self,flag=True):
wx.Frame.__init__(self,None)
self.first=0
self.flag=flag
self.sp=wx.SplitterWindow(self)# 创建一个分割窗,parent是frame
self.p1=wx.Panel(self.sp,style=wx.SUNKEN_BORDER) #创建子面板p1
self.p2=wx.Panel(self.sp,style=wx.SUNKEN_BORDER) # 创建子面板p2
self.p1.Hide() # 确保备用的子面板被隐藏
self.p2.Hide()
self.sp1 = wx.SplitterWindow(self.p1) # 创建一个子分割窗,parent是p1
self.box = wx.BoxSizer(wx.VERTICAL)#创建一个垂直布局
self.box.Add(self.sp1, , wx.EXPAND)#将子分割窗布局延伸至整个p1空间
self.p1.SetSizer(self.box)
self.p2.SetBackgroundColour("blue")
self.p1_1 = wx.Panel(self.sp1, style=wx.SUNKEN_BORDER)#在子分割窗self.sp1的基础上创建子画板p1_1
self.p1_2 = wx.Panel(self.sp1, style=wx.SUNKEN_BORDER)#在子分割窗self.sp1的基础上创建子画板p1_2
self.p1_1.Hide()
self.p1_2.Hide()
self.p1_1.SetBackgroundColour("red")
self.p1_2.SetBackgroundColour("yellow")
self.sp.SplitHorizontally(self.p1, self.p2, )
self.sp1.SplitVertically(self.p1_1, self.p1_2, )
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBack)
def OnEraseBack(self,event):
if self.first<2 or self.flag:
self.sp.SetSashPosition()
self.sp1.SetSashPosition()
self.first=self.first+1
self.Refresh()
app = wx.PySimpleApp()
frame=Myframe(True)
frame.Show(True)
app.MainLoop()

代码很简单,下面我说下我这个程序的想法。首先,来一次水平分割窗,这是没有疑问的。然后在上面的分割窗的画板中嵌套一个垂直分割窗,那么我们就要铺满上面的这个画板。我们或许可以计算self.p1的宽度喝高度然后self.sp1 = wx.SplitterWindow(self.p1,size=(width,height)),但是,你这个要是动态的,比如frame窗口被放大或者缩小了,要监听wx.EVT_SIZE事件,动态改变self.p1的宽度和高度,但是如果这样就太舍近求远了,所以我们想到BoxSizer布局,利用proportion=1和wx.EXPAND让它动态的总是填满self.p1。其次,self.flag和self.first参数意义上面已经说明。如果想每次Frame窗口大小变动时,都会动态均分布局,那么就使self.flap=True吧。好了这个布局你会了是吧。

第二种场景:与上面的类似我们实现一个如下布局的分割窗:

这种嵌套分割窗的特点是,水平分割线距离底部固定的距离,垂直分割线距离左边框也固定的距离。其实我们的python IDE(pycharm)就是这种格局,代码也很简单,贴出来一下吧,就在上面的例子中,改变sashPosition为固定的值,其中值得正负代表不同的含义,上面我已经说了,代码如下:

#coding=utf-8
import wx
import time
class Myframe(wx.Frame):
def __init__(self,flag=True):
wx.Frame.__init__(self,None)
self.first=0
self.flag=flag
self.sp=wx.SplitterWindow(self)# 创建一个分割窗,parent是frame
self.p1=wx.Panel(self.sp,style=wx.SUNKEN_BORDER) #创建子面板p1
self.p2=wx.Panel(self.sp,style=wx.SUNKEN_BORDER) # 创建子面板p2
self.p1.Hide() # 确保备用的子面板被隐藏
self.p2.Hide()
self.sp1 = wx.SplitterWindow(self.p1) # 创建一个子分割窗,parent是p1
self.box = wx.BoxSizer(wx.VERTICAL)#创建一个垂直布局
self.box.Add(self.sp1, , wx.EXPAND)#将子分割窗布局延伸至整个p1空间
self.p1.SetSizer(self.box)
self.p2.SetBackgroundColour("blue")
self.p1_1 = wx.Panel(self.sp1, style=wx.SUNKEN_BORDER)#在子分割窗self.sp1的基础上创建子画板p1_1
self.p1_2 = wx.Panel(self.sp1, style=wx.SUNKEN_BORDER)#在子分割窗self.sp1的基础上创建子画板p1_2
self.p1_1.Hide()
self.p1_2.Hide()
self.p1_1.SetBackgroundColour("red")
self.p1_2.SetBackgroundColour("yellow")
self.sp.SplitHorizontally(self.p1, self.p2, )
self.sp1.SplitVertically(self.p1_1, self.p1_2, )
self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBack)
def OnEraseBack(self,event):
if self.first<2 or self.flag:
self.sp.SetSashPosition()
self.sp1.SetSashPosition(-100)
self.first=self.first+1
     self.Refresh()
app = wx.PySimpleApp()
frame=Myframe(True)
frame.Show(True)
app.MainLoop()

这个没什么解释了吧,具体参数意义与上面一直,其实Pycharm的用的是self.flag=True的风格。

第三种:我想实现了水平分割和垂直分割动态分配,比如水平分割底部窗口占用frame框架高度的1/4(顶部3/4),垂直分割模式左边窗口占用frame框架宽度的1/4(右边3/4),那么我们可能要监听EVT_SIZE事件了,具体代码如下:

#coding=utf-8
import wx
class Myframe(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None)
self.width=self.Size.width
self.height=self.Size.height
self.up = self.height/*2 #上面窗口高度
self.left = self.width/ #嵌套窗口左窗口宽度
self.sp=wx.SplitterWindow(self,size=(self.width,self.height))# 创建一个分割窗
self.p1=wx.Panel(self.sp,style=wx.SUNKEN_BORDER) #创建子面板
self.p2=wx.Panel(self.sp,style=wx.SUNKEN_BORDER)
self.box = wx.BoxSizer(wx.VERTICAL)
self.sp1 = wx.SplitterWindow(self.p1) # 创建一个子分割窗
self.box.Add(self.sp1,,wx.EXPAND)
self.p1.SetSizer(self.box)
self.p2.SetBackgroundColour("blue")
self.p1_1=wx.Panel(self.sp1,style=wx.SUNKEN_BORDER)
self.p1_2=wx.Panel(self.sp1,style=wx.SUNKEN_BORDER)
self.p1_1.Hide()
self.p1_2.Hide()
self.p1_1.SetBackgroundColour("red")
self.p1_2.SetBackgroundColour("yellow")
self.p1.Hide() # 确保备用的子面板被隐藏
self.p2.Hide()
self.sp1.SplitVertically(self.p1_1, self.p1_2, self.left)
self.sp.SplitHorizontally(self.p1, self.p2, self.up)
self.Bind(wx.EVT_SIZE,self.SizeOnchange)
def SizeOnchange(self,evt):
size = evt.Size
self.sp.Size=size#这一句很重要
self.sp.SetSashPosition(size.height/*)
self.sp1.SetSashPosition(size.width/4 ) app = wx.PySimpleApp()
frame=Myframe()
frame.Show(True)
app.MainLoop()

代码与上面大同小异,主要的区别就是监听EVT_SIZE事件,动态的改变self.sp的大小,以及水平分割条和垂直分割条的相对宽度和高度。图片就不贴了吧,其实这个也可以取代上面的代码。主要通过一些手段达到我们的目的!!关于分割窗我想应该讲的很清楚了吧。

wxpython分割窗研究(解决sashPosition=0无效的BUG)的更多相关文章

  1. 基于MIndSpore框架的道路场景语义分割方法研究

    基于MIndSpore框架的道路场景语义分割方法研究 概述 本文以华为最新国产深度学习框架Mindspore为基础,将城市道路下的实况图片解析作为任务背景,以复杂城市道路进行高精度的语义分割为任务目标 ...

  2. cookie工具类,解决servlet3.0以前不能添加httpOnly属性的问题

    最近在解决XSS注入的问题,由于使用的servlet版本是2.5,不支持httpOnly的属性,故做了个工具类来实现cookie的httpOnly的功能.全类如下: /** * cookie工具类,解 ...

  3. [VC6] 小谈如何解决VC6.0 open崩溃的问题(已解决)(转)

    [昨天重装了系统,开始用VC6还是可以的,后来装了WPS,在用VC6里面的Open就崩溃了. 这个解决方法就这么几步:1.把FileTool.dll放到指定的AddIns目录.2. 注册这个dll到注 ...

  4. 解决 ios7.0 以后自定义导航栏左边按钮靠右的问题

    解决 ios7.0 以后自定义导航栏左边按钮靠右的问题 www.111cn.net 编辑:edit02_lz 来源:转载 最近开发了一个ios的app,在ios7.0+出现自定义导航栏左边按钮出现靠右 ...

  5. Cocos2d-X研究之3.0 场景切换特效汇总

    Cocos2d-X研究之3.0 场景切换特效汇总 2014-08-05      0个评论    来源:游戏编程    收藏    我要投稿 cocos2d-x 3.0中场景切换特效比较多,而且游戏开 ...

  6. Java 实现C#中的String.format效果 解决("我是{0},今年了","whaozl") bug

        /**      * 需要引入com.alibaba.fastjson.1.2.8      * 两种调用方式      * String template1="我是{0},今年{1 ...

  7. 如何解决vue2.0 打包之后 打开index.html出现空白页

    如何解决vue2.0 打包之后 打开index.html出现空白页 1.打包之前修改三个文件       1.1.第一步,找到build文件,在webpack.prod.conf.js 第25行左右 ...

  8. 教你如何解决T+0的问题

    摘要:T+0查询是指实时数据查询,数据查询统计时将涉及到最新产生的数据. 本文分享自华为云社区<大数据解决方案:解决T+0问题>,作者: 小虚竹 . T+0问题 T+0查询是指实时数据查询 ...

  9. Oracle 11gR2 11.2.0.1 ( 11.2.0.1的BUG?):ohasd不能正常启动:ioctl操作:npohasd的问题:【chmod a+wr /var/tmp/.oracle/npohasd】

    问题1:执行安装,编译成功后,执行asmca时,失败,无法成功创建后台相关服务 问题2:os系统重启后,ohasd无法正常启动,css服务失败 原因:11.2.0.1的BUG:/var/tmp/.or ...

随机推荐

  1. jmeter测试计划

    测试计划配置 用户定义的变量: 测试计划上可以添加用户定义的变量.一般添加一些系统常用的配置.如果测试过程中想切换环境,切换配置,一般不建议在测试计划上添加变量,因为不方便启用和禁用,一般是直接添加用 ...

  2. 大大维的贪吃蛇v1

    虽然本人一直是个免费的游戏测试员(/手动滑稽),但一直有着一个游戏架构师的梦想.正如马爸爸所说,梦想还是要有的,万一实现了呢? 这些天放寒假,有些空闲时间,就想着做一个简单的游戏机.能达到小时候十几块 ...

  3. Java偏向锁实现原理(Biased Locking)

    http://kenwublog.com/theory-of-java-biased-locking 阅读本文的读者,需要对Java轻量级锁有一定的了解,知道lock record, mark wor ...

  4. Jcompress: 一款基于huffman编码和最小堆的压缩、解压缩小程序

    前言 最近基于huffman编码和最小堆排序算法实现了一个压缩.解压缩的小程序.其源代码已经上传到github上面: Jcompress下载地址 .在本人的github上面有一个叫Utility的re ...

  5. cygwin 运行窗口程序

    首先, 默认安装的cygwin是不能运行窗口程序的 比如,一段python窗口程序: import * from tkinter Tk() mainloop() 如果使用命令行: python3 py ...

  6. Superwebsocket 模拟微信聊天室

    在园子里潜水几年了,工作以来算是有些积累,突然想写点东西方便以后温故而知新,希望自己能够坚持下去. 关于Superwebsocket的介绍我就不多说了,请点击:http://www.cnblogs.c ...

  7. PowerShell 批量修改AD属性

    环境:win 2008 R2 在管理工具中打开用于 windows powershell 的ActiveDirectory模块命令行窗口或打开命令提示符窗口输入PowerShell回车再输入impor ...

  8. Python开发项目:大型模拟战争游戏(外星人入侵)

    外星人入侵 游戏概述: 现在准备用python开始搞一个大型游戏,模拟未来战争,地球人狙击外星人大战(其实就是小蜜蜂游戏2333),玩家控制一个飞船,用子弹歼灭屏幕上空的外星飞船:项目用到了Pygam ...

  9. Gulp自动构建Web前端程序

    这两天在一个朋友在项目上碰到了一个这样的问题,在运营过程中,用户在浏览器上对某个表单进行数据提交时,需要引入新的平台接口数据的业务,通过评估,开发团队马上修改了相关后台代码和部分的前端脚本代码,通过简 ...

  10. 读书笔记 effective c++ Item 7 在多态基类中将析构函数声明为虚析构函数

    1. 继承体系中关于对象释放遇到的问题描述 1.1 手动释放 关于时间记录有很多种方法,因此为不同的计时方法创建一个TimeKeeper基类和一些派生类就再合理不过了: class TimeKeepe ...