[wxWidgets] wxChartDir 차트 그리기
C, C++ 2026. 5. 5. 11:27 |wxChartDir은 ChartDir(ChartDirector)의 wxWidgets 프레임워크 래퍼(wrapper)다.
실제 차트를 그리고(렌더링) 데이터를 처리하는 핵심 엔진은 ChartDir이기 때문에, wxChartDir을 사용하려면 ChartDir이 필요하다.
wxChartDir로 wxWidgets 애플리케이션에 차트를 그려보자.
우선 wxChartDir을 빌드하자.

ChartDirector에서 ChartDirector for C++을 다운로드받자.

다운로드받은 ChartDirector for C++을 확인해 보면 위와 같은 구조로 되어 있는데, 개발에 필요한 부분은 include와 lib64(또는 lib32) 디렉토리의 파일들이다.
위에서 vcpkg로 빌드한 wxChartDir의 폴더에 각각의 파일을 복사해 넣자. (chartdirXX.dll 파일도 lib64 디렉토리에 있다)
Include, Library 폴더 등을 적당히 세팅하고 아래 코드를 입력한다.
#include <wx/wx.h>
#include <wxchartdir/chartdir.h>
#include <wxchartdir/wxchartviewer.h>
#pragma comment(lib, "chartdir70.lib")
#pragma comment(lib, "wxchartdir.lib")
class MyApp : public wxApp
{
public:
bool OnInit() override;
};
wxIMPLEMENT_APP(MyApp);
class MyFrame : public wxFrame
{
public:
MyFrame();
private:
void OnHello(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
wxPanel* panel;
};
enum
{
ID_Hello = 1
};
bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame();
frame->Show(true);
return true;
}
// 메인 프레임의 위치(default position)와 크기(550x400)를 설정한다.
MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, "Hello World", wxDefaultPosition, wxSize(550, 400))
{
wxMenu* menuFile = new wxMenu;
menuFile->Append(ID_Hello, "&Hello...\tCtrl-H", "Help string shown in status bar for this menu item");
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT);
wxMenu* menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT);
wxMenuBar* menuBar = new wxMenuBar;
menuBar->Append(menuFile, "&File");
menuBar->Append(menuHelp, "&Help");
SetMenuBar(menuBar);
CreateStatusBar();
SetStatusText("Welcome to wxWidgets!");
panel = new wxPanel(this, wxID_ANY);
wxButton* myButton = new wxButton(panel, wxID_ANY, "Press Me", wxPoint(420, 30), wxSize(100, 30));
myButton->Bind(wxEVT_BUTTON, &MyFrame::OnHello, this);
Bind(wxEVT_MENU, &MyFrame::OnHello, this, ID_Hello);
Bind(wxEVT_MENU, &MyFrame::OnAbout, this, wxID_ABOUT);
Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT);
}
void MyFrame::OnExit(wxCommandEvent& event)
{
Close(true);
}
void MyFrame::OnAbout(wxCommandEvent& event)
{
wxMessageBox("This is a wxWidgets Hello World example", "About Hello World", wxOK | wxICON_INFORMATION);
}
void MyFrame::OnHello(wxCommandEvent& event)
{
// 데이터와 라벨 준비
double data[] = { 85, 156, 179.5, 211, 123 };
const char* labels[] = { "Mon", "Tue", "Wed", "Thu", "Fri" };
// 400x300 크기의 XYChart 객체 생성
XYChart* c = new XYChart(400, 300);
// 플롯 영역 설정 (여백 조정)
c->setPlotArea(40, 20, 300, 250);
// 바 차트 레이어 추가
c->addBarLayer(DoubleArray(data, (int)(sizeof(data) / sizeof(data[0]))));
// X축에 라벨 설정
c->xAxis()->setLabels(StringArray(labels, (int)(sizeof(labels) / sizeof(labels[0]))));
// 차트 뷰어 생성 (wxPanel 위에 위치)
wxChartViewer* chartViewer = new wxChartViewer(panel, wxID_ANY, wxPoint(10, 10), wxSize(400, 300));
// 생성한 차트를 뷰어에 연결
// wxChartViewer는 setChart에서 내부적으로 상태를 업데이트하거나 사용할 수 있지만,
// 라이브러리의 방식에 따라 BaseChart(XYChart)의 생명주기를 어떻게 관리해야 할지 주의해야 한다.
chartViewer->setChart(c);
// 문서에 따르면 setChart를 호출한 후 곧바로 BaseChart를 삭제하거나 로컬 스택에 할당하는 것도 완벽하게 안전하다.
// "So it is perfectly safe to immediately delete the BaseChart after calling wxChartViewer::setChart"
delete c;
}

ChartDirector는 상용 라이브러리이기 때문에 라이선스를 구입하지 않으면 아래에 노란 광고가 표시된다.
ChartViewer의 크기를 XYChart 객체보다 10픽셀 정도(정확히는 9픽셀) 작게 하면 노란 광고를 보이지 않게 할 수 있다.
(이 예제는 필요 없지만, X축 레이블이 광고와 겹친다면 setPlotArea()에서 플롯 영역의 높이도 조정해야 한다, 아래 히스토그램 예제 참고)
.
.
.
// 400x300 크기의 XYChart 객체 생성
XYChart* c = new XYChart(400, 300);
.
.
.
// 차트 뷰어 생성 (wxPanel 위에 위치)
wxChartViewer* chartViewer = new wxChartViewer(panel, wxID_ANY, wxPoint(10, 10), wxSize(400, 300-10));
// 차트 뷰어의 크기를 XYChart 객체보다 10픽셀 정도 작게 설정하면 노란 광고 배너가 보이지 않게 된다.
.
.
.

Line Chart를 만들어 보자.
#include <wx/wx.h>
#include <wxchartdir/chartdir.h>
#include <wxchartdir/wxchartviewer.h>
#pragma comment(lib, "chartdir70.lib")
#pragma comment(lib, "wxchartdir.lib")
class MyApp : public wxApp
{
public:
bool OnInit() override;
};
wxIMPLEMENT_APP(MyApp);
class MyFrame : public wxFrame
{
public:
MyFrame();
private:
void OnHello(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
wxPanel* panel;
};
enum
{
ID_Hello = 1
};
bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame();
frame->Show(true);
return true;
}
// 메인 프레임의 위치(default position)와 크기(550x400)를 설정한다.
MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, "Hello World", wxDefaultPosition, wxSize(550, 400))
{
wxMenu* menuFile = new wxMenu;
menuFile->Append(ID_Hello, "&Hello...\tCtrl-H", "Help string shown in status bar for this menu item");
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT);
wxMenu* menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT);
wxMenuBar* menuBar = new wxMenuBar;
menuBar->Append(menuFile, "&File");
menuBar->Append(menuHelp, "&Help");
SetMenuBar(menuBar);
CreateStatusBar();
SetStatusText("Welcome to wxWidgets!");
panel = new wxPanel(this, wxID_ANY);
wxButton* myButton = new wxButton(panel, wxID_ANY, "Press Me", wxPoint(420, 30), wxSize(100, 30));
myButton->Bind(wxEVT_BUTTON, &MyFrame::OnHello, this);
Bind(wxEVT_MENU, &MyFrame::OnHello, this, ID_Hello);
Bind(wxEVT_MENU, &MyFrame::OnAbout, this, wxID_ABOUT);
Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT);
}
void MyFrame::OnExit(wxCommandEvent& event)
{
Close(true);
}
void MyFrame::OnAbout(wxCommandEvent& event)
{
wxMessageBox("This is a wxWidgets Hello World example", "About Hello World", wxOK | wxICON_INFORMATION);
}
void MyFrame::OnHello(wxCommandEvent& event)
{
// The data for the line chart
double data[] = { 30, 28, 40, 55, 75, 68, 54, 60, 50, 62, 75, 65, 75, 91, 60, 55, 53, 35, 50, 66,
56, 48, 52, 65, 62 };
const int data_size = (int)(sizeof(data) / sizeof(*data));
// The labels for the line chart
const char* labels[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12",
"13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24" };
const int labels_size = (int)(sizeof(labels) / sizeof(*labels));
// Create a XYChart object of size 250 x 250 pixels
XYChart* c = new XYChart(250, 250);
// Set the plotarea at (30, 20) and of size 200 x 200 pixels
c->setPlotArea(30, 20, 200, 200);
// Add a line chart layer using the given data
c->addLineLayer(DoubleArray(data, data_size));
// Set the labels on the x axis.
c->xAxis()->setLabels(StringArray(labels, labels_size));
// Display 1 out of 3 labels on the x-axis.
c->xAxis()->setLabelStep(3);
// Create a ChartViewer to display the chart
wxChartViewer* chartViewer = new wxChartViewer(panel, wxID_ANY, wxPoint(10, 10), wxSize(250, 250));
chartViewer->setChart(c);
//free up resources
delete c;
}

간단한 리얼타임 차트 만들기
2026.05.10 - [C, C++] - [wxWidgets] wxChartDir Simple Real-Time Chart 간단한 리얼타임 차트
히스토그램과 벨 커브를 그려보자.
#include <wx/wx.h>
#include <wxchartdir/chartdir.h>
#include <wxchartdir/wxchartviewer.h>
#include <math.h>
#pragma comment(lib, "chartdir70.lib")
#pragma comment(lib, "wxchartdir.lib")
class MyApp : public wxApp
{
public:
bool OnInit() override;
};
wxIMPLEMENT_APP(MyApp);
class MyFrame : public wxFrame
{
public:
MyFrame();
private:
void OnHello(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
wxPanel* panel;
};
enum
{
ID_Hello = 1
};
bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame();
frame->Show(true);
return true;
}
// 메인 프레임의 위치(default position)와 크기(750x500)를 설정한다.
MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, "Hello World", wxDefaultPosition, wxSize(750, 500))
{
wxMenu* menuFile = new wxMenu;
menuFile->Append(ID_Hello, "&Hello...\tCtrl-H", "Help string shown in status bar for this menu item");
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT);
wxMenu* menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT);
wxMenuBar* menuBar = new wxMenuBar;
menuBar->Append(menuFile, "&File");
menuBar->Append(menuHelp, "&Help");
SetMenuBar(menuBar);
CreateStatusBar();
SetStatusText("Welcome to wxWidgets!");
panel = new wxPanel(this, wxID_ANY);
wxButton* myButton = new wxButton(panel, wxID_ANY, "Press Me", wxPoint(620, 30), wxSize(100, 30));
myButton->Bind(wxEVT_BUTTON, &MyFrame::OnHello, this);
Bind(wxEVT_MENU, &MyFrame::OnHello, this, ID_Hello);
Bind(wxEVT_MENU, &MyFrame::OnAbout, this, wxID_ABOUT);
Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT);
}
void MyFrame::OnExit(wxCommandEvent& event)
{
Close(true);
}
void MyFrame::OnAbout(wxCommandEvent& event)
{
wxMessageBox("This is a wxWidgets Hello World example", "About Hello World", wxOK | wxICON_INFORMATION);
}
void MyFrame::OnHello(wxCommandEvent& event)
{
char buffer[1024];
//
// This example demonstrates creating a histogram with a bell curve from raw data. About half of
// the code is to sort the raw data into slots and to generate the points on the bell curve. The
// remaining half of the code is the actual charting code.
//
// 히스토그램 및 벨 커브 데이터 생성
// Generate a random guassian distributed data series as the input data for this example.
RanSeries* r = new RanSeries(66);
DoubleArray samples = r->getGaussianSeries(200, 100, 10);
//
// Classify the numbers into slots. In this example, the slot width is 5 units.
//
double slotSize = 5;
// Compute the min and max values, and extend them to the slot boundary.
ArrayMath m = ArrayMath(samples);
double minX = (int)(m.minValue() / slotSize) * slotSize;
double maxX = (int)(m.maxValue() / slotSize) * slotSize + slotSize;
// We can now determine the number of slots
// slot의 개수를 계산한다. slotCount는 (maxX - minX) / slotSize로 계산되며, 소수점 첫째 자리에서 반올림하여 정수로 변환된다.
int slotCount = (int)((maxX - minX) / slotSize + 0.5);
double* frequency = new double[slotCount];
memset(frequency, 0, sizeof(*frequency) * slotCount);
// Count the data points contained in each slot
// frequency에 각 slot에 해당하는 데이터 포인트의 개수를 저장한다. samples 배열의 각 요소에 대해, 해당 요소가 속하는 slot의
// 인덱스를 계산하여 frequency 배열에서 해당 slot의 개수를 증가시킨다. slotIndex는 (samples[i] - minX) / slotSize로 계산되며
// 이는 samples[i]가 minX에서 시작하여 slotSize 간격으로 나누어진 slot 중 어디에 위치하는지를 나타낸다.
for (int i = 0; i < samples.len; ++i) {
int slotIndex = (int)((samples[i] - minX) / slotSize);
frequency[slotIndex] = frequency[slotIndex] + 1;
}
//
// Compute Normal Distribution Curve
//
// The mean and standard deviation of the data
double mean = m.avg();
double stdDev = m.stdDev();
// The normal distribution curve (bell curve) is a standard statistics curve. We need to
// vertically scale it to make it proportion to the frequency count.
double scaleFactor = slotSize * samples.len / stdDev / sqrt(2 * 3.1416);
// In this example, we plot the bell curve up to 3 standard deviations.
double stdDevWidth = 3.0;
// We generate 4 points per standard deviation to be joined with a spline curve.
int bellCurveResolution = (int)(stdDevWidth * 4 + 1);
double* bellCurve = new double[bellCurveResolution];
for (int i = 0; i < bellCurveResolution; ++i) {
double z = 2 * i * stdDevWidth / (bellCurveResolution - 1) - stdDevWidth;
bellCurve[i] = exp(-z * z / 2) * scaleFactor;
}
//
// At this stage, we have obtained all data and can plot the chart.
//
// 히스토그램과 벨 커브 차트 생성
// Create a XYChart object of size 600 x 360 pixels
XYChart* c = new XYChart(600, 360);
// Set the plotarea at (50, 30) and of size 500 x 300 pixels, with transparent background and
// border and light grey (0xcccccc) horizontal grid lines
c->setPlotArea(50, 30, 500, 300, Chart::Transparent, -1, Chart::Transparent, 0xcccccc);
// 이 예제대로 플롯 영역의 높이를 300으로 설정하면 노란 광고 배너가 플롯 영역의 x축 레이블과 겹쳐서 보이게 된다.
// 플롯 영역의 높이를 20정도 줄이면(300-20) 광고 배너와 겹치지 않게 된다. 그리고 아래 chartViewer의 크기도 20정도
// 줄이면(360-20) 광고 배너도 보이지않게 할 수 있다.
// Display the mean and standard deviation on the chart
sprintf(buffer, "Mean = %.1f, Standard Deviation = %.1f", mean, stdDev);
c->addTitle(buffer, "Arial");
// Set the x and y axis label font to 12pt Arial
c->xAxis()->setLabelStyle("Arial", 12);
c->yAxis()->setLabelStyle("Arial", 12);
// Set the x and y axis stems to transparent, and the x-axis tick color to grey (0x888888)
c->xAxis()->setColors(Chart::Transparent, Chart::TextColor, Chart::TextColor, 0x888888);
c->yAxis()->setColors(Chart::Transparent);
// 벨 커브 그리기, 벨 커브가 필요 없다면 이 부분을 제거하면 된다.
// Draw the bell curve as a spline layer in red (0xdd0000) with 2-pixel line width
SplineLayer* bellLayer = c->addSplineLayer(DoubleArray(bellCurve, bellCurveResolution), 0xdd0000
);
bellLayer->setXData(mean - stdDevWidth * stdDev, mean + stdDevWidth * stdDev);
bellLayer->setLineWidth(2);
// 히스토그램 그리기
// Draw the histogram as bars in blue (0x6699bb) with dark blue (0x336688) border
BarLayer* histogramLayer = c->addBarLayer(DoubleArray(frequency, slotCount), 0x6699bb);
histogramLayer->setBorderColor(0x336688);
// The center of the bars span from minX + half_bar_width to maxX - half_bar_width
histogramLayer->setXData(minX + slotSize / 2.0, maxX - slotSize / 2.0);
// Configure the bars to touch each other with no gap in between
histogramLayer->setBarGap(Chart::TouchBar);
// Use rounded corners for decoration
histogramLayer->setRoundedCorners();
// ChartDirector by default will extend the x-axis scale by 0.5 unit to cater for the bar width.
// It is because a bar plotted at x actually occupies (x +/- half_bar_width), and the bar width
// is normally 1 for label based x-axis. However, this chart is using a linear x-axis instead of
// label based. So we disable the automatic extension and add a dummy layer to extend the x-axis
// scale to cover minX to maxX.
c->xAxis()->setIndent(false);
c->addLineLayer()->setXData(minX, maxX);
// For the automatic y-axis labels, set the minimum spacing to 40 pixels.
c->yAxis()->setTickDensity(40);
// 차트 뷰어 생성 (wxPanel 위에 위치)
wxChartViewer* chartViewer = new wxChartViewer(panel, wxID_ANY, wxPoint(10, 10), wxSize(600, 360));
// 생성한 차트를 뷰어에 연결
chartViewer->setChart(c);
// 문서에 따르면 setChart를 호출한 후 곧바로 BaseChart를 삭제하거나 로컬 스택에 할당하는 것도 완벽하게 안전하다.
// "So it is perfectly safe to immediately delete the BaseChart after calling wxChartViewer::setChart"
delete c;
}

Angular Meter를 만들어 보자.
#include <wx/wx.h>
#include <wxchartdir/chartdir.h>
#include <wxchartdir/wxchartviewer.h>
#pragma comment(lib, "chartdir70.lib")
#pragma comment(lib, "wxchartdir.lib")
class MyApp : public wxApp
{
public:
bool OnInit() override;
};
wxIMPLEMENT_APP(MyApp);
class MyFrame : public wxFrame
{
public:
MyFrame();
private:
void OnHello(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
wxPanel* panel;
};
enum
{
ID_Hello = 1
};
bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame();
frame->Show(true);
return true;
}
// 메인 프레임의 위치(default position)와 크기(550x400)를 설정한다.
MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, "Hello World", wxDefaultPosition, wxSize(550, 400))
{
wxMenu* menuFile = new wxMenu;
menuFile->Append(ID_Hello, "&Hello...\tCtrl-H", "Help string shown in status bar for this menu item");
menuFile->AppendSeparator();
menuFile->Append(wxID_EXIT);
wxMenu* menuHelp = new wxMenu;
menuHelp->Append(wxID_ABOUT);
wxMenuBar* menuBar = new wxMenuBar;
menuBar->Append(menuFile, "&File");
menuBar->Append(menuHelp, "&Help");
SetMenuBar(menuBar);
CreateStatusBar();
SetStatusText("Welcome to wxWidgets!");
panel = new wxPanel(this, wxID_ANY);
wxButton* myButton = new wxButton(panel, wxID_ANY, "Press Me", wxPoint(420, 30), wxSize(100, 30));
myButton->Bind(wxEVT_BUTTON, &MyFrame::OnHello, this);
Bind(wxEVT_MENU, &MyFrame::OnHello, this, ID_Hello);
Bind(wxEVT_MENU, &MyFrame::OnAbout, this, wxID_ABOUT);
Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT);
}
void MyFrame::OnExit(wxCommandEvent& event)
{
Close(true);
}
void MyFrame::OnAbout(wxCommandEvent& event)
{
wxMessageBox("This is a wxWidgets Hello World example", "About Hello World", wxOK | wxICON_INFORMATION);
}
void MyFrame::OnHello(wxCommandEvent& event)
{
// The value to display on the meter
double value = 72.55;
// Create an AngularMeter object of size 300 x 180 pixels with transparent background
//AngularMeter* m = new AngularMeter(300, 180, Chart::Transparent);
//
// Chart::Transparent는 wxChartViewer에서 검정색으로 렌더링될 수 있으므로, 패널의 배경색을 설정한다.
wxColour bgColour = panel->GetBackgroundColour();
int chartBgColor = (bgColour.Red() << 16) | (bgColour.Green() << 8) | bgColour.Blue();
AngularMeter* m = new AngularMeter(300, 180, chartBgColor);
// Center at (150, 150), scale radius = 128 pixels, scale angle -90 to +90 degrees
m->setMeter(150, 150, 128, -90, 90);
// Add a pale grey (0xeeeeee) scale background of 148 pixels radius, with a 10 pixel thick light
// grey (0xcccccc) border
m->addScaleBackground(148, 0xeeeeee, 10, 0xcccccc);
// Meter scale is 0 - 100, with major tick every 20 units, minor tick every 10 units, and micro
// tick every 5 units
m->setScale(0, 100, 20, 10, 5);
// Set the scale label style to 15pt Arial Italic. Set the major/minor/micro tick lengths to
// 16/16/10 pixels pointing inwards, and their widths to 2/1/1 pixels.
m->setLabelStyle("Arial Italic", 16);
m->setTickLength(-16, -16, -10);
m->setLineWidth(0, 2, 1, 1);
// Add a smooth color scale to the meter
double smoothColorScale[] = { 0, 0x3333ff, 25, 0x0088ff, 50, 0x00ff00, 75, 0xdddd00, 100, 0xff0000 };
const int smoothColorScale_size = (int)(sizeof(smoothColorScale) / sizeof(*smoothColorScale));
m->addColorScale(DoubleArray(smoothColorScale, smoothColorScale_size));
// Add a text label centered at (150, 125) with 15pt Arial Italic font
m->addText(150, 125, "CPU", "Arial Italic", 15, Chart::TextColor, Chart::BottomCenter);
// Add a red (0xff0000) pointer at the specified value
// addPointer() 메서드와 addPointer2() 메서드는 포인터의 모양이 약간 다르다.
m->addPointer2(value, 0xff0000);
// Create a wxChartViewer to display the meter, with a size of 300 x 180 pixels
wxChartViewer* meterViewer = new wxChartViewer(panel, wxID_ANY, wxPoint(10, 10), wxSize(300, 180));
meterViewer->setChart(m);
//free up resources
delete m;
}

※ 참고
'C, C++' 카테고리의 다른 글
| [wxWidgets] wxChartDir Simple Real-Time Chart 간단한 리얼타임 차트 (0) | 2026.05.10 |
|---|---|
| [wxWidgets] Timer 타이머 (0) | 2026.05.10 |
| [wxWidgets] wxCharts 차트 그리기 (0) | 2026.05.04 |
| [wxWidgets] Window Handle & Titlebar Icon 윈도우 핸들 구하기 및 타이틀바 아이콘 바꾸기 (0) | 2026.05.04 |
| [wxWidgets] wxFormBuiler XRC 파일 사용하기 (0) | 2026.05.03 |
