wxpython分割窗研究(解决sashPosition=0无效的BUG)
用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)的更多相关文章
- 基于MIndSpore框架的道路场景语义分割方法研究
基于MIndSpore框架的道路场景语义分割方法研究 概述 本文以华为最新国产深度学习框架Mindspore为基础,将城市道路下的实况图片解析作为任务背景,以复杂城市道路进行高精度的语义分割为任务目标 ...
- cookie工具类,解决servlet3.0以前不能添加httpOnly属性的问题
最近在解决XSS注入的问题,由于使用的servlet版本是2.5,不支持httpOnly的属性,故做了个工具类来实现cookie的httpOnly的功能.全类如下: /** * cookie工具类,解 ...
- [VC6] 小谈如何解决VC6.0 open崩溃的问题(已解决)(转)
[昨天重装了系统,开始用VC6还是可以的,后来装了WPS,在用VC6里面的Open就崩溃了. 这个解决方法就这么几步:1.把FileTool.dll放到指定的AddIns目录.2. 注册这个dll到注 ...
- 解决 ios7.0 以后自定义导航栏左边按钮靠右的问题
解决 ios7.0 以后自定义导航栏左边按钮靠右的问题 www.111cn.net 编辑:edit02_lz 来源:转载 最近开发了一个ios的app,在ios7.0+出现自定义导航栏左边按钮出现靠右 ...
- Cocos2d-X研究之3.0 场景切换特效汇总
Cocos2d-X研究之3.0 场景切换特效汇总 2014-08-05 0个评论 来源:游戏编程 收藏 我要投稿 cocos2d-x 3.0中场景切换特效比较多,而且游戏开 ...
- Java 实现C#中的String.format效果 解决("我是{0},今年了","whaozl") bug
/** * 需要引入com.alibaba.fastjson.1.2.8 * 两种调用方式 * String template1="我是{0},今年{1 ...
- 如何解决vue2.0 打包之后 打开index.html出现空白页
如何解决vue2.0 打包之后 打开index.html出现空白页 1.打包之前修改三个文件 1.1.第一步,找到build文件,在webpack.prod.conf.js 第25行左右 ...
- 教你如何解决T+0的问题
摘要:T+0查询是指实时数据查询,数据查询统计时将涉及到最新产生的数据. 本文分享自华为云社区<大数据解决方案:解决T+0问题>,作者: 小虚竹 . T+0问题 T+0查询是指实时数据查询 ...
- 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 ...
随机推荐
- DDD创始人Eric Vans:要实现DDD原始意图,必须CQRS+Event Sourcing架构
http://www.infoq.com/interviews/Technology-Influences-DDD# 要实现DDD(domain drive design 领域驱动设计)原始意图,必 ...
- sublime text 添加到鼠标右键功能
安装sublime text的同学可能在安装的时候忘了设置sublime text的右键功能.那我们介绍如何添加. 我们要创建一个.reg为后缀的文件sublime_addright.reg.那么…… ...
- WPF开发进阶 - Fody/PropertyChanged(二)
前一篇 简单的介绍了Fody/PropertyChanged的使用方法, 这一篇,我们详细介绍它的一些比较重要的特性和规则 1. Attributes 通过在类或属性上标记这些特性,可以在编译代码时, ...
- 蓝桥网试题 java 基础练习 矩形面积交
------------------------------------------------------------------------------------------- 思路见锦囊2 - ...
- MongoDB基础之一:Conetos下安装MongoDB
1.下载自己需要的版本,我这用的是mongodb-linux-x86_64-2.4.9.tgz #cd /usr/local/src # wget http://fastdl.mongodb.org/ ...
- java反射的理解与应用(某大神博客中看到的博文,写的真的太好了,果断转载作为笔记)
原文地址:http://www.cnblogs.com/jqyp/archive/2012/03/29/2423112.html#undefined 一.什么是反射机制 简单的来说,反射机制指的是程序 ...
- 一个web应用的诞生--数据表单
下面把角色分为两种,普通用户和管理员用户,至少对于普通用户来说,直接修改DB是不可取的,要有用户注册的功能,下面就开始进行用户注册的开发. 用户表 首先要想好用户注册的时候需要提供什么信息:用户名.密 ...
- python中的select模块
介绍: Python中的select模块专注于I/O多路复用,提供了select poll epoll三个方法(其中后两个在Linux中可用,windows仅支持select),另外也提供了kqu ...
- java-5
1.请查看String.equals()方法的实现代码,注意学习其实现方法 将此字符串与指定的对象比较.当且仅当该参数不为 null,并且是与此对象表示相同字符序列的 String 对象时,结果才为 ...
- PCB信号集
每一个进程都有一个pcb进程控制块,用来控制进程的信息,同时信号在pcb中有两个队列去维护他,一个是未决信号集,每一位对应一个信号的状态,0,1,1表示未决态,另一个是信号屏蔽字(阻塞信号集),也就0 ...