布局管理
一个典型的应用程序由各种各样的组件组成,这些组件被放置在容器组件内。一个程序员必须要管理应用程序的界面布局,这不是一个简单的工作,在wxWidgets里面我们有两个选择:
1.使用绝对位置放置组件
2.使用布局控件
绝对位置
程序员以像素单位去指定一个组件的位置和大小,当你使用绝对位置时,你会明白以下几点:
1.当你缩放主窗口时,组件的位置和大小不会改变。
2.程序在不同的平台上看起来不同(蹩脚的)。
3.在你的程序中更改字体也许会破坏布局。
4.如果你决定改变你的布局,你必须要完全重做你的布局,这将是单调乏味且浪费时间的工作。
【实例】
下面是一个使用绝对位置布局的例子:
main.h
1 #include <wx/wx.h> 2 //定义主框架类 3 class MyFrame : public wxFrame 4 { 5 public: 6 MyFrame(const wxString & title); 7 //定义菜单条 8 wxMenuBar * menubar; 9 wxMenu * file; 10 wxMenu * edit; 11 wxMenu * help; 12 //定义一个静态文本框 13 wxTextCtrl * textctrl; 14 }; 15 //定义应用程序类 16 class MyApp : public wxApp 17 { 18 public: 19 virtual bool OnInit(); 20 };
main.cpp
1 #include "main.h" 2 //主框架类的实现 3 MyFrame::MyFrame(const wxString & title) 4 : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(250, 180)) 5 { 6 //定义一个面板容器 7 wxPanel * panel = new wxPanel(this, wxID_ANY); 8 //添加菜单条 9 menubar = new wxMenuBar; 10 file = new wxMenu; 11 edit = new wxMenu; 12 help = new wxMenu; 13 14 menubar->Append(file, _T("&File")); 15 menubar->Append(edit, _T("&Edit")); 16 menubar->Append(help, _T("&Help")); 17 18 SetMenuBar(menubar); 19 //向面板添加一个静态文本框,使用wxPoint(-1, -1)对静态文本框绝对位置布局 20 textctrl = new wxTextCtrl(panel, -1, _T(""), wxPoint(-1, -1), wxSize(250, 150)); 21 22 Centre(); 23 } 24 //声明应用程序 25 IMPLEMENT_APP(MyApp) 26 27 bool MyApp::OnInit() 28 { 29 MyFrame * myFrame = new MyFrame(_T("MyFrame")); 30 myFrame->Show(true); 31 32 return true; 33 }
展示效果:
调整大小之前:
调整大小之后:
可以看出使用绝对位置布局的静态文本框,不会随着整体框架大小的改变而改变。
使用布局控件
wxWidgets里面的布局控件处理关于组件的位置的所有问题。
我们能够在以下这些布局控件中选择:
1.wxBoxSizer
2.wxStaticBoxSizer
3.wxGridSizer
4.wxFlexGridSizer
5.wxGridBagSizer
【实例】
下面是一个静态文本框会随着主框架的大小而改变大小的实例。
main.h
1 #include <wx/wx.h> 2 /* 3 使用绝对位置进行布局 4 */ 5 //定义主框架类 6 class MyFrame : public wxFrame 7 { 8 public: 9 MyFrame(const wxString & title); 10 //定义菜单条 11 wxMenuBar * menubar; 12 wxMenu * file; 13 wxMenu * edit; 14 wxMenu * help; 15 //定义一个静态文本框 16 wxTextCtrl * textctrl; 17 }; 18 //定义应用程序类 19 class MyApp : public wxApp 20 { 21 public: 22 virtual bool OnInit(); 23 };
main.cpp
1 #include "main.h" 2 //主框架类的实现 3 MyFrame::MyFrame(const wxString & title) 4 : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(250, 180)) 5 { 6 //添加菜单条 7 menubar = new wxMenuBar; 8 file = new wxMenu; 9 edit = new wxMenu; 10 help = new wxMenu; 11 12 menubar->Append(file, _T("&File")); 13 menubar->Append(edit, _T("&Edit")); 14 menubar->Append(help, _T("&Help")); 15 16 SetMenuBar(menubar); 17 //向面板添加一个静态文本框 18 textctrl = new wxTextCtrl(this, -1, _T(""), wxPoint(-1, -1), wxSize(250, 150)); 19 20 Centre(); 21 } 22 //声明应用程序 23 IMPLEMENT_APP(MyApp) 24 25 bool MyApp::OnInit() 26 { 27 MyFrame * myFrame = new MyFrame(_T("MyFrame")); 28 myFrame->Show(true); 29 30 return true; 31 }
效果展示
调整大小之前:
调整大小之后:
wxBoxSizer
这个布局控件允许我们把多个组件放在一行或者一列上,我们能在一个布局控件中放入另一个布局控件。这种设计使得我们能够设计非常复杂的布局。
1 wxBoxSizer(int orient) 2 wxSIzerItem * Add(wxWindow * window, int proportion = 0, int flag = 0, int border = 0)
参数orient可以是wxVERTICAL或者wxHORIZONTAL。通过Add()方法添加组件到wxBoxSizer内,为了能够更好的理解它,我们需要看它的参数。
参数proportion定义了组件在指定的排列方向内自由缩放的比例,让我们假定有三个按钮,它们的proportion分别是0、1、2它们被添加进一个水平布局控,proportion = 0的按钮始终都不会改变,proportion = 2的按钮会比proportion = 1的按钮在水平尺寸上多缩放一倍的尺寸。
有了flag参数你能够进一步设置wxBoxSizer内的组件的行为,我们能够控制两个组件之间的边界距离,我们可以在两个组件之间填充一些空白像素。为了显示边框,我们需要定义哪个方向上的边框需要使用。我们能够使用|运算符把它们组合起来,例如wxLEFT | wxBOTTOM,我们能够下面这些标志中选择:
1.wxLEFT 2.wxRIGHT 3.wxBOTTOM 4.wxTOP 5.wxALL
【实例】一个wxPanel组件周围的边框
main.h
1 #include <wx/wx.h> 2 /* 3 使用wxBoxSizer进行布局 4 */ 5 //定义主框架类 6 class MyFrame : public wxFrame 7 { 8 public: 9 MyFrame(const wxString & title); 10 }; 11 //定义应用程序类 12 class MyApp : public wxApp 13 { 14 public: 15 virtual bool OnInit(); 16 };
main.cpp
1 #include "main.h" 2 //主框架类的实现 3 MyFrame::MyFrame(const wxString & title) 4 : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(250, 180)) 5 { 6 //颜色定义 7 wxColor col1, col2; 8 col1.Set(_T("#4F50F9")); 9 col2.Set(_T("#EDEDED")); 10 //定义底层面板,并为面板设置背景色 11 wxPanel * panel = new wxPanel(this, -1); 12 panel->SetBackgroundColour(col1); 13 //定义顶层面板,并为面板设置背景色 14 wxPanel * midPan = new wxPanel(panel, wxID_ANY); 15 midPan->SetBackgroundColour(col2); 16 //定义wxBoxSizer容器,布局方式为垂直布局 17 wxBoxSizer * vbox = new wxBoxSizer(wxVERTICAL); 18 //将midpan加载到vbox中 19 vbox->Add(midPan, 1, wxEXPAND | wxALL, 30); 20 //将wxBoxSizer容器放入Panel面板中 21 panel->SetSizer(vbox); 22 23 Centre(); 24 } 25 //声明应用程序 26 IMPLEMENT_APP(MyApp) 27 28 bool MyApp::OnInit() 29 { 30 MyFrame * myFrame = new MyFrame(_T("MyFrame")); 31 myFrame->Show(true); 32 33 return true; 34 }
在这个例子中,我们创建了两个panels,第二个panel在其自身周围有一圈空白。
Box->Add(midPan, 1, wxEXPAND | wxALL, 20);
我们在midPan这个panel周围放置了宽度为20px的边框,wxALL表示边框适用于全部四个方向。如果我们使用wxEXPAND标识,这个组件会在允许的边框内扩展到最大。
最后,我们也可以定义组件的对齐标识,我们使用以下标识去定义:
1.wxALIGN_LEFT 2.wxALIGN_RIGHT 3.wxALIGN_TOP 4.wxALIGN_BOTTOM 5.wxALIGN_CENTER_VERTICAL 6.wxALIGN_CENTER_HORIZONTAL 7.wxALIGN_CENTER
【实例】组件对齐
main.h
1 #include <wx/wx.h> 2 /* 3 使用wxBoxSizer进行布局,组件的对齐方式 4 */ 5 //定义主框架类 6 class MyFrame : public wxFrame 7 { 8 public: 9 MyFrame(const wxString & title); 10 }; 11 //定义应用程序类 12 class MyApp : public wxApp 13 { 14 public: 15 virtual bool OnInit(); 16 };
main.cpp
1 #include "main.h" 2 //主框架类的实现 3 MyFrame::MyFrame(const wxString & title) 4 : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(250, 180)) 5 { 6 //颜色定义 7 wxColor col1; 8 col1.Set(_T("#4F50F9")); 9 10 //定义底层面板,并为面板设置背景色 11 wxPanel * panel = new wxPanel(this, -1); 12 panel->SetBackgroundColour(col1); 13 14 //定义了一个垂直布局控件,两个水平布局控件 15 wxBoxSizer * vbox = new wxBoxSizer(wxVERTICAL); 16 wxBoxSizer * hbox1 = new wxBoxSizer(wxHORIZONTAL); 17 wxBoxSizer * hbox2 = new wxBoxSizer(wxHORIZONTAL); 18 //定义两个按钮 19 wxButton * ok = new wxButton(panel, wxID_ANY, _T("OK")); 20 wxButton * cancel = new wxButton(panel, wxID_ANY, _T("Cancel")); 21 //在水平布局控件hbox1中,新建了一个空白面板 22 hbox1->Add(new wxPanel(panel, wxID_ANY)); 23 //在水平布局控件hbox2中,加入了两个按钮 24 hbox2->Add(ok); 25 hbox2->Add(cancel); 26 //将两个水平布局控件加载到垂直布局控件中 27 vbox->Add(hbox1, 1, wxEXPAND); 28 //水平布局控件hbox2右对齐,且与底部。右侧的间隔为10像素(注意这些单词的大小写,大小写敏感) 29 vbox->Add(hbox2, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 10); 30 //将垂直布局控件vbox加载到panel中 31 panel->SetSizer(vbox); 32 33 //将wxBoxSizer容器放入Panel面板中 34 panel->SetSizer(vbox); 35 36 Centre(); 37 } 38 //声明应用程序 39 IMPLEMENT_APP(MyApp) 40 41 bool MyApp::OnInit() 42 { 43 MyFrame * myFrame = new MyFrame(_T("MyFrame")); 44 myFrame->Show(true); 45 46 return true; 47 }
在这个实例中,我们创建了三个布局控件,一个垂直控件和两个水平控件。我们把这两个水平布局控件放置到垂直布局控件中。
1 Hbox->Add(new wxPanel(panel, wxID_ANY)); 2 Vbox->Add(hbox, 1, wxEXPAND);
我们把一个wxPanel放置在第一个水平控件中,我们把缩放因子设置为1并且设置了wxEXPAND标识,这样做这个布局控件就会占据除了hbox2之外的所有空间。
1 Vbox->Add(hbox2, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 10);
我们把两个按钮放置在hbox2这个控件中,在hbox2中的控件是右对齐排列的,而且我们在这两个按钮的底部和右边放置了宽度为10px的空白元素。
wxGridSizer
wxGridSizer把控件布局在一个格子中,每一个格子都有相同的大小。
1 wxGridSizer(int rows, int cols, int vgap, int hgap);
在构造函数中我们指定网格的行数和列数和每个格子的垂直、水平间距。在我们的例子中我们建立了一个计算器的框架,这是一个介绍wxGridSizer的完美的例子。
main.h
1 #include <wx/wx.h> 2 //定义主框架类 3 class GridSizer : public wxFrame 4 { 5 public: 6 GridSizer(const wxString & title); 7 8 wxBoxSizer * sizer; 9 wxGridSizer * gs; 10 wxTextCtrl * display; 11 }; 12 13 //定义应用程序类 14 class MyApp : public wxApp 15 { 16 public: 17 virtual bool OnInit(); 18 };
main.cpp
1 #include "main.h" 2 3 //主框架类的实现 4 GridSizer::GridSizer(const wxString & title) 5 : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(270, 220)) 6 { 7 sizer = new wxBoxSizer(wxVERTICAL);//垂直布局控件wxBoxSizer 8 //静态文本框,用于显示数据 9 display = new wxTextCtrl(this, wxID_ANY, _T(""), wxDefaultPosition, wxDefaultSize, wxTE_RIGHT); 10 //将静态文本框,加载到wxBoxSizer中 11 sizer->Add(display, 0, wxEXPAND | wxTOP | wxBOTTOM, 4); 12 //定义一个wxGridSizer控件,5行 4列 垂直间隔为3 水平间隔为3 13 gs = new wxGridSizer(5, 4, 3, 3); 14 //按照每行4个子组件,先wxGridSizer中添加内容 15 gs->Add(new wxButton(this, -1, _T("Cls")), 0, wxEXPAND); 16 gs->Add(new wxButton(this, -1, _T("Bck")), 0, wxEXPAND); 17 gs->Add(new wxStaticText(this, -1, _T("")), 0, wxEXPAND); 18 gs->Add(new wxButton(this, -1, _T("Close")), 0, wxEXPAND); 19 20 gs->Add(new wxButton(this, -1, _T("7")), 0, wxEXPAND); 21 gs->Add(new wxButton(this, -1, _T("8")), 0, wxEXPAND); 22 gs->Add(new wxButton(this, -1, _T("9")), 0, wxEXPAND); 23 gs->Add(new wxButton(this, -1, _T("/")), 0, wxEXPAND); 24 25 gs->Add(new wxButton(this, -1, _T("4")), 0, wxEXPAND); 26 gs->Add(new wxButton(this, -1, _T("5")), 0, wxEXPAND); 27 gs->Add(new wxButton(this, -1, _T("6")), 0, wxEXPAND); 28 gs->Add(new wxButton(this, -1, _T("*")), 0, wxEXPAND); 29 30 gs->Add(new wxButton(this, -1, _T("1")), 0, wxEXPAND); 31 gs->Add(new wxButton(this, -1, _T("2")), 0, wxEXPAND); 32 gs->Add(new wxButton(this, -1, _T("3")), 0, wxEXPAND); 33 gs->Add(new wxButton(this, -1, _T("-")), 0, wxEXPAND); 34 35 gs->Add(new wxButton(this, -1, _T("0")), 0, wxEXPAND); 36 gs->Add(new wxButton(this, -1, _T(".")), 0, wxEXPAND); 37 gs->Add(new wxButton(this, -1, _T("=")), 0, wxEXPAND); 38 gs->Add(new wxButton(this, -1, _T("+")), 0, wxEXPAND); 39 40 //将wxGridSizer加载到wxBoxSizer中 41 sizer->Add(gs, 1, wxEXPAND); 42 //将wxBoxSizer加载到主框架中 43 this->SetSizer(sizer); 44 //设置主框架的最小尺寸 45 this->SetMinSize(wxSize(270, 220)); 46 47 Centre(); 48 } 49 //声明应用程序 50 IMPLEMENT_APP(MyApp) 51 //应用程序类初始化函数 52 bool MyApp::OnInit() 53 { 54 GridSizer * gs = new GridSizer(_T("GridSizer")); 55 gs->Show(true); 56 57 return true; 58 }
在我们的例子中,我们为wxFrame建立一个垂直布局控件,我们把一个静态文本和一个网格布局控件放进垂直布局控件。注意我们是如何在Bck和Close按钮之间添加空白的,我们只是简单的添加了一个空的wxStaticText,这是一个很常用的技巧。
gs->Add(new wxButton(this, -1, _T("Cls")), 0, wxEXPAND);
我们调用Add()方法许多次,组件被顺序放置进网格布局控件,第一行被放满,第二行,第三行同样。
效果展示:
wxFlexGridSizer
这个布局控件和wxGridSizer有点相似,它同样把组件布局到有两个尺寸的格子中,但是它添加了一些灵活性,wxGridSizer的格子都是相同大小的,在wxFlexSizer中所有的格子在一行上有相同的高度,一列上有相同的宽度,但是所有的行和列不一定有相同的高度和宽度。
wxFlexGridSize(int rows, int cols, int vgap, int hgap);
rows和cols指定了布局控件中的行数和列数。vgap和hgap在组件之间两个方向上添加了一些空白。
许多时候程序员需要开发一个对话框用来进行数据录入或修改,wxFlexGridSIzer很适合这个任务,一个程序员可以使用这个布局控件轻松的创建一个对话框,使用wxGridSizer或许同样可以完成这个任务,但是这样会影响美观,因为每一个网格的大小都一样会显得很不自然。
【实例】
main.h
1 #include <wx/wx.h> 2 //定义主框架类 3 class FlexGridSizer : public wxFrame 4 { 5 public: 6 FlexGridSizer(const wxString & title); 7 }; 8 9 //定义应用程序类 10 class MyApp : public wxApp 11 { 12 public: 13 virtual bool OnInit(); 14 };
main.cpp
1 #include "main.h" 2 //主框架类的实现 3 FlexGridSizer::FlexGridSizer(const wxString & title) 4 : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(270, 220)) 5 { 6 //定义一个面板 7 wxPanel * panel = new wxPanel(this, wxID_ANY); 8 //定义一个wxBoxSizer水平布局控件 9 wxBoxSizer * hbox = new wxBoxSizer(wxHORIZONTAL); 10 //定义一个wxFlexGridSizer布局控件,行数为3, 列数为2, 垂直间隔为9, 水平间隔为25 11 wxFlexGridSizer * fgs = new wxFlexGridSizer(3, 2, 9, 25); 12 13 //定义三个静态文本 14 wxStaticText * thetitle = new wxStaticText(panel, wxID_ANY, _T("Title :")); 15 wxStaticText * author = new wxStaticText(panel, wxID_ANY, _T("Author:")); 16 wxStaticText * review = new wxStaticText(panel, wxID_ANY, _T("Review:")); 17 //定义三个文本框 18 wxTextCtrl * tc1 = new wxTextCtrl(panel, wxID_ANY); 19 wxTextCtrl * tc2 = new wxTextCtrl(panel, wxID_ANY); 20 wxTextCtrl * tc3 = new wxTextCtrl(panel, wxID_ANY, _T(""), 21 wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE); 22 23 //将静态文版和文本框加载到wxFlexGridSizer布局控件中 24 fgs->Add(thetitle); 25 fgs->Add(tc1, 1, wxEXPAND); 26 fgs->Add(author); 27 fgs->Add(tc2, 1, wxEXPAND); 28 fgs->Add(review, 1, wxEXPAND); 29 fgs->Add(tc3, 1, wxEXPAND); 30 31 //将第三行和第二列设置为可扩展的 32 fgs->AddGrowableRow(2, 1); 33 fgs->AddGrowableCol(1, 1); 34 35 //将wxFlexGridSizer布局控件加载到wxBoxSizer水平布局控件中 36 hbox->Add(fgs, 1, wxALL | wxEXPAND, 15); 37 //将水平布局控件加载到Panel中 38 panel->SetSizer(hbox); 39 Centre(); 40 } 41 42 IMPLEMENT_APP(MyApp) 43 44 bool MyApp::OnInit() 45 { 46 FlexGridSizer * fgs = new FlexGridSizer(_T("FlexGridSizer")); 47 fgs->Show(true); 48 49 return true; 50 }
效果展示:
1 wxBoxSizer * hbox = new wxBoxSizer(wxHORIZONTAL); 2 Hbox->Add(fgs, 1, wxALL | wxEXPAND, 15);
我们创建了一个水平布局控件,用来在组件的边界制造出15px的空白。
fgs->Add(thetitle);
我们像使用wxGridSizer一样把组件添加进布局控件。
1 fgs->AddGrowableRow(2, 1); 2 fgs->AddGrowableCol(1, 1);
我们让第三行和第二列成为可扩展的,这样当主窗口缩放时,第三个多行文本控件就可以自动扩展。前两个文本控件会在水平方向自动扩展,第三个会在两个方向自动扩展,我们必须使用wxEXPAND确保它们正常工作。