wxWidgets 中的自定义控件
最后修改于 2023 年 10 月 18 日
工具包通常只提供最常见的控件,如按钮、文本控件、滚动条、滑块等。没有一个工具包可以提供所有可能的控件。wxWidgets 有许多不同的控件;更专业的控件由客户端程序员创建。
自定义控件是通过使用工具包提供的绘图工具创建的。有两种可能性:程序员可以修改或增强现有的控件,或者可以从头开始创建自定义控件。
燃烧控件
这是一个我们从头开始创建的控件的例子。这个控件可以在各种媒体刻录应用程序中找到,比如 Nero Burning ROM。
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <wx/wx.h>
class Widget : public wxPanel
{
public:
Widget(wxPanel *parent, int id );
wxPanel *m_parent;
void OnSize(wxSizeEvent& event);
void OnPaint(wxPaintEvent& event);
};
#endif
widget.cpp
#include <wx/wx.h>
#include "widget.h"
#include "burning.h"
int num[] = { 75, 150, 225, 300, 375, 450, 525, 600, 675 };
int asize = sizeof(num)/sizeof(num[1]);
Widget::Widget(wxPanel *parent, int id)
: wxPanel(parent, id, wxDefaultPosition, wxSize(-1, 30), wxSUNKEN_BORDER)
{
m_parent = parent;
Connect(wxEVT_PAINT, wxPaintEventHandler(Widget::OnPaint));
Connect(wxEVT_SIZE, wxSizeEventHandler(Widget::OnSize));
}
void Widget::OnPaint(wxPaintEvent& event)
{
wxFont font(9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
wxFONTWEIGHT_NORMAL, false, wxT("Courier 10 Pitch"));
wxPaintDC dc(this);
dc.SetFont(font);
wxSize size = GetSize();
int width = size.GetWidth();
Burning *burn = (Burning *) m_parent->GetParent();
int cur_width = burn->GetCurWidth();
int step = (int) round(width / 10.0);
int till = (int) ((width / 750.0) * cur_width);
int full = (int) ((width / 750.0) * 700);
if (cur_width >= 700) {
dc.SetPen(wxPen(wxColour(255, 255, 184)));
dc.SetBrush(wxBrush(wxColour(255, 255, 184)));
dc.DrawRectangle(0, 0, full, 30);
dc.SetPen(wxPen(wxColour(255, 175, 175)));
dc.SetBrush(wxBrush(wxColour(255, 175, 175)));
dc.DrawRectangle(full, 0, till-full, 30);
} else {
dc.SetPen(wxPen(wxColour(255, 255, 184)));
dc.SetBrush(wxBrush(wxColour(255, 255, 184)));
dc.DrawRectangle(0, 0, till, 30);
}
dc.SetPen(wxPen(wxColour(90, 80, 60)));
for ( int i=1; i <= asize; i++ ) {
dc.DrawLine(i*step, 0, i*step, 6);
wxSize size = dc.GetTextExtent(wxString::Format(wxT("%d"), num[i-1]));
dc.DrawText(wxString::Format(wxT("%d"), num[i-1]),
i*step-size.GetWidth()/2, 8);
}
}
void Widget::OnSize(wxSizeEvent& event)
{
Refresh();
}
burning.h
#ifndef BURNING_H
#define BURNING_H
#include <wx/wx.h>
#include "widget.h"
class Burning : public wxFrame
{
public:
Burning(const wxString& title);
void OnScroll(wxScrollEvent& event);
int GetCurWidth();
wxSlider *m_slider;
Widget *m_wid;
int cur_width;
};
#endif
burning.cpp
#include "burning.h"
#include "widget.h"
int ID_SLIDER = 1;
Burning::Burning(const wxString& title)
: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(350, 200))
{
cur_width = 75;
wxPanel *panel = new wxPanel(this, wxID_ANY);
wxPanel *centerPanel = new wxPanel(panel, wxID_ANY);
m_slider = new wxSlider(centerPanel, ID_SLIDER, 75, 0, 750, wxPoint(-1, -1),
wxSize(150, -1), wxSL_LABELS);
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
wxBoxSizer *hbox = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *hbox2 = new wxBoxSizer(wxHORIZONTAL);
wxBoxSizer *hbox3 = new wxBoxSizer(wxHORIZONTAL);
m_wid = new Widget(panel, wxID_ANY);
hbox->Add(m_wid, 1, wxEXPAND);
hbox2->Add(centerPanel, 1, wxEXPAND);
hbox3->Add(m_slider, 0, wxTOP | wxLEFT, 35);
centerPanel->SetSizer(hbox3);
vbox->Add(hbox2, 1, wxEXPAND);
vbox->Add(hbox, 0, wxEXPAND);
panel->SetSizer(vbox);
m_slider->SetFocus();
Connect(ID_SLIDER, wxEVT_COMMAND_SLIDER_UPDATED,
wxScrollEventHandler(Burning::OnScroll));
Centre();
}
void Burning::OnScroll(wxScrollEvent& WXUNUSED(event))
{
cur_width = m_slider->GetValue();
m_wid->Refresh();
}
int Burning::GetCurWidth()
{
return cur_width;
}
main.h
#include <wx/wx.h>
class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
main.cpp
#include "main.h"
#include "burning.h"
IMPLEMENT_APP(MyApp)
bool MyApp::OnInit()
{
Burning *burning = new Burning(wxT("The Burning Widget"));
burning->Show(true);
return true;
}
我们在窗口底部放置一个 wxPanel,并手动绘制整个控件。所有重要的代码都位于控件类的 OnPaint 方法中。该控件以图形方式显示媒体的总容量和可用的剩余空间。该控件由滑块控制。我们自定义控件的最小值是 0,最大值是 750。如果我们达到 700 的值,我们就开始用红色绘制。这表示超刻录。
wxSize size = GetSize(); int width = size.GetWidth(); ... int till = (int) ((width / 750.0) * cur_width); int full = (int) ((width / 750.0) * 700);
我们动态地绘制控件。窗口越大,燃烧控件越大。反之亦然。这就是为什么我们必须计算用于绘制自定义控件的 wxPanel 的大小。till 参数决定要绘制的总大小。这个值来自滑块控件。它是整个区域的一部分。full 参数决定我们开始用红色绘制的点。请注意浮点算术的使用。这是为了实现更高的精度。
实际的绘制包括三个步骤。我们绘制黄色或红色和黄色的矩形。然后我们绘制垂直线,将部件分成几个部分。最后,我们绘制数字,表示介质的容量。
void Widget::OnSize(wxSizeEvent& event)
{
Refresh();
}
每次调整窗口大小时,我们都会刷新控件。这将导致控件重新绘制自身。
void Burning::OnScroll(wxScrollEvent& WXUNUSED(event))
{
cur_width = m_slider->GetValue();
m_wid->Refresh();
}
如果我们滚动滑块的拇指,我们会得到实际值并将其保存到 cur_width 变量中。当绘制燃烧控件时,将使用该值。然后我们导致控件被重绘。
在 wxWidgets 教程的这一部分中,我们创建了一个自定义控件。