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;
}
히스토그램과 벨 커브를 그려보자.
#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;
}
MyFrame::MyFrame()
: wxFrame(nullptr, wxID_ANY, "Hello World")
{
// 윈도우 타이틀바 왼쪽에 표시될 아이콘 설정
// 프로젝트(혹은 실행 파일) 경로에 "icon.ico" 파일이 존재해야 정상적으로 로드된다.
SetIcon(wxIcon("icon.ico", wxBITMAP_TYPE_ICO));
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);
.
.
.
타이틀바 왼쪽의 아이콘이 체리로 바뀌었다.
이번엔 리소스 파일을 이용해 타이틀바의 아이콘뿐만 아니라 실행 파일의 아이콘도 바꿔보자.
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// 영어(미국) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_KOR)
LANGUAGE 18, 1
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // 영어(미국) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
// Windows 탐색기에서 표시할 실행 파일 메인 아이콘
MAINICON ICON "icon.ico"
// 참고: wxWidgets 기본 리소스(매니페스트 및 기본 커서 등)를 포함하려면 아래 줄을 추가하는 것이 좋다.
#include "wx/msw/wx.rc"
리소스 파일 아래에 위와 같이 MAINICON ICON "icon.ico" 문장을 추가한다. Windows는 프로젝트에 포함된 리소스 파일 중 가장 먼저(또는 가장 낮은 ID로) 정의된 아이콘을 실행 파일의 주 아이콘으로 사용한다. (#include "wx/msw/wx.rc"를 추가하면 wxWidgets 기본 리소스를 사용할 수 있다)
이 상태에서 다시 빌드하면 실행 파일의 아이콘이 아래와 같이 변경된다.
또, 리소스에 아이콘을 등록했으므로 타이틀바 아이콘 설정도 아래와 같이 바꿀 수 있다.
MyFrame::MyFrame()
: wxFrame(nullptr, wxID_ANY, "Hello World")
{
// 윈도우 타이틀바 왼쪽에 표시될 아이콘 설정
// 프로젝트(혹은 실행 파일) 경로에 "icon.ico" 파일이 존재해야 정상적으로 로드된다.
//SetIcon(wxIcon("icon.ico", wxBITMAP_TYPE_ICO));
//
// 리소스에 아이콘을 등록했기 때문에, Source.cpp에서 파일 경로를 직접 적지 않고 리소스에 등록된 아이콘명을
// 활용하여 타이틀바 아이콘을 설정할 수 있다. (wxIcon() 대신 매크로 wxICON() 사용)
SetIcon(wxICON(MAINICON)); // 리소스에 등록된 아이콘을 사용하여 타이틀바 아이콘 설정("MAINICON"은 리소스에 선언한 이름)
//
// 리소스에 #include "wx/msw/wx.rc"를 추가했기 때문에, wxWidgets의 기본 리소스(매니페스트 및 기본 커서 등)가 포함되어 있다.
// 따라서 wx.rc에 정의된 기본 아이콘을 사용할 수 있다. 예를 들어, wxICON(wxICON_INFORMATION)과 같이 기본 아이콘을 사용할 수 있다.
//SetIcon(wxICON(wxICON_INFORMATION));
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);
.
.
.
#include <wx/wx.h>
#include <opencv2/opencv.hpp>
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* m_panel; // 버튼과 이미지를 담을 패널
wxStaticBitmap* m_imageCtrl; // 이미지 표시를 위한 컨트롤
};
enum
{
ID_Hello = 1
};
bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame();
frame->Show(true);
return true;
}
MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, "Hello World")
{
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!");
m_panel = new wxPanel(this, wxID_ANY);
wxButton* myButton = new wxButton(m_panel, wxID_ANY, "Press Me", wxPoint(30, 30), wxSize(100, 30));
myButton->Bind(wxEVT_BUTTON, &MyFrame::OnHello, this);
//Bind(wxEVT_BUTTON, &MyFrame::OnHello, this, myButton->GetId());
m_imageCtrl = new wxStaticBitmap(m_panel, wxID_ANY, wxNullBitmap, wxPoint(30, 80));
// 이미지 표시 컨트롤 초기화. wxNullBitmap으로 초기화하여 나중에 이미지가 로드될 때 업데이트할 수 있도록 함.
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)
{
//wxLogMessage("Hello world from wxWidgets!");
cv::Mat image = cv::imread("palvin1.png");
if (image.empty())
{
wxLogError("Could not load image!");
}
else
{
// OpenCV는 BGR을 사용하므로 wxImage에서 필요로 하는 RGB 형식으로 변환.
cv::cvtColor(image, image, cv::COLOR_BGR2RGB);
// wxWidgets 구조상 플랫폼 비의존적(Device Independent)인 픽셀 데이터를 플랫폼 의존적(Device Dependent)인
// 이미지로 그리기 위해서는 wxImage를 거쳐 wxBitmap으로 변환해야 한다. wxImage는 픽셀 데이터를 소유하므로
// 이미지가 wxImage에 의해 관리되도록 true를 전달한다.
wxImage wxImg(image.cols, image.rows, image.data, true);
wxBitmap bitmap(wxImg);
if (m_imageCtrl)
{
m_imageCtrl->SetBitmap(bitmap);
m_imageCtrl->SetSize(bitmap.GetWidth(), bitmap.GetHeight());
m_panel->Refresh();
}
}
}
wxStaticBitmap과 같은 컨트롤을 거치지 않고 wxPaintDC(Device Context)를 이용하여 디스플레이에 직접 그리는 방식도 가능하다. 이 방법은 비디오 캡처 등으로 계속 프레임이 바뀔 때 컨트롤을 계속 업데이트하는 오버헤드를 줄이는데 유리할 수 있다.
#include <wx/wx.h>
//#include <wx/dcbuffer.h> // wxBufferedPaintDC를 사용하기 위한 헤더
#include <opencv2/opencv.hpp>
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);
void OnPaint(wxPaintEvent& event);
wxPanel* m_panel; // 버튼과 이미지를 담을 패널
wxBitmap m_bitmap; // 직접 그릴 비트맵 데이터
};
enum
{
ID_Hello = 1
};
bool MyApp::OnInit()
{
MyFrame* frame = new MyFrame();
frame->Show(true);
return true;
}
MyFrame::MyFrame() : wxFrame(nullptr, wxID_ANY, "Hello World")
{
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!");
m_panel = new wxPanel(this, wxID_ANY);
//m_panel->SetBackgroundStyle(wxBG_STYLE_PAINT);
// wxBufferedPaintDC 사용시 검은 화면 방지, wxBG_STYLE_PAINT는 직접 패널 배경을 그리겠다는 의미로
// OnPaint에서 DC의 배경색으로 패널의 배경색을 설정하고 Clear()를 호출하여 화면을 지워야 한다.
wxButton* myButton = new wxButton(m_panel, wxID_ANY, "Press Me", wxPoint(30, 30), wxSize(100, 30));
myButton->Bind(wxEVT_BUTTON, &MyFrame::OnHello, this);
//Bind(wxEVT_BUTTON, &MyFrame::OnHello, this, myButton->GetId());
// 패널에 페인트 이벤트를 바인드하여 DC를 통해 직접 그린다.
m_panel->Bind(wxEVT_PAINT, &MyFrame::OnPaint, this);
m_panel->Bind(wxEVT_ERASE_BACKGROUND, [](wxEraseEvent& event) { event.Skip(); }); // 배경 지우기 이벤트를 무시하여 깜빡임 방지
m_panel->SetDoubleBuffered(true); // 깜빡임 방지 위해 더블 버퍼링 활성화
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)
{
//wxLogMessage("Hello world from wxWidgets!");
cv::Mat image = cv::imread("palvin1.png");
if (image.empty())
{
wxLogError("Could not load image!");
}
else
{
// OpenCV는 BGR을 사용하므로 wxImage에서 필요로 하는 RGB 형식으로 변환.
cv::cvtColor(image, image, cv::COLOR_BGR2RGB);
// wxWidgets 구조상 플랫폼 비의존적(Device Independent)인 픽셀 데이터를 플랫폼 의존적(Device Dependent)인
// 이미지로 그리기 위해서는 wxImage를 거쳐 wxBitmap으로 변환해야 한다. wxImage는 픽셀 데이터를 소유하므로
// 이미지가 wxImage에 의해 관리되도록 true를 전달한다.
wxImage wxImg(image.cols, image.rows, image.data, true);
m_bitmap = wxBitmap(wxImg);
m_panel->Refresh(); // 패널을 다시 그리도록 요청하여 OnPaint가 호출되게 한다.
}
}
void MyFrame::OnPaint(wxPaintEvent& event)
{
// m_panel에 대한 Paint DC 생성 (페인트 이벤트 안에서만 사용 가능)
wxPaintDC dc(m_panel);
// wxPaintDC 대신 메모리에 먼저 그리고 화면에 출력하는 wxBufferedPaintDC 클래스를 사용해도 부드러운 출력이
// 가능하며 깜빡임도 줄일 수 있다고 한다. wxBufferedPaintDC는 wxPaintDC를 상속하므로 wxPaintDC 대신
// wxBufferedPaintDC를 사용하면 된다. 이 경우 m_panel에 더블 버퍼링은 활성화할 필요가 없다.
// 둘 중 하나만 사용하면 된다. wxPaintDC + SetDoubleBuffered 사용을 추천.
// wxBufferedPaintDC는 운영체제의 네이티브 더블 버퍼링을 지원하지 않는 옛날 OS나 특수한 상황을 위해 존재한다.
// 상단의 #include <wx/dcbuffer.h> 주석 취소.
//wxBufferedPaintDC dc(m_panel);
//dc.SetBackground(wxBrush(m_panel->GetBackgroundColour()));
//dc.Clear();
// 패널 기본 배경색으로 화면을 지워준다.
// wxBG_STYLE_PAINT 스타일을 사용했다면 OnPaint에서 DC의 배경색을 패널의 배경색으로 설정하고 Clear()를 호출하여 화면을
// 지워야 검은색으로 나타나지 않는다.
if (m_bitmap.IsOk()) // 비트맵이 유효한 경우에만 그린다.
{
// 지정된 좌표 (30, 80)에 비트맵을 직접 그린다. 마스크 사용은 false로 설정.
dc.DrawBitmap(m_bitmap, 30, 80, false);
}
}