ChartDirector 7.1 (C++ Edition)

XY Zooming and Scrolling (Qt)




NOTE: This section describes XY Zooming and Scrolling for Qt only. For MFC, please refer to XY Zooming and Scrolling (MFC).

NOTE: For conciseness, some of the following descriptions only mention QChartViewer (for Qt Widgets applications). Those descriptions apply to QmlChartViewer (for QML/Qt Quick applications) as well.

This example demonstrates zooming and scrolling in both horizontal and vertical directions. In addition to using mouse click and drag, this example demonstrates using a slider, the mouse wheel and a QViewPortControl to control scrolling and zooming. This example also includes a crosshair track cursor with dynamic labels on the x-axis and y-axis showing the mouse cursor position, and an image map for data point tooltips. A save button is included to save the chart in PNG, JPG, GIF, BMP, SVG or PDF.

The main source code listing of this sample program is included at the end of this section. The code consists of the following main parts:

Source Code Listing

[Qt Widgets version] qtdemo/xyzoomscroll.cpp
#include <QApplication> #include <QPushButton> #include <QMouseEvent> #include <QFileDialog> #include "xyzoomscroll.h" #include "hotspotdialog.h" #include <sstream> #include <algorithm> XYZoomScroll::XYZoomScroll(QWidget *parent) : QDialog(parent) { setWindowTitle("XY Zooming and Scrolling"); setFixedSize(644, 488); QFrame *frame = new QFrame(this); frame->setGeometry(4, 4, 120, 481); frame->setFrameShape(QFrame::StyledPanel); // Pointer push button QPushButton *pointerPB = new QPushButton(QIcon(":/icons/scroll_icon.png"), "Scroll", frame); pointerPB->setGeometry(4, 8, 112, 28); pointerPB->setStyleSheet("QPushButton { text-align:left; padding:5px}"); pointerPB->setCheckable(true); // Zoom In push button QPushButton *zoomInPB = new QPushButton(QIcon(":/icons/zoomin_icon.png"), "Zoom In", frame); zoomInPB->setGeometry(4, 36, 112, 28); zoomInPB->setStyleSheet("QPushButton { text-align:left; padding:5px}"); zoomInPB->setCheckable(true); // Zoom Out push button QPushButton *zoomOutPB = new QPushButton(QIcon(":/icons/zoomout_icon.png"), "Zoom Out", frame); zoomOutPB->setGeometry(4, 64, 112, 28); zoomOutPB->setStyleSheet("QPushButton { text-align:left; padding:5px}"); zoomOutPB->setCheckable(true); // Save push button QPushButton *savePB = new QPushButton(QIcon(":/icons/save_icon.png"), "Save", frame); savePB->setStyleSheet("QPushButton { text-align:left; padding:5px}"); savePB->setGeometry(4, 120, 112, 28); connect(savePB, SIGNAL(clicked(bool)), SLOT(onSave(bool))); // The Pointer/Zoom In/Zoom Out buttons form a button group mouseUsage = new QButtonGroup(frame); mouseUsage->addButton(pointerPB, Chart::MouseUsageScroll); mouseUsage->addButton(zoomInPB, Chart::MouseUsageZoomIn); mouseUsage->addButton(zoomOutPB, Chart::MouseUsageZoomOut); connect(mouseUsage, SIGNAL(buttonPressed(QAbstractButton*)), SLOT(onMouseUsageChanged(QAbstractButton*))); // Zoom Mode label QLabel *zoomModeLabel = new QLabel("Zoom Level", frame); zoomModeLabel->setGeometry(6, 180, 110, 18); zoomModeLabel->setAlignment(Qt::AlignHCenter); // Zoom level bar m_ZoomBar = new QSlider(Qt::Horizontal, frame); m_ZoomBar->setGeometry(12, 205, 102, 41); m_ZoomBar->setRange(1, 100); m_ZoomBar->setTickPosition(QSlider::TicksBothSides); m_ZoomBar->setInvertedAppearance(true); connect(m_ZoomBar, SIGNAL(valueChanged(int)), SLOT(onZoomBarChanged(int))); // Viewport Control m_ViewPortControl = new QViewPortControl(frame); m_ViewPortControl->setGeometry(QRect(5, 334, 112, 112)); // Chart Viewer m_ChartViewer = new QChartViewer(this); m_ChartViewer->setGeometry(QRect(134, 4, 500, 480)); connect(m_ChartViewer, SIGNAL(viewPortChanged()), SLOT(onViewPortChanged())); connect(m_ChartViewer, SIGNAL(mouseMovePlotArea(QMouseEvent*)), SLOT(onMouseMovePlotArea(QMouseEvent*))); connect(m_ChartViewer, SIGNAL(clicked(QMouseEvent*)), SLOT(onClickChart(QMouseEvent*))); // // Initialize member variables // // Set initial zoom/scroll direction m_ChartViewer->setScrollDirection(Chart::DirectionHorizontalVertical); m_ChartViewer->setZoomDirection(Chart::DirectionHorizontalVertical); // Initially set the mouse to drag to scroll mode pointerPB->click(); // Enable mouse wheel zooming by setting the zoom ratio to 1.1 per wheel event m_ChartViewer->setMouseWheelZoomRatio(1.1); // Load the data loadData(); // Can update chart now m_ChartViewer->updateViewPort(true, true); // Configure the CViewPortControl to use transparent black for region outside the viewport, // that is, to darken the outside region. m_ViewPortControl->setViewPortExternalColor(0x7f000000); m_ViewPortControl->setViewPortBorderColor(0x7fffffff); m_ViewPortControl->setSelectionBorderColor(0x7fffffff); // Draw and display the full chart in the QViewPortControl drawFullChart(m_ViewPortControl, m_ChartViewer); // Bind the QChartViewer to the QViewPortControl m_ViewPortControl->setViewer(m_ChartViewer); } XYZoomScroll::~XYZoomScroll() { delete m_ChartViewer->getChart(); delete m_ViewPortControl->getChart(); } // // The ViewPortChanged event handler. This event occurs if the user scrolls or zooms in or // out the chart by dragging or clicking on the chart. It can also be triggered by calling // QChartViewer.updateViewPort. // void XYZoomScroll::onViewPortChanged() { // In addition to updating the chart, we may also need to update other controls that // changes based on the view port. updateControls(m_ChartViewer); // // Update chart and image map if necessary // if (m_ChartViewer->needUpdateChart()) drawChart(m_ChartViewer); if (m_ChartViewer->needUpdateImageMap()) updateImageMap(m_ChartViewer); } // // Update other controls that may be affected by the viewport. // void XYZoomScroll::updateControls(QChartViewer *viewer) { // // Update the Zoom slider to reflect the current zoom level of the view port // double smallerSide = viewer->getViewPortWidth() > viewer->getViewPortHeight() ? viewer->getViewPortHeight() : viewer->getViewPortWidth(); m_ZoomBar->setValue((int)(smallerSide * m_ZoomBar->maximum() + 0.5)); } // // Load the data // void XYZoomScroll::loadData() { // // For simplicity, in this demo, we just use hard coded data. // double X0[] = {10, 15, 6, -12, 14, -8, 13, -3, 16, 12, 10.5, -7, 3, -10, -5, 2, 5}; double Y0[] = {130, 150, 80, 110, -110, -105, -130, -15, -170, 125, 125, 60, 25, 150, 150, 15, 120}; double X1[] = {6, 7, -4, 3.5, 7, 8, -9, -10, -12, 11, 8, -3, -2, 8, 4, -15, 15}; double Y1[] = {65, -40, -40, 45, -70, -80, 80, 10, -100, 105, 60, 50, 20, 170, -25, 50, 75}; double X2[] = {-10, -12, 11, 8, 6, 12, -4, 3.5, 7, 8, -9, 3, -13, 16, -7.5, -10, -15}; double Y2[] = {65, -80, -40, 45, -70, -80, 80, 90, -100, 105, 60, -75, -150, -40, 120, -50, -30}; dataX0.assign(X0, X0 + sizeof(X0) / sizeof(*X0)); dataY0.assign(Y0, Y0 + sizeof(Y0) / sizeof(*Y0)); dataX1.assign(X1, X1 + sizeof(X1) / sizeof(*X1)); dataY1.assign(Y1, Y1 + sizeof(Y1) / sizeof(*Y1)); dataX2.assign(X2, X2 + sizeof(X2) / sizeof(*X2)); dataY2.assign(Y2, Y2 + sizeof(Y2) / sizeof(*Y2)); } // // Draw the chart and display it in the given viewer // void XYZoomScroll::drawChart(QChartViewer *viewer) { // Create an XYChart object 500 x 480 pixels in size, with the same background color // as the container QColor bgColor = palette().color(backgroundRole()).rgb(); XYChart *c = new XYChart(500, 480, (bgColor.red() << 16) + (bgColor.green() << 8) + bgColor.blue()); // Set the plotarea at (50, 40) and of size 400 x 400 pixels. Use light grey (c0c0c0) // horizontal and vertical grid lines. Set 4 quadrant coloring, where the colors of // the quadrants alternate between lighter and deeper grey (dddddd/eeeeee). c->setPlotArea(50, 40, 400, 400, -1, -1, -1, 0xc0c0c0, 0xc0c0c0 )->set4QBgColor(0xdddddd, 0xeeeeee, 0xdddddd, 0xeeeeee, 0x000000); // Enable clipping mode to clip the part of the data that is outside the plot area. c->setClipping(); // Set 4 quadrant mode, with both x and y axes symetrical around the origin c->setAxisAtOrigin(Chart::XYAxisAtOrigin, Chart::XAxisSymmetric + Chart::YAxisSymmetric); // Add a legend box at (450, 40) (top right corner of the chart) with vertical layout // and 8 pts Arial Bold font. Set the background color to semi-transparent grey. LegendBox *legendBox = c->addLegend(450, 40, true, "Arial Bold", 8); legendBox->setAlignment(Chart::TopRight); legendBox->setBackground(0x40dddddd); // Add a titles to axes c->xAxis()->setTitle("Alpha Index"); c->yAxis()->setTitle("Beta Index"); // Set axes width to 2 pixels c->xAxis()->setWidth(2); c->yAxis()->setWidth(2); // The default ChartDirector settings has a denser y-axis grid spacing and less-dense // x-axis grid spacing. In this demo, we want the tick spacing to be symmetrical. // We use around 50 pixels between major ticks and 25 pixels between minor ticks. c->xAxis()->setTickDensity(50, 25); c->yAxis()->setTickDensity(50, 25); // // In this example, we represent the data by scatter points. If you want to represent // the data by somethings else (lines, bars, areas, floating boxes, etc), just modify // the code below to use the layer type of your choice. // // Add scatter layer, using 11 pixels red (ff33333) X shape symbols c->addScatterLayer(vectorToArray(dataX0), vectorToArray(dataY0), "Group A", Chart::Cross2Shape(), 11, 0xff3333); // Add scatter layer, using 11 pixels green (33ff33) circle symbols c->addScatterLayer(vectorToArray(dataX1), vectorToArray(dataY1), "Group B", Chart::CircleShape, 11, 0x33ff33); // Add scatter layer, using 11 pixels blue (3333ff) triangle symbols c->addScatterLayer(vectorToArray(dataX2), vectorToArray(dataY2), "Group C", Chart::TriangleSymbol, 11, 0x3333ff); // // In this example, we have not explicitly configured the full x and y range. In this case, // the first time syncLinearAxisWithViewPort is called, ChartDirector will auto-scale the axis // and assume the resulting range is the full range. In subsequent calls, ChartDirector will // set the axis range based on the view port and the full range. // viewer->syncLinearAxisWithViewPort("x", c->xAxis()); viewer->syncLinearAxisWithViewPort("y", c->yAxis()); // We need to update the track line too. If the mouse is moving on the chart (eg. if // the user drags the mouse on the chart to scroll it), the track line will be updated // in the MouseMovePlotArea event. Otherwise, we need to update the track line here. if ((!viewer->isInMouseMoveEvent()) && viewer->isMouseOnPlotArea()) crossHair(c, viewer->getPlotAreaMouseX(), viewer->getPlotAreaMouseY()); // Set the chart image to the QChartViewer delete viewer->getChart(); viewer->setChart(c); } // // Draw the full thumbnail chart and display it in the given CViewPortControl // void XYZoomScroll::drawFullChart(QViewPortControl *vpc, QChartViewer *viewer) { // Create an XYChart object 110 x 110 pixels in size XYChart *c = new XYChart(110, 110); // Set the plotarea to cover the entire chart. Disable grid lines by setting their colors // to transparent. Set 4 quadrant coloring, where the colors of the quadrants alternate // between lighter and deeper grey (dddddd/eeeeee). c->setPlotArea(0, 0, c->getWidth() - 1, c->getHeight() - 1, -1, -1, -1, Chart::Transparent, Chart::Transparent)->set4QBgColor(0xdddddd, 0xeeeeee, 0xdddddd, 0xeeeeee, 0x000000); // Set 4 quadrant mode, with both x and y axes symetrical around the origin c->setAxisAtOrigin(Chart::XYAxisAtOrigin, Chart::XAxisSymmetric + Chart::YAxisSymmetric); // The x and y axis scales reflect the full range of the view port c->xAxis()->setLinearScale(viewer->getValueAtViewPort("x", 0), viewer->getValueAtViewPort( "x", 1), Chart::NoValue); c->yAxis()->setLinearScale(viewer->getValueAtViewPort("y", 0), viewer->getValueAtViewPort( "y", 1), Chart::NoValue); // Add scatter layer, using 11 pixels red (ff33333) X shape symbols c->addScatterLayer(vectorToArray(dataX0), vectorToArray(dataY0), "", Chart::Cross2Shape(), 3, 0xff3333, 0xff3333); // Add scatter layer, using 11 pixels green (33ff33) circle symbols c->addScatterLayer(vectorToArray(dataX1), vectorToArray(dataY1), "", Chart::CircleShape, 3, 0x33ff33, 0x33ff33); // Add scatter layer, using 11 pixels blue (3333ff) triangle symbols c->addScatterLayer(vectorToArray(dataX2), vectorToArray(dataY2), "", Chart::TriangleSymbol, 3, 0x3333ff, 0x3333ff); // Set the chart image to the WinChartViewer vpc->setChart(c); } // // Update the image map // void XYZoomScroll::updateImageMap(QChartViewer *viewer) { if (0 == viewer->getImageMapHandler()) { // no existing image map - creates a new one viewer->setImageMap(viewer->getChart()->getHTMLImageMap("clickable", "", "title='[{dataSetName}] Alpha = {x}, Beta = {value}'")); } } // // User clicks on the QChartViewer // void XYZoomScroll::onClickChart(QMouseEvent *) { ImageMapHandler *handler = m_ChartViewer->getImageMapHandler(); if (0 != handler) { // Query the ImageMapHandler to see if the mouse is on a clickable hot spot. We // consider the hot spot as clickable if its href ("path") parameter is not empty. const char *path = handler->getValue("path"); if ((0 != path) && (0 != *path)) { // In this sample code, we just show all hot spot parameters. HotSpotDialog hs; hs.setData(handler); hs.exec(); } } } // // The Pointer, Zoom In or Zoom out button is pressed // void XYZoomScroll::onMouseUsageChanged(QAbstractButton *b) { m_ChartViewer->setMouseUsage(mouseUsage->id(b)); } // // The Save button is pressed // void XYZoomScroll::onSave(bool) { QString fileName = QFileDialog::getSaveFileName(this, "Save", "chartdirector_demo", "PNG (*.png);;JPG (*.jpg);;GIF (*.gif);;BMP (*.bmp);;SVG (*.svg);;PDF (*.pdf)"); if (!fileName.isEmpty()) { // Save the chart BaseChart *c = m_ChartViewer->getChart(); if (0 != c) c->makeChart(fileName.toUtf8().constData()); } } // // User moves the Zoom slider control // void XYZoomScroll::onZoomBarChanged(int value) { if (!m_ChartViewer->isInViewPortChangedEvent()) { // Remember the center point double centerX = m_ChartViewer->getViewPortLeft() + m_ChartViewer->getViewPortWidth() / 2; double centerY = m_ChartViewer->getViewPortTop() + m_ChartViewer->getViewPortHeight() / 2; // Aspect ratio and zoom factor double aspectRatio = m_ChartViewer->getViewPortWidth() / m_ChartViewer->getViewPortHeight(); double zoomTo = ((double)value) / m_ZoomBar->maximum(); // Zoom by adjusting ViewPortWidth and ViewPortHeight while maintaining the aspect ratio m_ChartViewer->setViewPortWidth(zoomTo * ((aspectRatio < 1) ? 1 : aspectRatio)); m_ChartViewer->setViewPortHeight(zoomTo * ((aspectRatio > 1) ? 1 : (1 / aspectRatio))); // Adjust ViewPortLeft and ViewPortTop to keep center point unchanged m_ChartViewer->setViewPortLeft(centerX - m_ChartViewer->getViewPortWidth() / 2); m_ChartViewer->setViewPortTop(centerY - m_ChartViewer->getViewPortHeight() / 2); // Update the chart image only, but no need to update the image map. m_ChartViewer->updateViewPort(true, false); } } // // Draw track cursor when mouse is moving over plotarea, and update image map if necessary // void XYZoomScroll::onMouseMovePlotArea(QMouseEvent *) { // Draw crosshair track cursor crossHair((XYChart *)m_ChartViewer->getChart(), m_ChartViewer->getPlotAreaMouseX(), m_ChartViewer->getPlotAreaMouseY()); m_ChartViewer->updateDisplay(); // Hide the track cursor when the mouse leaves the plot area m_ChartViewer->removeDynamicLayer("mouseLeavePlotArea"); // Update image map if necessary. If the mouse is still dragging, the chart is still // updating and not confirmed, so there is no need to set up the image map. if (!m_ChartViewer->isMouseDragging()) updateImageMap(m_ChartViewer); } // // Draw cross hair cursor with axis labels // void XYZoomScroll::crossHair(XYChart *c, int mouseX, int mouseY) { // Clear the current dynamic layer and get the DrawArea object to draw on it. DrawArea *d = c->initDynamicLayer(); // The plot area object PlotArea *plotArea = c->getPlotArea(); // Draw a vertical line and a horizontal line as the cross hair d->vline(plotArea->getTopY(), plotArea->getBottomY(), mouseX, d->dashLineColor(0x000000, 0x0101)); d->hline(plotArea->getLeftX(), plotArea->getRightX(), mouseY, d->dashLineColor(0x000000, 0x0101)); // Draw y-axis label std::ostringstream ylabel; ylabel << "<*block,bgColor=FFFFDD,margin=3,edgeColor=000000*>" << c->formatValue(c->getYValue( mouseY, c->yAxis()), "{value|P4}") << "<*/*>"; TTFText *t = d->text(ylabel.str().c_str(), "Arial Bold", 8); t->draw(plotArea->getLeftX() - 5, mouseY, 0x000000, Chart::Right); t->destroy(); // Draw x-axis label std::ostringstream xlabel; xlabel << "<*block,bgColor=FFFFDD,margin=3,edgeColor=000000*>" << c->formatValue(c->getXValue( mouseX), "{value|P4}") << "<*/*>"; t = d->text(xlabel.str().c_str(), "Arial Bold", 8); t->draw(mouseX, plotArea->getBottomY() + 5, 0x000000, Chart::Top); t->destroy(); } // // Convert std::vector to a DoubleArray // DoubleArray XYZoomScroll::vectorToArray(std::vector<double> &v) { return (v.size() == 0) ? DoubleArray() : DoubleArray(&(v[0]), v.size()); }

[QML/Qt Quick version] qmldemo/xyzoomscroll.qml
import QtQuick import QtQuick.Window import QtQuick.Controls import QtQuick.Dialogs import advsofteng.com 1.0 Window { title: "XY Zooming and Scrolling" width: 620 height: 480 visible: true modality: Qt.ApplicationModal Pane { id: leftPane width: 120 padding: 5 anchors.top: parent.top; anchors.bottom: parent.bottom; Column { Button { width: 110 contentItem: Row { padding: 2; leftPadding: 5 Image { source: "icons/scroll_icon.png"; width:16; height:16; } Text { text: " Pointer"; font.pixelSize: 12; } } checked: viewer.mouseUsage == QmlChartViewer.MouseUsageScroll onClicked: viewer.mouseUsage = QmlChartViewer.MouseUsageScroll } Button { width: 110 contentItem: Row { padding: 2; leftPadding: 5 Image { source: "icons/zoomin_icon.png"; width:16; height:16; } Text { text: " Zoom In"; font.pixelSize: 12; } } checked: viewer.mouseUsage == QmlChartViewer.MouseUsageZoomIn onClicked: viewer.mouseUsage = QmlChartViewer.MouseUsageZoomIn } Button { width: 110 contentItem: Row { padding: 2; leftPadding: 5 Image { source: "icons/zoomout_icon.png"; width:16; height:16; } Text { text: " Zoom Out"; font.pixelSize: 12; } } checked: viewer.mouseUsage == QmlChartViewer.MouseUsageZoomOut onClicked: viewer.mouseUsage = QmlChartViewer.MouseUsageZoomOut } // Spacer Item {width: 1; height: 30} Button { width: 110 contentItem: Row { padding: 2; leftPadding: 5 Image { source: "icons/save_icon.png"; width:16; height:16; } Text { text: " Save"; font.pixelSize: 12; } } onClicked: saveImageDialog.open() } // Spacer Item {width: 1; height: 50} Text { width: 100 bottomPadding: 4 text: "Zoom Level" horizontalAlignment: Text.AlignHCenter } Slider { id: zoomSlider width: 110 onValueChanged: { // The slider and viewport can update each others. To avoid infinite loop, // the slider will update the viewport only if the viewport is not updating // the slider. if (viewer.isInViewPortChangedEvent) return; // Remember the center point var centerX = (viewer.viewPortLeft + viewer.viewPortRight) / 2; var centerY = (viewer.viewPortTop + viewer.viewPortBottom) / 2; // Aspect ratio and zoom factor var aspectRatio = viewer.viewPortWidth / viewer.viewPortHeight; var zoomTo = Math.max(0.01, value); // Zoom by adjusting ViewPortWidth and ViewPortHeight while maintaining the aspect ratio viewer.viewPortWidth = zoomTo * ((aspectRatio < 1) ? 1 : aspectRatio); viewer.viewPortHeight = zoomTo * ((aspectRatio > 1) ? 1 : (1 / aspectRatio)); // Adjust ViewPortLeft and ViewPortTop to keep center point unchanged viewer.viewPortLeft = centerX - viewer.viewPortWidth / 2; viewer.viewPortTop = centerY - viewer.viewPortHeight / 2; // Update the chart image only, but no need to update the image map. viewer.updateViewPort(true, false); } } } QmlViewPortControl { id: vpCtrl; anchors.bottom: parent.bottom; anchors.bottomMargin: 40 width: 110 height: 110 // Bind the viewport control to the viewer viewer: viewer; // Set the viewport coloring scheme viewPortExternalColor: 0x7f000000 viewPortBorderColor: 0x7fffffff selectionBorderColor: 0x7fffffff } } FileDialog { id: saveImageDialog title: "Save" currentFile: "chartdirector_demo" fileMode: FileDialog.SaveFile nameFilters: ["PNG (*.png);", "JPG (*.jpg)", "GIF (*.gif)", "BMP (*.bmp)", "SVG (*.svg)", "PDF (*.pdf)"] onAccepted: demo.saveChartImage(viewer, selectedFile) } // The QML ChartViewer QmlChartViewer { id: viewer anchors.left: leftPane.right //anchors.margins: 5 // set default mouse usage to scroll and mouse wheel to zoom mouseUsage: QmlChartViewer.MouseUsageScroll mouseWheelZoomRatio: 1.1 scrollDirection: QmlChartViewer.HorizontalVertical zoomDirection: QmlChartViewer.HorizontalVertical // Update track cursor on mouse move onMouseMovePlotArea: demo.drawTrackCursor(this, plotAreaMouseX, plotAreaMouseY); // Update chart on viewport change. Update the slider too. onViewPortChanged: { if (needUpdateChart) demo.drawChart(this); if (needUpdateImageMap) demo.updateImageMap(this); zoomSlider.value = Math.min(viewPortWidth, viewPortHeight) } } // The backend implementation of this demo. XYZoomScroll { id: demo } Component.onCompleted: { demo.initChartViewer(viewer, vpCtrl); viewer.updateViewPort(true, true); } }

[QML/Qt Quick version] qmldemo/xyzoomscroll.cpp
#include "xyzoomscroll.h" #include <sstream> // // Utlity to convert std::vector to a DoubleArray // static DoubleArray vectorToArray(std::vector<double> &v) { return (v.size() == 0) ? DoubleArray() : DoubleArray(&(v[0]), v.size()); } XYZoomScroll::XYZoomScroll(QObject *parent) : QObject(parent) { m_currentChart = 0; m_currentVpcChart = 0; // Load the data loadData(); } XYZoomScroll::~XYZoomScroll() { delete m_currentChart; delete m_currentVpcChart; } // // Load the data // void XYZoomScroll::loadData() { // // For simplicity, in this demo, we just use hard coded data. // double X0[] = {10, 15, 6, -12, 14, -8, 13, -3, 16, 12, 10.5, -7, 3, -10, -5, 2, 5}; double Y0[] = {130, 150, 80, 110, -110, -105, -130, -15, -170, 125, 125, 60, 25, 150, 150, 15, 120}; double X1[] = {6, 7, -4, 3.5, 7, 8, -9, -10, -12, 11, 8, -3, -2, 8, 4, -15, 15}; double Y1[] = {65, -40, -40, 45, -70, -80, 80, 10, -100, 105, 60, 50, 20, 170, -25, 50, 75}; double X2[] = {-10, -12, 11, 8, 6, 12, -4, 3.5, 7, 8, -9, 3, -13, 16, -7.5, -10, -15}; double Y2[] = {65, -80, -40, 45, -70, -80, 80, 90, -100, 105, 60, -75, -150, -40, 120, -50, -30}; dataX0.assign(X0, X0 + sizeof(X0) / sizeof(*X0)); dataY0.assign(Y0, Y0 + sizeof(Y0) / sizeof(*Y0)); dataX1.assign(X1, X1 + sizeof(X1) / sizeof(*X1)); dataY1.assign(Y1, Y1 + sizeof(Y1) / sizeof(*Y1)); dataX2.assign(X2, X2 + sizeof(X2) / sizeof(*X2)); dataY2.assign(Y2, Y2 + sizeof(Y2) / sizeof(*Y2)); } void XYZoomScroll::initChartViewer(QmlChartViewer *viewer, QmlViewPortControl *vpCtrl) { // Trigger the ViewPortChanged event to draw the chart viewer->updateViewPort(true, true); // Draw and display the full chart in the CViewPortControl drawFullChart(vpCtrl); } // // Draw the chart and display it in the given viewer // void XYZoomScroll::drawChart(QmlChartViewer *viewer) { // Create an XYChart object 500 x 480 pixels in size, with the same background color // as the container XYChart *c = new XYChart(500, 480, Chart::Transparent); // Set the plotarea at (50, 40) and of size 400 x 400 pixels. Use light grey (c0c0c0) // horizontal and vertical grid lines. Set 4 quadrant coloring, where the colors of // the quadrants alternate between lighter and deeper grey (dddddd/eeeeee). c->setPlotArea(50, 40, 400, 400, -1, -1, -1, 0xc0c0c0, 0xc0c0c0 )->set4QBgColor(0xdddddd, 0xeeeeee, 0xdddddd, 0xeeeeee, 0x000000); // Enable clipping mode to clip the part of the data that is outside the plot area. c->setClipping(); // Set 4 quadrant mode, with both x and y axes symetrical around the origin c->setAxisAtOrigin(Chart::XYAxisAtOrigin, Chart::XAxisSymmetric + Chart::YAxisSymmetric); // Add a legend box at (450, 40) (top right corner of the chart) with vertical layout // and 8 pts Arial Bold font. Set the background color to semi-transparent grey. LegendBox *legendBox = c->addLegend(450, 40, true, "Arial Bold", 8); legendBox->setAlignment(Chart::TopRight); legendBox->setBackground(0x40dddddd); // Add a titles to axes c->xAxis()->setTitle("Alpha Index"); c->yAxis()->setTitle("Beta Index"); // Set axes width to 2 pixels c->xAxis()->setWidth(2); c->yAxis()->setWidth(2); // The default ChartDirector settings has a denser y-axis grid spacing and less-dense // x-axis grid spacing. In this demo, we want the tick spacing to be symmetrical. // We use around 50 pixels between major ticks and 25 pixels between minor ticks. c->xAxis()->setTickDensity(50, 25); c->yAxis()->setTickDensity(50, 25); // // In this example, we represent the data by scatter points. If you want to represent // the data by somethings else (lines, bars, areas, floating boxes, etc), just modify // the code below to use the layer type of your choice. // // Add scatter layer, using 11 pixels red (ff33333) X shape symbols c->addScatterLayer(vectorToArray(dataX0), vectorToArray(dataY0), "Group A", Chart::Cross2Shape(), 11, 0xff3333); // Add scatter layer, using 11 pixels green (33ff33) circle symbols c->addScatterLayer(vectorToArray(dataX1), vectorToArray(dataY1), "Group B", Chart::CircleShape, 11, 0x33ff33); // Add scatter layer, using 11 pixels blue (3333ff) triangle symbols c->addScatterLayer(vectorToArray(dataX2), vectorToArray(dataY2), "Group C", Chart::TriangleSymbol, 11, 0x3333ff); // // In this example, we have not explicitly configured the full x and y range. In this case, // the first time syncLinearAxisWithViewPort is called, ChartDirector will auto-scale the axis // and assume the resulting range is the full range. In subsequent calls, ChartDirector will // set the axis range based on the view port and the full range. // viewer->syncLinearAxisWithViewPort("x", c->xAxis()); viewer->syncLinearAxisWithViewPort("y", c->yAxis()); // We need to update the track line too. If the mouse is moving on the chart (eg. if // the user drags the mouse on the chart to scroll it), the track line will be updated // in the MouseMovePlotArea event. Otherwise, we need to update the track line here. if ((!viewer->isInMouseMoveEvent()) && viewer->isMouseOnPlotArea()) crossHair(c, viewer->getPlotAreaMouseX(), viewer->getPlotAreaMouseY()); // Set the chart image to the QChartViewer delete viewer->getChart(); viewer->setChart(m_currentChart = c); } // // Draw the full thumbnail chart and display it in the given CViewPortControl // void XYZoomScroll::drawFullChart(QmlViewPortControl *vpc) { // Create an XYChart object 110 x 110 pixels in size XYChart *c = new XYChart(110, 110); // Set the plotarea to cover the entire chart. Disable grid lines by setting their colors // to transparent. Set 4 quadrant coloring, where the colors of the quadrants alternate // between lighter and deeper grey (dddddd/eeeeee). c->setPlotArea(0, 0, c->getWidth() - 1, c->getHeight() - 1, -1, -1, -1, Chart::Transparent, Chart::Transparent)->set4QBgColor(0xdddddd, 0xeeeeee, 0xdddddd, 0xeeeeee, 0x000000); // Set 4 quadrant mode, with both x and y axes symetrical around the origin c->setAxisAtOrigin(Chart::XYAxisAtOrigin, Chart::XAxisSymmetric + Chart::YAxisSymmetric); // The x and y axis scales reflect the full range of the view port QmlChartViewer *viewer = vpc->getViewer(); if (viewer) { c->xAxis()->setLinearScale(viewer->getValueAtViewPort("x", 0), viewer->getValueAtViewPort( "x", 1), Chart::NoValue); c->yAxis()->setLinearScale(viewer->getValueAtViewPort("y", 0), viewer->getValueAtViewPort( "y", 1), Chart::NoValue); } // Add scatter layer, using 11 pixels red (ff33333) X shape symbols c->addScatterLayer(vectorToArray(dataX0), vectorToArray(dataY0), "", Chart::Cross2Shape(), 3, 0xff3333, 0xff3333); // Add scatter layer, using 11 pixels green (33ff33) circle symbols c->addScatterLayer(vectorToArray(dataX1), vectorToArray(dataY1), "", Chart::CircleShape, 3, 0x33ff33, 0x33ff33); // Add scatter layer, using 11 pixels blue (3333ff) triangle symbols c->addScatterLayer(vectorToArray(dataX2), vectorToArray(dataY2), "", Chart::TriangleSymbol, 3, 0x3333ff, 0x3333ff); // Set the chart image to the WinChartViewer vpc->setChart(m_currentVpcChart = c); } // // Update the image map // void XYZoomScroll::updateImageMap(QmlChartViewer *viewer) { if (0 == viewer->getImageMapHandler()) { // no existing image map - creates a new one viewer->setImageMap(viewer->getChart()->getHTMLImageMap("", "", "title='[{dataSetName}] Alpha = {x}, Beta = {value}'")); } } // // The Save button is pressed // void XYZoomScroll::saveChartImage(QmlChartViewer *viewer, QUrl url) { QString path = url.toLocalFile(); if (!path.isEmpty()) { // Save the chart BaseChart *c = viewer->getChart(); if (0 != c) c->makeChart(path.toUtf8().constData()); } } // // Draw track cursor when mouse is moving over plotarea, and update image map if necessary // void XYZoomScroll::drawTrackCursor(QmlChartViewer *viewer, int mouseX, int mouseY) { // Draw crosshair track cursor crossHair((XYChart *)viewer->getChart(), mouseX, mouseY); viewer->updateDisplay(); // Hide the track cursor when the mouse leaves the plot area viewer->removeDynamicLayer("mouseLeavePlotArea"); // Update image map if necessary. If the mouse is still dragging, the chart is still // updating and not confirmed, so there is no need to set up the image map. if (!viewer->isMouseDragging()) updateImageMap(viewer); } // // Draw cross hair cursor with axis labels // void XYZoomScroll::crossHair(XYChart *c, int mouseX, int mouseY) { // Clear the current dynamic layer and get the DrawArea object to draw on it. DrawArea *d = c->initDynamicLayer(); // The plot area object PlotArea *plotArea = c->getPlotArea(); // Draw a vertical line and a horizontal line as the cross hair d->vline(plotArea->getTopY(), plotArea->getBottomY(), mouseX, d->dashLineColor(0x000000, 0x0101)); d->hline(plotArea->getLeftX(), plotArea->getRightX(), mouseY, d->dashLineColor(0x000000, 0x0101)); // Draw y-axis label std::ostringstream ylabel; ylabel << "<*block,bgColor=FFFFDD,margin=3,edgeColor=000000*>" << c->formatValue(c->getYValue( mouseY, c->yAxis()), "{value|P4}") << "<*/*>"; TTFText *t = d->text(ylabel.str().c_str(), "Arial Bold", 8); t->draw(plotArea->getLeftX() - 5, mouseY, 0x000000, Chart::Right); t->destroy(); // Draw x-axis label std::ostringstream xlabel; xlabel << "<*block,bgColor=FFFFDD,margin=3,edgeColor=000000*>" << c->formatValue(c->getXValue( mouseX), "{value|P4}") << "<*/*>"; t = d->text(xlabel.str().c_str(), "Arial Bold", 8); t->draw(mouseX, plotArea->getBottomY() + 5, 0x000000, Chart::Top); t->destroy(); }