wxWidgets Programming: Sizer, Frame and Panel
个人感觉在wxWidgets UI编程时,一开始最难于理解和使用的就是Sizer,尤其是在与各种控件、Panel嵌套搭配的时候,包括对控件间隙以及父窗口大小发生改变时各个控件的变化定义等。在网上发现这篇文章非常详细的总结了Sizer, Frame和Panel的关系并通过简单的实例深入浅出地讲解了这一部分,转载在此供大家参考,如果你有相关技术问题或者开发心得也可以发评论参加讨论。
[原文如下] 最近在研究跨平台的C/C++ GUI库,基本说来功能强大,稳定且开源的库就是QT和wxWidgets了,关于他们之间具体的比较详见《WxWidgets Compared To Other Toolkits》。 N年以前研究过一段时间QT的代码,不过主要因为其在Windows开发商用软件还是要一定的license费用,所以不能说是真正的免费跨平台。 wxWidgets则是一套免费开源的跨平台C++ GUI库,它的一个特点就是可以转换为native 的api图形界面。就是说用同样的代码,在windows平台看起来就是window风格的,在X Windows平台看起来就是linux风格的。
代码和命名上,wxWidgets比较接近 MFC, 但是它的物件封装比 MFC 要好。一般知名的 MFC 程式都会选择用 wxWidgets 改写,来快速移植原程式到其他平台,例如 eMule 用 wxWidgets 移植出 aMule, xMule。 wxWidgets在GUI的控件和窗口布局上可以使用坐标,更多且更方便的是用sizer来布局。sizer就像是一个布局用的容器,只要指定了 sizer的布局方式,然后往里面一个一个的插入待布局的元素就可以了,类似于QT的slot。而且用sizer有一个最大的好处就是sizer内部的元 素可以根据窗口的变化自动的安装指定模式改变自身的大小。 最初根据官方文档用panel和sizer布局的时候总是发现有问题,拷贝过来的示例代码也不同于文档描述的显示界面。经过一段时间的代码试验,找到了一些思路,现分析如下。
首先简要介绍下frame,panel和sizer。 Panel是一个用来控制布局的窗口,通常放在frame里面。类wxPanel继承于父类wxWindow,但重载了父类的一些函数,主要用来控制对话框的外观和功能。 If the wxPanel is the only child window of the frame then it will automatically take up the entire client area of the frame. App类 (简单的初始化窗口,类似代码不再显示)
bool testApp::OnInit() {
testFrame* dlg = new testFrame(0L, _(”wxWidgets Application Template”));
dlg->SetIcon(wxICON(aaaa)); // To Set App Icon
dlg->Show();
return true; }
框架类: testFrame::testFrame(wxFrame *dlg, const wxString &title):
wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(300, 200)) {
wxPanel *panel = new wxPanel(this, -1); //新建一个panel
wxButton *ok = new wxButton(panel, -1, wxT(”Ok”)); //在panel上画一个按钮 }
正常的显示结果,如图
在框架类中添加一行 wxPanel *panel = new wxPanel(this, -1);
wxButton *ok = new wxButton(panel, -1, wxT(”Ok”));
wxButton *cancel = new wxButton(panel, -1, wxT(”Cancel”)); 运行结果,发现显示没有变化 稍微修改一下,加一个坐标
wxPanel *panel = new wxPanel(this, -1);
wxButton *ok = new wxButton(panel, -1, wxT(”Ok”));
wxButton *cancel = new wxButton(panel, -1, wxT(”Cancel”),wxPoint(30,30)); 结果如图
由此可以分析知道在panel上的控件都是基于坐标进行定位的,相同坐标的控件覆盖顺序是前面的覆盖后面的 下面我们再进一步,添加一个垂直的sizer
wxPanel *panel = new wxPanel(this, -1);
wxButton *ok = new wxButton(panel, -1, wxT(”Ok”));
wxButton *cancel = new wxButton(panel, -1, wxT(”Cancel”),wxPoint(30,30));
wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL ); sizer->Add(panel); SetSizer(sizer); 运行结果如图 
黑底的部分是frame的底色,白色的部分则是使用sizer下的panel部分,可以看到panel的大小也改变了,不再占据整个frame的位置,而是它包含控件的总面积 让我们试试sizer不包含panel,而是换为控件button。 sizer->Add(ok); sizer->Add(cancel); panel->SetSizer(sizer); 结果如图
注意这里用的是panel->SetSizer()而不是SetSizer()。
因为Sizer是一个隐式的布局对象,而panel可以作为 底色单独显示出来。panel->SetSizer()就好比先用sizer对包含的元素重新布局,然后把布好局的元素重新印到panel面板上。 SetSizer()隐式调用的是this->SetSizer(),这里的this对象是frame的实例,而两个button的父窗口是 panel,所以如果这里调用SetSizer(),则frame重新布局为一个隐式的布局对象,显示结果将会变的很奇怪。 接下来我们换一下,用dialog替换frame,构造函数还是用最基本的一个button testDialog::testDialog(wxDialog *dlg, const wxString &title): wxDialog(NULL, -1, title, wxPoint(-1, -1), wxSize(300, 200)) {wxPanel *panel = new wxPanel(this, -1);wxButton *ok = new wxButton(panel, -1, wxT(”Ok”));} 显示结果很奇怪,如图
一样的构造函数代码,只是继承的父类不同就产生了不同的结果。
其原因是因为新创建的Frame没有关联的子窗口panel,所以一旦new了一个 panel,它会自动和父窗口frame关联,并自动填充整个frame的位置。但是新创建的dialog对话框自己就有一个panel,再new一个新 的子窗口panel并不会自动填充dialog的位置,而是覆在原来的panel上面。把两个panel都加上颜色分辨的比较清楚
wxPanel *panel = new wxPanel(this, -1); //wxButton *ok = new wxButton(panel, -1, wxT(”Ok”));
wxColour col1,col2; col1.Set(wxT(”#4f5049″));
col2.Set(wxT(”#7d7ded”));
this->SetBackgroundColour(col1);
panel->SetBackgroundColour(col2); 结果如图
可以清楚的看到,下面的大框就是对话框的底色,左上角的小篮框就是new出来的panel。因此,要在对话框中实现上述效果,只要直接使用this指针控件的父窗口就可,而不需要重新new一个panel。
wxButton *ok = new wxButton(this, -1, wxT(”Ok”));
wxButton *cancel = new wxButton(this, -1, wxT(”Cancel”),wxPoint(30,30));
wxBoxSizer* topSizer = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* bottomSizer=new wxBoxSizer(wxHORIZONTAL);
wxButton *h1 = new wxButton(this, -1, wxT(”h1″));
wxButton *h2 = new wxButton(this, -1, wxT(”h2″));
bottomSizer->Add(h1); bottomSizer->Add(h2);
topSizer->Add(ok); topSizer->Add(cancel); topSizer->Add(bottomSizer); SetSizer(topSizer); 运行结果如图
这里用了2个sizer,一个垂直的topSizer包含了一个水平的 bottomSizer.topSizer->Add(bottomSizer);这里所有的控件都是基于对话框自己的panel,他们的父窗口都 是this指针。
如果控件是在不同的panel上面,则代码需要修改了 wxPanel *panel = new wxPanel(this, -1); //一个新的panel wxButton *ok = new wxButton(this, -1, wxT(”Ok”)); wxButton *cancel = new wxButton(this, -1, wxT(”Cancel”),wxPoint(30,30)); wxBoxSizer* topSizer = new wxBoxSizer( wxVERTICAL ); wxBoxSizer* bottomSizer=new wxBoxSizer(wxHORIZONTAL); wxButton *h1 = new wxButton(panel, -1, wxT(”h1″)); wxButton *h2 = new wxButton(panel, -1, wxT(”h2″)); //button都在新的panel上 bottomSizer->Add(h1); bottomSizer->Add(h2); topSizer->Add(ok); topSizer->Add(cancel); panel->SetSizer(bottomSizer);//这里panel要用sizer来重新布局 topSizer->Add(panel);//add的是panel而不是sizer SetSizer(topSizer); 运行结果基本和上图一样。比较两段代码可以看到,如果控件是属于不同的panel,用sizer组合起来的话不能直接add sizer了,就像前面所说的,sizer只是一个隐式的布局对象,如果不放在panel上的话是无法正常显示的。不同panel的sizer不能直接相 互包容,必须通过panel设置sizer模式,上层sizer再包容panel来进行组合布局。
总结一下:
1.frame默认没有自己的子窗口panel,new出来的唯一子窗口panel将会占据它的全面空间
2.对话框默认有自己的子窗口panel,new出来的额外panel可以通过sizer来组合布局,否则会以最小形式直接覆盖在原panel上面。
3.sizer可以Add sizer或panel.只有所有元素属于同一个panel的sizer才能相互直接add,否则应该待包含的panel应该调用SetSizer方法来重新实例化自己的布局,然后用上层sizer来包含这个panel。
总的来说,用wxWidgets实现GUI的界面还是很直观的,基本可以说是有了实例化的对象就可以看到图形。对习惯了MFC中对象和资源分开匹配的开发者来说,实在是一个惊喜,特别是有了sizer布局容器的存在,整体的设计将会更加方便。
本站所有文章若非注明均为原创,转载请保留作者和出处www.yichao.info
Related posts:
- A rough guide for wxWidgets programming
- Book: Cross-Platform GUI Programming with wxWidgets (Bruce Perens)(含中英文版本下载地址)
- Using XRC in wxWidgets based Application for UI design
- wxWidget Layout Algorithm Demo - BoxPlanner
- Python Programming – Sqlite for data persistence
- Python programming- List extend() and append()
- Core Python Programming(1) - Basic
- Code::Blocks IDE in openSUSE 11.1编译和安装指南
- Debian Lenny编译wxWidgets与Code::Blocks SVN版
- wxWidget 类型转换(wxString, wxdatatime)















