wxPieCtrl and wxProgressPie

If you have a cool piece of software to share, but you are not hosting it officially yet, please dump it in here. If you have code snippets that are useful, please donate!
Post Reply
User avatar
T-Rex
Moderator
Moderator
Posts: 1206
Joined: Sat Oct 23, 2004 9:58 am
Location: Zaporizhzhya, Ukraine
Contact:

wxPieCtrl and wxProgressPie

Post by T-Rex »

OK, ... I've created something and it looks nice. The control is not so perfect but it looks nice for me :wink:

wxPieCtrl.h

Code: Select all

#ifndef _WX_PIE_CTRL
#define _WX_PIE_CTRL

#include <wx/wx.h>
#include <wx/image.h>
#include <wx/dynarray.h>

#ifndef M_PI
#define M_PI 3.14159265358979
#endif

WX_DECLARE_OBJARRAY(double, wxArrayDouble);

class wxPiePart
{
	double m_Value;
	wxColour m_Colour;
public:
	wxPiePart();
	wxPiePart(double value, wxColour colour);
	wxPiePart(const wxPiePart & part);	
	wxColour GetColour() {return m_Colour;}
	void SetColour(wxColour colour) {m_Colour = colour;}
	double GetValue() {return m_Value;}
	void SetValue(double value) {m_Value = fabs(value);}
};

WX_DECLARE_OBJARRAY(wxPiePart, wxPieSeries);

class wxPieCtrl : public wxWindow
{
	double m_Angle;
	double m_RotationAngle;
	int m_Height;
	wxArrayDouble m_Values;
	void GetPartAngles(wxArrayDouble & angles);
	void DrawParts(wxMemoryDC & dc, int cx, int cy, int w, int h);
	wxBitmap m_Background;
protected:
	void Draw(wxPaintDC & pdc);
public:
	wxPieSeries m_Series;
	wxPieCtrl(wxWindow * parent, wxWindowID id = -1, wxPoint pos = wxDefaultPosition,
		wxSize sz = wxDefaultSize, long style = 0, wxString name = "wxPieCtrl");
	void SetAngle(double angle);
	void SetRotationAngle(double angle);
	void SetHeight(int value) {m_Height = abs(value);}
	void SetBackground(wxBitmap bmp);
	DECLARE_EVENT_TABLE()
	void OnPaint(wxPaintEvent & event);
	void OnEraseBackground(wxEraseEvent & event);
	void OnSize(wxSizeEvent & event);
};


class wxProgressPie : public wxPieCtrl
{
	double m_MaxValue;
	double m_Value;
	wxColour m_FilledColour;
	wxColour m_UnfilledColour;
public:
	wxProgressPie(wxWindow * parent, wxWindowID id = -1, double maxvalue = 100, double value = 50,
		wxPoint pos = wxDefaultPosition, wxSize sz = wxDefaultSize, long style = 0);
	void SetValue(double value);
	double GetValue() {return m_Value;}
	void SetMaxValue(double value);
	double GetMaxValue() {return m_MaxValue;}
};

#endif
wxPieCtrl.cpp

Code: Select all

#include "wxPieCtrl.h"
#include <wx/arrimpl.cpp>
#include <algorithm>

using namespace std;

WX_DEFINE_OBJARRAY(wxArrayDouble);
WX_DEFINE_OBJARRAY(wxPieSeries);

wxPiePart::wxPiePart()
: m_Value(0)
{
}

wxPiePart::wxPiePart(double value, wxColour colour)
: m_Value(value), m_Colour(colour)
{
}

wxPiePart::wxPiePart(const wxPiePart & part)
: m_Value(part.m_Value), m_Colour(part.m_Colour)
{
}

BEGIN_EVENT_TABLE(wxPieCtrl, wxWindow)
EVT_PAINT(wxPieCtrl::OnPaint)
EVT_ERASE_BACKGROUND(wxPieCtrl::OnEraseBackground)
EVT_SIZE(wxPieCtrl::OnSize)
END_EVENT_TABLE()

wxPieCtrl::wxPieCtrl(wxWindow * parent, wxWindowID id, wxPoint pos,
		wxSize sz, long style, wxString name)
		:wxWindow(parent, id, pos, sz, style, name), m_Angle(M_PI/12), m_RotationAngle(0), m_Height(10),
		m_Background(wxNullBitmap)
{
}

void wxPieCtrl::SetBackground(wxBitmap bmp)
{
	m_Background = bmp;
	Refresh();
}

void wxPieCtrl::DrawParts(wxMemoryDC & dc, int cx, int cy, int w, int h)
{
	wxArrayDouble angles;	
	GetPartAngles(angles);	
	wxPen oldpen = dc.GetPen();
	for(unsigned int i = 0; i < angles.Count(); i++)
	{
		if(i > 0)
		{	
			dc.SetPen(*wxBLACK_PEN);
			dc.SetBrush(wxBrush(m_Series[i-1].GetColour()));
			if(angles[i-1] != angles[i])
			dc.DrawEllipticArc(0, (1-sin(m_Angle))*(h/2)+cy, w, h * sin(m_Angle), angles[i-1]+m_RotationAngle/M_PI*180, angles[i]+m_RotationAngle/M_PI*180);
		}
	}
	if(m_Series.Count() == 1)
	{
		dc.SetBrush(wxBrush(m_Series[0].GetColour()));
		dc.DrawEllipticArc(0, (1-sin(m_Angle))*(h/2)+cy, w, h * sin(m_Angle), 0, 360);
	}
	dc.SetPen(oldpen);
}

void wxPieCtrl::OnSize(wxSizeEvent & event)
{
	Refresh();
}

void wxPieCtrl::GetPartAngles(wxArrayDouble & angles)
{
	angles.Clear();
	double total(0);
	unsigned int i;
	for(i = 0; i < m_Series.Count(); i++)
	{
		total += m_Series[i].GetValue();
	}
	double current(0);
	angles.Add(current);	
	for(i = 0; i < m_Series.Count(); i++)
	{
		current += m_Series[i].GetValue();
		angles.Add(360 * (double)current / (double)total);		
	}
}

void wxPieCtrl::SetAngle(double angle)
{
	if(angle < 0) angle = 0;
	if(angle > M_PI/2) angle = M_PI/2;
	m_Angle = angle;	
	Refresh();
}

void wxPieCtrl::SetRotationAngle(double angle)
{
	if(angle < 0) angle = 0;
	if(angle > 2 * M_PI) angle = 2 * M_PI;
	m_RotationAngle = angle;	
	Refresh();
}

void wxPieCtrl::Draw(wxPaintDC & pdc)
{
	int w,h,i,j;
	int px, py;
	GetSize(&w,&h);
	wxBitmap bmp(w,h);
	wxMemoryDC dc;
	dc.SelectObject(bmp);
	dc.BeginDrawing();	
	dc.SetBackground(*wxWHITE_BRUSH);
	dc.Clear();
	if(m_Background != wxNullBitmap)
	{
		for(i = 0; i < w; i+= m_Background.GetWidth())
		{
			for(j = 0; j < h; j+= m_Background.GetHeight())
			{
				dc.DrawBitmap(m_Background,i,j);
			}
		}
	}
	dc.SetBrush(*wxGREEN_BRUSH);
	dc.SetPen(wxPen(*wxGREEN));		
	if(m_Angle > M_PI/2)
	{		
		DrawParts(dc, 0, 0, w,h);
	} else DrawParts(dc, 0, m_Height*cos(m_Angle), w,h);
	wxPoint points[4], points1[4];	
	dc.SetPen(wxPen(*wxBLACK));	
	wxArrayDouble angles;
	GetPartAngles(angles);
	unsigned angleindex(0);
	dc.SetBrush(wxBrush(wxColour(m_Series[angleindex].GetColour().Red(),
									m_Series[angleindex].GetColour().Green(),
									m_Series[angleindex].GetColour().Blue())));		
	double x;
	for(x = 0; x <= 2 * M_PI; x += 0.05)
	{
		if(angleindex < angles.Count())
		{
			if((double)x/(double)M_PI*(double)180 >= angles[angleindex+1]) 
			{
				angleindex = angleindex + 1;				
				x = angles[angleindex]*M_PI/180;
			}
		}
		points[0].x = points[1].x;
		points[0].y = points[1].y;
		points[3].x = points[2].x;
		points[3].y = points[2].y;		
		px = w/2 * (1+cos(x+m_RotationAngle));
		py = h/2-sin(m_Angle)*h/2*sin(x+m_RotationAngle)-1;		
		points[1].x = px;
		points[1].y = py;
		points[2].x = px;
		points[2].y = py+m_Height*cos(m_Angle);		
		if(w > 0) 
		{
			dc.SetBrush(wxBrush(wxColour(m_Series[angleindex].GetColour().Red()*((double)1-(double)px/(double)w),
									m_Series[angleindex].GetColour().Green()*((double)1-(double)px/(double)w),
									m_Series[angleindex].GetColour().Blue()*((double)1-(double)px/(double)w))));			
		}		
		if(sin(x+m_RotationAngle)<0 && sin(x-0.05+m_RotationAngle)<=0 && x > 0)dc.DrawPolygon(4, points);
	}	
	x = 2 * M_PI;
	points[0].x = points[1].x;
	points[0].y = points[1].y;
	points[3].x = points[2].x;
	points[3].y = points[2].y;
	px = w/2 * (1+cos(x+m_RotationAngle));
	py = h/2-sin(m_Angle)*h/2*sin(x+m_RotationAngle)-1;		
	points[1].x = px;
	points[1].y = py;
	points[2].x = px;
	points[2].y = py+m_Height*cos(m_Angle);		
	if(w > 0) 
	{
		dc.SetBrush(wxBrush(wxColour(m_Series[angleindex].GetColour().Red()*((double)1-(double)px/(double)w),
			m_Series[angleindex].GetColour().Green()*((double)1-(double)px/(double)w),
			m_Series[angleindex].GetColour().Blue()*((double)1-(double)px/(double)w))));			
	}	
	if(sin(x+m_RotationAngle)<0 && sin(x-0.05+m_RotationAngle)<0)dc.DrawPolygon(4, points);	
	dc.SetBrush(*wxGREEN_BRUSH);
	dc.SetPen(wxPen(*wxGREEN));
	if(m_Angle <= M_PI/2)
	{		
		DrawParts(dc, 0, 0, w,h);
	} else DrawParts(dc, 0, m_Height*cos(m_Angle), w,h);
	dc.EndDrawing();
	pdc.Blit(0,0,w,h,&dc,0,0);
}

void wxPieCtrl::OnPaint(wxPaintEvent & event)
{
	wxPaintDC pdc(this);	
	Draw(pdc);
}

void wxPieCtrl::OnEraseBackground(wxEraseEvent & event)
{
}

wxProgressPie::wxProgressPie(wxWindow * parent, wxWindowID id, double maxvalue, double value,
		wxPoint pos, wxSize sz, long style)
		:wxPieCtrl(parent, id, pos, sz, style), m_MaxValue(maxvalue), m_Value(value)
{
	m_FilledColour = wxColour(0,0,127);
	m_UnfilledColour = *wxWHITE;
	wxPiePart part;
	part.SetColour(m_FilledColour);
	double a = min(value, m_MaxValue);
	part.SetValue(max(a, (double)0.0));
	m_Series.Add(part);
	part.SetColour(m_UnfilledColour);
	part.SetValue(max((double)0.0, (double)(m_MaxValue-part.GetValue())));
	m_Series.Add(part);
}

void wxProgressPie::SetValue(double value)
{
	m_Value = min(value, m_MaxValue);
	m_Series[0].SetValue(max(m_Value, (double)0.0));
	m_Series[1].SetValue(max((double)(m_MaxValue-m_Value), (double)0.0));
	Refresh();
}

void wxProgressPie::SetMaxValue(double value)
{
	m_MaxValue = value;
	m_Value = min(m_Value, m_MaxValue);
	m_Series[0].SetValue(max(m_Value, (double)0.0));
	m_Series[1].SetValue(max((double)(m_MaxValue-m_Value), (double)0.0));
	Refresh();
}
Tested with MSVC.NET 2003 and wxDev-CPP + wxWidgets-DevPack-2.6
Last edited by T-Rex on Thu Jun 02, 2005 4:32 pm, edited 1 time in total.
User avatar
T-Rex
Moderator
Moderator
Posts: 1206
Joined: Sat Oct 23, 2004 9:58 am
Location: Zaporizhzhya, Ukraine
Contact:

Post by T-Rex »

wxPieCtrlTest.cpp

Code: Select all

#include <wx/wx.h>
#include <wx/image.h>
#include "wxPieCtrl.h"

class MyTimer;

class MyFrame : public wxFrame
{
	MyTimer * m_Timer;
public:
	wxPieCtrl * m_Pie;
	wxProgressPie * m_ProgressPie;
	wxSlider * m_Slider;
	wxSlider * m_AngleSlider;
	MyFrame(wxWindow * parent);	
	~MyFrame();
	DECLARE_EVENT_TABLE()	
	void OnSlider(wxScrollEvent & event);
	void OnAngleSlider(wxScrollEvent & event);
};

class MyTimer : public wxTimer
{
	MyFrame * m_Frame;
	int incr;
public:
	MyTimer(MyFrame * frame) : wxTimer(), m_Frame(frame), incr(1) {}
	void Notify() 
	{ 
		if(m_Frame->m_ProgressPie->GetValue() <= 0) incr = 1;
		if(m_Frame->m_ProgressPie->GetValue() >= m_Frame->m_ProgressPie->GetMaxValue())	incr = -1;		
		m_Frame->m_ProgressPie->SetValue(m_Frame->m_ProgressPie->GetValue()+incr);
	}
};

BEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_COMMAND_SCROLL(10002, MyFrame::OnSlider)
EVT_COMMAND_SCROLL(10003, MyFrame::OnAngleSlider)
END_EVENT_TABLE()

MyFrame::MyFrame(wxWindow * parent)
: wxFrame(parent, -1, "Test", wxPoint(100,100), wxSize(400,500))
{		
	m_ProgressPie = new wxProgressPie(this, 10004, 100, 50, wxDefaultPosition, wxSize(180, 200));
	m_Pie = new wxPieCtrl(this, 10001, wxDefaultPosition, wxSize(180,270));
	m_Pie->SetBackground(wxBitmap("background.png", wxBITMAP_TYPE_PNG));
	m_ProgressPie->SetBackground(wxBitmap("background.png", wxBITMAP_TYPE_PNG));
	m_Pie->SetHeight(30);
	wxPiePart part;
	part.SetValue(250);
	part.SetColour(wxColour(200,100,100));
	m_Pie->m_Series.Add(part);
	part.SetValue(200);
	part.SetColour(wxColour(0,200,50));
	m_Pie->m_Series.Add(part);
	part.SetValue(120);
	part.SetColour(wxColour(50,50,250));
	m_Pie->m_Series.Add(part);
	m_Slider = new wxSlider(this, 10002, 15, 0, 90);
	m_AngleSlider = new wxSlider(this, 10003, 45, 0, 360, wxDefaultPosition, wxDefaultSize, wxSL_VERTICAL);
	wxBoxSizer * sizer = new wxBoxSizer(wxVERTICAL);
	wxBoxSizer * hsizer = new wxBoxSizer(wxHORIZONTAL);
	SetSizer(sizer);	
	hsizer->Add(m_ProgressPie, 1, wxEXPAND|wxALL, 5);
	hsizer->Add(m_Pie, 1, wxEXPAND|wxALL, 5);
	hsizer->Add(m_AngleSlider, 0, wxGROW|wxALL, 5);
	sizer->Add(hsizer, 1, wxEXPAND|wxALL, 5);
	sizer->Add(m_Slider, 0, wxGROW|wxALL, 5);
	sizer->Fit(this);
	m_Timer = new MyTimer(this);
	m_Timer->Start(50);
	Centre();
}

MyFrame::~MyFrame()
{
	delete m_Timer;
}

void MyFrame::OnSlider(wxScrollEvent & event)
{
	m_Pie->SetAngle((double)m_Slider->GetValue() / (double)180 * (double)M_PI);
	m_ProgressPie->SetAngle((double)m_Slider->GetValue() / (double)180 * (double)M_PI);
}

void MyFrame::OnAngleSlider(wxScrollEvent & event)
{
	m_Pie->SetRotationAngle((double)m_AngleSlider->GetValue() / (double)180 * (double)M_PI);
	m_ProgressPie->SetRotationAngle((double)m_AngleSlider->GetValue() / (double)180 * (double)M_PI);
}

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

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
	wxImage::AddHandler( new wxPNGHandler );
	MyFrame * frame = new MyFrame(NULL);	
	frame->Show();	
	SetTopWindow(frame);
	return true;
}
User avatar
T-Rex
Moderator
Moderator
Posts: 1206
Joined: Sat Oct 23, 2004 9:58 am
Location: Zaporizhzhya, Ukraine
Contact:

Post by T-Rex »

Screenshot: Image
Jorg
Moderator
Moderator
Posts: 3971
Joined: Fri Aug 27, 2004 9:38 pm
Location: Delft, Netherlands
Contact:

Post by Jorg »

Looks very sweet! It seems we need to have something to host all this stuff. There was a discussion going on wxCode. As it is the perfect repository, I dislike the layout as it is now. But Otto already did some work in changing it which is good. I do not have infinite space on my account, but might be able to host projects as long as they are small. Maybe I should set up another user and people can use this as FTP to store small code in zip files and update these periodically.

Awesome work!
- Jorgen
Forensic Software Engineer
Netherlands Forensic Insitute
http://english.forensischinstituut.nl/
-------------------------------------
Jorg's WasteBucket
http://www.xs4all.nl/~jorgb/wb
Post Reply