[wxWidgets] Timer 타이머

C, C++ 2026. 5. 10. 19:41 |
반응형

타이머를 사용해 보자.

 

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
	bool OnInit() override;
};

wxIMPLEMENT_APP(MyApp);

class MyFrame : public wxFrame
{
public:
	MyFrame();

private:
	wxTimer m_timer; // 타이머 객체 추가

	void OnHello(wxCommandEvent& event);
	void OnExit(wxCommandEvent& event);
	void OnAbout(wxCommandEvent& event);
	void OnTimer(wxTimerEvent& event); // 타이머 이벤트 핸들러 추가
};

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!");

	// 타이머 초기화 및 시작 방법 1: 타이머 객체에 직접 이벤트 핸들러 바인딩
	m_timer.Bind(wxEVT_TIMER, &MyFrame::OnTimer, this, m_timer.GetId()); // 타이머 이벤트 핸들러 바인딩	
	m_timer.Start(1000); // 1초마다 타이머 이벤트 발생

	// 타이머 초기화 및 시작 방법 2: 프레임에서 타이머 이벤트 핸들러 바인딩
	//m_timer.SetOwner(this); // 타이머의 소유자를 현재 프레임으로 설정
	//Bind(wxEVT_TIMER, &MyFrame::OnTimer, this, m_timer.GetId()); // 현재 프레임에서 타이머 이벤트 핸들러 바인딩
	//m_timer.Start(1000); // 1초마다 타이머 이벤트 발생

	wxPanel* panel = new wxPanel(this, wxID_ANY);
	wxButton* myButton = new wxButton(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());

	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!");
}

void MyFrame::OnTimer(wxTimerEvent& event) // 타이머 이벤트 핸들러 구현
{
	static int count = 0;
	count++;
	SetStatusText(wxString::Format("Timer tick: %d", count));
}

 

상태표시줄에 타이머 동작이 표시된다.

 

복수의 타이머도 동일한 방법으로 생성한다.

#include <wx/wx.h>

class MyApp : public wxApp
{
public:
	bool OnInit() override;
};

wxIMPLEMENT_APP(MyApp);

class MyFrame : public wxFrame
{
public:
	MyFrame();

private:
	wxTimer m_timer; // 타이머 객체 추가
	wxTimer m_timer2; // 타이머 객체 추가

	void OnHello(wxCommandEvent& event);
	void OnExit(wxCommandEvent& event);
	void OnAbout(wxCommandEvent& event);
	void OnTimer(wxTimerEvent& event); // 타이머 이벤트 핸들러 추가
	void OnTimer2(wxTimerEvent& event); // 타이머 이벤트 핸들러 추가
};

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!");

	// 타이머 초기화 및 시작 방법 1: 타이머 객체에 직접 이벤트 핸들러 바인딩
	m_timer.Bind(wxEVT_TIMER, &MyFrame::OnTimer, this, m_timer.GetId()); // 타이머 이벤트 핸들러 바인딩	
	m_timer.Start(1000); // 1초마다 타이머 이벤트 발생

	m_timer2.Bind(wxEVT_TIMER, &MyFrame::OnTimer2, this, m_timer2.GetId()); // 타이머2 이벤트 핸들러 바인딩
	m_timer2.Start(1000); // 1초마다 타이머2 이벤트 발생

	// 타이머 초기화 및 시작 방법 2: 프레임에서 타이머 이벤트 핸들러 바인딩
	//m_timer.SetOwner(this); // 타이머의 소유자를 현재 프레임으로 설정
	//Bind(wxEVT_TIMER, &MyFrame::OnTimer, this, m_timer.GetId()); // 현재 프레임에서 타이머 이벤트 핸들러 바인딩
	//m_timer.Start(1000); // 1초마다 타이머 이벤트 발생

	//m_timer2.SetOwner(this); // 타이머2의 소유자를 현재 프레임으로 설정
	//Bind(wxEVT_TIMER, &MyFrame::OnTimer2, this, m_timer2.GetId()); // 현재 프레임에서 타이머2 이벤트 핸들러 바인딩
	//m_timer2.Start(1000); // 1초마다 타이머2 이벤트 발생

	wxPanel* panel = new wxPanel(this, wxID_ANY);
	wxButton* myButton = new wxButton(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());

	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!");
}

void MyFrame::OnTimer(wxTimerEvent& event) // 타이머 이벤트 핸들러 구현
{
	static int count = 0;
	count++;
	SetStatusText(wxString::Format("Timer tick: %d", count));
}

void MyFrame::OnTimer2(wxTimerEvent& event) // 타이머 이벤트 핸들러 구현
{
	static int count = 0;
	count++;
	SetTitle(wxString::Format("Timer2 tick: %d", count));
}

 

 

반응형
Posted by J-sean
:
반응형

wxChartDir은 ChartDir(ChartDirector)의 wxWidgets 프레임워크 래퍼(wrapper)다.

실제 차트를 그리고(렌더링) 데이터를 처리하는 핵심 엔진은 ChartDir이기 때문에, wxChartDir을 사용하려면 ChartDir이 필요하다.

wxChartDir로 wxWidgets 애플리케이션에 차트를 그려보자.

 

우선 wxChartDir을 빌드하자.

vcpkg install 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;
}

 

 

 

히스토그램과 벨 커브를 그려보자.

#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;
}

 

 

※ 참고

ChartDirector Documentation

wxChartDir

wxChartDir Documentation

 

반응형
Posted by J-sean
:
반응형

wxCharts를 이용해 차트를 그려보자.

 

vcpkg install wxcharts

Include, Library 등을 적당히 세팅한다.

 

#include <wx/wx.h>
#include <wxcharts/wx/charts/wxchart.h> // wxCharts 라이브러리의 헤더 파일을 포함한다.
#include <wxcharts/wx/charts/wxbarchartctrl.h> // wxBarChartCtrl 클래스의 헤더 파일을 포함한다.

#pragma comment(lib, "wxchartsd.lib") // wxCharts 라이브러리를 링크한다.

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)
{
	//wxLogMessage("Hello world from wxWidgets!");

	// wxCharts 라이브러리를 사용하여 막대 그래프를 생성
	// Create the data for the bar chart widget
	wxVector<wxString> labels;
	labels.push_back("January");
	labels.push_back("February");
	labels.push_back("March");
	labels.push_back("April");
	labels.push_back("May");
	labels.push_back("June");
	labels.push_back("July");
	wxChartsCategoricalData::ptr chartData = wxChartsCategoricalData::make_shared(labels);

	// Add the first dataset
	wxVector<wxDouble> points1;
	points1.push_back(3);
	points1.push_back(2.5);
	points1.push_back(1.2);
	points1.push_back(3);
	points1.push_back(6);
	points1.push_back(5);
	points1.push_back(1);
	wxChartsDoubleDataset::ptr dataset1(new wxChartsDoubleDataset("Dataset 1", points1));
	chartData->AddDataset(dataset1);

	// Add the second dataset
	wxVector<wxDouble> points2;
	points2.push_back(1);
	points2.push_back(1.33);
	points2.push_back(2.5);
	points2.push_back(2);
	points2.push_back(3);
	points2.push_back(1.8);
	points2.push_back(0.4);
	wxChartsDoubleDataset::ptr dataset2(new wxChartsDoubleDataset("Dataset 2", points2));
	chartData->AddDataset(dataset2);

	// Create the bar chart widget
	wxBarChartCtrl* barChartCtrl = new wxBarChartCtrl(panel, wxID_ANY, chartData);
	barChartCtrl->SetSize(400, 300);

	// Set up the sizer for the panel
	//wxBoxSizer* panelSizer = new wxBoxSizer(wxHORIZONTAL);
	//panelSizer->Add(barChartCtrl, 1, wxEXPAND);
	//panel->SetSizer(panelSizer);
}

 

버튼을 클릭한다.

 

Panel에 차트가 표시된다.

 

 

grid를 없애 보자.

void MyFrame::OnHello(wxCommandEvent& event)
{
	//wxLogMessage("Hello world from wxWidgets!");

	// wxCharts 라이브러리를 사용하여 막대 그래프를 생성
	// Create the data for the bar chart widget
	wxVector<wxString> labels;
	labels.push_back("January");
	labels.push_back("February");
	labels.push_back("March");
	labels.push_back("April");
	labels.push_back("May");
	labels.push_back("June");
	labels.push_back("July");
	wxChartsCategoricalData::ptr chartData = wxChartsCategoricalData::make_shared(labels);

	// Add the first dataset
	wxVector<wxDouble> points1;
	points1.push_back(3);
	points1.push_back(2.5);
	points1.push_back(1.2);
	points1.push_back(3);
	points1.push_back(6);
	points1.push_back(5);
	points1.push_back(1);
	wxChartsDoubleDataset::ptr dataset1(new wxChartsDoubleDataset("Dataset 1", points1));
	chartData->AddDataset(dataset1);

	// Add the second dataset
	wxVector<wxDouble> points2;
	points2.push_back(1);
	points2.push_back(1.33);
	points2.push_back(2.5);
	points2.push_back(2);
	points2.push_back(3);
	points2.push_back(1.8);
	points2.push_back(0.4);
	wxChartsDoubleDataset::ptr dataset2(new wxChartsDoubleDataset("Dataset 2", points2));
	chartData->AddDataset(dataset2);

	// barchart 옵션 설정
	wxBarChartOptions::ptr options(new wxBarChartOptions());

	// 라이브러리의 GetGridOptions()가 const 참조만 반환하므로
	// const_cast를 사용하여 상수성을 일시적으로 제거하여 옵션을 수정한다.
	wxChartsGridOptions& gridOptions = const_cast<wxChartsGridOptions&>(options->GetGridOptions());
	gridOptions.GetHorizontalGridLineOptions().SetShowGridLines(false);
	gridOptions.GetVerticalGridLineOptions().SetShowGridLines(false);

	// Create the bar chart widget with options
	wxBarChartCtrl* barChartCtrl = new wxBarChartCtrl(panel, wxID_ANY, chartData, options);
	barChartCtrl->SetSize(400, 300);

	// Set up the sizer for the panel
	//wxBoxSizer* panelSizer = new wxBoxSizer(wxHORIZONTAL);
	//panelSizer->Add(barChartCtrl, 1, wxEXPAND);
	//panel->SetSizer(panelSizer);
}

 

 

 

세로 막대 그래프를 그려 보자.

#include <wx/wx.h>
#include <wxcharts/wx/charts/wxchart.h> // wxCharts 라이브러리의 헤더 파일을 포함한다.
#include <wxcharts/wx/charts/wxcolumnchartctrl.h> // wxColumnChartCtrl 클래스의 헤더 파일을 포함한다.

#pragma comment(lib, "wxchartsd.lib") // wxCharts 라이브러리를 링크한다.

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)
{
	//wxLogMessage("Hello world from wxWidgets!");

	// wxCharts 라이브러리를 사용하여 막대 그래프를 생성
	// Create the data for the bar chart widget
	wxVector<wxString> labels;
	labels.push_back("January");
	labels.push_back("February");
	labels.push_back("March");
	labels.push_back("April");
	labels.push_back("May");
	labels.push_back("June");
	labels.push_back("July");
	wxChartsCategoricalData::ptr chartData = wxChartsCategoricalData::make_shared(labels);

	// Add the first dataset
	wxVector<wxDouble> points1;
	points1.push_back(3);
	points1.push_back(2.5);
	points1.push_back(1.2);
	points1.push_back(3);
	points1.push_back(6);
	points1.push_back(5);
	points1.push_back(1);
	wxChartsDoubleDataset::ptr dataset1(new wxChartsDoubleDataset("Dataset 1", points1));
	chartData->AddDataset(dataset1);

	// Add the second dataset
	wxVector<wxDouble> points2;
	points2.push_back(1);
	points2.push_back(1.33);
	points2.push_back(2.5);
	points2.push_back(2);
	points2.push_back(3);
	points2.push_back(1.8);
	points2.push_back(0.4);
	wxChartsDoubleDataset::ptr dataset2(new wxChartsDoubleDataset("Dataset 2", points2));
	chartData->AddDataset(dataset2);

	// Create the column chart widget (Month가 X축에 표시됨)
	wxColumnChartCtrl* columnChartCtrl = new wxColumnChartCtrl(panel, wxID_ANY, chartData);
	columnChartCtrl->SetSize(400, 300);

	// Set up the sizer for the panel
	//wxBoxSizer* panelSizer = new wxBoxSizer(wxHORIZONTAL);
	//panelSizer->Add(columnChartCtrl, 1, wxEXPAND);
	//panel->SetSizer(panelSizer);
}

 

세로 막대 그래프가 표시된다.

 

세로 막대 그래프는 wxColumnChartOptions::prt가 없어서 grid를 없애지 못한다. 아직 완전한 라이브러리는 아닌 것 같다.

 

라인 차트를 그려보자.

#include <wx/wx.h>
#include <wxcharts/wx/charts/wxchart.h> // wxchart.h 헤더 파일을 포함한다.
#include <wxcharts/wx/charts/wxlinechartctrl.h> // wxlinechartctrl.h 헤더 파일을 포함한다.

#pragma comment(lib, "wxchartsd.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)
{
	wxVector<wxString> labels;
	labels.push_back("2023-01-01");
	labels.push_back("2023-01-02");
	labels.push_back("2023-01-03");
	labels.push_back("2023-01-04");
	labels.push_back("2023-01-05");
	wxChartsCategoricalData::ptr chartData = wxChartsCategoricalData::make_shared(labels);

	wxVector<wxDouble> points;
	points.push_back(3);
	points.push_back(2.5);
	points.push_back(1.2);
	points.push_back(3);
	points.push_back(6);
	wxChartsDoubleDataset::ptr dataset1(new wxChartsDoubleDataset("Data", points));
	chartData->AddDataset(dataset1);

	// Create the line chart widget
	wxLineChartCtrl* lineChartCtrl = new wxLineChartCtrl(panel, wxID_ANY, chartData, wxCHARTSLINETYPE_STRAIGHT, wxDefaultPosition, wxDefaultSize, wxBORDER_DEFAULT);
	lineChartCtrl->SetSize(400, 300);

	// Set up the sizer for the panel
	//wxBoxSizer* panelSizer = new wxBoxSizer(wxHORIZONTAL);
	//panelSizer->Add(lineChartCtrl, 1, wxEXPAND);
	//panel->SetSizer(panelSizer);
}

 

 

timeseries 차트는 아직 구현이 제대로 된 것 같지 않다. 사용할 수가 없어 라인 차트를 만들어 본 것이다.

아직 제대로 된 튜토리얼이나 문서도 없다. 아래 샘플을 참고하자.

 

※ 참고

wxCharts

Samples

 

반응형
Posted by J-sean
:
반응형

wxWidgets로 만든 앱에서 윈도우 핸들(HWND)을 확인해 보자.

 

#include <wx/wx.h>

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);
};

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!");

	wxPanel* panel = new wxPanel(this, wxID_ANY);
	wxButton* myButton = new wxButton(panel, wxID_ANY, "Press Me", wxPoint(30, 30), wxSize(200, 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)
{
	// 버튼의 윈도우 핸들을 가져와서 텍스트를 변경한다.
	// 실행시간에 이벤트가 발생한 객체가 wxButton인지 확인하기 위해 dynamic_cast를 사용한다.
	wxButton* button = dynamic_cast<wxButton*>(event.GetEventObject());
	if (button)
	{
		// GetHandle()을 통해 네이티브 운영체제의 윈도우 핸들(HWND)을 가져올 수 있다.
		// 이벤트가 발생한 객체가 wxButton인 경우, 해당 버튼의 핸들을 표시한다.
		void* handle = button->GetHandle();
		//void* handle = button->GetHWND(); // GetHWND()는 wxButton의 핸들을 반환하는 메서드로, GetHandle()과 동일한 값을 반환한다.
		button->SetLabel(wxString::Format("HWND: %p", handle));
	}
	else
	{
		// 버튼이 아닌 메뉴에서 호출된 경우, 메인 윈도우(MyFrame)의 핸들을 표시한다.
		void* frameHandle = this->GetHandle();
		wxLogMessage(wxString::Format("Main Window HWND: %p", frameHandle));
	}
}

 

 

버튼을 클릭하면 버튼의 윈도우 핸들을 표시한다.

 

File - Hello... 클릭

 

메인 윈도우의 핸들을 표시한다.

 

 

이번엔 타이틀바 왼쪽의 아이콘을 바꿔보자.

 

icon.ico
0.01MB

 

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);
    .
    .
    .

 

반응형
Posted by J-sean
:
반응형

wxFormBuilder로 만든 XRC 파일을 사용해 보자.

 

wxFormBuilder에서 Form에 버튼을 하나 배치하고 적당히 디자인한다.

 

XRC 파일을 저장한다. (gui.xrc)

 

#include <wx/wx.h>
#include <wx/xrc/xmlres.h>

#ifdef _DEBUG
#pragma comment(lib, "wxbase33ud_xml.lib")
#pragma comment(lib, "wxmsw33ud_xrc.lib")
#else
#pragma comment(lib, "wxbase33u_xml.lib")
#pragma comment(lib, "wxmsw33u_xrc.lib")
#endif

class MyApp : public wxApp {
public:
	bool OnInit() override;
};

wxIMPLEMENT_APP(MyApp);

class MyFrame : public wxFrame
{
public:
	MyFrame();
private:
	void OnButtonClicked(wxCommandEvent& event);
};

MyFrame::MyFrame() {
	// XRC 리소스 로드
	wxXmlResource::Get()->Load(wxT("gui.xrc")); // 생성한 xrc 파일명
	// Frame 로드
	wxXmlResource::Get()->LoadFrame(this, nullptr, wxT("MyFrame1")); // wxFormBuilder에서 설정한 Frame 이름
	// 컨트롤 바인딩 (이벤트 처리용)
	wxButton* button = static_cast<wxButton*>(FindWindowByName(wxT("btnSubmit"))); // wxFormBuilder에서 설정한 버튼 이름
	if (button) {
		button->Bind(wxEVT_BUTTON, &MyFrame::OnButtonClicked, this);
	}
}

void MyFrame::OnButtonClicked(wxCommandEvent& event) {
	wxMessageBox(wxT("Hello from XRC!"));
}

bool MyApp::OnInit() {
	wxInitAllImageHandlers();
	wxXmlResource::Get()->InitAllHandlers(); // XRC 핸들러 초기화

	MyFrame* frame = new MyFrame();
	frame->Show(true);
	return true;
}

 

버튼만 하나 배치하면 화면을 가득 채운다.

 

※ 참고

wxFormBuilder

 

반응형
Posted by J-sean
:
반응형

wxWidgets을 빌드하고 간단한 예제를 만들어 보자.

 

vcpkg install wxwidgets

 

약 4분 정도가 걸린다.

 

 

 

 

콘솔 프로젝트라면 SubSystem을 Windows로 바꿔준다.

 

 

간단한 예제.

#include <wx/wx.h>

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);
};

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!");

	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!");
}

 

 

 

버튼을 추가해 보자.

.
.
.
	CreateStatusBar();
	SetStatusText("Welcome to wxWidgets!");

	wxPanel* panel = new wxPanel(this, wxID_ANY);
	wxButton* myButton = new wxButton(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());

	Bind(wxEVT_MENU, &MyFrame::OnHello, this, ID_Hello);
	Bind(wxEVT_MENU, &MyFrame::OnAbout, this, wxID_ABOUT);
	Bind(wxEVT_MENU, &MyFrame::OnExit, this, wxID_EXIT);
.
.
.

 

 

코드만으로 UI를 디자인하기는 쉽지 않다. 필요하다면 wysiwyg 방식으로 디자인할 수 있는 wxFormBuilder를 사용하자.

wxFormBuilder

 

이번에는 OpenCV를 이용해 이미지를 표시해 보자.

#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);
	}
}

 

결과는 이전 코드와 같다.

 

※ 참고

Hello World Example

Documentation

Tutorials

 

반응형
Posted by J-sean
: