ChartDirector Ver 4.1 (C++ Edition)
Using ChartDirector with Other GUI Frameworks
This section is intended for developers that need to use ChartDirector with GUI frameworks that are not MFC based, or for developers who are interested in understanding the internal workings of the
CChartViewer MFC control (so as to customize it). If you are only using ChartDirector with MFC and with the standard CChartViewer control, you may consider to skip this session.
Because there is no standard GUI framework in C/C++ (eg. basic Win32, MFC, WTL, wxWindows, QT, GTK, Motif, to name a few), it is impractical for ChartDirector to include sample code for all possible GUI frameworks. Instead, ChartDirector includes a CChartViewer control for MFC, which is released in source code, so as to serve as an example on how to use ChartDirector with GUI in general.
In this section, we will describe the general approach of using ChartDirector with any GUI framework.
ChartDirector outputs charts as standard based images, and they should work with all common GUI frameworks. ChartDirector also includes GUI neutral libraries for handling hot spots and view port user interactions, so features such as tool tips, clickable charts, etc., should be possible in all common GUI frameworks.
Displaying Charts On Screen
ChartDirector output charts in standard formats in memory, as BMP, GIF, PNG, JPG, etc. To display the chart on screen, one only needs to use the standard methods of the GUI framework for displaying images.
In the following sample code, we will use BMP in Windows as examples.
The BMP format is also known as DIB (device independent bitmap). The BMP created by ChartDirector includes both BITMAPFILEHEADER and BITMAPINFOHEADER, and hence is exactly the same as the file format BMP.
On Windows, the basic Win32 API for blitting the DIB to screen is StretchDIBits. An example would be like:
// Output the chart as BMP
MemBlock bmp = c->makeChart(Chart::BMP);
// The BITMAPINFOHEADER is 14 bytes offset from the beginning
LPBITMAPINFO header = (LPBITMAPINFO)(bmp.data + 14);
// The bitmap data
LPBYTE bitData = (LPBYTE)(bmp.data) +
((LPBITMAPFILEHEADER)(bmp.data))->bfOffBits;
// Output to screen device context
StretchDIBits(myDeviceContext,
0, 0, header->bmiHeader.biWidth, header->bmiHeader.biHeight,
0, 0, header->bmiHeader.biWidth, header->bmiHeader.biHeight,
bitData, header, DIB_RGB_COLORS, SRCCOPY);
However, on Windows, more often than not, one would use HBITMAP instead of blitting the DIB to screen directly. The Win32 API to convert DIB to HBITMAP is CreateDIBitmap. For example, the following utility function creates a chart object as a HBITMAP.
HBITMAP makeChartAsHBITMAP(HDC hdc, BaseChart *c)
{
// Output the chart as BMP
MemBlock bmp = c->makeChart(Chart::BMP);
// The BITMAPINFOHEADER is 14 bytes offset from the beginning
LPBITMAPINFO header = (LPBITMAPINFO)(bmp.data + 14);
// The bitmap data
LPBYTE bitData = (LPBYTE)(bmp.data) +
((LPBITMAPFILEHEADER)(bmp.data))->bfOffBits;
// Convert the DIB to HBITMAP
return CreateDIBitmap(hdc, &(header->bmiHeader), CBM_INIT, bitData, header,
DIB_RGB_COLORS);
}
In MFC, one may use the CStatic control to display the resulting HBITMAP on screen:
// Get the device context
CDC *cdc = GetDC();
//output the chart as HBITMAP
HBITMAP hBMP = makeChartAsHBITMAP(myDeviceContext, chart);
// Put the chart in the picture control (m_Picture). Also delete any old
// HBITMAP in the picture control
if (0 != (hBMP = m_Picture->SetBitmap(hBMP)))
DeleteObject(hBMP);
// Release the CDC
ReleaseDC(cdc);
Handling Hot Spot Mouse Interactions
Hot spots are special regions in on the chart that are usually used to represent chart objects, such as data representation objects (sectors for pie chart, bars for bar charts, etc). One can display tool tips when the mouse is over the hot spots, and/or to make the hot spots clickable with mouse cursor feedback.
ChartDirector for C++ includes an
ImageMapHandler class that determine if a given point on a hot spot, and to retrieve the various parameters associated with the hot spot. ImageMapHandler accepts standard HTML image maps for defining the hot spots. The
BaseChart.getHTMLImageMap method can be used to generate image maps automatically for a chart.
In any GUI framework, one may put code in the "mouse move" event handler to check if the mouse is over a hot spot or not. If it is over a hot spot, the code can display pop up tool tips and/or change the mouse cursor shape (eg. a "hand" shape to denote that the hot spot is clickable).
Using Win32/MFC as an example, the code in the mouse over handler for showing tool tips may be something like:
void MyControl::OnMouseMove(UINT nFlags, CPoint point)
{
// m_hotSpotTester = ChartDirector ImageMapHandler handler object previously
// created with the image map of the chart
if (0 != m_hotSpotTester)
{
// Retrieve the hot spot under the mouse cursor
m_hotSpotTester->getHotSpot(point.x, point.y);
// Get the tool tip on the hot spot (returns NULL if not on any hot spot)
const char *tooltip = m_hotSpotTester->getValue("title");
// Show the tool tip. We assume there is an MFC CToolTipCtrl control
// (m_ToolTip) to show tool tips. As ChartDirector uses UTF8 strings, while
// MFC uses TCHAR for strings, we need to use UTF8toTCHAR for conversion.
m_ToolTip.UpdateTipText(UTF8toTCHAR(tooltip), this);
}
}
Similar approach can be used to handle other mouse interactions, such as mouse clicks, or to provide mouse cursor feedback.
Handling View Port Interactions
A view port can be imagined as a rectangular window of an underlying rectangular surface. For example, a chart that has 10 years of data can be imagined as a very long chart. If one only displays one of the year, we can say the view port covers only 10% of the underlying chart.
With the view port concept, scrolling can be handled as moving the view port, while zooming in and out can be handled as changing the view port size.
ChartDirector for C++ includes a
ViewPortManager class for managing the view port. To illustrate how it can be used, we will describe the general steps for implementing the "drag to select" style of zooming for a general GUI framework:
- Initially, use ViewPortManager.setChartMetrics to set the chart metrics to the ViewPortManager object, so it knows where is the plot area.
- In the mouse move event handler, use ViewPortManager.inPlotArea to test if the mouse is over the plot area. If the result is positive, one may change the mouse cursor to a magnifying class icon with a plus sign to provide user feedback.
- When the mouse button is pressed, save the position of the mouse cursor. When the mouse then moves with the button still pressed (dragging), put a rectangle on top of the chart using the saved position and the current mouse position. This creates the selection box.
In the CChartViewer MFC control included in ChartDirector, the rectangle is created by putting four "line controls" (CStatic controls with background color set to black and sized to look like thin lines) on top of the chart control, so the chart is not modified at all. Similar methods may be used in other GUI frameworks.
- When the mouse button is released, pass the selection box coordinates to ViewPortManager.zoomTo. The ViewPortManager will then update the view port position and size.
- Redraw the chart based on the new view port position and size.
Similarly, for zoom out, the followings general steps may be used:
- In the mouse move event handler, use ViewPortManager.inPlotArea to test if the mouse is over the plot area. If the result is positive, one may change the mouse cursor to a magnifying class icon with a minus sign to provide user feedback.
- When the mouse is clicked, pass the coordinates and the zoom out ratio to ViewPortManager.zoomAt. The ViewPortManager will then update the view port position and size.
- Redraw the chart based on the new view port position and size.
Printing Charts On Paper
In many GUI frameworks, printing charts on paper is similar to displaying charts on screen. Instead of using a "screen device context", one simply uses a "printer device context".
In the previous section
Using ChartDirector with MFC, there is a printing example using MFC/Win32 API. The code is repeated here as a reference.
// Output the chart as BMP
MemBlock bmp = c->makeChart(Chart::BMP);
// The BITMAPINFOHEADER is 14 bytes offset from the beginning
LPBITMAPINFO header = (LPBITMAPINFO)(bmp.data + 14);
// The bitmap data
LPBYTE bitData = (LPBYTE)(bmp.data) +
((LPBITMAPFILEHEADER)(bmp.data))->bfOffBits;
// The scaling factor to adjust for printer resolution
// (pDC = CDC pointer representing the printer device context)
double xScaleFactor = pDC->GetDeviceCaps(LOGPIXELSX) / 96.0;
double yScaleFactor = pDC->GetDeviceCaps(LOGPIXELSY) / 96.0;
// Output to device context
StretchDIBits(pDC->m_hDC,
(int)(40 * xScaleFactor),
(int)(40 * yScaleFactor),
(int)(header->bmiHeader.biWidth * xScaleFactor),
(int)(header->bmiHeader.biHeight * yScaleFactor),
0, 0, header->bmiHeader.biWidth, header->bmiHeader.biHeight,
bitData, header, DIB_RGB_COLORS, SRCCOPY);
Note that charts are grey scale images. Their printing resolution requirements are more demanding than black and white text. A printer driver will use multiple printer dots to emulate the grey levels of one pixel. For example, if a printer driver uses 8 x 8 dots to emulate 1 grey level pixels (supporting 65 possible grey levels), a 600 dpi printer can only output at 75 pixels per inch. The exact achieved resolution depends on your printer brand and driver. In general, to produce screen quality image output, one needs at least 600 dpi printer.
© 2006 Advanced Software Engineering Limited. All rights reserved.