[ZETCODE]wxWidgets教程五:布局管理


本教程原文鏈接:http://zetcode.com/gui/wxwidgets/layoutmanagement/

翻譯:瓶哥

日期:2013年12月4日星期三

郵箱:414236069@qq.com

主頁:http://www.cnblogs.com/pingge/

若有翻譯錯誤或者歧義請聯系我!

 

一個典型的應用程序由各種各樣的組件組成,這些組件被放置在容器組件內。一個程序員必須要管理應用程序的界面布局,這不是一個簡單的工作,在wxWidgets里面我們有兩個選擇:

1.使用絕對位置放置組件

2.使用布局控件

 

絕對位置

程序員以像素單位去指定一個組件的位置和大小,當你使用絕對位置時,你會明白以下幾點:

1.當你縮放主窗口時,組件的位置和大小不會改變。

2.程序在不同的平台上看起來不同(蹩腳的)。

3.在你的程序中更改字體也許會破壞布局。

4.如果你決定改變你的布局,你必須要完全重做你的布局,這將是單調乏味且浪費時間的工作。

 

然而有些地方也許能夠使用絕對位置去布局。例如,例如我的教程,我不想使我的例子變得太難理解,所以我經常使用絕對位置布局來解釋一些主題。但是,在真正的應用程序中,程序員通常使用布局控件。

 

在我們的例子中,我們有一個簡單的文本編輯器的骨架,如果我們縮放主窗口,wxTextCtrl這個組件的大小不會像我們預期的那樣去改變。

 

調整大小之前

調整大小之后

absolute.h

#include <wx/wx.h>

class Absolute : public wxFrame
{
public:
    Absolute(const wxString & title);

    wxMenuBar * menubar;
    wxMenu * file;
    wxMenu * edit;
    wxMenu * help;
    wxTextCtrl * textctrl;
};
View Code

absolute.cpp

#include "absolute.h"

Absolute::Absolute(const wxString & title)
        : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(250, 180))
{
    wxPanel * panel = new wxPanel(this, wxID_ANY);

    menubar = new wxMenuBar;
    file = new wxMenu;
    edit = new wxMenu;
    help = new wxMenu;

    menubar->Append(file, _T("&File"));
    menubar->Append(edit, _T("&Edit"));
    menubar->Append(help, _T("&Help"));

    SetMenuBar(menubar);

    textctrl = new wxTextCtrl(panel, -1, _T(""), wxPoint(-1, -1), wxSize(250, 150));

    Centre();
}
View Code

main.h

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};
View Code

main.cpp

#include "main.h"
#include "absolute.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    Absolute * absolute = new Absolute(_T("Absolute"));
    absolute->Show(true);

    return true;
}
View Code

 

這是一個使用絕對位置布局的例子,我們把一個wxTextCtrl組件放置在一個面板組件中。

Textctrl = new wxTextCtrl(panel, -1, _T(""), wxPoint(-1, -1), wxSize(250, 150));

我們在wxTextCtrl組件的構造函數中完成了絕對位置布局,在我們的例子中,我們把它放置在默認的位置,指定寬度250px和高度150px。

 

使用布局控件

wxWidgets里面的布局控件處理關於組件的位置的所有問題。

我們能夠在以下這些布局控件中選擇:

1.wxBoxSizer

2.wxStaticBoxSizer

3.wxGridSizer

4.wxFlexGridSizer

5.wxGridBagSizer

調整大小之前

調整大小之后

sizer.h

#include <wx/wx.h>

class Sizer : public wxFrame
{
public:
    Sizer(const wxString & title);

    wxMenuBar * menubar;
    wxMenu * file;
    wxMenu * edit;
    wxMenu * help;
    wxTextCtrl * textctrl;
};
View Code

sizer.cpp

#include "sizer.h"

Sizer::Sizer(const wxString & title)
     : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(250, 180))
{
    menubar = new wxMenuBar;
    file = new wxMenu;
    edit = new wxMenu;
    help = new wxMenu;

    menubar->Append(file, _T("&File"));
    menubar->Append(edit, _T("&Edit"));
    menubar->Append(help, _T("&Help"));
    SetMenuBar(menubar);

    textctrl = new wxTextCtrl(this, -1, _T(""), wxPoint(-1, -1), wxSize(250, 150));

    Centre();
}
View Code

main.h

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};
View Code

main.cpp

#include "main.h"
#include "sizer.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    Sizer * sizer = new Sizer(_T("Sizer"));
    sizer->Show(true);
    
    return true;
}
View Code

 

wxBoxSizer

這個布局控件允許我們把多個組件放在一行或者一列上,我們能在一個布局控件中放入另一個布局控件。這種設計使得我們能夠設計非常復雜的布局。

wxBoxSizer(int orient)

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組件周圍的邊框

border.h

#include <wx/wx.h>

class Border : public wxFrame
{
public:
    Border(const wxString & title);
};
View Code

border.cpp

#include "border.h"

Border::Border(const wxString & title)
      : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 200))
{
    wxColor col1, col2;
    col1.Set(_T("#4F5049"));
    col2.Set(_T("#EDEDED"));

    wxPanel * panel = new wxPanel(this, -1);
    panel->SetBackgroundColour(col1);

    wxPanel * midPan = new wxPanel(panel, wxID_ANY);
    midPan->SetBackgroundColour(col2);

    wxBoxSizer * vbox = new wxBoxSizer(wxVERTICAL);
    vbox->Add(midPan, 1, wxEXPAND | wxALL, 20);
    panel->SetSizer(vbox);

    Centre();
}
View Code

main.h

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};
View Code

main.cpp

#include "main.h"
#include "border.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    Border * border = new Border(_T("Border"));
    border->Show(true);

    return true;
}
View Code

 

在這個例子中,我們創建了兩個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

 

接下來我們把兩個按鈕放置到窗口的右下角:

align.h

#include <wx/wx.h>

class Align : public wxFrame
{
public:
    Align(const wxString & title);
};
View Code

align.cpp

#include "align.h"

Align::Align(const wxString & title)
     : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(300, 200))
{
    wxPanel * panel = new wxPanel(this, -1);

    wxBoxSizer * vbox = new wxBoxSizer(wxVERTICAL);
    wxBoxSizer * hbox1 = new wxBoxSizer(wxHORIZONTAL);
    wxBoxSizer * hbox2 = new wxBoxSizer(wxHORIZONTAL);

    wxButton * ok = new wxButton(panel, wxID_ANY, _T("OK"));
    wxButton * cancel = new wxButton(panel, wxID_ANY, _T("Cancel"));

    hbox1->Add(new wxPanel(panel, wxID_ANY));
    hbox2->Add(ok);
    hbox2->Add(cancel);

    vbox->Add(hbox1, 1, wxEXPAND);
    vbox->Add(hbox2, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 10);
    panel->SetSizer(vbox);

    Centre();
}
View Code

main.h

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};
View Code

main.cpp

#include "main.h"
#include "align.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    Align * align = new Align(_T("Align"));
    align->Show(true);

    return true;
}
View Code

 

我們創建了三個布局控件,一個垂直控件和兩個水平控件。我們把這兩個水平布局控件放置到垂直布局控件中。

Hbox->Add(new wxPanel(panel, wxID_ANY));

Vbox->Add(hbox, 1, wxEXPAND);

我們把一個wxPanel放置在第一個水平控件中,我們把縮放因子設置為1並且設置了wxEXPAND標識,這樣做這個布局控件就會占據除了hbox2之外的所有空間。

Vbox->Add(hbox2, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 10);

我們把兩個按鈕放置在hbox2這個控件中,在hbox2中的控件是右對齊排列的,而且我們在這兩個按鈕的底部和右邊放置了寬度為10px的空白元素。

 

Go To Class

在接下來的例子中,我們將介紹幾個重要的觀念。

gotoclass.h

#include <wx/wx.h>

class GotoClass : public wxFrame
{
public:
    GotoClass(const wxString & title);
};
View Code

gotoclass.cpp

#include "gotoclass.h"

GotoClass::GotoClass(const wxString & title)
         : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(450, 400))
{
    wxPanel * panel = new wxPanel(this, wxID_ANY);

    /**< Main sizer */
    wxBoxSizer * vbox = new wxBoxSizer(wxVERTICAL);

    /**< hbox1 */
    wxBoxSizer * hbox1 = new wxBoxSizer(wxHORIZONTAL);
    //(*
    wxStaticText * st1 = new wxStaticText(panel, wxID_ANY, _T("CLass Name"));
    hbox1->Add(st1, 0, wxRIGHT, 8);
    wxTextCtrl * tc = new wxTextCtrl(panel, wxID_ANY);
    hbox1->Add(tc, 1);
    vbox->Add(hbox1, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10);
    vbox->Add(-1, 10); // 10 pix space
    //*)

    /**< hbox2 */
    wxBoxSizer * hbox2 = new wxBoxSizer(wxHORIZONTAL);
    //(*
    wxStaticText * st2 = new wxStaticText(panel, wxID_ANY, _T("Matching classes"));
    hbox2->Add(st2, 0);
    vbox->Add(hbox2, 0, wxLEFT | wxTOP, 10);
    vbox->Add(-1, 10);
    //*)

    /**< hbox3 */
    wxBoxSizer * hbox3 = new wxBoxSizer(wxHORIZONTAL);
    //(*
    wxTextCtrl * tc2 = new wxTextCtrl(panel, wxID_ANY, _T(""),
                                      wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
    hbox3->Add(tc2, 1, wxEXPAND);
    vbox->Add(hbox3, 1, wxLEFT | wxRIGHT | wxEXPAND, 10);
    vbox->Add(-1, 25);
    //*)

    /**< hbox4 */
    wxBoxSizer * hbox4 = new wxBoxSizer(wxHORIZONTAL);
    //(*
    wxCheckBox * cb1 = new wxCheckBox(panel, wxID_ANY, _T("Case Sensitive"));
    hbox4->Add(cb1);
    wxCheckBox * cb2 = new wxCheckBox(panel, wxID_ANY, _T("Nexted Classes"));
    hbox4->Add(cb2, 0, wxLEFT, 10);
    wxCheckBox * cb3 = new wxCheckBox(panel, wxID_ANY, _T("Non-Project Classes"));
    hbox4->Add(cb3, 0, wxLEFT, 10);
    vbox->Add(hbox4, 0, wxLEFT, 10);
    vbox->Add(-1, 25);
    //*)

    /**< hbox5 */
    wxBoxSizer * hbox5 = new wxBoxSizer(wxHORIZONTAL);
    //(*
    wxButton * btn1 = new wxButton(panel, wxID_ANY, _T("OK"));
    hbox5->Add(btn1, 0);
    wxButton * btn2 = new wxButton(panel, wxID_ANY, _T("Close"));
    hbox5->Add(btn2, 0, wxLEFT | wxBOTTOM, 5);
    vbox->Add(hbox5, 0, wxALIGN_RIGHT | wxRIGHT, 10);
    //*)

    panel->SetSizer(vbox);
}
View Code

main.h

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};
View Code

main.cpp

#include "main.h"
#include "gotoclass.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    GotoClass * gotoclass = new GotoClass(_T("GotoClass"));
    gotoclass->Show(true);

    return true;
}
View Code

 

這是一個綜合使用wxBoxSizer的例子,布局得比較緊湊,我們創建了一個垂直布局控件,然后我們把5個水平布局控件添加進去。

Vbox->Add(hbox3, 1, wxLEFT | wxRIGHT | wxEXPAND, 10);

Vbox->Add(-1, 25);

我們已經知道我們可以使用flag參數和border參數控制控件之間的間距。但是有一點很不自然,在Add()方法中我們只能給所有的方向指定同一個邊界寬度。在我們的例子中,我們在右和左邊緣給出10px空白,但是我們不能給底部指定25px的空白,我們所能做的只是給底部10px空白或者省略wxBOTTOM給底部0px空白。所以我們需要另一種重載的Add()方法添加一些額外的空白。

Vbox->Add(hbox5, 0, wxALIGN_RIGHT | wxRIGHT, 10);

我們把兩個按鈕放置到窗體的右下角,我們是如何做到的?要達到這種目的,需要注意三樣東西,proportion、align flag、wxEXPAND flag,proportion參數一定要等於0,這樣兩個按鈕就不會改變它們的大小,當我們縮放我們的主窗口的時候我們不能指定wxEXPAND標識。最后,我們必須指定wxALIGN_RIGHT標識,這個水平布局控件會從主窗體的左邊擴展到右邊,所以如果我們指定wxALIGN_RIGHT標識,兩個按鈕會被放置在右邊,這恰好是我們期望的。

 

wxGridSizer

wxGridSizer把控件布局在一個格子中,每一個格子都有相同的大小。

wxGridSizer(int rows, int cols, int vgap, int hgap);

在構造函數中我們指定網格的行數和列數和每個格子的垂直、水平間距。

在我們的例子中我們建立了一個計算器的框架,這是一個介紹wxGridSizer的完美的例子。

gridsizer.h

#include <wx/wx.h>

class GridSizer : public wxFrame
{
public:
    GridSizer(const wxString & title);

    wxBoxSizer * sizer;
    wxGridSizer * gs;
    wxTextCtrl * display;
};
View Code

gridsizer.cpp

#include "gridsizer.h"

GridSizer::GridSizer(const wxString & title)
         : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(270, 220))
{
    sizer = new wxBoxSizer(wxVERTICAL);

    display = new wxTextCtrl(this, wxID_ANY, _T(""), wxDefaultPosition, wxDefaultSize, wxTE_RIGHT);

    sizer->Add(display, 0, wxEXPAND | wxTOP | wxBOTTOM, 4);

    gs = new wxGridSizer(5, 4, 3, 3);

    gs->Add(new wxButton(this, -1, _T("Cls")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("Bck")), 0, wxEXPAND);
    gs->Add(new wxStaticText(this, -1, _T("")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("Close")), 0, wxEXPAND);

    gs->Add(new wxButton(this, -1, _T("7")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("8")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("9")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("/")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("4")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("5")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("6")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("*")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("1")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("2")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("3")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("-")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("0")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T(".")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("=")), 0, wxEXPAND);
    gs->Add(new wxButton(this, -1, _T("+")), 0, wxEXPAND);

    sizer->Add(gs, 1, wxEXPAND);
    SetSizer(sizer);

    SetMinSize(wxSize(270, 220));

    Centre();
}
View Code

main.h

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};
View Code

main.cpp

#include "main.h"
#include "gridsizer.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    GridSizer * gs = new GridSizer(_T("GridSizer"));
    gs->Show(true);

    return true;
}
View Code

 

 

在我們的例子中,我們為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或許同樣可以完成這個任務,但是這樣會影響美觀,因為每一個網格的大小都一樣會顯得很不自然。

flexgridsizer.h

#include <wx/wx.h>

class FlexGridSizer : public wxFrame
{
public:
    FlexGridSizer(const wxString & title);
};
View Code

flexgridsizer.cpp

#include "flexgridsizer.h"

FlexGridSizer::FlexGridSizer(const wxString & title)
             : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(270, 220))
{
    wxPanel * panel = new wxPanel(this, wxID_ANY);

    wxBoxSizer * hbox = new wxBoxSizer(wxHORIZONTAL);

    wxFlexGridSizer * fgs = new wxFlexGridSizer(3, 2, 9, 25);

    wxStaticText * thetitle = new wxStaticText(panel, wxID_ANY, _T("Title"));
    wxStaticText * author   = new wxStaticText(panel, wxID_ANY, _T("Author"));
    wxStaticText * review   = new wxStaticText(panel, wxID_ANY, _T("Review"));

    wxTextCtrl * tc1 = new wxTextCtrl(panel, wxID_ANY);
    wxTextCtrl * tc2 = new wxTextCtrl(panel, wxID_ANY);
    wxTextCtrl * tc3 = new wxTextCtrl(panel, wxID_ANY, _T(""),
                                      wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);

    fgs->Add(thetitle);
    fgs->Add(tc1, 1, wxEXPAND);
    fgs->Add(author);
    fgs->Add(tc2, 1, wxEXPAND);
    fgs->Add(review, 1, wxEXPAND);
    fgs->Add(tc3, 1, wxEXPAND);

    fgs->AddGrowableRow(2, 1);
    fgs->AddGrowableCol(1, 1);

    hbox->Add(fgs, 1, wxALL | wxEXPAND, 15);
    panel->SetSizer(hbox);
    Centre();
}
View Code

main.h

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};
View Code

main.cpp

#include "main.h"
#include "flexgridsizer.h"

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    FlexGridSizer * fgs = new FlexGridSizer(_T("FlexGridSizer"));
    fgs->Show(true);

    return true;
}
View Code

 

在我們的例子中我們創建了一個簡單的對話框,它可以用來把數據插入到數據庫。

wxBoxSizer * hbox = new wxBoxSizer(wxHORIZONTAL);

Hbox->Add(fgs, 1, wxALL | wxEXPAND, 15);

我們創建了一個水平布局控件,用來在組件的邊界制造出15px的空白。

fgs->Add(thetitle);

我們像使用wxGridSizer一樣把組件添加進布局控件。

fgs->AddGrowableRow(2, 1);

fgs->AddGrowableCol(1, 1);

我們讓第三行和第二列成為可擴展的,這樣當主窗口縮放時,第三個多行文本控件就可以自動擴展。前兩個文本控件會在水平方向自動擴展,第三個會在兩個方向自動擴展,我們必須使用wxEXPAND確保它們正常工作。

這一章的wxWidgets教程介紹了如何管理布局。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM