0%

wxWidgets入门教程之八:组件专题1

在这一章中,我们将展示几个关于控件的小例子,控件组成了我们的应用程序,wxWidgets中包含了大量有用的控件。控件被定义为一个GUI的基本对象,widget这个词通常在UNIX系统上使用,在windows系统上一个控件通常被叫做control。

wxCheckBox

wxCheckBox是一个有两个状态的控件,选中/未选中。它看起来是一个选框和一个标签,这个标签可以被设置在选框的左边或者右边,如果这个选框被选中,在这选框中会有一个√标记,一个wxCheckBox可以用来控制在启动时显示或隐藏LOGO,显示或隐藏一个工具栏,等等。
checkbox.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14

#include <wx/wx.h>

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

void OnToggle(wxCommandEvent & event);

wxCheckBox * m_cb;

enum{ID_CHECKBOX};
};

checkbox.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include "checkbox.h"

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

m_cb = new wxCheckBox(panel, ID_CHECKBOX, _T("Show title"), wxPoint(200 / 2, 80 / 2));

m_cb->SetValue(true);

Connect(ID_CHECKBOX, wxEVT_COMMAND_CHECKBOX_CLICKED,
wxCommandEventHandler(CheckBox::OnToggle));
Centre();
}

void CheckBox::OnToggle(wxCommandEvent & WXUNUSED(event))
{
if(m_cb->GetValue())
{
this->SetTitle(_T("CheckBox"));
}
else
{
this->SetTitle(_T(""));
}
}

在我们的例子中,我们显示了一个checkbox,我们通过这个选框来显示或隐藏主窗口的标题。

m_cb = new wxCheckBox(panel, ID_CHECKBOX, _T(“Show title”), wxPoint(200 / 2, 80 / 2));
m_cb->SetValue(true);

我们创建了一个checkbox,并调用SetValue()方法,设置默认为选中状态。

Connect(ID_CHECKBOX, wxEVT_COMMAND_CHECKBOX_CLICKED,
wxCommandEventHandler(CheckBox::OnToggle));

如果我们点击了这个选框,一个wxEVT_COMMAND_CHECKBOX_CLICKED事件产生,我们把这个事件同OnToggle()方法关联起来。

if(m_cb->GetValue())
{
this->SetTitle(_T(“CheckBox”));
}
else
{
this->SetTitle(_T(“”));
}

在OnToggle()方法内,我们检查了checkbox的状态,如果是选中状态,我们把标题设置为”CheckBox”,如果是未选中,我们隐藏标题字符串。

wxBitmapButton

一个位图按钮是一个特殊的按钮,它的主体是一张图片。一个位图按钮上的图片可以随意设置。下面我们可以为三种音量状态指定不同的图片。

bitmapbutton.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <wx/wx.h>
#include <wx/slider.h>

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

wxSlider * slider;
wxBitmapButton * button;
int pos;

void OnScroll(wxScrollEvent & event);

enum{ID_SLIDER};
};

bitmapbutton.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include "bitmapbutton.h"

BitmapButton::BitmapButton(const wxString & title)
: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 130))
{
wxImage::AddHandler(new wxPNGHandler);

wxPanel * panel = new wxPanel(this);

slider = new wxSlider(panel, ID_SLIDER, 0, 0, 100, wxPoint(10, 30), wxSize(140, -1));

button = new wxBitmapButton(panel, wxID_ANY, wxBitmap(_T("muted.png"), wxBITMAP_TYPE_PNG), wxPoint(180, 20));

Connect(ID_SLIDER, wxEVT_COMMAND_SLIDER_UPDATED,
wxScrollEventHandler(BitmapButton::OnScroll));

Centre();
}

void BitmapButton::OnScroll(wxScrollEvent & event)
{
pos = slider->GetValue();

if(pos == 0)
{
button->SetBitmapLabel(wxBitmap(_T("muted.png"), wxBITMAP_TYPE_PNG));
}
else if(pos > 0 && pos <= 30)
{
button->SetBitmapLabel(wxBitmap(_T("low.png"), wxBITMAP_TYPE_PNG));
}
else if(pos > 30 && pos < 80)
{
button->SetBitmapLabel(wxBitmap(_T("medium.png"), wxBITMAP_TYPE_PNG));
}
else
{
button->SetBitmapLabel(wxBitmap(_T("high.png"), wxBITMAP_TYPE_PNG));
}
}

在我们的例子中,我们创建了一个滑块和一个位图按钮,我们通过拖动滑块改变按钮的图标来模仿了一个音量控制器。

wxImage::AddHandler(new wxPNGHandler);

我们将要用到PNG图片,所以我们初始化了PNG图像处理模块。

button = new wxBitmapButton(panel, wxID_ANY, wxBitmap(_T(“muted.png”), wxBITMAP_TYPE_PNG), wxPoint(180, 20));

我们创建了一个位图按钮,我们指定了位图的类型wxBITMAP_TYPE_PNG

pos = slider->GetValue();

我们获取了滑块的数据,使用这个数据来设置按钮上的不同图标,我们有四种音量:静音、最低、中等、最大。我们使用SetBitmapLabel()方法来改变按钮上的图片。
)

wxToggleButton

wxToggleButton是一个有两种状态的按钮:按下或未按下,你可以通过点击这个按钮在这两种状态间切换。在有些情况下这种功能非常有用。

togglebutton.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <wx/wx.h>
#include <wx/tglbtn.h>

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

void OnToggleRed(wxCommandEvent & event);
void OnToggleGreen(wxCommandEvent & evnet);
void OnToggleBlue(wxCommandEvent & event);

protected:
wxToggleButton * m_tgbutton1;
wxToggleButton * m_tgbutton2;
wxToggleButton * m_tgbutton3;

wxPanel * m_panel;
wxColour * colour;

enum{ID_TGBUTTON1,
ID_TGBUTTON2,
ID_TGBUTTON3};
};

togglebutton.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include "togglebutton.h"

ToggleButton::ToggleButton(const wxString & title)
: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(280, 180))
{
wxPanel * panel = new wxPanel(this, wxID_ANY);

colour = new wxColour(0, 0, 0);

m_tgbutton1 = new wxToggleButton(panel, ID_TGBUTTON1, _T("Red"), wxPoint(20, 20));
m_tgbutton2 = new wxToggleButton(panel, ID_TGBUTTON2, _T("Green"), wxPoint(20, 70));
m_tgbutton3 = new wxToggleButton(panel, ID_TGBUTTON3, _T("Blue"), wxPoint(20, 120));

Connect(ID_TGBUTTON1, wxEVT_COMMAND_TOGGLEBUTTON_CLICKED,
wxCommandEventHandler(ToggleButton::OnToggleRed));
Connect(ID_TGBUTTON2, wxEVT_COMMAND_TOGGLEBUTTON_CLICKED,
wxCommandEventHandler(ToggleButton::OnToggleGreen));
Connect(ID_TGBUTTON3, wxEVT_COMMAND_TOGGLEBUTTON_CLICKED,
wxCommandEventHandler(ToggleButton::OnToggleBlue));

m_panel = new wxPanel(panel, wxID_ANY, wxPoint(150, 20), wxSize(110, 110), wxSUNKEN_BORDER);

m_panel->SetBackgroundColour(colour->GetAsString());

Centre();
}

void ToggleButton::OnToggleRed(wxCommandEvent & WXUNUSED(event))
{
unsigned char green = colour->Green();
unsigned char blue = colour->Blue();
if(colour->Red())
{
colour->Set(0, green, blue);
}
else
{
colour->Set(255, green, blue);
}
m_panel->SetBackgroundColour(wxColour(255, 0, 0));

Refresh();
}

void ToggleButton::OnToggleGreen(wxCommandEvent & WXUNUSED(event))
{
unsigned char red = colour->Red();
unsigned char blue = colour->Blue();

if(colour->Green())
{
colour->Set(red, 0, blue);
}
else
{
colour->Set(red, 255, blue);
}
m_panel->SetBackgroundColour(colour->GetAsString());

Refresh();
}

void ToggleButton::OnToggleBlue(wxCommandEvent & WXUNUSED(event))
{
unsigned char red = colour->Red();
unsigned char green = colour->Green();

if(colour->Blue())
{
colour->Set(red, green, 0);
}
else
{
colour->Set(red, green, 255);
}
m_panel->SetBackgroundColour(colour->GetAsString());

Refresh();
}

在我们的例子中,我们显示了三个开关按钮和一个panel,我们把panel的 背景色设置为黑色,通过开关按钮来显示红色绿色和蓝色,背景色会根据不同开关按钮的状态发生改变。

colour = new wxColour(0, 0, 0);

这是一个颜色值的初始化,RGB=#000000表示黑色,理论上来说黑色不是一种颜色orz

m_tgbutton1 = new wxToggleButton(panel, ID_TGBUTTON1, _T(“Red”), wxPoint(20, 20));

这里我们创建了一个开关按钮。

Connect(ID_TGBUTTON1, wxEVT_COMMAND_TOGGLEBUTTON_CLICKED,
wxCommandEventHandler(ToggleButton::OnToggleRed));

如果我们点击了一个开关按钮,一个wxEVT_COMMAND_TOGGLEBUTTON_CLICKED事件产生,我们把处理函数和这个事件连接起来。注意,我们也可以不把这个事件同开关按钮的处理函数连接,而是连接到wxFrame组件(这个开关按钮的父亲组件),因为事件指令会传递到父控件,在我们的例子中就是,toggle button→panel→frame。如果我们想把这个事件连接到按钮我们应该派生出自己的按钮类,这意味着更多的代码。

if(colour->Red())
{
colour->Set(0, green, blue);
}
else
{
colour->Set(255, green, blue);
}

在事件处理函数中我们指定了不同的wxColour参数。

m_panel->SetForegroundColour(colour->GetAsString());

我们设置了panel的背景色

最后不要忘了刷新背景,原教程中由于没有刷新背景,害我找了1小时的错。。。坑爹啊,不知道原作者是怎么运行出来的,难道Linux会自己刷新?。。

Refresh();

刷新背景

wxStaticLine

这个组件在窗口内显示一条简单的线段,它可是水平的也可以是垂直的。

staticline.h

1
2
3
4
5
6
7
#include <wx/wx.h>

class Staticline : public wxDialog
{
public:
Staticline(const wxString & title);
};

staticline.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include "staticline.h"
#include <wx/stattext.h>
#include <wx/statline.h>

Staticline::Staticline(const wxString & title)
: wxDialog(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(360, 350))
{
wxFont font(10, wxDEFAULT, wxNORMAL, wxBOLD);
wxStaticText * heading = new wxStaticText(this, wxID_ANY, _T("The Central Europe"), wxPoint(30, 15));
heading->SetFont(font);

wxStaticLine * sl1 = new wxStaticLine(this, wxID_ANY, wxPoint(25, 50), wxSize(300,1));

wxStaticText * st1 = new wxStaticText(this, wxID_ANY, _T("Slovakia"), wxPoint(25, 80));
wxStaticText * st2 = new wxStaticText(this, wxID_ANY, _T("Hungary"), wxPoint(25, 100));
wxStaticText * st3 = new wxStaticText(this, wxID_ANY, _T("Poland"), wxPoint(25, 120));
wxStaticText * st4 = new wxStaticText(this, wxID_ANY, _T("Czech Republic"), wxPoint(25, 140));
wxStaticText * st5 = new wxStaticText(this, wxID_ANY, _T("Germany"), wxPoint(25, 160));
wxStaticText * st6 = new wxStaticText(this, wxID_ANY, _T("Slovenia"), wxPoint(25, 180));
wxStaticText * st7 = new wxStaticText(this, wxID_ANY, _T("Austria"), wxPoint(25, 200));
wxStaticText * st8 = new wxStaticText(this, wxID_ANY, _T("Switzerland"), wxPoint(25, 220));

wxStaticText * st9 = new wxStaticText(this, wxID_ANY, _T("5 379 000"), wxPoint(220, 80), wxSize(90, -1), wxALIGN_RIGHT);
wxStaticText * st10 = new wxStaticText(this, wxID_ANY, _T("10 084 000"), wxPoint(220, 100), wxSize(90, -1), wxALIGN_RIGHT);
wxStaticText * st11 = new wxStaticText(this, wxID_ANY, _T("38 635 000"), wxPoint(220, 120), wxSize(90, -1), wxALIGN_RIGHT);
wxStaticText * st12 = new wxStaticText(this, wxID_ANY, _T("10 240 000"), wxPoint(220, 140), wxSize(90, -1), wxALIGN_RIGHT);
wxStaticText * st13 = new wxStaticText(this, wxID_ANY, _T("82 443 000"), wxPoint(220, 160), wxSize(90, -1), wxALIGN_RIGHT);
wxStaticText * st14 = new wxStaticText(this, wxID_ANY, _T("2 001 000"), wxPoint(220, 180), wxSize(90, -1), wxALIGN_RIGHT);
wxStaticText * st15 = new wxStaticText(this, wxID_ANY, _T("8 032 000"), wxPoint(220, 200), wxSize(90, -1), wxALIGN_RIGHT);
wxStaticText * st16 = new wxStaticText(this, wxID_ANY, _T("7 288 000"), wxPoint(220, 220), wxSize(90, -1), wxALIGN_RIGHT);

wxStaticLine * sl2 = new wxStaticLine(this, wxID_ANY, wxPoint(25, 260), wxSize(300, 1));

wxStaticText * sum = new wxStaticText(this, wxID_ANY, _T("164 102 000"), wxPoint(220, 280));
wxFont sum_font = sum->GetFont();
sum_font.SetWeight(wxBOLD);
sum->SetFont(sum_font);

Centre();
}

在这个例子中,我们显示了欧洲的国家和它们有的人口,使用wxStaticLine使得界面看上去更友好。

wxStaticLine * sl1 = new wxStaticLine(this, wxID_ANY, wxPoint(25, 50), wxSize(300,1));

这里我们创建了一个水平的分隔线,宽度为300像素,高度为1像素。

wxStaticText

一个wxStaticText组件可以显示一行或多行只读的文本。

statictext.h

1
2
3
4
5
6
7
#include <wx/wx.h>

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

statictext.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "statictext.h"

StaticText::StaticText(const wxString & title)
: wxFrame(NULL, wxID_ANY, title)
{
wxPanel *panel = new wxPanel(this, wxID_ANY);
wxString text = wxT("'Cause sometimes you feel tired,\n\
feel weak, and when you feel weak,\
you feel like you wanna just give up.\n\
But you gotta search within you,\
you gotta find that inner strength\n\
and just pull that shit out of you\
and get that motivation to not give up\n\
and not be a quitter,\
no matter how bad you wanna just fall flat on your face and collapse.");


wxStaticText * st = new wxStaticText(panel, wxID_ANY, text, wxPoint(10, 10), wxDefaultSize, wxALIGN_CENTRE);

SetSize(600, 130);
Centre();
}

在我们的例子中,我们显示了巴拉巴拉。。。

wxStaticText * st = new wxStaticText(panel, wxID_ANY, text, wxPoint(10, 10), wxDefaultSize, wxALIGN_CENTRE);

这里我们创建了一个wxStaticText组件,静态文本内容被放置在中央。

wxSlider

wxSlider是一个有一个简单控件的组件,这个控件可以被来回拖动。有些时候使用一个滑块比提供一个数字旋钮(wxSpinCtrl)更加自然。

slider.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <wx/wx.h>
#include <wx/slider.h>

class MyPanel : public wxPanel
{
public:
MyPanel(wxFrame * parent);

void OnPaint(wxPaintEvent & event);
void OnScroll(wxScrollEvent & event);

wxSlider * slider;
int fill;

enum{ID_SLIDER};
};

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

MyPanel * panel;
};

slider.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include "slider.h"

Slider::Slider(const wxString & title)
: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(270, 200))
{
panel = new MyPanel(this);

Centre();
}

MyPanel::MyPanel(wxFrame * parent)
: wxPanel(parent, wxID_ANY)
{
fill = 0;
slider = new wxSlider(this, ID_SLIDER, 0, 0, 140, wxPoint(50, 30), wxSize(-1, 140), wxSL_VERTICAL);

Connect(ID_SLIDER, wxEVT_COMMAND_SLIDER_UPDATED, wxScrollEventHandler(MyPanel::OnScroll));

Connect(wxEVT_PAINT, wxPaintEventHandler(MyPanel::OnPaint));
}

void MyPanel::OnScroll(wxScrollEvent & event)
{
fill = slider->GetValue();
Refresh();
}

void MyPanel::OnPaint(wxPaintEvent & event)
{
wxPaintDC dc(this);

wxPen pen(wxColour(212, 212, 212));
dc.SetPen(pen);

dc.DrawRectangle(wxRect(140, 30, 80, 140));

wxBrush brush1(wxColour(197, 108, 0));
dc.SetBrush(brush1);

dc.DrawRectangle(wxRect(140, 30, 80, fill));
}

在我们的例子中,我们显示了一个滑块控件。我们通过拖动这个滑块来改变panel的背景色,在这种类型的程序中,使用一个滑块比一个数字旋钮方便的多。

slider = new wxSlider(this, ID_SLIDER, 0, 0, 140, wxPoint(50, 30), wxSize(-1, 140), wxSL_VERTICAL);

我们创建了一个垂直滑块,初值为0,最小值是0,最大值为140,我们没有显示标尺。

Connect(ID_SLIDER, wxEVT_COMMAND_SLIDER_UPDATED, wxScrollEventHandler(MyPanel::OnScroll));

这里我们把wxEVT_COMMAND_SLIDER_UPDATED事件同OnScroll()方法连接起来。

Connect(wxEVT_PAINT, wxPaintEventHandler(MyPanel::OnPaint));

我们也要画一些图案,所以我们把OnPaint()方法和wxEVT_PAINT事件连接起来。

fill = slider->GetValue();
Refresh();

在OnScroll()方法中,我们获得了滑块当前的值,我们调用Refresh()方法产生了一个wxEVT_PAINT方法。

dc.DrawRectangle(wxRect(140, 30, 80, 140));
wxBrush brush1(wxColour(197, 108, 0));
dc.SetBrush(brush1);
dc.DrawRectangle(wxRect(140, 30, 80, fill));

在OnPaint()方法里面,我们画了两个矩形,第一个是灰色边框的白色矩形,第二个是一个褐色的矩形。矩形的高度由滑块的当前值fill控制。

在这一章的wxWidgets教程中,我们简要的概括了几个组件。