继上一篇【wxWidgest教程】界面布局:自动布局之后,本篇将进入介绍界面布局器wxSizer的使用方法。wxWidgets教程完整目录
调整器使用的布局算法与其他GUI工具包(如Java的AWT、GTK工具包或Qt工具包)中的布局系统几乎相同。它基于单个子窗口报告其最小所需大小以及在父窗口大小发生更改时拉伸。这通常意味着您不设置对话框的启动大小,而是为对话框分配一个调整器,并询问这个调整器的建议大小,调整器将依次查询其子级(可以是普通窗体、空白区域或其他调整器),以便可以构造调整器的层次结构。
使调整器非常适合在wxWidgets中使用的原因是,每个控件都报告自己的最小大小,并且算法可以处理不同平台上字体大小或不同窗口(对话框项)大小的差异,而不会出现问题。例如,如果标准字体以及Linux/GTK小部件的总体设计需要比Windows上更多的空间,那么Linux/GTK上的初始对话框大小将自动比Windows上的大。
调整器同时也是一类容器,用于布局并包含子控件。使用wxWindow::Show()方法,可以像隐藏任何控件一样隐藏大小调整器中包含的控件。不过,wxSizer还提供了一个单独的方法,可以告诉调整器在其大小计算中不要考虑该控件。调用boolShow(wxWindow*window,boolshow=true,boolrecursive=false ),然后必须调用调整器上的void wxSizer::Layout()来强制更新。这在隐藏部分窗口时很有用,因为您可以避免从调整器中删除控件,以后再添加它们,仅wxBoxSizer和wxFlexGridSizer支持该功能。
一、一维布局
(一)wxBoxSizer
wxBoxSizer基于这样一个认识,即窗口通常以非常简单的基本几何形式布局,通常是一行或一列。
wxBoxSizer可以垂直或水平布局其子项,具体取决于其构造函数wxBoxSizer(intorient)中使用的标志是。orient为wxVERTICAL,使用垂直方向排列,每个子项都可以居中、右对齐或左对齐。相应地,orient为wxHORIZONTAL,水平方向排列,每个子项都可以居中、在底部对齐或在顶部对齐。拉伸因子用于主方向,确定子项水平或垂拉伸的程度。
下面的示例演示wxBoxSizer的使用,例子同时使用了两种标志设置方式。
MyFrame::MyFrame(wxWindowID id,const wxString& title,const wxPoint& pos, const wxSize& size ,long style , const wxString& name):wxFrame(NULL,id,title,pos,size,style,name){ //ctor wxSizerFlags sizerFlag; sizerFlag.Border(wxALL,1).Proportion(1).Expand(); wxBoxSizer* outBox=new wxBoxSizer(wxVERTICAL); wxBoxSizer* hBox1=new wxBoxSizer(wxHORIZONTAL); hBox1->Add(new wxButton(this,wxID_ANY,"Test Button 1"),sizerFlag); hBox1->Add(new wxButton(this,wxID_ANY,"Test Button 2"),1,wxEXPAND|wxALL); wxBoxSizer* hBox2=new wxBoxSizer(wxHORIZONTAL); hBox2->Add(new wxTextCtrl(this,wxID_ANY,"hbox2 text box1", wxDefaultPosition,wxDefaultSize, wxTE_MULTILINE),sizerFlag); hBox2->Add(new wxTextCtrl(this,wxID_ANY,"hbox2 text box2", wxDefaultPosition,wxDefaultSize, wxTE_MULTILINE),2,wxEXPAND|wxALL); hBox2->Add(new wxTextCtrl(this,wxID_ANY,"hbox2 text box3", wxDefaultPosition,wxDefaultSize, wxTE_MULTILINE),1,wxEXPAND|wxALL); outBox->Add(hBox1,sizerFlag); outBox->Add(hBox2,wxSizerFlags(1).Expand().Border(wxALL)); SetSizerAndFit(outBox); }(二)wxStaticBoxSizer
wxStaticBoxSizer与wxBoxSizer相同,但由一个静态框包围。下面是一个示例:
MyFrame::MyFrame(wxWindowID id,const wxString& title):wxFrame(NULL,id,title){ //ctor wxBoxSizer *topSizer=new wxBoxSizer(wxVERTICAL); wxStaticBoxSizer* sizer1=new wxStaticBoxSizer(wxHORIZONTAL,this,"wxStaticBoxSizer"); sizer1->Add(new wxCheckBox(this,wxID_ANY,"option 1"),wxALL|wxEXPAND); sizer1->Add(new wxCheckBox(this,wxID_ANY,"option 2"),wxALL|wxEXPAND); sizer1->Add(new wxCheckBox(this,wxID_ANY,"option 3"),wxALL|wxEXPAND); sizer1->Add(new wxCheckBox(this,wxID_ANY,"option 4"),wxALL|wxEXPAND); wxStaticBoxSizer* sizer2=new wxStaticBoxSizer(wxHORIZONTAL,this,"wxStaticBoxSizer"); sizer2->Add(new wxCheckBox(this,wxID_ANY,"option 1"),wxALL|wxEXPAND); sizer2->Add(new wxCheckBox(this,wxID_ANY,"option 2"),wxALL|wxEXPAND); sizer2->Add(new wxCheckBox(this,wxID_ANY,"option 3"),wxALL|wxEXPAND); sizer2->Add(new wxCheckBox(this,wxID_ANY,"option 4"),wxALL|wxEXPAND); topSizer->Add(sizer1,wxSizerFlags(1).Expand().Border(wxALL)); topSizer->Add(sizer2,wxSizerFlags(1).Expand().Border(wxALL)); SetSizerAndFit(topSizer); }(三)wxWrapSizer
一个wxWrapSizer将它的子项排成一行,就像wxBoxSizer一样,只要在主方向有可用的空间,不同的是,当空间占满后,wxWrapSizer将在次方向上添加新行或列。
二、二维布局
(一)wxGridSizer
wxGridSizer是一个二维的调整器。所有子项都被赋予相同的大小,其大小与最大子项所需的最小大小相同。在本例中是左下边框中的文本控件。列数或行数都是固定的,如果添加了新的子项,则网格大小调整器将按各自的其他方向增长:
MyFrame::MyFrame(wxWindowID id , const wxString& title) : wxFrame( nullptr, id, title){ //ctor wxGridSizer *sizer=new wxGridSizer(4,3,wxSize(1,1)); wxString btnNames[]={"0","1","2","3","4","5","6","7","8","9","+","-"}; for(wxString bn:btnNames){ sizer->Add(new wxButton(this,wxWindow::NewControlId(),bn), wxSizerFlags(1).Expand().Border(wxALL,1)); } SetSizerAndFit(sizer); }在wxSizer的基础上,wxGridSizer增加了如下函数,用于设置网络的行列数及行列之间的间隔:voidSetRows(introws);voidSetCols(introws);voidSetVGap(intgap);voidSetHGap(intgap);
(二)wxFlexGridSizer
从wxGridSizer派生的另一个二维调整器。每一列的宽度和每一行的高度是根据各自最大的子级的最小要求分别计算的。此外,如果为调整器分配的大小不同于它所请求的大小,则可以将列和行声明为可扩展的。下面的示例显示的对话框与上面的示例相同,但使用的是wxFlexGridSizer:
MyFrame::MyFrame(wxWindowID id ,const wxString& title) : wxFrame( nullptr, id, title) { //ctor wxFlexGridSizer *sizer=new wxFlexGridSizer(4,3,wxSize(1,1)); wxString btnNames[]={"0","1","2","3","4"," 5+3=5; 2*222=444 \r\nf(n)=100*n", "6","7","8","9","+","-"}; for(wxString bn:btnNames){ sizer->Add(new wxButton(this,wxWindow::NewControlId(),bn),wxSizerFlags(1).Expand().Border(wxALL,1)); } SetSizerAndFit(sizer); }在wxGridSizer的基础上,wxFlexGridSizer增加了以下操作:
设置行列拉伸因子
如同一维布局中子控件的proportion参数一样,在二维弹性布局中,行和列也具有拉伸因子,当调整器中有额外的空间时,该拉伸因子决定了行或列如何扩展以填充剩余空间。通过voidAddGrowableCol(size_tidx,intproportion=0)和voidAddGrowableRow(size_tidx,intproportion=0)函数可以设置行或列的拉伸因子。idx:从0开始计数,标识行号或列号;proportion:行或列的空间占比大小。
如果取消空间占比,则可调用voidRemoveGrowableCol(size_tidx)和voidRemoveGrowableRow(size_tidx)函数。boolIsRowGrowable(size_tidx)和boolIsColGrowable(size_tidx)可判断特定行列是否可拉伸。
设置拉伸方向
从wxWidgets 2.5.0开始,wxFlexGridSizer还可以在一个方向上等长调整控件大小,而在另一个方向则相对灵活,不要求长度相等。可以调用voidSetFlexibleDirection(intdirection)设置这个相对灵活的方向,参数direction可以是wxVERTICAL、wxHORIZONTAL或wxBOTH。而在另一个方向则需要调用voidSetNonFlexibleGrowMode(wxFlexSizerGrowModemode)设置子控件填充剩余空间的方式。mode可以设置为下面的值:
表 11wxFlexSizerGrowMode定义
wxFLEX_GROWMODE_NONE | Sizer不会在非弹性方向上拉伸其子控件 |
wxFLEX_GROWMODE_SPECIFIED | Sizer使用AddGrowtableCol()和AddGrowtableRow()设置的proportion来决定列或行的最小大小(这是默认值) |
wxFLEX_GROWMODE_ALL | Sizer平等地拉伸所有列或行,与proportion参数无关 |
(三)wxGridBagSizer
wxGridBagSizer可以像wxFlexGridSizer一样在虚拟网格中布局控件。与之不同的是,该调整器还能够使用wxGBPosition显式地指定以(col、row)为坐标指定的网格位置,以及使用wxGBSpan指定子控件占用的行数、列数,类似于HTML<table>中的colspan和rowspan属性。该调整器的子项添加操作与其父类不同:
wxSizerItem*Add(wxWindow*window,const wxGBPosition&pos,
const wxGBSpan&span=wxDefaultSpan,
intflag=0,intborder=0,
wxObject*userData=NULL);
三、总结
本文介绍了wxWidgets中wxSizer布局器的使用方法。wxSizer采用基于控件最小大小和拉伸因子的布局算法,支持跨平台自适应。文章详细讲解了一维布局器(wxBoxSizer、wxStaticBoxSizer、wxWrapSizer)和二维布局器(wxGridSizer、wxFlexGridSizer、wxGridBagSizer)的特点及使用场景。重点说明了弹性布局中的行列拉伸因子设置、控件隐藏方法等实用功能,并通过代码示例展示了各类布局器的实际应用。
还有一些界面布局的高级用法,如标准布局、弹簧等,后续将进一步讨论。