5、wxWidgets布局管理

布局管理

一个典型的应用程序由各种各样的组件组成,这些组件被放置在容器组件内。一个程序员必须要管理应用程序的界面布局,这不是一个简单的工作,在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确保它们正常工作。

 

Published by

风君子

独自遨游何稽首 揭天掀地慰生平

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注