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 教程的这一部分中,我们创建了一个自定义控件。