Page 1 of 1

C++ Windows Charting Library Part 3 Rate Topic: -----

#1 snoopy11  Icon User is offline

  • Engineering ● Software
  • member icon

Reputation: 833
  • View blog
  • Posts: 2,457
  • Joined: 20-March 10

Posted 04 January 2013 - 07:32 AM

C++ Windows Charting Library Part 3

In Part 3 of this series, I will discuss the Line Graph making capabilities of the Library and an example program on how to use all the types of graph together through the use of memory dc's.

In ChartKM.h

//Draws Line Chart
void DrawLine(HDC hdcDst,int x, int y, int szBarX, int szBarY, COLORREF bkcolor);
............
// Adds Item Data for Line Charts
void AddLineData(string nmItem, float valueX, float valueY);
// Adds Group and Range Data for Line Charts
void AddRangeData(float RangeX, float RangeY, int GroupNo);
............
// Add Titles for x and y axis
void SetAxisTitles(string szAxisX, string szAxisY);




The above code excerpts are specific to making line graphs in the chartObject class.

DrawLine draws a new Line Chart.
AddLineData adds x and y plot data with an nmItem identifier.
AddRangeData adds the range of the x and y axis.

Finally setAxisTitles makes the axis titles for the Line Graphs.


In ChartKM.cpp
void chartObject::AddLineData(string nmItem, float valueX, float valueY)
{
	ItemName.push_back(nmItem);
	LineValueX.push_back(valueX);
	LineValueY.push_back(valueY);
}

void chartObject::AddRangeData(float RangeX, float RangeY, int GroupNo)
{
	LineRangeX = RangeX;
	LineRangeY = RangeY;
	ItemGroup.push_back(GroupNo);

}

void chartObject::DrawLine(HDC hdcDst,int x, int y, int szBarX, int szBarY, COLORREF bkcolor)
{
	HPEN pen = CreatePen(PS_SOLID,1,bkcolor);
	SelectObject(hdcDst,pen);
	HFONT font;
	//Set Background Color for Text
	SetBkColor(hdcDst,GetPixel(hdcDst,0,0));
	font = CreateFont(14,0,900,900,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Arial Bold"));
	SelectObject(hdcDst, font);
	//Draw y axis
	MoveToEx(hdcDst,x+50+14,(szBarY)+y,NULL);
	LineTo(hdcDst,x+50+14,y);
	//Draw x axis
	MoveToEx(hdcDst,x+50+14,(szBarY)+y,NULL);
	LineTo(hdcDst,szBarX+x+50+14,(szBarY)+y);
	TextOut(hdcDst,x,((szBarY+y/2)+(yName.length()/2)),yName.c_str(),yName.length());// Draw y-axis label
	// change font
	font = CreateFont(14,0,0,0,FW_DONTCARE,FALSE,FALSE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS,
		CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Arial Bold"));
	SelectObject(hdcDst, font);
	TextOut(hdcDst,((szBarX+x+50)/2)-(xName.length()/2)+50,y+szBarY+42,xName.c_str(),xName.length());//Draw x-axis label
	TextOut(hdcDst,x+(50),y+szBarY,"0",1);// Draw a zero at origin
	//Draw Range at top of graph y axis
	stringstream ss;
	ss << LineRangeY;
	string number;
	ss >> number;
	TextOut(hdcDst,x+50-(number.length()*4),y-4,number.c_str(),number.length());
	//Draw Range at top of graph x axis
	ss.clear();
	ss << LineRangeX;
	ss >> number;
	TextOut(hdcDst,x+szBarX+50+14-(number.length()*4),y+szBarY+4,number.c_str(),number.length());

	// Add Title
	TextOut(hdcDst,((szBarX+x+50+14)/2)-((Title.length()*4)/2),y-24,Title.c_str(),Title.length());//Draw Title
	// Draw Halfway segment points
	MoveToEx(hdcDst, ((szBarX/2)+x+50+14), y+szBarY,NULL);
	LineTo(hdcDst, ((szBarX/2)+x+50+14), y+szBarY+4);
	ss.clear();
	ss << (LineRangeX/2);
	ss >> number;
	TextOut(hdcDst,(szBarX/2)+50+14+x-((number.length()*4)/2),y+szBarY+6,number.c_str(),number.length());
	MoveToEx(hdcDst,x+50+14,y+(szBarY/2),NULL);
	LineTo(hdcDst, (x+50+14)-4,y+(szBarY/2));
	ss.clear();
	ss << (LineRangeY/2);
	ss >> number;
	TextOut(hdcDst,50+x-((number.length()*4)),y+(szBarY/2),number.c_str(),number.length());
	string container;
	HPEN pen2;
	int x1=x+50+14;
	int y1=y+szBarY;
	//Start Drawing Data inside loops
	for(unsigned k = 0; k < GroupName.size(); k++)
	{
		container.clear();
		container = GroupName[k];
		x1=x+50+14;
		y1=y+szBarY;
		MoveToEx(hdcDst,x1,y1,NULL);
		for(unsigned j=0; j<GroupName.size(); j++)
		{
			if(GroupName[j] == container)
			{
				pen2 = CreatePen(PS_SOLID,1,colors[j]);
				SelectObject(hdcDst, pen2);
			}
		}

		for(unsigned i=0; i<LineValueX.size(); i++)
		{
			if(ItemName[i] == container)
			{
				x1=  (int)(x+50+14+((LineValueX[i]/LineRangeX)*szBarX));
				y1 = (int)(y +szBarY-((LineValueY[i]/LineRangeY)*szBarY));
				LineTo(hdcDst,x1,y1);
			}
			MoveToEx(hdcDst,x1,y1,NULL);
		}//End loop i
	}//End loop k

	HBRUSH hbr = NULL;
	//Draw Legend
	if(vLegend)
	{
		//Draw Legend
		TextOut(hdcDst,x+szBarX+15+50,y,"Legend",6);
		string buffer;


		//float d = 0.0f;
		for(unsigned i = 0; i<GroupName.size(); i++)
		{
			buffer = "";



			// write strings
			buffer.append(GroupName[i]);
			TextOut(hdcDst,x+szBarX+70+15,y+(25*(i+1)),buffer.c_str(),buffer.size());
			//Draw Colored Boxes
			Rectangle(hdcDst,x+szBarX+15+50,y+(25*(i+1)),x+szBarX+28+50,y+(25*(i+1)+12));
			RECT rc;
			rc.left= x+szBarX+16+50;
			rc.top = y+(25*(i+1))+1;
			rc.right = x+szBarX+28-1+50;
			rc.bottom = y+(25*(i+1)+12)-1;
			hbr = CreateSolidBrush(colors[i]);

			FillRect(hdcDst,&rc,hbr);
		}
	}


	SelectObject(hdcDst,pen);
	DeleteObject(pen);
	DeleteObject(pen2);
	DeleteObject(font);
	DeleteObject(hbr);
}




AddLineData adds the x, y data to the vectors LineValueX and LineValueY

AddRangeData adds the x , y range data to the vectors LineRangeX and LineRangeY

In our main function DrawLine...

We create a pen and a font.

Draw the X and Y axis

Draw the Labels on the axis.

Draw the range data at the top then draw the halway segment points on the graph.

We now start drawing the line data inside two main loops one loop for Groups one for Line Data.

We finally Draw the Legend and tidy up a bit.


Example code showing how to display multiple graphs



#include <Windows.h>
#include <ChartKM.h>

#pragma comment(lib, "ChartKM.lib")



/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
char szClassName[ ] = "CodeBlocksWindowsApp";

int WINAPI WinMain (HINSTANCE hThisInstance,
	HINSTANCE hPrevInstance,
	LPSTR lpszArgument,
	int nCmdShow)
{
	HWND hwnd;               /* This is the handle for our window */
	MSG messages;            /* Here messages to the application are saved */
	WNDCLASSEX wincl;        /* Data structure for the windowclass */

	/* The Window structure */
	wincl.hInstance = hThisInstance;
	wincl.lpszClassName = szClassName;
	wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
	wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
	wincl.cbSize = sizeof (WNDCLASSEX);

	/* Use default icon and mouse-pointer */
	wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
	wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
	wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
	wincl.lpszMenuName = NULL;                 /* No menu */
	wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
	wincl.cbWndExtra = 0;                      /* structure or the window instance */
	/* Use Windows's default colour as the background of the window */
	wincl.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);

	/* Register the window class, and if it fails quit the program */
	if (!RegisterClassEx (&wincl))
		return 0;

	/* The class is registered, let's create the program*/
	hwnd = CreateWindowEx (
		0,                   /* Extended possibilites for variation */
		szClassName,         /* Classname */
		"Charting Demo Application.",       /* Title Text */
		WS_OVERLAPPEDWINDOW, /* default window */
		CW_USEDEFAULT,       /* Windows decides the position */
		CW_USEDEFAULT,       /* where the window ends up on the screen */
		1100,                 /* The programs width */
		700,                 /* and height in pixels */
		HWND_DESKTOP,        /* The window is a child-window to desktop */
		NULL,                /* No menu */
		hThisInstance,       /* Program Instance handler */
		NULL                 /* No Window Creation data */
		);

	/* Make the window visible on the screen */
	ShowWindow (hwnd, nCmdShow);

	/* Run the message loop. It will run until GetMessage() returns 0 */
	while (GetMessage (&messages, NULL, 0, 0))
	{
		/* Translate virtual-key messages into character messages */
		TranslateMessage(&messages);
		/* Send message to WindowProcedure */
		DispatchMessage(&messages);
	}

	/* The program return-value is 0 - The value that PostQuitMessage() gave */
	return messages.wParam;
}


/*  This function is called by the Windows function DispatchMessage()  */

LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{

	static chartObject Chart;
	static chartObject Chart2;
	static chartObject Chart3;
	static HDC hdc,memhdc,memhdc2,memhdc3;
	static HBITMAP hbmNewBitMap, hbmNewBitMap2, hbmNewBitMap3;
	PAINTSTRUCT ps;

	switch (message)                  /* handle the messages */
	{
	case WM_CREATE:
		{
			Chart3.SetAxisTitles("Time in Hours", "Amount of Vehicles");
			Chart3.AddGroupData("Cars",RGB(255,0,0));
			Chart3.AddGroupData("Vans",RGB(0,0,255));
			Chart3.AddGroupData("MotorBikes",RGB(163,73,164));
			Chart3.AddGroupData("Bicycles",RGB(255,128,0));
			Chart3.AddGroupData("Buses",RGB(255,0,128));
			Chart3.AddGroupData("Trucks",RGB(0,0,0));
			Chart3.AddRangeData(4.0f,150.0f,6);
			Chart3.AddTitle("Traffic Census, Kirby 2003");

			Chart3.AddLineData("Cars",1.0f,30.0f);
			Chart3.AddLineData("Cars",2.0f,60.0f);
			Chart3.AddLineData("Cars",2.5f,90.0f);
			Chart3.AddLineData("Cars",3.0f,120.0f);
			Chart3.AddLineData("Cars",4.0f,140.0f);
			Chart3.AddLineData("Vans",1.0f,5.0f);
			Chart3.AddLineData("Vans",1.5f,9.0f);
			Chart3.AddLineData("Vans",2.0f,17.0f);
			Chart3.AddLineData("Vans",3.0f,26.0f);
			Chart3.AddLineData("Vans",4.0f,40.0f);
			Chart3.AddLineData("MotorBikes",1.0f,20.0f);
			Chart3.AddLineData("MotorBikes",2.0f,30.0f);
			Chart3.AddLineData("MotorBikes",2.5f,40.0f);
			Chart3.AddLineData("MotorBikes",3.0f,55.0f);
			Chart3.AddLineData("MotorBikes",4.0f,70.0f);
			Chart3.AddLineData("Bicycles",1.0f,2.0f);
			Chart3.AddLineData("Bicycles",2.0f,8.0f);
			Chart3.AddLineData("Bicycles",2.5f,13.0f);
			Chart3.AddLineData("Bicycles",3.0f,19.0f);
			Chart3.AddLineData("Bicycles",4.0f,25.0f);
			Chart3.AddLineData("Buses",1.0f,2.0f);
			Chart3.AddLineData("Buses",2.0f,3.0f);
			Chart3.AddLineData("Buses",2.5f,3.0f);
			Chart3.AddLineData("Buses",3.0f,3.0f);
			Chart3.AddLineData("Buses",4.0f,5.0f);
			Chart3.AddLineData("Trucks",1.0f,12.0f);
			Chart3.AddLineData("Trucks",2.0f,18.0f);
			Chart3.AddLineData("Trucks",2.5f,20.0f);
			Chart3.AddLineData("Trucks",3.0f,23.0f);
			Chart3.AddLineData("Trucks",4.0f,25.0f);

			Chart2.AddData("Cars",140.0f, RGB(255,0,0));
			Chart2.AddData("Vans",40.0f,RGB(0,0,255));
			Chart2.AddData("MotorBikes",70.0f,RGB(0,255,0));
			Chart2.AddData("Bicycles",25.0f,RGB(0,0,128));
			Chart2.AddData("Buses",5.0f,RGB(255,242,0));
			Chart2.AddData("Trucks",25.0f,RGB(163,73,164));
			Chart2.AddTitle("Traffic Survey High Street, Kirby 2003");
			Chart.AddTitle("Traffic Survey High Street, Kirby");
			Chart.SetAxisTitles("Vehicles", "Amount Per Hour");
			Chart.AddGroupData("2003",RGB(0,0,255));
			Chart.AddGroupData("2005",RGB(255,0,0));
			Chart.AddGroupData("2007",RGB(0,255,0));
			Chart.AddItemData("Cars",140.0f,1);
			Chart.AddItemData("Cars",150.0f,2);
			Chart.AddItemData("Cars",140.0f,3);
			Chart.AddItemData("Vans",40.0f,1);
			Chart.AddItemData("Vans",35.0f,2);
			Chart.AddItemData("Vans",40.0f,3);
			Chart.AddItemData("Bikes",70.0f,1);
			Chart.AddItemData("Bikes",75.0f,2);
			Chart.AddItemData("Bikes",80.0f,3);
			Chart.AddItemData("Bicycles",25.0f,1);
			Chart.AddItemData("Bicycles",23.0f,2);
			Chart.AddItemData("Bicycles",26.0f,3);
			Chart.AddItemData("Buses",5.0f,1);
			Chart.AddItemData("Buses",5.0f,2);
			Chart.AddItemData("Buses",5.0f,3);
			Chart.AddItemData("Trucks",25.0f,1);
			Chart.AddItemData("Trucks",27.0f,2);
			Chart.AddItemData("Trucks",26.0f,3);
			Chart2.Legend(TRUE);



			hdc = GetDC(hwnd);
			memhdc = CreateCompatibleDC(hdc);
			memhdc2 = CreateCompatibleDC(hdc);
			memhdc3 = CreateCompatibleDC(hdc);
			//create the bitmap
			hbmNewBitMap=CreateCompatibleBitmap(hdc, 600, 600);
			hbmNewBitMap2=CreateCompatibleBitmap(hdc, 600, 600);
			hbmNewBitMap3=CreateCompatibleBitmap(hdc, 600, 600);
			//shove the image into the dc
			SelectObject(memhdc, hbmNewBitMap);
			SelectObject(memhdc2, hbmNewBitMap2);
			SelectObject(memhdc3, hbmNewBitMap3);
			RECT rect;
			rect.left = 0;
			rect.top = 0;
			rect.right = 600;
			rect.bottom = 600;
			//Fill the bitmap with the screen color
			FillRect(memhdc,&rect,(HBRUSH)GetStockObject(WHITE_BRUSH));
			FillRect(memhdc2,&rect,(HBRUSH)GetStockObject(WHITE_BRUSH));
			FillRect(memhdc3,&rect,(HBRUSH)GetStockObject(WHITE_BRUSH));

			// Draw the Chart onto the memory dc	
			Chart3.DrawLine(memhdc,25,25,200,200,RGB(0,0,0));

			Chart2.DrawPie(memhdc2,25,25,200,200,RGB(0,0,0));

			Chart.DrawBar(memhdc3,25,25,500,150,RGB(0,0,0));
			return 0;
		}

	case WM_SIZE:
		{
            InvalidateRect(hwnd,NULL,TRUE);
			return 0;
		}

	case WM_PAINT:
		{

			BeginPaint(hwnd,&ps);


			//Draw the Charts from Memory to Screen device context
			BitBlt(ps.hdc,25,50,400,400,memhdc,0,0,SRCCOPY);

			BitBlt(ps.hdc,600,50,400,400,memhdc2,0,0,SRCCOPY);

			BitBlt(ps.hdc,200,350,700,350,memhdc3,0,0,SRCCOPY);
			EndPaint(hwnd,&ps);

			return 0;
		}


	case WM_DESTROY:
		PostQuitMessage (0); /* send a WM_QUIT to the message queue */
		DeleteDC(memhdc);
		DeleteDC(memhdc2);
		DeleteDC(memhdc3);
		DeleteObject((HBITMAP)hbmNewBitMap);
		DeleteObject((HBITMAP)hbmNewBitMap2);
		DeleteObject((HBITMAP)hbmNewBitMap3);

		break;
	default:                      /* for messages that we don't deal with */
		return DefWindowProc (hwnd, message, wParam, lParam);
	}

	return 0;
}






We basically will start in WM_CREATE as everything else before is pretty much standard windows stuff.

We create 3 chartObjects, 3 Memory device contexts and 3 different Bitmaps.


We add all the data into the Charts.

We then create compatible DC's with the current HWND

We then shove our bitmaps into the DC's.

Fill the bitmaps with the screen color.

Make the three charts.

In WM_PAINT:

We bit blit the three device contexts to the screen. Its important we do it this way. By doing it this way we only call our chart drawing routine once not again an again so we would soon run out of memory on resizing events.

So that is it it's a fairly basic charting Library but it's a good starting point for those who are looking at charting libraries and finding them not doing exactly what they want them to. Just take this one and modify it to your needs. Overall I think 3D effects are 'overrated' and actually get in the way of displaying data. But some people like them so I will try and make a 3D class inside the library probably something like chartObject3D.

For now till next time,

Regards

Snoopy.

This post has been edited by baavgai: 17 February 2013 - 01:00 PM
Reason for edit:: Just line feed cleaning


Is This A Good Question/Topic? 0
  • +

Page 1 of 1