ChartDirector 7.1 (C++ Edition)

Zooming and Scrolling with Viewport Control (Qt)




NOTE: This section describes Zooming and Scrolling with Viewport Control for Qt only. For MFC, please refer to Zooming and Scrolling with Viewport Control (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.

The example extends the Zooming and Scrolling with Track Line (1) (Qt) example by replacing the scrollbar with a QViewPortControl. There is also a save button to save the chart in PNG, JPG, GIF, BMP, SVG or PDF formats.

Most of the code in this example is similar to that in the Zooming and Scrolling with Track Line (1) (Qt) example. The key changes are:

Source Code Listing

[Qt Widgets version] qtdemo/viewportcontroldemo.cpp
#include <QApplication> #include <QPushButton> #include <QMouseEvent> #include <QFileDialog> #include <math.h> #include <sstream> #include <algorithm> #include "viewportcontroldemo.h" ViewPortControlDemo::ViewPortControlDemo(QWidget *parent) : QDialog(parent) { // // Set up the GUI // setFixedSize(772, 478); setWindowTitle("Zooming and Scrolling with Viewport Control"); // The frame on the left side QFrame *frame = new QFrame(this); frame->setGeometry(4, 4, 120, 470); 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->setStyleSheet("QPushButton { text-align:left; padding:5px}"); zoomOutPB->setGeometry(4, 64, 112, 28); 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*))); // Chart Viewer m_ChartViewer = new QChartViewer(this); m_ChartViewer->setGeometry(128, 4, 640, 400); connect(m_ChartViewer, SIGNAL(viewPortChanged()), SLOT(onViewPortChanged())); connect(m_ChartViewer, SIGNAL(mouseMovePlotArea(QMouseEvent*)), SLOT(onMouseMovePlotArea(QMouseEvent*))); m_ViewPortControl = new QViewPortControl(this); m_ViewPortControl->setGeometry(128, 403, 640, 70); // // Initialize the chart // // Load the data loadData(); // Initialize the QChartViewer initChartViewer(m_ChartViewer); // Initially set the mouse to drag to scroll mode pointerPB->click(); // Trigger the ViewPortChanged event to draw the chart m_ChartViewer->updateViewPort(true, true); // Draw and display the full chart in the CViewPortControl drawFullChart(m_ViewPortControl, m_ChartViewer); // Bind the CChartViewer to the CViewPortControl m_ViewPortControl->setViewer(m_ChartViewer); } ViewPortControlDemo::~ViewPortControlDemo() { delete m_ranSeries; delete m_ChartViewer->getChart(); delete m_ViewPortControl->getChart(); } // // Load the data // void ViewPortControlDemo::loadData() { // In this example, we just use random numbers as data. m_ranSeries = new RanSeries(127); m_timeStamps = m_ranSeries->getDateSeries(1827, Chart::chartTime(2010, 1, 1), 86400); m_dataSeriesA = m_ranSeries->getSeries(1827, 150, -10, 10); m_dataSeriesB = m_ranSeries->getSeries(1827, 200, -10, 10); m_dataSeriesC = m_ranSeries->getSeries(1827, 250, -8, 8); } // // Initialize the QChartViewer // void ViewPortControlDemo::initChartViewer(QChartViewer *viewer) { // Set the full x range to be the duration of the data viewer->setFullRange("x", m_timeStamps[0], m_timeStamps[m_timeStamps.len - 1]); // Initialize the view port to show the latest 20% of the time range viewer->setViewPortWidth(0.2); viewer->setViewPortLeft(1 - viewer->getViewPortWidth()); // Set the maximum zoom to 10 points viewer->setZoomInWidthLimit(10.0 / m_timeStamps.len); viewer->setMouseWheelZoomRatio(1.1); } // // 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 WinChartViewer.updateViewPort. // void ViewPortControlDemo::onViewPortChanged() { // Update chart if necessary if (m_ChartViewer->needUpdateChart()) drawChart(m_ChartViewer); } // // Draw the chart and display it in the given viewer // void ViewPortControlDemo::drawChart(QChartViewer *viewer) { // Get the start date and end date that are visible on the chart. double viewPortStartDate = viewer->getValueAtViewPort("x", viewer->getViewPortLeft()); double viewPortEndDate = viewer->getValueAtViewPort("x", viewer->getViewPortLeft() + viewer->getViewPortWidth()); // Get the array indexes that corresponds to the visible start and end dates int startIndex = (int)floor(Chart::bSearch(m_timeStamps, viewPortStartDate)); int endIndex = (int)ceil(Chart::bSearch(m_timeStamps, viewPortEndDate)); int noOfPoints = endIndex - startIndex + 1; // Extract the part of the data array that are visible. DoubleArray viewPortTimeStamps = DoubleArray(m_timeStamps.data + startIndex, noOfPoints); DoubleArray viewPortDataSeriesA = DoubleArray(m_dataSeriesA.data + startIndex, noOfPoints); DoubleArray viewPortDataSeriesB = DoubleArray(m_dataSeriesB.data + startIndex, noOfPoints); DoubleArray viewPortDataSeriesC = DoubleArray(m_dataSeriesC.data + startIndex, noOfPoints); // // At this stage, we have extracted the visible data. We can use those data to plot the chart. // /////////////////////////////////////////////////////////////////////////////////////// // Configure overall chart appearance. /////////////////////////////////////////////////////////////////////////////////////// // Create an XYChart object of size 640 x 400 pixels XYChart *c = new XYChart(640, 400); // Set the plotarea at (55, 55) with width 80 pixels less than chart width, and height 92 pixels // less than chart height. Use a vertical gradient from light blue (f0f6ff) to sky blue (a0c0ff) // as background. Set border to transparent and grid lines to white (ffffff). c->setPlotArea(55, 55, c->getWidth() - 80, c->getHeight() - 92, c->linearGradientColor(0, 55, 0, c->getHeight() - 35, 0xf0f6ff, 0xa0c0ff), -1, Chart::Transparent, 0xffffff, 0xffffff); // As the data can lie outside the plotarea in a zoomed chart, we need enable clipping. c->setClipping(); // Add a title box using dark grey (0x333333) 15pt Arial Bold font c->addTitle(" Zooming and Scrolling with Viewport Control", "Arial Bold", 15, 0x333333); // Set legend icon style to use line style icon, sized for 10pt font c->getLegend()->setLineStyleKey(); c->getLegend()->setFontSize(10); // Set the x and y axis stems to transparent and the label font to 10pt Arial c->xAxis()->setColors(Chart::Transparent); c->yAxis()->setColors(Chart::Transparent); c->xAxis()->setLabelStyle("Arial", 10); c->yAxis()->setLabelStyle("Arial", 10); // Add axis title using 10pt Arial Bold font c->yAxis()->setTitle("Ionic Temperature (C)", "Arial Bold", 10); /////////////////////////////////////////////////////////////////////////////////////// // Add data to chart /////////////////////////////////////////////////////////////////////////////////////// // // In this example, we represent the data by lines. You may modify the code below to use other // representations (areas, scatter plot, etc). // // Add a line layer for the lines, using a line width of 2 pixels LineLayer *layer = c->addLineLayer(); layer->setLineWidth(2); // In this demo, we do not have too many data points. In real code, the chart may contain a lot // of data points when fully zoomed out - much more than the number of horizontal pixels in this // plot area. So it is a good idea to use fast line mode. layer->setFastLineMode(); // Now we add the 3 data series to a line layer, using the color red (0xff3333), green // (0x008800) and blue (0x3333cc) layer->setXData(viewPortTimeStamps); layer->addDataSet(viewPortDataSeriesA, 0xff3333, "Alpha"); layer->addDataSet(viewPortDataSeriesB, 0x008800, "Beta"); layer->addDataSet(viewPortDataSeriesC, 0x3333cc, "Gamma"); /////////////////////////////////////////////////////////////////////////////////////// // Configure axis scale and labelling /////////////////////////////////////////////////////////////////////////////////////// // Set the x-axis as a date/time axis with the scale according to the view port x range. viewer->syncDateAxisWithViewPort("x", c->xAxis()); // For the automatic y-axis labels, set the minimum spacing to 30 pixels. c->yAxis()->setTickDensity(30); // // In this demo, the time range can be from a few years to a few days. We demonstrate how to // set up different date/time format based on the time range. // // If all ticks are yearly aligned, then we use "yyyy" as the label format. c->xAxis()->setFormatCondition("align", 360 * 86400); c->xAxis()->setLabelFormat("{value|yyyy}"); // If all ticks are monthly aligned, then we use "mmm yyyy" in bold font as the first // label of a year, and "mmm" for other labels. c->xAxis()->setFormatCondition("align", 30 * 86400); c->xAxis()->setMultiFormat(Chart::StartOfYearFilter(), "<*font=bold*>{value|mmm<*br*>yyyy}", Chart::AllPassFilter(), "{value|mmm}"); // If all ticks are daily algined, then we use "mmm dd<*br*>yyyy" in bold font as the // first label of a year, and "mmm dd" in bold font as the first label of a month, and // "dd" for other labels. c->xAxis()->setFormatCondition("align", 86400); c->xAxis()->setMultiFormat(Chart::StartOfYearFilter(), "<*block,halign=left*><*font=bold*>{value|mmm dd<*br*>yyyy}", Chart::StartOfMonthFilter(), "<*font=bold*>{value|mmm dd}"); c->xAxis()->setMultiFormat(Chart::AllPassFilter(), "{value|dd}"); // For all other cases (sub-daily ticks), use "hh:nn<*br*>mmm dd" for the first label of // a day, and "hh:nn" for other labels. c->xAxis()->setFormatCondition("else"); c->xAxis()->setMultiFormat(Chart::StartOfDayFilter(), "<*font=bold*>{value|hh:nn<*br*>mmm dd}", Chart::AllPassFilter(), "{value|hh:nn}"); /////////////////////////////////////////////////////////////////////////////////////// // Output the chart /////////////////////////////////////////////////////////////////////////////////////// // 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()) { trackLineLegend(c, (0 == viewer->getChart()) ? c->getPlotArea()->getRightX() : viewer->getPlotAreaMouseX()); } delete viewer->getChart(); viewer->setChart(c); } void ViewPortControlDemo::drawFullChart(QViewPortControl *vpc, QChartViewer *viewer) { // Create an XYChart object of size 640 x 70 pixels XYChart *c = new XYChart(640, 70); // Set the plotarea with the same horizontal position as that in the main chart for alignment. c->setPlotArea(55, 5, c->getWidth() - 80, c->getHeight() - 11, 0xc0d8ff, -1, 0x888888, Chart::Transparent, 0xffffff); // Set the x axis stem to transparent and the label font to 10pt Arial c->xAxis()->setColors(Chart::Transparent); c->xAxis()->setLabelStyle("Arial", 10); // Put the x-axis labels inside the plot area by setting a negative label gap. Use // setLabelAlignment to put the label at the right side of the tick. c->xAxis()->setLabelGap(-1); c->xAxis()->setLabelAlignment(1); // Set the y axis stem and labels to transparent (that is, hide the labels) c->yAxis()->setColors(Chart::Transparent, Chart::Transparent); // Add a line layer for the lines with fast line mode enabled LineLayer *layer = c->addLineLayer(); layer->setFastLineMode(); // Now we add the 3 data series to a line layer, using the color red (0xff3333), green // (0x008800) and blue (0x3333cc) layer->setXData(m_timeStamps); layer->addDataSet(m_dataSeriesA, 0xff3333); layer->addDataSet(m_dataSeriesB, 0x008800); layer->addDataSet(m_dataSeriesC, 0x3333cc); // The x axis scales should reflect the full range of the view port c->xAxis()->setDateScale(viewer->getValueAtViewPort("x", 0), viewer->getValueAtViewPort("x", 1)); // For the automatic x-axis labels, set the minimum spacing to 75 pixels. c->xAxis()->setTickDensity(75); // For the auto-scaled y-axis, as we hide the labels, we can disable axis rounding. This can // make the axis scale fit the data tighter. c->yAxis()->setRounding(false, false); // Output the chart vpc->setChart(c); } // // The Pointer, Zoom In or Zoom out button is pressed // void ViewPortControlDemo::onMouseUsageChanged(QAbstractButton *b) { m_ChartViewer->setMouseUsage(mouseUsage->id(b)); } // // The Save button is pressed // void ViewPortControlDemo::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()); } } // // Draw track cursor when mouse is moving over plotarea // void ViewPortControlDemo::onMouseMovePlotArea(QMouseEvent *) { trackLineLegend((XYChart *)m_ChartViewer->getChart(), m_ChartViewer->getPlotAreaMouseX()); m_ChartViewer->updateDisplay(); } // // Draw track line with data labels // void ViewPortControlDemo::trackLineLegend(XYChart *c, int mouseX) { // 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(); // Get the data x-value that is nearest to the mouse, and find its pixel coordinate. double xValue = c->getNearestXValue(mouseX); int xCoor = c->getXCoor(xValue); // Draw a vertical track line at the x-position d->vline(plotArea->getTopY(), plotArea->getBottomY(), xCoor, 0xaaaaaa); // Container to hold the legend entries std::vector<std::string> legendEntries; // Iterate through all layers to build the legend array for (int i = 0; i < c->getLayerCount(); ++i) { Layer *layer = c->getLayerByZ(i); // The data array index of the x-value int xIndex = layer->getXIndexOf(xValue); // Iterate through all the data sets in the layer for (int j = 0; j < layer->getDataSetCount(); ++j) { DataSet *dataSet = layer->getDataSetByZ(j); // We are only interested in visible data sets with names const char *dataName = dataSet->getDataName(); int color = dataSet->getDataColor(); if (dataName && *dataName && (color != Chart::Transparent)) { // Build the legend entry, consist of the legend icon, name and data value. double dataValue = dataSet->getValue(xIndex); std::ostringstream legendEntry; legendEntry << "<*block*>" << dataSet->getLegendIcon() << " " << dataName << ": " << ((dataValue == Chart::NoValue) ? "N/A" : c->formatValue(dataValue, "{value|P4}")) << "<*/*>"; legendEntries.push_back(legendEntry.str()); // Draw a track dot for data points within the plot area int yCoor = c->getYCoor(dataSet->getPosition(xIndex), dataSet->getUseYAxis()); if ((yCoor >= plotArea->getTopY()) && (yCoor <= plotArea->getBottomY())) { d->circle(xCoor, yCoor, 5, 5, color, color); } } } } // Create the legend by joining the legend entries std::ostringstream legendText; legendText << "<*block,maxWidth=" << plotArea->getWidth() << "*><*block*><*font=Arial Bold*>[" << c->xAxis()->getFormattedLabel(xValue, "mm/dd/yyyy") << "]<*/*>"; for (int i = ((int)legendEntries.size()) - 1; i >= 0; --i) legendText << " " << legendEntries[i]; // Display the legend on the top of the plot area TTFText *t = d->text(legendText.str().c_str(), "Arial Bold", 10); t->draw(plotArea->getLeftX() + 5, plotArea->getTopY() - 3, 0x333333, Chart::BottomLeft); t->destroy(); }

[QML/Qt Quick version] qmldemo/viewportcontroldemo.qml
import QtQuick import QtQuick.Window import QtQuick.Controls import QtQuick.Dialogs import advsofteng.com 1.0 Window { title: "Zooming and Scrolling with Viewport Control" width: 770 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() } } } 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) } QmlChartViewer { x: 140 id: viewer // set default mouse usage to scroll and mouse wheel to zoom mouseUsage: QmlChartViewer.MouseUsageScroll mouseWheelZoomRatio: 1.1 // Update track cursor on mouse move onMouseMovePlotArea: demo.drawTrackCursor(this, chartMouseX); // Update chart on viewport change onViewPortChanged: if (needUpdateChart) demo.drawChart(this); } QmlViewPortControl { id: vpCtrl; anchors.top: viewer.bottom anchors.left: viewer.left anchors.right: viewer.right // Bind the viewport control to the viewer viewer: viewer; } // The backend implementation of this demo. ViewPortControlDemo { id: demo } Component.onCompleted: { demo.initChartViewer(viewer, vpCtrl); viewer.updateViewPort(true, true); } }

[QML/Qt Quick version] qmldemo/viewportcontroldemo.cpp
#include "viewportcontroldemo.h" #include <sstream> #include <math.h> ViewPortControlDemo::ViewPortControlDemo(QObject *parent) : QObject(parent) { m_currentChart = 0; m_currentVpcChart = 0; // Load the data loadData(); } ViewPortControlDemo::~ViewPortControlDemo() { delete m_ranSeries; delete m_currentChart; delete m_currentVpcChart; } // // Load the data // void ViewPortControlDemo::loadData() { // In this example, we just use random numbers as data. m_ranSeries = new RanSeries(127); m_timeStamps = m_ranSeries->getDateSeries(1827, Chart::chartTime(2010, 1, 1), 86400); m_dataSeriesA = m_ranSeries->getSeries(1827, 150, -10, 10); m_dataSeriesB = m_ranSeries->getSeries(1827, 200, -10, 10); m_dataSeriesC = m_ranSeries->getSeries(1827, 250, -8, 8); } // // Initialize the QChartViewer // void ViewPortControlDemo::initChartViewer(QmlChartViewer *viewer, QmlViewPortControl *vpCtrl) { // Set the full x range to be the duration of the data viewer->setFullRange("x", m_timeStamps[0], m_timeStamps[m_timeStamps.len - 1]); // Initialize the view port to show the latest 20% of the time range viewer->setViewPortWidth(0.2); viewer->setViewPortLeft(1 - viewer->getViewPortWidth()); // Set the maximum zoom to 10 points viewer->setZoomInWidthLimit(10.0 / m_timeStamps.len); viewer->setMouseWheelZoomRatio(1.1); // 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 ViewPortControlDemo::drawChart(QmlChartViewer *viewer) { // Get the start date and end date that are visible on the chart. double viewPortStartDate = viewer->getValueAtViewPort("x", viewer->getViewPortLeft()); double viewPortEndDate = viewer->getValueAtViewPort("x", viewer->getViewPortLeft() + viewer->getViewPortWidth()); // Get the array indexes that corresponds to the visible start and end dates int startIndex = (int)floor(Chart::bSearch(m_timeStamps, viewPortStartDate)); int endIndex = (int)ceil(Chart::bSearch(m_timeStamps, viewPortEndDate)); int noOfPoints = endIndex - startIndex + 1; // Extract the part of the data array that are visible. DoubleArray viewPortTimeStamps = DoubleArray(m_timeStamps.data + startIndex, noOfPoints); DoubleArray viewPortDataSeriesA = DoubleArray(m_dataSeriesA.data + startIndex, noOfPoints); DoubleArray viewPortDataSeriesB = DoubleArray(m_dataSeriesB.data + startIndex, noOfPoints); DoubleArray viewPortDataSeriesC = DoubleArray(m_dataSeriesC.data + startIndex, noOfPoints); // // At this stage, we have extracted the visible data. We can use those data to plot the chart. // /////////////////////////////////////////////////////////////////////////////////////// // Configure overall chart appearance. /////////////////////////////////////////////////////////////////////////////////////// // Create an XYChart object of size 640 x 400 pixels XYChart *c = new XYChart(640, 400); // Set the plotarea at (55, 55) with width 80 pixels less than chart width, and height 92 pixels // less than chart height. Use a vertical gradient from light blue (f0f6ff) to sky blue (a0c0ff) // as background. Set border to transparent and grid lines to white (ffffff). c->setPlotArea(55, 55, c->getWidth() - 80, c->getHeight() - 92, c->linearGradientColor(0, 55, 0, c->getHeight() - 35, 0xf0f6ff, 0xa0c0ff), -1, Chart::Transparent, 0xffffff, 0xffffff); // As the data can lie outside the plotarea in a zoomed chart, we need enable clipping. c->setClipping(); // Add a title box using dark grey (0x333333) 15pt Arial Bold font c->addTitle(" Zooming and Scrolling with Viewport Control", "Arial Bold", 15, 0x333333); // Set legend icon style to use line style icon, sized for 10pt font c->getLegend()->setLineStyleKey(); c->getLegend()->setFontSize(10); // Set the x and y axis stems to transparent and the label font to 10pt Arial c->xAxis()->setColors(Chart::Transparent); c->yAxis()->setColors(Chart::Transparent); c->xAxis()->setLabelStyle("Arial", 10); c->yAxis()->setLabelStyle("Arial", 10); // Add axis title using 10pt Arial Bold font c->yAxis()->setTitle("Ionic Temperature (C)", "Arial Bold", 10); /////////////////////////////////////////////////////////////////////////////////////// // Add data to chart /////////////////////////////////////////////////////////////////////////////////////// // // In this example, we represent the data by lines. You may modify the code below to use other // representations (areas, scatter plot, etc). // // Add a line layer for the lines, using a line width of 2 pixels LineLayer *layer = c->addLineLayer(); layer->setLineWidth(2); // In this demo, we do not have too many data points. In real code, the chart may contain a lot // of data points when fully zoomed out - much more than the number of horizontal pixels in this // plot area. So it is a good idea to use fast line mode. layer->setFastLineMode(); // Now we add the 3 data series to a line layer, using the color red (0xff3333), green // (0x008800) and blue (0x3333cc) layer->setXData(viewPortTimeStamps); layer->addDataSet(viewPortDataSeriesA, 0xff3333, "Alpha"); layer->addDataSet(viewPortDataSeriesB, 0x008800, "Beta"); layer->addDataSet(viewPortDataSeriesC, 0x3333cc, "Gamma"); /////////////////////////////////////////////////////////////////////////////////////// // Configure axis scale and labelling /////////////////////////////////////////////////////////////////////////////////////// // Set the x-axis as a date/time axis with the scale according to the view port x range. viewer->syncDateAxisWithViewPort("x", c->xAxis()); // For the automatic y-axis labels, set the minimum spacing to 30 pixels. c->yAxis()->setTickDensity(30); // // In this demo, the time range can be from a few years to a few days. We demonstrate how to // set up different date/time format based on the time range. // // If all ticks are yearly aligned, then we use "yyyy" as the label format. c->xAxis()->setFormatCondition("align", 360 * 86400); c->xAxis()->setLabelFormat("{value|yyyy}"); // If all ticks are monthly aligned, then we use "mmm yyyy" in bold font as the first // label of a year, and "mmm" for other labels. c->xAxis()->setFormatCondition("align", 30 * 86400); c->xAxis()->setMultiFormat(Chart::StartOfYearFilter(), "<*font=bold*>{value|mmm<*br*>yyyy}", Chart::AllPassFilter(), "{value|mmm}"); // If all ticks are daily algined, then we use "mmm dd<*br*>yyyy" in bold font as the // first label of a year, and "mmm dd" in bold font as the first label of a month, and // "dd" for other labels. c->xAxis()->setFormatCondition("align", 86400); c->xAxis()->setMultiFormat(Chart::StartOfYearFilter(), "<*block,halign=left*><*font=bold*>{value|mmm dd<*br*>yyyy}", Chart::StartOfMonthFilter(), "<*font=bold*>{value|mmm dd}"); c->xAxis()->setMultiFormat(Chart::AllPassFilter(), "{value|dd}"); // For all other cases (sub-daily ticks), use "hh:nn<*br*>mmm dd" for the first label of // a day, and "hh:nn" for other labels. c->xAxis()->setFormatCondition("else"); c->xAxis()->setMultiFormat(Chart::StartOfDayFilter(), "<*font=bold*>{value|hh:nn<*br*>mmm dd}", Chart::AllPassFilter(), "{value|hh:nn}"); /////////////////////////////////////////////////////////////////////////////////////// // Output the chart /////////////////////////////////////////////////////////////////////////////////////// // 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()) { trackLineLegend(c, (0 == viewer->getChart()) ? c->getPlotArea()->getRightX() : viewer->getPlotAreaMouseX()); } delete viewer->getChart(); viewer->setChart(m_currentChart = c); } void ViewPortControlDemo::drawFullChart(QmlViewPortControl *vpc) { // Create an XYChart object of size 640 x 70 pixels XYChart *c = new XYChart(640, 70); // Set the plotarea with the same horizontal position as that in the main chart for alignment. c->setPlotArea(55, 5, c->getWidth() - 80, c->getHeight() - 11, 0xc0d8ff, -1, 0x888888, Chart::Transparent, 0xffffff); // Set the x axis stem to transparent and the label font to 10pt Arial c->xAxis()->setColors(Chart::Transparent); c->xAxis()->setLabelStyle("Arial", 10); // Put the x-axis labels inside the plot area by setting a negative label gap. Use // setLabelAlignment to put the label at the right side of the tick. c->xAxis()->setLabelGap(-1); c->xAxis()->setLabelAlignment(1); // Set the y axis stem and labels to transparent (that is, hide the labels) c->yAxis()->setColors(Chart::Transparent, Chart::Transparent); // Add a line layer for the lines with fast line mode enabled LineLayer *layer = c->addLineLayer(); layer->setFastLineMode(); // Now we add the 3 data series to a line layer, using the color red (0xff3333), green // (0x008800) and blue (0x3333cc) layer->setXData(m_timeStamps); layer->addDataSet(m_dataSeriesA, 0xff3333); layer->addDataSet(m_dataSeriesB, 0x008800); layer->addDataSet(m_dataSeriesC, 0x3333cc); // The x axis scales should reflect the full range of the view port QmlChartViewer *viewer = vpc->getViewer(); if (viewer) c->xAxis()->setDateScale(viewer->getValueAtViewPort("x", 0), viewer->getValueAtViewPort("x", 1)); // For the automatic x-axis labels, set the minimum spacing to 75 pixels. c->xAxis()->setTickDensity(75); // For the auto-scaled y-axis, as we hide the labels, we can disable axis rounding. This can // make the axis scale fit the data tighter. c->yAxis()->setRounding(false, false); // Output the chart vpc->setChart(m_currentVpcChart = c); } // // The Save button is pressed // void ViewPortControlDemo::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 // void ViewPortControlDemo::drawTrackCursor(QmlChartViewer *viewer, int mouseX) { trackLineLegend((XYChart *)viewer->getChart(), mouseX); viewer->updateDisplay(); } // // Draw track line with data labels // void ViewPortControlDemo::trackLineLegend(XYChart *c, int mouseX) { // 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(); // Get the data x-value that is nearest to the mouse, and find its pixel coordinate. double xValue = c->getNearestXValue(mouseX); int xCoor = c->getXCoor(xValue); // Draw a vertical track line at the x-position d->vline(plotArea->getTopY(), plotArea->getBottomY(), xCoor, 0xaaaaaa); // Container to hold the legend entries std::vector<std::string> legendEntries; // Iterate through all layers to build the legend array for (int i = 0; i < c->getLayerCount(); ++i) { Layer *layer = c->getLayerByZ(i); // The data array index of the x-value int xIndex = layer->getXIndexOf(xValue); // Iterate through all the data sets in the layer for (int j = 0; j < layer->getDataSetCount(); ++j) { DataSet *dataSet = layer->getDataSetByZ(j); // We are only interested in visible data sets with names const char *dataName = dataSet->getDataName(); int color = dataSet->getDataColor(); if (dataName && *dataName && (color != Chart::Transparent)) { // Build the legend entry, consist of the legend icon, name and data value. double dataValue = dataSet->getValue(xIndex); std::ostringstream legendEntry; legendEntry << "<*block*>" << dataSet->getLegendIcon() << " " << dataName << ": " << ((dataValue == Chart::NoValue) ? "N/A" : c->formatValue(dataValue, "{value|P4}")) << "<*/*>"; legendEntries.push_back(legendEntry.str()); // Draw a track dot for data points within the plot area int yCoor = c->getYCoor(dataSet->getPosition(xIndex), dataSet->getUseYAxis()); if ((yCoor >= plotArea->getTopY()) && (yCoor <= plotArea->getBottomY())) { d->circle(xCoor, yCoor, 5, 5, color, color); } } } } // Create the legend by joining the legend entries std::ostringstream legendText; legendText << "<*block,maxWidth=" << plotArea->getWidth() << "*><*block*><*font=Arial Bold*>[" << c->xAxis()->getFormattedLabel(xValue, "mm/dd/yyyy") << "]<*/*>"; for (int i = ((int)legendEntries.size()) - 1; i >= 0; --i) legendText << " " << legendEntries[i]; // Display the legend on the top of the plot area TTFText *t = d->text(legendText.str().c_str(), "Arial Bold", 10); t->draw(plotArea->getLeftX() + 5, plotArea->getTopY() - 3, 0x333333, Chart::BottomLeft); t->destroy(); }