ASE Home Page Products Download Purchase Support About ASE
ChartDirector Support
Forum HomeForum Home   SearchSearch

Message ListMessage List     Post MessagePost Message

  How to clear a chart
Posted by AS on Jan-20-2022 06:03
Hi, I am using Zoomable and Scrollable Charts (Zooming and Scrolling with Track Line (1) (Qt)).

After displaying data in a chart, I would like to clear the data and have an empty chart. How can I do that? Is there a clear function that can be called?

Thank you

  Re: How to clear a chart
Posted by Peter Kwan on Jan-21-2022 04:23
Attachments:
Hi AS,

The data are provided by your code. You can always clear your data. You can ask ChartDirector to update the chart with QChartViewer::updateViewPort. As there is no data, the updated chart will not display any data.

In the Zooming and Scrolling with Track Line (1) (Qt) sample code, the data are double arrays that come from a Random number generator. You can clear the data by simply setting the array size to 0. However, although no data can be plotted, the chart will still display something. For the sample code, the chart title, axis title will still be there even if there is no data, and the x-axis may show some default scale, etc..

I have attached an example created by modifying the sample code. It includes a "Clear Data" button that sets the array length to 0, and update the chart. I have modified the code so that if there is no data, it will not configure the x-axis scale or draw the track cursor or the legend box. It will still draw an empty plot area, and the chart and axis titles.

In your own code, you can modify it to draw or undraw other things. For example, you may  draw a text that displays "No Data" at the center of the plot area, or you may display some default x-axis and y-axis scale and grid lines on an empty plot area.

Hope this can help.

Regards
Peter Kwan
zoomscrolltrack.cpp
#include <QApplication>
#include <QPushButton>
#include <QMouseEvent>
#include <math.h>
#include <sstream>
#include <algorithm>
#include "zoomscrolltrack.h"


ZoomScrollTrack::ZoomScrollTrack(QWidget *parent) :
    QDialog(parent)
{
    //
    // Set up the GUI
    //

    setFixedSize(782, 376);
    setWindowTitle("Zooming and Scrolling with Track Line (1)");

    // The frame on the left side
    QFrame *frame = new QFrame(this);
    frame->setGeometry(4, 4, 120, 370);
    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);

    // 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*)));

    // Clear data button
    QPushButton *clearPB = new QPushButton("Clear Data", frame);
    clearPB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    clearPB->setGeometry(4, 120, 112, 28);
    connect(clearPB, SIGNAL(clicked(bool)), SLOT(onClearData(bool)));

    // Chart Viewer
    m_ChartViewer = new QChartViewer(this);
    m_ChartViewer->setGeometry(128, 4, 650, 350);
    connect(m_ChartViewer, SIGNAL(viewPortChanged()), SLOT(onViewPortChanged()));
    connect(m_ChartViewer, SIGNAL(mouseMovePlotArea(QMouseEvent*)),
        SLOT(onMouseMovePlotArea(QMouseEvent*)));

    // Horizontal scroll bar
    m_HScrollBar = new QScrollBar(Qt::Horizontal, this);
    m_HScrollBar->setGeometry(128, 356, 650, 17);
    connect(m_HScrollBar, SIGNAL(valueChanged(int)), SLOT(onHScrollBarChanged(int)));

    //
    // 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);
}

ZoomScrollTrack::~ZoomScrollTrack()
{
    delete m_ranSeries;
    delete m_ChartViewer->getChart();
}

//
// Load the data
//
void ZoomScrollTrack::loadData()
{
    // In this example, we just use random numbers as data.
    m_ranSeries = new RanSeries(127);
    m_timeStamps = m_ranSeries->getDateSeries(1827, Chart::chartTime(2007, 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 ZoomScrollTrack::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());

    // Enable mouse wheel zooming by setting the zoom ratio to 1.1 per wheel event
    viewer->setMouseWheelZoomRatio(1.1);

    // Set the maximum zoom to 10 points
    viewer->setZoomInWidthLimit(10.0 / m_timeStamps.len);
}

//
// 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 ZoomScrollTrack::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 if necessary
    if (m_ChartViewer->needUpdateChart())
        drawChart(m_ChartViewer);
}

//
// Update controls in the user interface when the view port changed
//
void ZoomScrollTrack::updateControls(QChartViewer *viewer)
{
    // The logical length of the scrollbar. It can be any large value. The actual value does
    // not matter.
    const int scrollBarLen = 1000000000;

    // Update the horizontal scroll bar
    m_HScrollBar->setEnabled(viewer->getViewPortWidth() < 1);
    m_HScrollBar->setPageStep((int)ceil(viewer->getViewPortWidth() * scrollBarLen));
    m_HScrollBar->setSingleStep((std::min)(scrollBarLen / 100, m_HScrollBar->pageStep()));
    m_HScrollBar->setRange(0, scrollBarLen - m_HScrollBar->pageStep());
    m_HScrollBar->setValue((int)(0.5 + viewer->getViewPortLeft() * scrollBarLen));
}

//
// Draw the chart and display it in the given viewer
//
void ZoomScrollTrack::drawChart(QChartViewer *viewer)
{
    DoubleArray viewPortTimeStamps;
    DoubleArray viewPortDataSeriesA;
    DoubleArray viewPortDataSeriesB;
    DoubleArray viewPortDataSeriesC;

    if (m_timeStamps.len > 0)
    {
        // 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.
        viewPortTimeStamps = DoubleArray(m_timeStamps.data + startIndex, noOfPoints);
        viewPortDataSeriesA = DoubleArray(m_dataSeriesA.data + startIndex, noOfPoints);
        viewPortDataSeriesB = DoubleArray(m_dataSeriesB.data + startIndex, noOfPoints);
        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 650 x 350 pixels, with a white (ffffff) background and grey
    // (aaaaaa) border
    XYChart *c = new XYChart(650, 350, 0xffffff, 0xaaaaaa);

    // Set the plotarea at (55, 55) with width 90 pixels less than chart width, and height 90 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() - 90, c->getHeight() - 90, 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 to the chart using 18 pts Times New Roman Bold Italic font
    c->addTitle("   Zooming and Scrolling with Track Line (1)", "Times New Roman Bold Italic", 18);

    // Set legend icon style to use line style icon, sized for 8pt font
    c->getLegend()->setLineStyleKey();
    c->getLegend()->setFontSize(8);

    // Set the axis stem to transparent
    c->xAxis()->setColors(Chart::Transparent);
    c->yAxis()->setColors(Chart::Transparent);

    // Add axis title using 10pts Arial Bold Italic font
    c->yAxis()->setTitle("Ionic Temperature (C)", "Arial Bold Italic", 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).
    //

    if (m_timeStamps.len > 0)
    {
        // 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 (ff0000), green
        // (00cc00) and blue (0000ff)
        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());

        //
        // 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 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);
}

//
// The Pointer, Zoom In or Zoom out button is pressed
//
void ZoomScrollTrack::onMouseUsageChanged(QAbstractButton *b)
{
    m_ChartViewer->setMouseUsage(mouseUsage->id(b));
}

void ZoomScrollTrack::onClearData(bool b)
{
    m_timeStamps.len = 0;
    m_ChartViewer->updateViewPort(true, true);
}

//
// User clicks on the the horizontal scroll bar
//
void ZoomScrollTrack::onHScrollBarChanged(int value)
{
    if (!m_ChartViewer->isInViewPortChangedEvent())
    {
        // Set the view port based on the scroll bar
        int scrollBarLen = m_HScrollBar->maximum() + m_HScrollBar->pageStep();
        m_ChartViewer->setViewPortLeft(value / (double)scrollBarLen);

        // Update the chart display without updating the image maps. (We can delay updating
        // the image map until scrolling is completed and the chart display is stable.)
        m_ChartViewer->updateViewPort(true, false);
    }
}

//
// Draw track cursor when mouse is moving over plotarea
//
void ZoomScrollTrack::onMouseMovePlotArea(QMouseEvent *)
{
    trackLineLegend((XYChart *)m_ChartViewer->getChart(), m_ChartViewer->getPlotAreaMouseX());
    m_ChartViewer->updateDisplay();
}

//
// Draw track line with data labels
//
void ZoomScrollTrack::trackLineLegend(XYChart *c, int mouseX)
{
    if (m_timeStamps.len <= 0)
        return;

    // 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, d->dashLineColor(0x000000, 0x0101));

    // 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 != (int)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, 4, 4, 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, "mmm 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", 8);
    t->draw(plotArea->getLeftX() + 5, plotArea->getTopY() - 3, 0x000000, Chart::BottomLeft);
    t->destroy();
}

zoomscrolltrack.h
#ifndef ZOOMSCROLLTRACK_H
#define ZOOMSCROLLTRACK_H

#include <QDialog>
#include <QScrollBar>
#include <QButtonGroup>
#include "qchartviewer.h"

class ZoomScrollTrack : public QDialog {
    Q_OBJECT
public:
    ZoomScrollTrack(QWidget *parent = 0);
    ~ZoomScrollTrack();

private:
    //
    // Data arrays for the scrollable / zoomable chart.
    // - In this demo, we just use a RanTable object to generate random data for the chart.
    //
    RanSeries *m_ranSeries;
    DoubleArray m_timeStamps;
    DoubleArray m_dataSeriesA;
    DoubleArray m_dataSeriesB;
    DoubleArray m_dataSeriesC;

    QButtonGroup *mouseUsage;
    QScrollBar *m_HScrollBar;
    QChartViewer *m_ChartViewer;

    void loadData();                                // Load data into data arrays
    void initChartViewer(QChartViewer *viewer);     // Initialize the QChartViewer
    void drawChart(QChartViewer *viewer);           // Draw chart
    void trackLineLegend(XYChart *c, int mouseX);   // Draw Track Cursor
    void updateControls(QChartViewer *viewer);      // Update other controls

private slots:
    void onHScrollBarChanged(int value);
    void onMouseUsageChanged(QAbstractButton *b);
    void onClearData(bool b);
    void onViewPortChanged();
    void onMouseMovePlotArea(QMouseEvent *event);
};

#endif // ZOOMSCROLLTRACK_H