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

Message ListMessage List     Post MessagePost Message

  can spread the charts from each other ?
Posted by jill on Feb-22-2020 04:35
Attachments:
Hi,Can the graphs be shown separately in one chart ?
Something like the picture I attached?
16ff1.jpg

  Re: can spread the charts from each other ?
Posted by Peter Kwan on Feb-23-2020 18:08
Hi Jill,

Yes, you can use a MultiChart. The following is an example that uses a MultiChart to contain two XYCharts.

https://www.advsofteng.com/doc/cdcpp.htm#dualhbar.htm

In brief, you can create multiple XYChart objects. You can then create a MultiChart to contain all the XYChart objects, and display the MultiChart.

// Create multiple XYChart objects for your charts
XYChart *c0 = .....;
XYChart *c1 = .....;
......

// Use a MultiChart to contain all of them
MultiChart *m = new MultiChart(width, height);
m->addChart(0, 0, c0);
m->addChart(0, 100, c1);
......

...... Display the MultiChart .......

Hope this can help.

Regards
Peter Kwan

  Re: can spread the charts from each other ?
Posted by jill on Apr-07-2020 04:04
Attachments:
I do this for zoomscrolltrack2  ,add 2 line chart but it didn't work
new 1.txt
c = new XYChart(600, 150, 0xffffff, 0xaaaaaa);
    c->setPlotArea(55, 0, c->getWidth() - 90, c->getHeight() - 90, c->linearGradientColor(0, 55, 0,
        c->getHeight() - 35, 0xf0f6ff, 0xa0c0ff), -1, Chart::Transparent, 0xffffff, 0xffffff);
    
    c->setClipping();

    // Add a legend box at (55, 30) using horizontal layout. Use 8pts Arial Bold as font. Set the
    // background and border color to Transparent and use line style legend key.
    LegendBox *b = c->addLegend(55, 30, false, "arialbd.ttf", 8);
    b->setBackground(Chart::Transparent);
    b->setLineStyleKey();

    // 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("mV/ms", "arialbd.ttf", 10);

    ///////////////////////////////////////////////////////////////////////////////////////
    // Draw chart c1
    ///////////////////////////////////////////////////////////////////////////////////////
    c1 = new XYChart(600, 200, 0xffffff, 0xaaaaaa);
  
     c1->setPlotArea(55, 0, c1->getWidth() - 90, c1->getHeight() - 90, c1->linearGradientColor(0, 55, 0,
         c1->getHeight() - 35, 0xf0f6ff, 0xa0c0ff), -1, Chart::Transparent, 0xffffff, 0xffffff);
     // c->setPlotArea(52, 60, 520, 205, 0xf0f6ff, -1, -1, 0xcccccc, 0xcccccc);
     // As the data can lie outside the plotarea in a zoomed chart, we need enable clipping.
     c1->setClipping();
     LegendBox *b1 = c1->addLegend(55, 30, false, "arialbd.ttf", 8);
     b1->setBackground(Chart::Transparent);
     b1->setLineStyleKey();

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

    ///////////////////////////////////////////////////////////////////////////////////////
    // 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 (ff0000), green
    // (00cc00) and blue (0000ff)
   
    layer->addDataSet(viewPortDataSeriesA, 0xff3333, "1"); //red


    // Add a line layer for the lines, using a line width of 2 pixels
    LineLayer *layer1 = c1->addLineLayer();
    layer1->setLineWidth(2);


    // plot area. So it is a good idea to use fast line mode.
    layer1->setFastLineMode();
    layer1->setXData(viewPortTimeStamps);
    layer1->addDataSet(viewPortDataSeriesB, 0x00cc00, "2"); //green


    ///////////////////////////////////////////////////////////////////////////////////////
    // 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());
     c->xAxis()->setTickDensity(25);
   

    // 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}");
    c->xAxis()->setLabelFormat("{value|hh:nn:ss}");


    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:ss}");

    ///////////////////////////////////////////////////////////////////////////////////////
    // Output the chart
    ///////////////////////////////////////////////////////////////////////////////////////
    // Create a MultiChart object of size 600 x 300 pixels.
        MultiChart *m = new MultiChart(600, 300);
    
	if ((!viewer->isInMouseMoveEvent()) && viewer->isMouseOnPlotArea())
        trackLineLabel(c, viewer->getPlotAreaMouseX());

    if ((!viewer->isInMouseMoveEvent()) && viewer->isMouseOnPlotArea())
        trackLineLabel(c1, viewer->getPlotAreaMouseX());

       m->addChart(600,0 , c);

       m->addChart(600, 150, c1);

    delete viewer->getChart();
    viewer->setChart(m);
}

  Re: can spread the charts from each other ?
Posted by Peter Kwan on Apr-07-2020 15:42
Attachments:
Hi Jill,

The lines:

m->addChart(600,0 , c);
m->addChart(600, 150, c1);

should be changed to:

m->addChart(0 ,0 , c);
m->addChart(0, 150, c1);

If you are using zoomscrolltrack2, there are other things you need to change:

(a) You cannot just delete the MultiChart using "delete viewer->getChart();". You need to delete the XYCharts too. In general, if you use "new" to create something, there must be a matching "delete" to avoid memory leak.

(b) In the original sample code, the track line is drawn assuming an XYChart. For example, in the MFC version of the sample code, there is a line of code like:

    trackLineLabel((XYChart *)m_ChartViewer.getChart(), m_ChartViewer.getPlotAreaMouseX());

If you use a MultiChart in the chart viewer, the you cannot cast the pointer to (XYChart *). It needs to be casted to (MultiChart *), and the trackLineLabel has to be modified to use the MultiChart, and in the code it needs to use a loop to iterate the XYCharts to draw the track cursors.

I happened to have a MFC/C++ example of "zoomscrolltrack" using a MultiChart. (Are you using MFC or Qt?) I have attached it for your reference.

Regards
Peter Kwan
multi_zoomscrolltrack_mfc.zip
multi_zoomscrolltrack_mfc.zip

42.15 Kb

  Re: can spread the charts from each other ?
Posted by jill on Apr-09-2020 18:38
Attachments:
Hi peter
Thanks for your help
I use QT and edited it like the version you attached but it still doesn't work
Where is my problem?
new 2.txt
#include <QApplication>
#include <QPushButton>
#include <QButtonGroup>
#include <QMouseEvent>
#include <math.h>
#include <sstream>
#include <algorithm>
#include "zoomscrolltrack2.h"

using namespace std;


//int main(int argc, char *argv[])
//{
//    QApplication app(argc, argv);
//    app.setStyleSheet("* {font-family:arialbd;font-size:11px}");
//    ZoomScrollTrack2 demo;
//    demo.show();
//    return app.exec();
//}

//
// Because QT uses QDateTime, while ChartDirector uses Chart::chartTime, we need
// utilities to convert from one to another
//

// Convert from QDateTime to chartTime
static double QDateTimeToChartTime(QDateTime q)
{
    QDate d = q.date();
    QTime t = q.time();
    return Chart::chartTime(d.year(), d.month(), d.day(), t.hour(), t.minute(),
                            t.second()) + t.msec() / 1000.0;
}

// Convert from chartTime to QDateTime
static QDateTime ChartTimeToQDateTime(double t)
{
    double ymdhms = floor(t);
    int ms = (int)(floor((t - ymdhms) * 1000));
    int ymd = Chart::getChartYMD(ymdhms);
    int hms = (int)fmod(ymdhms, 86400);

    return QDateTime(QDate(ymd / 10000, (ymd % 10000) / 100, ymd % 100),
        QTime(hms / 3600, (hms % 3600) / 60, hms % 60, ms));
}

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

    setFixedSize(732, 308);
    setWindowTitle("Offline Sample");

    // The frame on the left side
    frame = new QFrame(this);
    frame->setGeometry(4, 4, 120, 300);
    frame->setFrameShape(QFrame::StyledPanel);

    // Pointer push button
    QPushButton *pointerPB = new QPushButton(QIcon(":/new/prefix1/pointer.png"), "Pointer", frame);
    pointerPB->setGeometry(4, 12, 112, 48);
    pointerPB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    pointerPB->setCheckable(true);

    // Zoom In push button
    QPushButton *zoomInPB = new QPushButton(QIcon(":/new/prefix1/zoomin.png"), "Zoom In", frame);
    zoomInPB->setGeometry(4, 80, 112, 48);
    zoomInPB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    zoomInPB->setCheckable(true);

    // Zoom Out push button
    QPushButton *zoomOutPB = new QPushButton(QIcon(":/new/prefix1/zoomout.png"), "Zoom Out", frame);
    zoomOutPB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    zoomOutPB->setGeometry(4, 140, 112, 48);
    zoomOutPB->setCheckable(true);


    // Save push button
    saved_PB = new QPushButton(QIcon(":/new/prefix1/save.png"), "ذخیره/چاپ", frame);
    saved_PB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    saved_PB->setGeometry(4, 190, 112, 48);
    saved_PB->setCheckable(true);

    // The Pointer/Zoom In/Zoom Out buttons form a button group
    QButtonGroup *mouseUsage = new QButtonGroup(frame);
    mouseUsage->addButton(pointerPB, Chart::MouseUsageScroll);
    mouseUsage->addButton(zoomInPB, Chart::MouseUsageZoomIn);
    mouseUsage->addButton(zoomOutPB, Chart::MouseUsageZoomOut);
    connect(mouseUsage, SIGNAL(buttonPressed(int)), SLOT(onMouseUsageChanged(int)));


    bc1 = new QPushButton("", frame);
    bc1->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    bc1->setIcon(QIcon(":/new/prefix1/left_arrow.png"));
    bc1->setIconSize(QSize(90, 90));
    bc1->setGeometry(6, 250, 101, 41);


//    // Start Date control
//    (new QLabel("Start Date", frame))->setGeometry(6, 230, 112, 18);
//    m_StartDate = new QDateEdit(frame);
//    m_StartDate->setGeometry(4, 248, 113, 22);
//    connect(m_StartDate, SIGNAL(dateTimeChanged(QDateTime)), SLOT(onStartDateChanged(QDateTime)));

//    // Duration control
//    (new QLabel("End Date", frame))->setGeometry(6, 284, 112, 18);
//    m_EndDate = new QDateEdit(frame);
//    m_EndDate->setGeometry(4, 302, 113, 22);
//    connect(m_EndDate, SIGNAL(dateTimeChanged(QDateTime)), SLOT(onEndDateChanged(QDateTime)));

    // 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*)));
    connect(m_ChartViewer, SIGNAL(mouseWheel(QWheelEvent*)), SLOT(onMouseWheelChart(QWheelEvent*)));

    // Horizontal scroll bar
    m_HScrollBar = new QScrollBar(Qt::Horizontal, this);
    m_HScrollBar->setGeometry(128, 290, 600, 17);
    m_HScrollBar->setStyleSheet("background-color: rgb(161, 194, 231)");
    connect(m_HScrollBar, SIGNAL(valueChanged(int)), SLOT(onHScrollBarChanged(int)));

    //
    // Initialize the chart
    //
     m_ranSeries =0;
     m_timeStamps={};
     m_dataSeriesA[sampleSize]={0};
     m_dataSeriesB[sampleSize]={0};
     m_dataSeriesC[sampleSize]={0};
     m_dataSeriesD[sampleSize]={0};
     m_dataSeriesE[sampleSize]={0};
     m_dataSeriesF[sampleSize]={0};
     m_dataSeriesG[sampleSize]={0};
     m_dataSeriesH[sampleSize]={0};

   // add="/home/pi/file/issuance2.csv";
    //readfile(add);
    // Load the data
    //if(val1.size()>0)
   // 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);


}

ZoomScrollTrack2::~ZoomScrollTrack2()
{
    delete m_ranSeries;
    delete m_ChartViewer->getChart();
    MultiChart *m = (MultiChart *)m_ChartViewer->getChart();
    if (m != 0)
    {
        for (int i = 0; i < m->getChartCount(); ++i)
            delete m->getChart(i);
        delete m;
}
}

//
// Load the data
//
void ZoomScrollTrack2::loadData(QList<double> data1, QList<double> data2, QList<double> data3, QList<double> data4, QList<double> data5, QList<double> data6, QList<double> data7, QList<double> data8)
{
    // In this example, we just use random numbers as data.
    m_ranSeries = new RanSeries(data1.size());
    m_timeStamps = m_ranSeries->getDateSeries(data1.size(), Chart::chartTime(2020, 2, 5,13,30,01), 1);
    for(int z=0;z<data1.size();z++ ){
   m_dataSeriesA[z] =100*data1[z];//4*(((data1[z]/100)/2)-0.6);
   qDebug()<<m_dataSeriesC[z];
   m_dataSeriesB[z] =data2[z];//4*(((data2[z]/100)/2)-0.6);
   m_dataSeriesC[z] =10*data3[z];//4*(((data3[z]/100)/2)-0.6);
   m_dataSeriesD[z] =150*data4[z];//4*(((data4[z]/100)/2)-0.6);
   m_dataSeriesE[z] =20*data5[z];//4*(((data5[z]/100)/2)-0.6);
   m_dataSeriesF[z] =30*data6[z];//4*(((data6[z]/100)/2)-0.6);
   m_dataSeriesG[z] =40*data7[z];//4*(((data7[z]/100)/2)-0.6);
   m_dataSeriesH[z] =50*data8[z];//4*(((data8[z]/100)/2)-0.6);
    }

}

//
// Initialize the QChartViewer
//
void ZoomScrollTrack2::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);
}

//
// 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 ZoomScrollTrack2::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 ZoomScrollTrack2::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(min(scrollBarLen / 100, m_HScrollBar->pageStep()));
    m_HScrollBar->setRange(0, scrollBarLen - m_HScrollBar->pageStep());
    m_HScrollBar->setValue((int)(0.5 + viewer->getViewPortLeft() * scrollBarLen));

//    //
//    // Update the m_StartDate QDataEdit control.
//    //
//    m_StartDate->setDateTimeRange(ChartTimeToQDateTime(viewer->getValueAtViewPort("x", 0)),
//        ChartTimeToQDateTime(viewer->getValueAtViewPort("x", 1)));
//    m_StartDate->setDateTime(
//        ChartTimeToQDateTime(viewer->getValueAtViewPort("x", viewer->getViewPortLeft())));

//    //
//    // Update the m_EndDate QDataEdit control.
//    //
//    m_EndDate->setDateTimeRange(ChartTimeToQDateTime(viewer->getValueAtViewPort("x", 0)),
//        ChartTimeToQDateTime(viewer->getValueAtViewPort("x", 1)));
//    m_EndDate->setDateTime(ChartTimeToQDateTime(
//        viewer->getValueAtViewPort("x", viewer->getViewPortLeft() + viewer->getViewPortWidth())));
}

//
// Draw the chart and display it in the given viewer
//
void ZoomScrollTrack2::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 + startIndex, noOfPoints);
    DoubleArray viewPortDataSeriesB = DoubleArray(m_dataSeriesB + startIndex, noOfPoints);
    DoubleArray viewPortDataSeriesC = DoubleArray(m_dataSeriesC + startIndex, noOfPoints);
    DoubleArray viewPortDataSeriesD = DoubleArray(m_dataSeriesD + startIndex, noOfPoints);
    DoubleArray viewPortDataSeriesE = DoubleArray(m_dataSeriesE + startIndex, noOfPoints);
    DoubleArray viewPortDataSeriesF = DoubleArray(m_dataSeriesF + startIndex, noOfPoints);
    DoubleArray viewPortDataSeriesG = DoubleArray(m_dataSeriesG + startIndex, noOfPoints);
    DoubleArray viewPortDataSeriesH = DoubleArray(m_dataSeriesH + 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
   c = createXYChart();
  // c->yAxis()->setTitle("Ionic Temperature (C)", "arialbi.ttf", 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 (ff0000), green
    // (00cc00) and blue (0000ff)
    //layer->setXData(viewPortTimeStamps);
    layer->addDataSet(viewPortDataSeriesA, 0xff3333, "1"); //red

    ///////////////////////////////////////////////////////////////////////////////////////
    // 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());
     c->xAxis()->setTickDensity(25);
    //
    // 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}");
    c->xAxis()->setLabelFormat("{value|hh:nn:ss}");


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


    ///////////////////////////////////////////////////////////////////////////////////////
    // Create 2nd XYChart
    ///////////////////////////////////////////////////////////////////////////////////////

    XYChart *c2 = createXYChart();
    //c2->yAxis()->setTitle("Plasma Temperature (C)", "arialbi.ttf", 10);

    ///////////////////////////////////////////////////////////////////////////////////////
    // Add data to chart
    ///////////////////////////////////////////////////////////////////////////////////////

    // Add a line layer for the lines, using a line width of 2 pixels
    LineLayer *layer2 = c2->addLineLayer();
    layer2->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.
    layer2->setFastLineMode();

    // Now we add the 3 data series to a line layer, using the color red (ff0000), green
    // (00cc00) and blue (0000ff)
      layer2->setXData(viewPortTimeStamps);
      layer2->addDataSet(viewPortDataSeriesB, 0x00cc00, "2"); //green

    ///////////////////////////////////////////////////////////////////////////////////////
    // Configure axis scale and labelling
    ///////////////////////////////////////////////////////////////////////////////////////

    c2->xAxis()->copyAxis(c->xAxis());

    ///////////////////////////////////////////////////////////////////////////////////////
    // Output the chart
    ///////////////////////////////////////////////////////////////////////////////////////
    // Create a MultiChart object of size 600 x 300 pixels.
    MultiChart *m = new MultiChart(c->getWidth(), c->getHeight() + c2->getHeight());
    m->addChart(0, 0, c);
    m->addChart(0, c->getHeight(), c2);
    m->setMainChart(m);

    // 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())
    {
        trackLineLabel(m, (0 == viewer->getChart()) ? c->getPlotArea()->getRightX() :
            viewer->getPlotAreaMouseX());
    }

    // Clean up old chart
    MultiChart *previousChart = (MultiChart *)viewer->getChart();
    if (previousChart != 0)
    {
        for (int i = 0; i < previousChart->getChartCount(); ++i)
            delete previousChart->getChart(i);
        delete previousChart;
    }

    viewer->setChart(m);
}

//
// The Pointer, Zoom In or Zoom out button is pressed
//
void ZoomScrollTrack2::onMouseUsageChanged(int mouseUsage)
{
    m_ChartViewer->setMouseUsage(mouseUsage);
}

//
// User selects a start date from the QDateEdit control
//
//void ZoomScrollTrack2::onStartDateChanged(QDateTime date)
//{
//    if (!m_ChartViewer->isInViewPortChangedEvent())
//    {
//        // The updated view port width
//        double vpWidth = m_ChartViewer->getViewPortLeft() + m_ChartViewer->getViewPortWidth() -
//            m_ChartViewer->getViewPortAtValue("x", QDateTimeToChartTime(date));

//        // Make sure the updated view port width is within bounds
//        vpWidth = max(m_ChartViewer->getZoomInWidthLimit(), min(vpWidth,
//            m_ChartViewer->getViewPortLeft() + m_ChartViewer->getViewPortWidth()));

//        // Update view port and trigger a view port changed event to update the chart
//        m_ChartViewer->setViewPortLeft(m_ChartViewer->getViewPortLeft() +
//            m_ChartViewer->getViewPortWidth() - vpWidth);
//        m_ChartViewer->setViewPortWidth(vpWidth);
//        m_ChartViewer->updateViewPort(true, false);
//    }
//}

////
//// User selects an end date from the QDateEdit control
////
//void ZoomScrollTrack2::onEndDateChanged(QDateTime date)
//{
//    if (!m_ChartViewer->isInViewPortChangedEvent())
//    {
//        // The updated view port width
//        double vpWidth = m_ChartViewer->getViewPortAtValue("x", QDateTimeToChartTime(date)) -
//            m_ChartViewer->getViewPortLeft();

//        // Make sure the updated view port width is within bounds
//        vpWidth = max(m_ChartViewer->getZoomInWidthLimit(), min(vpWidth,
//            1 - m_ChartViewer->getViewPortLeft()));

//        // Update view port and trigger a view port changed event to update the chart
//        m_ChartViewer->setViewPortWidth(vpWidth);
//        m_ChartViewer->updateViewPort(true, false);
//    }
//}

////
// User clicks on the the horizontal scroll bar
//
void ZoomScrollTrack2::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);
    }
}

//
// When the mouse enters the chart, we will generate an image map for hot spots and tooltips
// support if it has not already been generated.
//
void ZoomScrollTrack2::onMouseWheelChart(QWheelEvent *event)
{
    // Process the mouse wheel only if the mouse is over the plot area
    if (!m_ChartViewer->isMouseOnPlotArea())
    {
        event->ignore();
        return;
    }

    // We zoom in or out by 10% depending on the mouse wheel direction.
    double newVpWidth = m_ChartViewer->getViewPortWidth() * (event->delta() > 0 ? 0.9 : 1 / 0.9);
    double newVpHeight = m_ChartViewer->getViewPortHeight() * (event->delta() > 0 ? 0.9 : 1 / 0.9);

    // We do not zoom beyond the zoom width or height limits.
    newVpWidth = max(m_ChartViewer->getZoomInWidthLimit(), min(newVpWidth,
        m_ChartViewer->getZoomOutWidthLimit()));
    newVpHeight = max(m_ChartViewer->getZoomInHeightLimit(), min(newVpWidth,
        m_ChartViewer->getZoomOutHeightLimit()));

    if ((newVpWidth != m_ChartViewer->getViewPortWidth()) ||
            (newVpHeight != m_ChartViewer->getViewPortHeight()))
    {
        // Set the view port position and size so that the point under the mouse remains under
        // the mouse after zooming.

        double deltaX = (m_ChartViewer->getPlotAreaMouseX() - m_ChartViewer->getPlotAreaLeft()) *
            (m_ChartViewer->getViewPortWidth() - newVpWidth) / m_ChartViewer->getPlotAreaWidth();
        m_ChartViewer->setViewPortLeft(m_ChartViewer->getViewPortLeft() + deltaX);
        m_ChartViewer->setViewPortWidth(newVpWidth);

        double deltaY = (m_ChartViewer->getPlotAreaMouseY() - m_ChartViewer->getPlotAreaTop()) *
            (m_ChartViewer->getViewPortHeight() - newVpHeight) / m_ChartViewer->getPlotAreaHeight();
        m_ChartViewer->setViewPortTop(m_ChartViewer->getViewPortTop() + deltaY);
        m_ChartViewer->setViewPortHeight(newVpHeight);

        m_ChartViewer->updateViewPort(true, false);
    }
}

//
// Draw track cursor when mouse is moving over plotarea
//
void ZoomScrollTrack2::onMouseMovePlotArea(QMouseEvent *)
{
    trackLineLabel((MultiChart *)m_ChartViewer->getChart(), m_ChartViewer->getPlotAreaMouseX());
    m_ChartViewer->updateDisplay();

    // Hide the track cursor when the mouse leaves the plot area
    m_ChartViewer->removeDynamicLayer("mouseLeavePlotArea");
}

//
// Draw track line with data labels
//
void ZoomScrollTrack2::trackLineLabel(MultiChart *c, int mouseX)
{
    // Clear the current dynamic layer and get the DrawArea object to draw on it.
    DrawArea *d = c->initDynamicLayer();

    for (int i = 0; i < m->getChartCount(); ++i)
    {
        XYChart *c = (XYChart *)m->getChart(i);
        int offsetX = c->getAbsOffsetX();
        int offsetY = c->getAbsOffsetY();

    // 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() + offsetY, plotArea->getBottomY()+ offsetY, xCoor + offsetX,
        d->dashLineColor(0x000000, 0x0101));

    // Draw a label on the x-axis to show the track line position.
    ostringstream xlabel;
    xlabel << "<*font,bgColor=000000*> " << c->xAxis()->getFormattedLabel(xValue, "mmm dd, yyyy")
        << " <*/font*>";
    TTFText *t = d->text(xlabel.str().c_str(), "arialbd.ttf", 8);

    // Restrict the x-pixel position of the label to make sure it stays inside the chart image.
    int xLabelPos = max(0, min(xCoor - t->getWidth() / 2, c->getWidth() - t->getWidth()));
    t->draw(xLabelPos, plotArea->getBottomY() + 6, 0xffffff);
    t->destroy();

    // Iterate through all layers to draw the data labels
    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);
            const char *dataSetName = dataSet->getDataName();

            // Get the color, name and position of the data label
            int color = dataSet->getDataColor();
            int yCoor = c->getYCoor(dataSet->getPosition(xIndex), dataSet->getUseYAxis());

            // Draw a track dot with a label next to it for visible data points in the plot area
            if ((yCoor >= plotArea->getTopY()) && (yCoor <= plotArea->getBottomY()) && (color !=
                (int)Chart::Transparent) && dataSetName && *dataSetName)
            {
                d->circle(xCoor, yCoor, 4, 4, color, color);

                ostringstream label;
                label << "<*font,bgColor=" << hex << color << "*> "
                    << c->formatValue(dataSet->getValue(xIndex), "{value|P4}") << " <*font*>";
                t = d->text(label.str().c_str(), "arialbd.ttf", 8);

                // Draw the label on the right side of the dot if the mouse is on the left side the
                // chart, and vice versa. This ensures the label will not go outside the chart image.
                if (xCoor <= (plotArea->getLeftX() + plotArea->getRightX()) / 2)
                    t->draw(xCoor + 5, yCoor, 0xffffff, Chart::Left);
                else
                    t->draw(xCoor - 5, yCoor, 0xffffff, Chart::Right);

                t->destroy();
            }
        }
    }
}
}

XYChart *ZoomScrollTrack2::createXYChart()
{
    // Create an XYChart object of size 650 x 220 pixels, with a white (ffffff) background
    XYChart *c = new XYChart(650, 300, 0xffffff, 0xaaaaaa);

    // Set the plotarea at (55, 25) with width 90 pixels less than chart width, and height 60 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, 25, c->getWidth() - 90, c->getHeight() - 60, c->linearGradientColor(0, 25, 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();

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

    // Add a legend box at (55, 30) using horizontal layout. Use 8pts Arial Bold as font. Set the
    // background and border color to Transparent and use line style legend key.
   // LegendBox *b = c->addLegend(55, 30, false, "arialbd.ttf", 8);
   // b->setBackground(Chart::Transparent);
   // b->setLineStyleKey();

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

    return c;
}

  Re: can spread the charts from each other ?
Posted by jill on Apr-09-2020 18:41
P.S: I call this class in other class so for this I commented the main function

  Re: can spread the charts from each other ?
Posted by Peter Kwan on Apr-10-2020 02:52
Attachments:
Hi jill,

From your code, apparently, the MultiChart would be around 600 pixels tall, but the Qt Window is configured to be only 308 pixels tall.

setFixedSize(732, 308);

You would need to resize the Qt window so that it is tall enough for everything in the Window, including the chart and the scrollbar. You would also need to move the scrollbar to the bottom of the resized window.

Another issue is that the first chart does not have x-coordinates (no setXData for the first chart), while the second chart has x-coordinates. Please add the x-coordinates to the first chart so the two charts can have consistent x-coordinates.

In your code, the loadData() and initChartViewer(m_ChartViewer) and m_ChartViewer->updateViewPort(true, true) are commented out. I assume your code will call these 3 functions in your "other class".

There are still some minor issues in the trackLineLabel.

I have modified your code to generate data from a formula so I can test the code myself. About the above modification, it works normally. I have attached the .cpp and .h files I use.

Hope this can help.

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

using namespace std;


int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    app.setStyleSheet("* {font-family:arialbd;font-size:11px}");
    ZoomScrollTrack2 demo;
    demo.show();
    return app.exec();
}

//
// Because QT uses QDateTime, while ChartDirector uses Chart::chartTime, we need
// utilities to convert from one to another
//

// Convert from QDateTime to chartTime
static double QDateTimeToChartTime(QDateTime q)
{
    QDate d = q.date();
    QTime t = q.time();
    return Chart::chartTime(d.year(), d.month(), d.day(), t.hour(), t.minute(),
                            t.second()) + t.msec() / 1000.0;
}

// Convert from chartTime to QDateTime
static QDateTime ChartTimeToQDateTime(double t)
{
    double ymdhms = floor(t);
    int ms = (int)(floor((t - ymdhms) * 1000));
    int ymd = Chart::getChartYMD(ymdhms);
    int hms = (int)fmod(ymdhms, 86400);

    return QDateTime(QDate(ymd / 10000, (ymd % 10000) / 100, ymd % 100),
        QTime(hms / 3600, (hms % 3600) / 60, hms % 60, ms));
}

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

    setFixedSize(732, 650);
    setWindowTitle("Offline Sample");

    // The frame on the left side
    QFrame *frame = new QFrame(this);
    frame->setGeometry(4, 4, 120, 300);
    frame->setFrameShape(QFrame::StyledPanel);

    // Pointer push button
    QPushButton *pointerPB = new QPushButton(QIcon(":/new/prefix1/pointer.png"), "Pointer", frame);
    pointerPB->setGeometry(4, 12, 112, 48);
    pointerPB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    pointerPB->setCheckable(true);

    // Zoom In push button
    QPushButton *zoomInPB = new QPushButton(QIcon(":/new/prefix1/zoomin.png"), "Zoom In", frame);
    zoomInPB->setGeometry(4, 80, 112, 48);
    zoomInPB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    zoomInPB->setCheckable(true);

    // Zoom Out push button
    QPushButton *zoomOutPB = new QPushButton(QIcon(":/new/prefix1/zoomout.png"), "Zoom Out", frame);
    zoomOutPB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    zoomOutPB->setGeometry(4, 140, 112, 48);
    zoomOutPB->setCheckable(true);


    // Save push button
    QPushButton *saved_PB = new QPushButton(QIcon(":/new/prefix1/save.png"), "ذخیره/چاپ", frame);
    saved_PB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    saved_PB->setGeometry(4, 190, 112, 48);
    saved_PB->setCheckable(true);

    // The Pointer/Zoom In/Zoom Out buttons form a button group
    QButtonGroup *mouseUsage = new QButtonGroup(frame);
    mouseUsage->addButton(pointerPB, Chart::MouseUsageScroll);
    mouseUsage->addButton(zoomInPB, Chart::MouseUsageZoomIn);
    mouseUsage->addButton(zoomOutPB, Chart::MouseUsageZoomOut);
    connect(mouseUsage, SIGNAL(buttonPressed(int)), SLOT(onMouseUsageChanged(int)));


    QPushButton *bc1 = new QPushButton("", frame);
    bc1->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    bc1->setIcon(QIcon(":/new/prefix1/left_arrow.png"));
    bc1->setIconSize(QSize(90, 90));
    bc1->setGeometry(6, 250, 101, 41);

//    // Start Date control
//    (new QLabel("Start Date", frame))->setGeometry(6, 230, 112, 18);
//    m_StartDate = new QDateEdit(frame);
//    m_StartDate->setGeometry(4, 248, 113, 22);
//    connect(m_StartDate, SIGNAL(dateTimeChanged(QDateTime)), SLOT(onStartDateChanged(QDateTime)));

//    // Duration control
//    (new QLabel("End Date", frame))->setGeometry(6, 284, 112, 18);
//    m_EndDate = new QDateEdit(frame);
//    m_EndDate->setGeometry(4, 302, 113, 22);
//    connect(m_EndDate, SIGNAL(dateTimeChanged(QDateTime)), SLOT(onEndDateChanged(QDateTime)));

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

    // Horizontal scroll bar
    m_HScrollBar = new QScrollBar(Qt::Horizontal, this);
    m_HScrollBar->setGeometry(128, 630, 600, 17);
    m_HScrollBar->setStyleSheet("background-color: rgb(161, 194, 231)");
    connect(m_HScrollBar, SIGNAL(valueChanged(int)), SLOT(onHScrollBarChanged(int)));

    //
    // Initialize the chart
    //
    // add="/home/pi/file/issuance2.csv";
    //readfile(add);
    // Load the data
    //if(val1.size()>0)
    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);


}

ZoomScrollTrack2::~ZoomScrollTrack2()
{
    delete m_ranSeries;
    delete m_ChartViewer->getChart();
    MultiChart *m = (MultiChart *)m_ChartViewer->getChart();
    if (m != 0)
    {
        for (int i = 0; i < m->getChartCount(); ++i)
            delete m->getChart(i);
        delete m;
}
}

//
// Load the data
//
void ZoomScrollTrack2::loadData()
{
    // In this example, we just use random numbers as data.
    m_ranSeries = new RanSeries(1000);
    m_timeStamps = m_ranSeries->getDateSeries(1000, Chart::chartTime(2020, 2, 5,13,30,01), 1);
    for(int z=0; z < 1000; z++){
       m_dataSeriesA[z] = 100 * sin(z / 10.0);
       m_dataSeriesB[z] = 100 * cos(z / 10.0);
       m_dataSeriesC[z] = 100 * (z % 20);
    }

}

//
// Initialize the QChartViewer
//
void ZoomScrollTrack2::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);
}

//
// 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 ZoomScrollTrack2::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 ZoomScrollTrack2::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(min(scrollBarLen / 100, m_HScrollBar->pageStep()));
    m_HScrollBar->setRange(0, scrollBarLen - m_HScrollBar->pageStep());
    m_HScrollBar->setValue((int)(0.5 + viewer->getViewPortLeft() * scrollBarLen));

//    //
//    // Update the m_StartDate QDataEdit control.
//    //
//    m_StartDate->setDateTimeRange(ChartTimeToQDateTime(viewer->getValueAtViewPort("x", 0)),
//        ChartTimeToQDateTime(viewer->getValueAtViewPort("x", 1)));
//    m_StartDate->setDateTime(
//        ChartTimeToQDateTime(viewer->getValueAtViewPort("x", viewer->getViewPortLeft())));

//    //
//    // Update the m_EndDate QDataEdit control.
//    //
//    m_EndDate->setDateTimeRange(ChartTimeToQDateTime(viewer->getValueAtViewPort("x", 0)),
//        ChartTimeToQDateTime(viewer->getValueAtViewPort("x", 1)));
//    m_EndDate->setDateTime(ChartTimeToQDateTime(
//        viewer->getValueAtViewPort("x", viewer->getViewPortLeft() + viewer->getViewPortWidth())));
}

//
// Draw the chart and display it in the given viewer
//
void ZoomScrollTrack2::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 + startIndex, noOfPoints);
    DoubleArray viewPortDataSeriesB = DoubleArray(m_dataSeriesB + 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 = createXYChart();
  // c->yAxis()->setTitle("Ionic Temperature (C)", "arialbi.ttf", 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 (ff0000), green
    // (00cc00) and blue (0000ff)
    //layer->setXData(viewPortTimeStamps);
    layer->addDataSet(viewPortDataSeriesA, 0xff3333, "1"); //red
    layer->setXData(viewPortTimeStamps);

    ///////////////////////////////////////////////////////////////////////////////////////
    // 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());
     c->xAxis()->setTickDensity(25);
    //
    // 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}");
    c->xAxis()->setLabelFormat("{value|hh:nn:ss}");


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


    ///////////////////////////////////////////////////////////////////////////////////////
    // Create 2nd XYChart
    ///////////////////////////////////////////////////////////////////////////////////////

    XYChart *c2 = createXYChart();
    //c2->yAxis()->setTitle("Plasma Temperature (C)", "arialbi.ttf", 10);

    ///////////////////////////////////////////////////////////////////////////////////////
    // Add data to chart
    ///////////////////////////////////////////////////////////////////////////////////////

    // Add a line layer for the lines, using a line width of 2 pixels
    LineLayer *layer2 = c2->addLineLayer();
    layer2->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.
    layer2->setFastLineMode();

    // Now we add the 3 data series to a line layer, using the color red (ff0000), green
    // (00cc00) and blue (0000ff)
      layer2->setXData(viewPortTimeStamps);
      layer2->addDataSet(viewPortDataSeriesB, 0x00cc00, "2"); //green

    ///////////////////////////////////////////////////////////////////////////////////////
    // Configure axis scale and labelling
    ///////////////////////////////////////////////////////////////////////////////////////

    c2->xAxis()->copyAxis(c->xAxis());

    ///////////////////////////////////////////////////////////////////////////////////////
    // Output the chart
    ///////////////////////////////////////////////////////////////////////////////////////
    // Create a MultiChart object of size 600 x 300 pixels.
    MultiChart *m = new MultiChart(c->getWidth(), c->getHeight() + c2->getHeight());
    m->addChart(0, 0, c);
    m->addChart(0, c->getHeight(), c2);
    m->setMainChart(m);

    // 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())
    {
        trackLineLabel(m, (0 == viewer->getChart()) ? c->getPlotArea()->getRightX() :
            viewer->getPlotAreaMouseX());
    }

    // Clean up old chart
    MultiChart *previousChart = (MultiChart *)viewer->getChart();
    if (previousChart != 0)
    {
        for (int i = 0; i < previousChart->getChartCount(); ++i)
            delete previousChart->getChart(i);
        delete previousChart;
    }

    viewer->setChart(m);
}

//
// The Pointer, Zoom In or Zoom out button is pressed
//
void ZoomScrollTrack2::onMouseUsageChanged(int mouseUsage)
{
    m_ChartViewer->setMouseUsage(mouseUsage);
}

//
// User selects a start date from the QDateEdit control
//
//void ZoomScrollTrack2::onStartDateChanged(QDateTime date)
//{
//    if (!m_ChartViewer->isInViewPortChangedEvent())
//    {
//        // The updated view port width
//        double vpWidth = m_ChartViewer->getViewPortLeft() + m_ChartViewer->getViewPortWidth() -
//            m_ChartViewer->getViewPortAtValue("x", QDateTimeToChartTime(date));

//        // Make sure the updated view port width is within bounds
//        vpWidth = max(m_ChartViewer->getZoomInWidthLimit(), min(vpWidth,
//            m_ChartViewer->getViewPortLeft() + m_ChartViewer->getViewPortWidth()));

//        // Update view port and trigger a view port changed event to update the chart
//        m_ChartViewer->setViewPortLeft(m_ChartViewer->getViewPortLeft() +
//            m_ChartViewer->getViewPortWidth() - vpWidth);
//        m_ChartViewer->setViewPortWidth(vpWidth);
//        m_ChartViewer->updateViewPort(true, false);
//    }
//}

////
//// User selects an end date from the QDateEdit control
////
//void ZoomScrollTrack2::onEndDateChanged(QDateTime date)
//{
//    if (!m_ChartViewer->isInViewPortChangedEvent())
//    {
//        // The updated view port width
//        double vpWidth = m_ChartViewer->getViewPortAtValue("x", QDateTimeToChartTime(date)) -
//            m_ChartViewer->getViewPortLeft();

//        // Make sure the updated view port width is within bounds
//        vpWidth = max(m_ChartViewer->getZoomInWidthLimit(), min(vpWidth,
//            1 - m_ChartViewer->getViewPortLeft()));

//        // Update view port and trigger a view port changed event to update the chart
//        m_ChartViewer->setViewPortWidth(vpWidth);
//        m_ChartViewer->updateViewPort(true, false);
//    }
//}

////
// User clicks on the the horizontal scroll bar
//
void ZoomScrollTrack2::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);
    }
}

//
// When the mouse enters the chart, we will generate an image map for hot spots and tooltips
// support if it has not already been generated.
//
void ZoomScrollTrack2::onMouseWheelChart(QWheelEvent *event)
{
    // Process the mouse wheel only if the mouse is over the plot area
    if (!m_ChartViewer->isMouseOnPlotArea())
    {
        event->ignore();
        return;
    }

    // We zoom in or out by 10% depending on the mouse wheel direction.
    double newVpWidth = m_ChartViewer->getViewPortWidth() * (event->delta() > 0 ? 0.9 : 1 / 0.9);
    double newVpHeight = m_ChartViewer->getViewPortHeight() * (event->delta() > 0 ? 0.9 : 1 / 0.9);

    // We do not zoom beyond the zoom width or height limits.
    newVpWidth = max(m_ChartViewer->getZoomInWidthLimit(), min(newVpWidth,
        m_ChartViewer->getZoomOutWidthLimit()));
    newVpHeight = max(m_ChartViewer->getZoomInHeightLimit(), min(newVpWidth,
        m_ChartViewer->getZoomOutHeightLimit()));

    if ((newVpWidth != m_ChartViewer->getViewPortWidth()) ||
            (newVpHeight != m_ChartViewer->getViewPortHeight()))
    {
        // Set the view port position and size so that the point under the mouse remains under
        // the mouse after zooming.

        double deltaX = (m_ChartViewer->getPlotAreaMouseX() - m_ChartViewer->getPlotAreaLeft()) *
            (m_ChartViewer->getViewPortWidth() - newVpWidth) / m_ChartViewer->getPlotAreaWidth();
        m_ChartViewer->setViewPortLeft(m_ChartViewer->getViewPortLeft() + deltaX);
        m_ChartViewer->setViewPortWidth(newVpWidth);

        double deltaY = (m_ChartViewer->getPlotAreaMouseY() - m_ChartViewer->getPlotAreaTop()) *
            (m_ChartViewer->getViewPortHeight() - newVpHeight) / m_ChartViewer->getPlotAreaHeight();
        m_ChartViewer->setViewPortTop(m_ChartViewer->getViewPortTop() + deltaY);
        m_ChartViewer->setViewPortHeight(newVpHeight);

        m_ChartViewer->updateViewPort(true, false);
    }
}

//
// Draw track cursor when mouse is moving over plotarea
//
void ZoomScrollTrack2::onMouseMovePlotArea(QMouseEvent *)
{
    trackLineLabel((MultiChart *)m_ChartViewer->getChart(), m_ChartViewer->getPlotAreaMouseX());
    m_ChartViewer->updateDisplay();

    // Hide the track cursor when the mouse leaves the plot area
    m_ChartViewer->removeDynamicLayer("mouseLeavePlotArea");
}

//
// Draw track line with data labels
//
void ZoomScrollTrack2::trackLineLabel(MultiChart *m, int mouseX)
{
    // Clear the current dynamic layer and get the DrawArea object to draw on it.
    DrawArea *d = m->initDynamicLayer();

    for (int i = 0; i < m->getChartCount(); ++i)
    {
        XYChart *c = (XYChart *)m->getChart(i);
        int offsetX = c->getAbsOffsetX();
        int offsetY = c->getAbsOffsetY();

        // 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() + offsetY, plotArea->getBottomY()+ offsetY, xCoor + offsetX,
            d->dashLineColor(0x000000, 0x0101));

        // Draw a label on the x-axis to show the track line position.
        ostringstream xlabel;
        xlabel << "<*font,bgColor=000000*> " << c->xAxis()->getFormattedLabel(xValue, "mmm dd, yyyy")
            << " <*/font*>";
        TTFText *t = d->text(xlabel.str().c_str(), "arialbd.ttf", 8);

        // Restrict the x-pixel position of the label to make sure it stays inside the chart image.
        int xLabelPos = max(0, min(xCoor - t->getWidth() / 2, c->getWidth() - t->getWidth()));
        t->draw(xLabelPos, plotArea->getBottomY() + 6, 0xffffff);
        t->destroy();

        // Iterate through all layers to draw the data labels
        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);
                const char *dataSetName = dataSet->getDataName();

                // Get the color, name and position of the data label
                int color = dataSet->getDataColor();
                int yCoor = c->getYCoor(dataSet->getPosition(xIndex), dataSet->getUseYAxis());

                // Draw a track dot with a label next to it for visible data points in the plot area
                if ((yCoor >= plotArea->getTopY()) && (yCoor <= plotArea->getBottomY()) && (color !=
                    (int)Chart::Transparent) && dataSetName && *dataSetName)
                {
                    d->circle(xCoor + offsetX, yCoor + offsetY, 4, 4, color, color);

                    ostringstream label;
                    label << "<*font,bgColor=" << hex << color << "*> "
                        << c->formatValue(dataSet->getValue(xIndex), "{value|P4}") << " <*font*>";
                    t = d->text(label.str().c_str(), "arialbd.ttf", 8);

                    // Draw the label on the right side of the dot if the mouse is on the left side the
                    // chart, and vice versa. This ensures the label will not go outside the chart image.
                    if (xCoor <= (plotArea->getLeftX() + plotArea->getRightX()) / 2)
                        t->draw(xCoor + 5 + offsetX, yCoor + offsetY, 0xffffff, Chart::Left);
                    else
                        t->draw(xCoor - 5 + offsetX, yCoor + offsetY, 0xffffff, Chart::Right);

                    t->destroy();
                }
            }
        }
    }
}

XYChart *ZoomScrollTrack2::createXYChart()
{
    // Create an XYChart object of size 650 x 220 pixels, with a white (ffffff) background
    XYChart *c = new XYChart(650, 300, 0xffffff, 0xaaaaaa);

    // Set the plotarea at (55, 25) with width 90 pixels less than chart width, and height 60 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, 25, c->getWidth() - 90, c->getHeight() - 60, c->linearGradientColor(0, 25, 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();

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

    // Add a legend box at (55, 30) using horizontal layout. Use 8pts Arial Bold as font. Set the
    // background and border color to Transparent and use line style legend key.
   // LegendBox *b = c->addLegend(55, 30, false, "arialbd.ttf", 8);
   // b->setBackground(Chart::Transparent);
   // b->setLineStyleKey();

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

    return c;
}
zoomscrolltrack2.h
#ifndef ZOOMSCROLLTRACK2_H
#define ZOOMSCROLLTRACK2_H

#include <QDialog>
#include <QDateEdit>
#include <QScrollBar>
#include <QDateTime>
#include "qchartviewer.h"


const int sampleSize = 10000;


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

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;
    double m_dataSeriesA[sampleSize];
    double m_dataSeriesB[sampleSize];
    double m_dataSeriesC[sampleSize];

    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 trackLineLabel(MultiChart *m, int mouseX);    // Draw Track Cursor
    void updateControls(QChartViewer *viewer);      // Update other controls

    XYChart *createXYChart();

private slots:
    void onHScrollBarChanged(int value);
    void onMouseUsageChanged(int mouseUsage);
    void onViewPortChanged();
    void onMouseMovePlotArea(QMouseEvent *event);
    void onMouseWheelChart(QWheelEvent *event);
};

#endif // ZOOMSCROLLTRACK2_H

  Re: can spread the charts from each other ?
Posted by jill on Apr-11-2020 03:21
Thank you so much for your help!
Instead of creating multiple charts, can I create multiple layers and add them to one chart?
Or put a gap between adding data to the chart? To separate charts lines like:

    layer->setXData(viewPortTimeStamps);
    layer->addDataSet(viewPortDataSeriesA, 0xff0000, "Alpha");
    layer->addSpace;
    layer->addDataSet(viewPortDataSeriesB, 0x00cc00, "Beta");

  Re: can spread the charts from each other ?
Posted by Peter Kwan on Apr-13-2020 16:07
Hi Jill,

Yes, you can create multiple layers and add them to one XYChart. As one XYChart can only have one plot area, so the lines need to be plotted in the same plot area.

If the lines are arrange vertically (one stacked on top of the other), there are two methods:

(a) Modify the y-coordinates so that the two lines do not overlap. For example, suppose  you know the y-coordinates of the line must be from 0 to 10. You can plot the first line from 0 to 10. For the second line, you can add 10 to all the y-coordinates, so the the y-coordinates become 10 to 20. If you plot these two lines they will not overlap and one will be on top of the other.

This method is best if you not need to display the y-coordinates. In the ECG chart you attached in your first post, the y-coordinates are not important and not displayed, so this method works. There is also an example at:

https://www.advsofteng.com/doc/cdcpp.htm#binaryseries.htm

(b) If you need to display the y-axis and the y-coordinates for the lines, you can add multiple y-axes to the chart, like:

https://www.advsofteng.com/doc/cdcpp.htm#multiaxes.htm

In the above example, the 4 y-axes are separated, but you can add 4 y-axes at the same position, so that they overlap and looks like a single y-axis with the labels overlapping. You can then configure the y-axis scale with axis margins (see Axis.setMargin) so that the y-axis are stacked vertically and the labels do not overlap.

As a simple example, if the plot area is 400 pixels high, and you have two y-axis, you can set:

// 200 pixels bottom margin, so the bottom 200 pixels will have no scale
yAxis1->setMargin(0, 200);

// 200 pixels top margin, so the top 200 pixels will have no scale
yAxis->setMargin(200, 0);

With the method above, the two y-axes will not overlap. You can use the same method for more axes too.


If the lines are arrange horizontally side by side, you can modify the x-coordinates of the data series, similar method (a) above.

You can add multiple x-axes like just you can add multiple y-axes. However, in ChartDirector, all data series must use the same x-axis, so the other x-axes is just for human reading. That means you can use method (a) to put the lines in the correct position, and then method (b) to create additional x-axes for human reading.

Regards
Peter Kwan

  Re: can spread the charts from each other ?
Posted by jill on Apr-19-2020 04:04
Attachments:
I choose method (a) and changed the Add data to the chart section, but the charts are not separated.
New Text Document.txt
#include <QApplication>
#include <QPushButton>
#include <QButtonGroup>
#include <QMouseEvent>
#include <math.h>
#include <sstream>
#include <algorithm>
#include "zoomscrolltrack2.h"

using namespace std;


//int main(int argc, char *argv[])
//{
//    QApplication app(argc, argv);
//    app.setStyleSheet("* {font-family:arialbd;font-size:11px}");
//    ZoomScrollTrack2 demo;
//    demo.show();
//    return app.exec();
//}

//
// Because QT uses QDateTime, while ChartDirector uses Chart::chartTime, we need
// utilities to convert from one to another
//

// Convert from QDateTime to chartTime
static double QDateTimeToChartTime(QDateTime q)
{
    QDate d = q.date();
    QTime t = q.time();
    return Chart::chartTime(d.year(), d.month(), d.day(), t.hour(), t.minute(),
                            t.second()) + t.msec() / 1000.0;
}

// Convert from chartTime to QDateTime
static QDateTime ChartTimeToQDateTime(double t)
{
    double ymdhms = floor(t);
    int ms = (int)(floor((t - ymdhms) * 1000));
    int ymd = Chart::getChartYMD(ymdhms);
    int hms = (int)fmod(ymdhms, 86400);
    
    return QDateTime(QDate(ymd / 10000, (ymd % 10000) / 100, ymd % 100),
                     QTime(hms / 3600, (hms % 3600) / 60, hms % 60, ms));
}

ZoomScrollTrack2::ZoomScrollTrack2(QWidget *parent) :
    QDialog(parent)
{
    //
    // Set up the GUI
    //
    
    setFixedSize(732, 308);
    setWindowTitle("Offline Sample");
    
    // The frame on the left side
    frame = new QFrame(this);
    frame->setGeometry(4, 4, 120, 300);
    frame->setFrameShape(QFrame::StyledPanel);
    
    // Pointer push button
    QPushButton *pointerPB = new QPushButton(QIcon(":/new/prefix1/pointer.png"), "Pointer", frame);
    pointerPB->setGeometry(4, 12, 112, 48);
    pointerPB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    pointerPB->setCheckable(true);
    
    // Zoom In push button
    QPushButton *zoomInPB = new QPushButton(QIcon(":/new/prefix1/zoomin.png"), "Zoom In", frame);
    zoomInPB->setGeometry(4, 80, 112, 48);
    zoomInPB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    zoomInPB->setCheckable(true);
    
    // Zoom Out push button
    QPushButton *zoomOutPB = new QPushButton(QIcon(":/new/prefix1/zoomout.png"), "Zoom Out", frame);
    zoomOutPB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    zoomOutPB->setGeometry(4, 140, 112, 48);
    zoomOutPB->setCheckable(true);
    
    
    // Save push button
    saved_PB = new QPushButton(QIcon(":/new/prefix1/save.png"), "?????/???", frame);
    saved_PB->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    saved_PB->setGeometry(4, 190, 112, 48);
    saved_PB->setCheckable(true);
    
    // The Pointer/Zoom In/Zoom Out buttons form a button group
    QButtonGroup *mouseUsage = new QButtonGroup(frame);
    mouseUsage->addButton(pointerPB, Chart::MouseUsageScroll);
    mouseUsage->addButton(zoomInPB, Chart::MouseUsageZoomIn);
    mouseUsage->addButton(zoomOutPB, Chart::MouseUsageZoomOut);
    connect(mouseUsage, SIGNAL(buttonPressed(int)), SLOT(onMouseUsageChanged(int)));
    
    
    bc1 = new QPushButton("", frame);
    bc1->setStyleSheet("QPushButton { text-align:left; padding:5px}");
    bc1->setIcon(QIcon(":/new/prefix1/left_arrow.png"));
    bc1->setIconSize(QSize(90, 90));
    bc1->setGeometry(6, 250, 101, 41);
    
    
    //    // Start Date control
    //    (new QLabel("Start Date", frame))->setGeometry(6, 230, 112, 18);
    //    m_StartDate = new QDateEdit(frame);
    //    m_StartDate->setGeometry(4, 248, 113, 22);
    //    connect(m_StartDate, SIGNAL(dateTimeChanged(QDateTime)), SLOT(onStartDateChanged(QDateTime)));
    
    //    // Duration control
    //    (new QLabel("End Date", frame))->setGeometry(6, 284, 112, 18);
    //    m_EndDate = new QDateEdit(frame);
    //    m_EndDate->setGeometry(4, 302, 113, 22);
    //    connect(m_EndDate, SIGNAL(dateTimeChanged(QDateTime)), SLOT(onEndDateChanged(QDateTime)));
    
    // 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*)));
    connect(m_ChartViewer, SIGNAL(mouseWheel(QWheelEvent*)), SLOT(onMouseWheelChart(QWheelEvent*)));
    
    // Horizontal scroll bar
    m_HScrollBar = new QScrollBar(Qt::Horizontal, this);
    m_HScrollBar->setGeometry(128, 290, 600, 17);
    m_HScrollBar->setStyleSheet("background-color: rgb(161, 194, 231)");
    connect(m_HScrollBar, SIGNAL(valueChanged(int)), SLOT(onHScrollBarChanged(int)));
    
    //
    // Initialize the chart
    //
    m_ranSeries =0;
    m_timeStamps={};
    m_dataSeriesA[sampleSize]={0};
    m_dataSeriesB[sampleSize]={0};
 
    
    // add="/home/pi/file/issuance2.csv";
    //readfile(add);
    // Load the data
    //if(val1.size()>0)
    // 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);
    
    
}

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

//
// Load the data
//
void ZoomScrollTrack2::loadData(QList<double> data1, QList<double> data2, QList<double> data3, QList<double> data4, QList<double> data5, QList<double> data6, QList<double> data7, QList<double> data8)
{
    // In this example, we just use random numbers as data.
    factSize =data1.size();
    m_ranSeries = new RanSeries(data1.size());
    m_timeStamps = m_ranSeries->getDateSeries(data1.size(), Chart::chartTime(2020, 4, 5,13,30,01), 1);
    for(int z=0;z<data1.size();z++ ){
        m_dataSeriesA[z] =100*data1[z];//4*(((data1[z]/100)/2)-0.6);
        //  qDebug()<<m_dataSeriesC[z];
        m_dataSeriesB[z] =data2[z];//4*(((data2[z]/100)/2)-0.6);
      
    }
    
}

//
// Initialize the QChartViewer
//
void ZoomScrollTrack2::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);
}

//
// 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 ZoomScrollTrack2::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 ZoomScrollTrack2::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(min(scrollBarLen / 100, m_HScrollBar->pageStep()));
    m_HScrollBar->setRange(0, scrollBarLen - m_HScrollBar->pageStep());
    m_HScrollBar->setValue((int)(0.5 + viewer->getViewPortLeft() * scrollBarLen));
    
    //    //
    //    // Update the m_StartDate QDataEdit control.
    //    //
    //    m_StartDate->setDateTimeRange(ChartTimeToQDateTime(viewer->getValueAtViewPort("x", 0)),
    //        ChartTimeToQDateTime(viewer->getValueAtViewPort("x", 1)));
    //    m_StartDate->setDateTime(
    //        ChartTimeToQDateTime(viewer->getValueAtViewPort("x", viewer->getViewPortLeft())));
    
    //    //
    //    // Update the m_EndDate QDataEdit control.
    //    //
    //    m_EndDate->setDateTimeRange(ChartTimeToQDateTime(viewer->getValueAtViewPort("x", 0)),
    //        ChartTimeToQDateTime(viewer->getValueAtViewPort("x", 1)));
    //    m_EndDate->setDateTime(ChartTimeToQDateTime(
    //        viewer->getValueAtViewPort("x", viewer->getViewPortLeft() + viewer->getViewPortWidth())));
}

//
// Draw the chart and display it in the given viewer
//
void ZoomScrollTrack2::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 + startIndex, noOfPoints);
    DoubleArray viewPortDataSeriesB = DoubleArray(m_dataSeriesB + 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
    c = new XYChart(600, 300, 0xffffff, 0xaaaaaa);
    QColor bgColor = palette().color(backgroundRole()).rgb();
    c->setRoundedFrame((bgColor.red() << 16) + (bgColor.green() << 8) + bgColor.blue());
    // 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);
    // c->setPlotArea(52, 60, 520, 205, 0xf0f6ff, -1, -1, 0xcccccc, 0xcccccc);
    // 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(" ???? ???", "BTitrBd.ttf", 20);
    
    // Add a legend box at (55, 30) using horizontal layout. Use 8pts Arial Bold as font. Set the
    // background and border color to Transparent and use line style legend key.
    LegendBox *b = c->addLegend(55, 30, false, "arialbd.ttf", 8);
    b->setBackground(Chart::Transparent);
    b->setLineStyleKey();
    
    // 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("mV/ms", "arialbd.ttf", 10);
    
    // Add a title on top of the primary (left) y axis.
    c->yAxis()->setTitle("mV/ms")->setAlignment(Chart::TopLeft2);
    // Set the axis, label and title colors for the primary y axis to red (c00000) to match the
    // first data set
    c->yAxis()->setColors(0xcc0000, 0xcc0000, 0xcc0000);
    
    // Add a title on top of the secondary (right) y axis.
    c->yAxis2()->setTitle("mV/ms")->setAlignment(Chart::TopRight2);
    // Set the axis, label and title colors for the secondary y axis to green (00800000) to match
    // the second data set
    c->yAxis2()->setColors(0x008000, 0x008000, 0x008000);
    
    // Add the third y-axis at 50 pixels to the left of the plot area
    Axis *leftAxis = c->addAxis(Chart::Left, 50);
    // Add a title on top of the third y axis.
    leftAxis->setTitle("Temp\\n(C)")->setAlignment(Chart::TopLeft2);
    // Set the axis, label and title colors for the third y axis to blue (0000cc) to match the third
    // data set
    leftAxis->setColors(0x0000cc, 0x0000cc, 0x0000cc);
    
    ///////////////////////////////////////////////////////////////////////////////////////
    // 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).
    //
    
    ArrayMath shiftedLine0 = ArrayMath(((viewPortDataSeriesA))).add(4.5); //, (int)(sizeof(viewPortDataSeriesA) / sizeof(viewPortDataSeriesA[0]))
    
    // Add step lines using the original and the reversed data
    StepLineLayer *layer0 = c->addStepLineLayer(shiftedLine0, 0x0000ff);
    layer0->setUseYAxis2();
    
    ArrayMath shiftedLine1 = ArrayMath(viewPortDataSeriesB).add(2.5); //, (int)(sizeof(viewPortDataSeriesB) / sizeof(viewPortDataSeriesB[0])
    StepLineLayer *layer1 = c->addStepLineLayer(shiftedLine1, 0x008800);
    layer1->setUseYAxis(leftAxis);
    // 0xcc0000, "Power");
    layer0->setLineWidth(2);
    layer1->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.
    layer0->setFastLineMode();
    layer1->setFastLineMode();
    
    
    layer0->setXData(DoubleArray(viewPortTimeStamps));
    layer1->setXData(DoubleArray(viewPortTimeStamps));
    // 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, "1"); //red
    //    layer->addDataSet(viewPortDataSeriesB, 0x00cc00, "2"); //green
 
    
    ///////////////////////////////////////////////////////////////////////////////////////
    // 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());
    c->xAxis()->setTickDensity(25);
    //
    // 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}");
    c->xAxis()->setLabelFormat("{value|hh:nn:ss}");
    
    
    // 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:ss}");
    
    ///////////////////////////////////////////////////////////////////////////////////////
    // 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()) && viewer->isMouseOnPlotArea())
        trackLineLabel(c, viewer->getPlotAreaMouseX());
    
    delete viewer->getChart();
    viewer->setChart(c);
}

//
// The Pointer, Zoom In or Zoom out button is pressed
//
void ZoomScrollTrack2::onMouseUsageChanged(int mouseUsage)
{
    m_ChartViewer->setMouseUsage(mouseUsage);
}

//
// User selects a start date from the QDateEdit control
//
//void ZoomScrollTrack2::onStartDateChanged(QDateTime date)
//{
//    if (!m_ChartViewer->isInViewPortChangedEvent())
//    {
//        // The updated view port width
//        double vpWidth = m_ChartViewer->getViewPortLeft() + m_ChartViewer->getViewPortWidth() -
//            m_ChartViewer->getViewPortAtValue("x", QDateTimeToChartTime(date));

//        // Make sure the updated view port width is within bounds
//        vpWidth = max(m_ChartViewer->getZoomInWidthLimit(), min(vpWidth,
//            m_ChartViewer->getViewPortLeft() + m_ChartViewer->getViewPortWidth()));

//        // Update view port and trigger a view port changed event to update the chart
//        m_ChartViewer->setViewPortLeft(m_ChartViewer->getViewPortLeft() +
//            m_ChartViewer->getViewPortWidth() - vpWidth);
//        m_ChartViewer->setViewPortWidth(vpWidth);
//        m_ChartViewer->updateViewPort(true, false);
//    }
//}

////
//// User selects an end date from the QDateEdit control
////
//void ZoomScrollTrack2::onEndDateChanged(QDateTime date)
//{
//    if (!m_ChartViewer->isInViewPortChangedEvent())
//    {
//        // The updated view port width
//        double vpWidth = m_ChartViewer->getViewPortAtValue("x", QDateTimeToChartTime(date)) -
//            m_ChartViewer->getViewPortLeft();

//        // Make sure the updated view port width is within bounds
//        vpWidth = max(m_ChartViewer->getZoomInWidthLimit(), min(vpWidth,
//            1 - m_ChartViewer->getViewPortLeft()));

//        // Update view port and trigger a view port changed event to update the chart
//        m_ChartViewer->setViewPortWidth(vpWidth);
//        m_ChartViewer->updateViewPort(true, false);
//    }
//}

////
// User clicks on the the horizontal scroll bar
//
void ZoomScrollTrack2::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);
    }
}

//
// When the mouse enters the chart, we will generate an image map for hot spots and tooltips
// support if it has not already been generated.
//
void ZoomScrollTrack2::onMouseWheelChart(QWheelEvent *event)
{
    // Process the mouse wheel only if the mouse is over the plot area
    if (!m_ChartViewer->isMouseOnPlotArea())
    {
        event->ignore();
        return;
    }
    
    // We zoom in or out by 10% depending on the mouse wheel direction.
    double newVpWidth = m_ChartViewer->getViewPortWidth() * (event->delta() > 0 ? 0.9 : 1 / 0.9);
    double newVpHeight = m_ChartViewer->getViewPortHeight() * (event->delta() > 0 ? 0.9 : 1 / 0.9);
    
    // We do not zoom beyond the zoom width or height limits.
    newVpWidth = max(m_ChartViewer->getZoomInWidthLimit(), min(newVpWidth,
                                                               m_ChartViewer->getZoomOutWidthLimit()));
    newVpHeight = max(m_ChartViewer->getZoomInHeightLimit(), min(newVpWidth,
                                                                 m_ChartViewer->getZoomOutHeightLimit()));
    
    if ((newVpWidth != m_ChartViewer->getViewPortWidth()) ||
            (newVpHeight != m_ChartViewer->getViewPortHeight()))
    {
        // Set the view port position and size so that the point under the mouse remains under
        // the mouse after zooming.
        
        double deltaX = (m_ChartViewer->getPlotAreaMouseX() - m_ChartViewer->getPlotAreaLeft()) *
                (m_ChartViewer->getViewPortWidth() - newVpWidth) / m_ChartViewer->getPlotAreaWidth();
        m_ChartViewer->setViewPortLeft(m_ChartViewer->getViewPortLeft() + deltaX);
        m_ChartViewer->setViewPortWidth(newVpWidth);
        
        double deltaY = (m_ChartViewer->getPlotAreaMouseY() - m_ChartViewer->getPlotAreaTop()) *
                (m_ChartViewer->getViewPortHeight() - newVpHeight) / m_ChartViewer->getPlotAreaHeight();
        m_ChartViewer->setViewPortTop(m_ChartViewer->getViewPortTop() + deltaY);
        m_ChartViewer->setViewPortHeight(newVpHeight);
        
        m_ChartViewer->updateViewPort(true, false);
    }
}

//
// Draw track cursor when mouse is moving over plotarea
//
void ZoomScrollTrack2::onMouseMovePlotArea(QMouseEvent *)
{
    trackLineLabel((XYChart *)m_ChartViewer->getChart(), m_ChartViewer->getPlotAreaMouseX());
    m_ChartViewer->updateDisplay();
    
    // Hide the track cursor when the mouse leaves the plot area
    m_ChartViewer->removeDynamicLayer("mouseLeavePlotArea");
}

//
// Draw track line with data labels
//
void ZoomScrollTrack2::trackLineLabel(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,
             d->dashLineColor(0x000000, 0x0101));
    
    // Draw a label on the x-axis to show the track line position.
    ostringstream xlabel;
    xlabel << "<*font,bgColor=000000*> " << c->xAxis()->getFormattedLabel(xValue, "mmm dd, yyyy")
           << " <*/font*>";
    TTFText *t = d->text(xlabel.str().c_str(), "arialbd.ttf", 8);
    
    // Restrict the x-pixel position of the label to make sure it stays inside the chart image.
    int xLabelPos = max(0, min(xCoor - t->getWidth() / 2, c->getWidth() - t->getWidth()));
    t->draw(xLabelPos, plotArea->getBottomY() + 6, 0xffffff);
    t->destroy();
    
    // Iterate through all layers to draw the data labels
    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);
            const char *dataSetName = dataSet->getDataName();
            
            // Get the color, name and position of the data label
            int color = dataSet->getDataColor();
            int yCoor = c->getYCoor(dataSet->getPosition(xIndex), dataSet->getUseYAxis());
            
            // Draw a track dot with a label next to it for visible data points in the plot area
            if ((yCoor >= plotArea->getTopY()) && (yCoor <= plotArea->getBottomY()) && (color !=
                                                                                        (int)Chart::Transparent) && dataSetName && *dataSetName)
            {
                d->circle(xCoor, yCoor, 4, 4, color, color);
                
                ostringstream label;
                label << "<*font,bgColor=" << hex << color << "*> "
                      << c->formatValue(dataSet->getValue(xIndex), "{value|P4}") << " <*font*>";
                t = d->text(label.str().c_str(), "arialbd.ttf", 8);
                
                // Draw the label on the right side of the dot if the mouse is on the left side the
                // chart, and vice versa. This ensures the label will not go outside the chart image.
                if (xCoor <= (plotArea->getLeftX() + plotArea->getRightX()) / 2)
                    t->draw(xCoor + 5, yCoor, 0xffffff, Chart::Left);
                else
                    t->draw(xCoor - 5, yCoor, 0xffffff, Chart::Right);
                
                t->destroy();
            }
        }
    }
}


  Re: can spread the charts from each other ?
Posted by Peter Kwan on Apr-20-2020 01:51
Hi jill.

Your code uses two different axes for the two lines:

    layer0->setUseYAxis2();

    layer1->setUseYAxis(leftAxis);

If you add values to a line, ChartDirector will just change the axis scale so that the line is at around the center of the chart. That means no matter what values you add, the two lines are always near the center of the chart.

To shift the line relative to another line, the two lines must use the same axis scale, such as:

    layer0->setUseYAxis2();
    layer1->setUseYAxis2();

Also that in your code, you added 2.5 and 4.5 to the two lines. This will make the two lines separate by 2 y-axis units. If your data values are less than 2, then separating the lines by 2 y-units should work.

Hope this can help.

Regards
Peter Kwan

  Re: can spread the charts from each other ?
Posted by jill on Apr-21-2020 03:36
Attachments:
Well I edit it , but still the charts are not separated.



void ZoomScrollTrack2::loadData(QList<double> data1, QList<double> data2)
{
    // In this example, we just use random numbers as data.
    factSize =data1.size();
    m_ranSeries = new RanSeries(data1.size());
    m_timeStamps = m_ranSeries->getDateSeries(data1.size(), Chart::chartTime(2020, 4, 5,13,30,01), 1);
    for(int z=0;z<data1.size();z++ ){
        m_dataSeriesA[z] =100*data1[z];
        m_dataSeriesB[z] =data2[z];

    }

}

///////////////////////////////////////////////////////////////////////////////////////
    // Configure overall chart appearance.
    ///////////////////////////////////////////////////////////////////////////////////////

    // Create an XYChart object of size 650 x 350 pixels, with a white (ffffff) background and grey
    // (aaaaaa) border
    c = new XYChart(600, 300, 0xffffff, 0xaaaaaa);
    QColor bgColor = palette().color(backgroundRole()).rgb();
    c->setRoundedFrame((bgColor.red() << 16) + (bgColor.green() << 8) + bgColor.blue());
    // 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);
    // c->setPlotArea(52, 60, 520, 205, 0xf0f6ff, -1, -1, 0xcccccc, 0xcccccc);
    // 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(" نوار قلب", "BTitrBd.ttf", 20);

    // Add a legend box at (55, 30) using horizontal layout. Use 8pts Arial Bold as font. Set the
    // background and border color to Transparent and use line style legend key.
    LegendBox *b = c->addLegend(55, 30, false, "arialbd.ttf", 8);
    b->setBackground(Chart::Transparent);
    b->setLineStyleKey();

    // 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("mV/ms", "arialbd.ttf", 10);

    // Add a title on top of the primary (left) y axis.
    c->yAxis()->setTitle("mV/ms")->setAlignment(Chart::TopLeft2);
    // Set the axis, label and title colors for the primary y axis to red (c00000) to match the
    // first data set
    c->yAxis()->setColors(0xcc0000, 0xcc0000, 0xcc0000);

    // Add a title on top of the secondary (right) y axis.
    c->yAxis2()->setTitle("mV/ms")->setAlignment(Chart::TopRight2);
    // Set the axis, label and title colors for the secondary y axis to green (00800000) to match
    // the second data set
    c->yAxis2()->setColors(0x008000, 0x008000, 0x008000);

    // Add the third y-axis at 50 pixels to the left of the plot area
    Axis *leftAxis = c->addAxis(Chart::Left, 50);
    // Add a title on top of the third y axis.
    leftAxis->setTitle("Tempn(C)")->setAlignment(Chart::TopLeft2);
    // Set the axis, label and title colors for the third y axis to blue (0000cc) to match the third
    // data set
    leftAxis->setColors(0x0000cc, 0x0000cc, 0x0000cc);

    ///////////////////////////////////////////////////////////////////////////////////////
    // 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).
    //

    ArrayMath shiftedLine0 = ArrayMath(((viewPortDataSeriesA))).add(2); //, (int)(sizeof(viewPortDataSeriesA) / sizeof(viewPortDataSeriesA[0]))

    // Add step lines using the original and the reversed data
    StepLineLayer *layer0 = c->addStepLineLayer(shiftedLine0, 0x0000ff);
    layer0->setUseYAxis2();

    ArrayMath shiftedLine1 = ArrayMath(viewPortDataSeriesB).add(2); //, (int)(sizeof(viewPortDataSeriesB) / sizeof(viewPortDataSeriesB[0])
    StepLineLayer *layer1 = c->addStepLineLayer(shiftedLine1, 0x008800);
    layer1->setUseYAxis2();
    // 0xcc0000, "Power");
    layer0->setLineWidth(2);
    layer1->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.
    layer0->setFastLineMode();
    layer1->setFastLineMode();


    layer0->setXData(DoubleArray(viewPortTimeStamps));
    layer1->setXData(DoubleArray(viewPortTimeStamps));
}
PHOTO-2020-04-20-23-58-00.jpg

  Re: can spread the charts from each other ?
Posted by Peter Kwan on Apr-21-2020 12:58
Hi jill,

There are two issues with the code:

(a) You shift the two lines by the same amount of 2 y-units. One line is shifted up for 2 units. The other line is also shifted up for 2 units. So the two lines still overlap.

(b) Your data seems to be from 100 to -50. But you shift the line for only 2 units. This is too small for your data. I suggest you shift by 200 units.

// Do not shift the first line
ArrayMath shiftedLine0 = ArrayMath(((viewPortDataSeriesA))).add(0);
.......


// Shift the second line down by 200 units
ArrayMath shiftedLine1 = ArrayMath(viewPortDataSeriesB).add(-200);
.......

With the above code, the two lines will be separated by 200 y-axis units.

Regards
Peter Kwan

  Re: can spread the charts from each other ?
Posted by jill on Apr-22-2020 02:22
Thank you , is it possible to hide yAxis() ? I used
c->yAxis()->setColors(Chart::Transparent);
c->yAxis2()->setColors(Chart::Transparent);

But it didn't hide yAxis()

  Re: can spread the charts from each other ?
Posted by Peter Kwan on Apr-22-2020 21:21
Hi jill,

In the code you attached in your previous message, I found the following lines:

c->xAxis()->setColors(Chart::Transparent);
c->yAxis()->setColors(Chart::Transparent);

.......

c->yAxis()->setColors(0xcc0000, 0xcc0000, 0xcc0000);

.......

c->yAxis2()->setColors(0x008000, 0x008000, 0x008000);


So your code sets the axis to transparent, then it sets the axis back to non-transparent. As a result, the axis is non-transparent.

To set the axis to fully transparent, please do not set the axis to non-transparent. Instead, use only:

c->yAxis()->setColors(Chart::Transparent, Chart::Transparent);
c->yAxis2()->setColors(Chart::Transparent, Chart::Transparent);

Regards
Peter Kwan

  Re: can spread the charts from each other ?
Posted by pmontazer on Nov-29-2021 02:42
Attachments:
Hi Peter I followed this question and like Jill I want to add more separate windows but the two windows are only shown to me can you help?
realtimemultithread.cpp
#include <QApplication>
#include <QButtonGroup>
#include <QIcon>
#include <QPushButton>
#include <QComboBox>
#include <QFileDialog>
#include "realtimemultithread.h"
#include "chartdir.h"
#include <math.h>
#include <vector>
#include <sstream>
#include <QScreen>
#include <QDesktopWidget>

using namespace std;

int main(int argc, char *argv[]) {
  QApplication app(argc, argv);
  app.setStyleSheet("* {font-family:arial;font-size:11px}");
  RealTimeMultiThread demo;
  demo.show();
  return app.exec();
}

RealTimeMultiThread::RealTimeMultiThread(QWidget *parent) : QDialog(parent) {
  //
  // Set up the GUI
  //
  QScreen *screen = QGuiApplication::primaryScreen();
  QRect screenGeometry = screen->geometry();
  WindowHeight = screenGeometry.height();
  WindowWidth = screenGeometry.width();
  setFixedSize(WindowWidth * 0.99f, WindowHeight * 0.95f);
  setWindowTitle(
      "Multithreading Real-Time Chart with Zoom/Scroll and Track Line");

  QFrame *frame = new QFrame(this);
  frame->setGeometry(4, 4, 120, WindowHeight * 0.94f);
  frame->setFrameShape(QFrame::StyledPanel);

  // Pointer push button
  QPushButton *pointerPB =
      new QPushButton(QIcon(":/pointer.png"), "Pointer", 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(":/zoomin.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(":/zoomout.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(":/save.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
  QButtonGroup *mouseUsage = new QButtonGroup(frame);
  mouseUsage->addButton(pointerPB, Chart::MouseUsageScroll);
  mouseUsage->addButton(zoomInPB, Chart::MouseUsageZoomIn);
  mouseUsage->addButton(zoomOutPB, Chart::MouseUsageZoomOut);
  connect(mouseUsage, SIGNAL(buttonPressed(int)),
          SLOT(onMouseUsageChanged(int)));

  // Chart Viewer
  m_ChartViewer = new QChartViewer(this);
  m_ChartViewer->setGeometry(128, 4, WindowWidth, WindowHeight);
  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, WindowHeight * 0.92f, WindowWidth * 0.9f, 17);
  // m_HScrollBar->setGeometry(128, WindowWidth * 0.99f, WindowHeight * 0.94f,
  // 17);
  connect(m_HScrollBar, SIGNAL(valueChanged(int)),
          SLOT(onHScrollBarChanged(int)));

  //
  // Initialize member variables
  //
  m_currentIndex = 0;

  // Initially, auto-move the track line to make it follow the data series
  trackLineEndPos = 0;
  trackLineIsAtEnd = true;

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

  // Start the random data generator
  dataSource = new RandomWalk(OnData, this);
  dataSource->start();

  // Set up the chart update timer
  m_ChartUpdateTimer = new QTimer(this);
  connect(m_ChartUpdateTimer, SIGNAL(timeout()), SLOT(onChartUpdateTimer()));

  // The chart update rate is set to 100ms
  m_ChartUpdateTimer->start(100);
}

RealTimeMultiThread::~RealTimeMultiThread() {
  delete m_ChartViewer->getChart();
  delete dataSource;
}

//
// The Pointer, Zoom In or Zoom out button is pressed
//
void RealTimeMultiThread::onMouseUsageChanged(int mouseUsage) {
  m_ChartViewer->setMouseUsage(mouseUsage);
}

//
// The Save button is pressed
//
void RealTimeMultiThread::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());
  }
}

//
// Handles realtime data from RandomWalk. The RandomWalk will call this method
// from its own thread.
// This is a static method.
//
void RealTimeMultiThread::OnData(void *self, double elapsedTime, double series0,
                                 double series1, double series2, double series3,
                                 double series4, double series5, double series6,
                                 double series7) {
  qDebug() << "in onDataaaaaaaaaaaaaa";
  // Copy the data into a structure and store it in the queue.
  DataPacket packet;
  packet.elapsedTime = elapsedTime;
  packet.series0 = series0;
  packet.series1 = series1;
  packet.series2 = series2;
  packet.series3 = series3;
  packet.series4 = series4;
  packet.series5 = series5;
  packet.series6 = series6;
  packet.series7 = series7;
  ((RealTimeMultiThread *)self)->buffer.put(packet);
}

//
// Get data from the queue, update the viewport and update the chart display if
// necessary.
//
void RealTimeMultiThread::onChartUpdateTimer() {
  QChartViewer *viewer = m_ChartViewer;

  // Enables auto scroll if the viewport is showing the latest data before the
  // update
  bool autoScroll =
      (m_currentIndex > 0) &&
      (0.001 +
           viewer->getValueAtViewPort("x", viewer->getViewPortLeft() +
                                               viewer->getViewPortWidth()) >=
       m_timeStamps[m_currentIndex - 1]);

  //
  // Get new data from the queue and append them to the data arrays
  //
  int count;
  DataPacket *packets;
  if ((count = buffer.get(&packets)) <= 0)
    return;

  // if data arrays have insufficient space, we need to remove some old data.
  if (m_currentIndex + count >= sampleSize) {
    // For safety, we check if the queue contains too much data than the entire
    // data arrays. If
    // this is the case, we only use the latest data to completely fill the data
    // arrays.
    if (count > sampleSize) {
      packets += count - sampleSize;
      count = sampleSize;
    }

    // Remove oldest data to leave space for new data. To avoid frequent
    // removal, we ensure at
    // least 5% empty space available after removal.
    int originalIndex = m_currentIndex;
    m_currentIndex = sampleSize * 95 / 100 - 1;
    if (m_currentIndex > sampleSize - count)
      m_currentIndex = sampleSize - count;

    for (int i = 0; i < m_currentIndex; ++i) {
      int srcIndex = i + originalIndex - m_currentIndex;
      m_timeStamps[i] = m_timeStamps[srcIndex];
      m_dataSeriesA[i] = m_dataSeriesA[srcIndex];
      m_dataSeriesB[i] = m_dataSeriesB[srcIndex];
      m_dataSeriesC[i] = m_dataSeriesC[srcIndex];
      m_dataSeriesD[i] = m_dataSeriesD[srcIndex];
      m_dataSeriesE[i] = m_dataSeriesE[srcIndex];
      m_dataSeriesF[i] = m_dataSeriesF[srcIndex];
      m_dataSeriesG[i] = m_dataSeriesG[srcIndex];
      m_dataSeriesH[i] = m_dataSeriesH[srcIndex];
    }
  }

  // Append the data from the queue to the data arrays
  for (int n = 0; n < count; ++n) {
    m_timeStamps[m_currentIndex] = packets[n].elapsedTime;
    m_dataSeriesA[m_currentIndex] = packets[n].series0;
    m_dataSeriesB[m_currentIndex] = packets[n].series1;
    m_dataSeriesC[m_currentIndex] = packets[n].series2;
    m_dataSeriesD[m_currentIndex] = packets[n].series3;
    m_dataSeriesE[m_currentIndex] = packets[n].series4;
    m_dataSeriesF[m_currentIndex] = packets[n].series5;
    m_dataSeriesG[m_currentIndex] = packets[n].series6;
    m_dataSeriesH[m_currentIndex] = packets[n].series7;
    ++m_currentIndex;
  }

  //
  // As we added more data, we may need to update the full range of the
  // viewport.
  //

  double startDate = m_timeStamps[0];
  double endDate = m_timeStamps[m_currentIndex - 1];

  // Use the initialFullRange (which is 60 seconds in this demo) if this is
  // sufficient.
  double duration = endDate - startDate;
  if (duration < initialFullRange)
    endDate = startDate + initialFullRange;

  // Update the new full data range to include the latest data
  bool axisScaleHasChanged = viewer->updateFullRangeH("x", startDate, endDate,
                                                      Chart::KeepVisibleRange);

  if (autoScroll) {
    // Scroll the viewport if necessary to display the latest data
    double viewPortEndPos =
        viewer->getViewPortAtValue("x", m_timeStamps[m_currentIndex - 1]);
    if (viewPortEndPos >
        viewer->getViewPortLeft() + viewer->getViewPortWidth()) {
      viewer->setViewPortLeft(viewPortEndPos - viewer->getViewPortWidth());
      axisScaleHasChanged = true;
    }
  }

  // Set the zoom in limit as a ratio to the full range
  viewer->setZoomInWidthLimit(zoomInLimit /
                              (viewer->getValueAtViewPort("x", 1) -
                               viewer->getValueAtViewPort("x", 0)));

  // Trigger the viewPortChanged event. Updates the chart if the axis scale has
  // changed
  // (scrolling or zooming) or if new data are added to the existing axis scale.
  viewer->updateViewPort(axisScaleHasChanged || (duration < initialFullRange),
                         false);
}

//
// View port changed event
//
void RealTimeMultiThread::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 the chart if necessary
  if (m_ChartViewer->needUpdateChart())
    drawChart(m_ChartViewer);
}

//
// User clicks on the the horizontal scroll bar
//
void RealTimeMultiThread::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);
  }
}

//
// Update controls in the user interface when the view port changed
//
void RealTimeMultiThread::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(
      min(scrollBarLen / 100, m_HScrollBar->pageStep()));
  m_HScrollBar->setRange(0, scrollBarLen - m_HScrollBar->pageStep());
  m_HScrollBar->setValue((int)(0.5 + viewer->getViewPortLeft() * scrollBarLen));
}

//
// Draw chart
//
void RealTimeMultiThread::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());

  // Extract the part of the data arrays that are visible.
  DoubleArray viewPortTimeStamps;
  DoubleArray viewPortDataSeriesA;
  DoubleArray viewPortDataSeriesB;
  DoubleArray viewPortDataSeriesC;
  DoubleArray viewPortDataSeriesD;
  DoubleArray viewPortDataSeriesE;
  DoubleArray viewPortDataSeriesF;
  DoubleArray viewPortDataSeriesG;
  DoubleArray viewPortDataSeriesH;

  if (m_currentIndex > 0) {
    // Get the array indexes that corresponds to the visible start and end dates
    int startIndex = (int)floor(Chart::bSearch(
        DoubleArray(m_timeStamps, m_currentIndex), viewPortStartDate));
    int endIndex = (int)ceil(Chart::bSearch(
        DoubleArray(m_timeStamps, m_currentIndex), viewPortEndDate));
    int noOfPoints = endIndex - startIndex + 1;

    // Extract the visible data
    viewPortTimeStamps = DoubleArray(m_timeStamps + startIndex, noOfPoints);
    viewPortDataSeriesA = DoubleArray(m_dataSeriesA + startIndex, noOfPoints);
    viewPortDataSeriesB = DoubleArray(m_dataSeriesB + startIndex, noOfPoints);
    viewPortDataSeriesC = DoubleArray(m_dataSeriesC + startIndex, noOfPoints);
    viewPortDataSeriesD = DoubleArray(m_dataSeriesD + startIndex, noOfPoints);
    viewPortDataSeriesE = DoubleArray(m_dataSeriesE + startIndex, noOfPoints);
    viewPortDataSeriesF = DoubleArray(m_dataSeriesF + startIndex, noOfPoints);
    viewPortDataSeriesG = DoubleArray(m_dataSeriesG + startIndex, noOfPoints);
    viewPortDataSeriesH = DoubleArray(m_dataSeriesH + startIndex, noOfPoints);
    // Keep track of the latest available data at chart plotting time
    trackLineEndPos = m_timeStamps[m_currentIndex - 1];
  }

  //
  // At this stage, we have extracted the visible data. We can use those data to
  // plot the chart.
  //

  //================================================================================
  // Configure overall chart appearance.
  //================================================================================
  channel0 = createXYChart();
  channel1 = createXYChart();
  channel2 = createXYChart();
  channel3 = createXYChart();
  channel4 = createXYChart();
  channel5 = createXYChart();
  channel6 = createXYChart();
  channel7 = createXYChart();
  //================================================================================
  // 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).
  //

  ArrayMath shiftedLine0 = ArrayMath(((viewPortDataSeriesA)));
  // Add step lines using the original and the reversed data
  StepLineLayer *layer0 = channel0->addStepLineLayer(shiftedLine0, 0xff3333);
  // layer0->setUseYAxis2();

  ArrayMath shiftedLine1 = ArrayMath(viewPortDataSeriesB);
  StepLineLayer *layer1 =
      channel1->addStepLineLayer(shiftedLine1, 0x00cc00); // green

  ArrayMath shiftedLine2 = ArrayMath(viewPortDataSeriesC);
  StepLineLayer *layer2 =
      channel2->addStepLineLayer(shiftedLine2, 0x0000ff); // blue

  ArrayMath shiftedLine3 = ArrayMath(viewPortDataSeriesD);
  StepLineLayer *layer3 =
      channel3->addStepLineLayer(shiftedLine3, 0xaa00ff); // violet

  ArrayMath shiftedLine4 = ArrayMath(viewPortDataSeriesE);
  StepLineLayer *layer4 =
      channel4->addStepLineLayer(shiftedLine4, 0xff8000); // darkOrange

  ArrayMath shiftedLine5 = ArrayMath(viewPortDataSeriesF);
  StepLineLayer *layer5 =
      channel5->addStepLineLayer(shiftedLine5, 0xffff00); // yellow

  ArrayMath shiftedLine6 = ArrayMath(viewPortDataSeriesG);
  StepLineLayer *layer6 =
      channel6->addStepLineLayer(shiftedLine6, 0x696969); // gray

  ArrayMath shiftedLine7 = ArrayMath(viewPortDataSeriesH);
  StepLineLayer *layer7 =
      channel7->addStepLineLayer(shiftedLine7, 0xa52a2a); // brown

  layer0->setLineWidth(2);
  layer1->setLineWidth(2);
  layer2->setLineWidth(2);
  layer3->setLineWidth(2);
  layer4->setLineWidth(2);
  layer5->setLineWidth(2);
  layer6->setLineWidth(2);
  layer7->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.
  layer0->setFastLineMode();
  layer1->setFastLineMode();
  layer2->setFastLineMode();
  layer3->setFastLineMode();
  layer4->setFastLineMode();
  layer5->setFastLineMode();
  layer6->setFastLineMode();
  layer7->setFastLineMode();

  layer0->setXData(DoubleArray(viewPortTimeStamps));
  layer1->setXData(DoubleArray(viewPortTimeStamps));
  layer2->setXData(DoubleArray(viewPortTimeStamps));
  layer3->setXData(DoubleArray(viewPortTimeStamps));
  layer4->setXData(DoubleArray(viewPortTimeStamps));
  layer5->setXData(DoubleArray(viewPortTimeStamps));
  layer6->setXData(DoubleArray(viewPortTimeStamps));
  layer7->setXData(DoubleArray(viewPortTimeStamps));

  channel0->yAxis()->setColors(Chart::Transparent, Chart::Transparent);
  channel0->yAxis2()->setColors(Chart::Transparent, Chart::Transparent);

  channel1->yAxis()->setColors(Chart::Transparent, Chart::Transparent);
  channel1->yAxis2()->setColors(Chart::Transparent, Chart::Transparent);

  channel2->yAxis()->setColors(Chart::Transparent, Chart::Transparent);
  channel2->yAxis2()->setColors(Chart::Transparent, Chart::Transparent);

  channel3->yAxis()->setColors(Chart::Transparent, Chart::Transparent);
  channel3->yAxis2()->setColors(Chart::Transparent, Chart::Transparent);

  channel4->yAxis()->setColors(Chart::Transparent, Chart::Transparent);
  channel4->yAxis2()->setColors(Chart::Transparent, Chart::Transparent);

  channel5->yAxis()->setColors(Chart::Transparent, Chart::Transparent);
  channel5->yAxis2()->setColors(Chart::Transparent, Chart::Transparent);

  channel6->yAxis()->setColors(Chart::Transparent, Chart::Transparent);
  channel6->yAxis2()->setColors(Chart::Transparent, Chart::Transparent);

  channel7->yAxis()->setColors(Chart::Transparent, Chart::Transparent);
  channel7->yAxis2()->setColors(Chart::Transparent, Chart::Transparent);

  //================================================================================
  // Configure axis scale and labelling channel0
  //================================================================================
  // qDebug("Configure axis scale and labelling");
  // Set the x-axis as a date/time axis with the scale according to the view
  // port x range.
  if (m_currentIndex > 0)
    channel0->xAxis()->setDateScale(viewPortStartDate, viewPortEndDate);

  // For the automatic axis labels, set the minimum spacing to 75/30 pixels for
  // the x/y axis.
  channel0->xAxis()->setTickDensity(75);
  channel0->yAxis()->setTickDensity(30);

  // We use "hh:nn:ss" as the axis label format.
  channel0->xAxis()->setLabelFormat("{value|hh:nn:ss}");

  // We make sure the tick increment must be at least 1 second.
  channel0->xAxis()->setMinTickInc(1);

  // Set the auto-scale margin to 0.05, and the zero affinity to 0.6
  channel0->yAxis()->setAutoScale(0.05, 0.05, 0.6);

  //================================================================================
  // Configure axis scale and labelling channel1
  //================================================================================

  // Set the x-axis as a date/time axis with the scale according to the view
  // port x range.
  if (m_currentIndex > 0)
    channel1->xAxis()->setDateScale(viewPortStartDate, viewPortEndDate);

  // For the automatic axis labels, set the minimum spacing to 75/30 pixels for
  // the x/y axis.
  channel1->xAxis()->setTickDensity(75);
  channel1->yAxis()->setTickDensity(30);

  // We use "hh:nn:ss" as the axis label format.
  channel1->xAxis()->setLabelFormat("{value|hh:nn:ss}");

  // We make sure the tick increment must be at least 1 second.
  channel1->xAxis()->setMinTickInc(1);

  // Set the auto-scale margin to 0.05, and the zero affinity to 0.6
  channel1->yAxis()->setAutoScale(0.05, 0.05, 0.6);

  //================================================================================
  // Configure axis scale and labelling channel2
  //================================================================================

  // Set the x-axis as a date/time axis with the scale according to the view
  // port x range.
  if (m_currentIndex > 0)
    channel2->xAxis()->setDateScale(viewPortStartDate, viewPortEndDate);

  // For the automatic axis labels, set the minimum spacing to 75/30 pixels for
  // the x/y axis.
  channel2->xAxis()->setTickDensity(75);
  channel2->yAxis()->setTickDensity(30);

  // We use "hh:nn:ss" as the axis label format.
  channel2->xAxis()->setLabelFormat("{value|hh:nn:ss}");

  // We make sure the tick increment must be at least 1 second.
  channel2->xAxis()->setMinTickInc(1);

  // Set the auto-scale margin to 0.05, and the zero affinity to 0.6
  channel2->yAxis()->setAutoScale(0.05, 0.05, 0.6);

  //================================================================================
  // Configure axis scale and labelling channel3
  //================================================================================

  // Set the x-axis as a date/time axis with the scale according to the view
  // port x range.
  if (m_currentIndex > 0)
    channel3->xAxis()->setDateScale(viewPortStartDate, viewPortEndDate);

  // For the automatic axis labels, set the minimum spacing to 75/30 pixels for
  // the x/y axis.
  channel3->xAxis()->setTickDensity(75);
  channel3->yAxis()->setTickDensity(30);

  // We use "hh:nn:ss" as the axis label format.
  channel3->xAxis()->setLabelFormat("{value|hh:nn:ss}");

  // We make sure the tick increment must be at least 1 second.
  channel3->xAxis()->setMinTickInc(1);

  // Set the auto-scale margin to 0.05, and the zero affinity to 0.6
  channel3->yAxis()->setAutoScale(0.05, 0.05, 0.6);

  //================================================================================
  // Configure axis scale and labelling channel4
  //================================================================================

  // Set the x-axis as a date/time axis with the scale according to the view
  // port x range.
  if (m_currentIndex > 0)
    channel4->xAxis()->setDateScale(viewPortStartDate, viewPortEndDate);

  // For the automatic axis labels, set the minimum spacing to 75/30 pixels for
  // the x/y axis.
  channel4->xAxis()->setTickDensity(75);
  channel4->yAxis()->setTickDensity(30);

  // We use "hh:nn:ss" as the axis label format.
  channel4->xAxis()->setLabelFormat("{value|hh:nn:ss}");

  // We make sure the tick increment must be at least 1 second.
  channel4->xAxis()->setMinTickInc(1);

  // Set the auto-scale margin to 0.05, and the zero affinity to 0.6
  channel4->yAxis()->setAutoScale(0.05, 0.05, 0.6);

  //================================================================================
  // Configure axis scale and labelling channel5
  //================================================================================

  // Set the x-axis as a date/time axis with the scale according to the view
  // port x range.
  if (m_currentIndex > 0)
    channel5->xAxis()->setDateScale(viewPortStartDate, viewPortEndDate);

  // For the automatic axis labels, set the minimum spacing to 75/30 pixels for
  // the x/y axis.
  channel5->xAxis()->setTickDensity(75);
  channel5->yAxis()->setTickDensity(30);

  // We use "hh:nn:ss" as the axis label format.
  channel5->xAxis()->setLabelFormat("{value|hh:nn:ss}");

  // We make sure the tick increment must be at least 1 second.
  channel5->xAxis()->setMinTickInc(1);

  // Set the auto-scale margin to 0.05, and the zero affinity to 0.6
  channel5->yAxis()->setAutoScale(0.05, 0.05, 0.6);

  //================================================================================
  // Configure axis scale and labelling channel6
  //================================================================================

  // Set the x-axis as a date/time axis with the scale according to the view
  // port x range.
  if (m_currentIndex > 0)
    channel6->xAxis()->setDateScale(viewPortStartDate, viewPortEndDate);

  // For the automatic axis labels, set the minimum spacing to 75/30 pixels for
  // the x/y axis.
  channel6->xAxis()->setTickDensity(75);
  channel6->yAxis()->setTickDensity(30);

  // We use "hh:nn:ss" as the axis label format.
  channel6->xAxis()->setLabelFormat("{value|hh:nn:ss}");

  // We make sure the tick increment must be at least 1 second.
  channel6->xAxis()->setMinTickInc(1);

  // Set the auto-scale margin to 0.05, and the zero affinity to 0.6
  channel6->yAxis()->setAutoScale(0.05, 0.05, 0.6);

  //================================================================================
  // Configure axis scale and labelling channel7
  //================================================================================

  // Set the x-axis as a date/time axis with the scale according to the view
  // port x range.
  if (m_currentIndex > 0)
    channel7->xAxis()->setDateScale(viewPortStartDate, viewPortEndDate);

  // For the automatic axis labels, set the minimum spacing to 75/30 pixels for
  // the x/y axis.
  channel7->xAxis()->setTickDensity(75);
  channel7->yAxis()->setTickDensity(30);

  // We use "hh:nn:ss" as the axis label format.
  channel7->xAxis()->setLabelFormat("{value|hh:nn:ss}");

  // We make sure the tick increment must be at least 1 second.
  channel7->xAxis()->setMinTickInc(1);

  // Set the auto-scale margin to 0.05, and the zero affinity to 0.6
  channel7->yAxis()->setAutoScale(0.05, 0.05, 0.6);

  //================================================================================
  // Output the chart
  //================================================================================
  // Create a MultiChart object of size Width x Height pixels.
  MultiChart *m =
      new MultiChart(channel0->getWidth() + channel1->getWidth(),
                     channel0->getHeight() + channel2->getHeight() +
                         channel4->getHeight() + channel6->getHeight());
  m->addChart(0, 0, channel0);
  m->addChart(channel0->getWidth(), 0, channel1);
  m->addChart(channel0->getWidth(), channel0->getHeight(), channel2);
  //  m->addChart(channel0->getWidth() + channel2->getWidth(), 0, channel3);
  //  m->addChart(0, 0, channel4);
  //  m->addChart(channel0->getWidth() + channel2->getWidth() +
  //                  channel4->getWidth(),
  //             0, channel5);
  //  m->addChart(0, 0, channel6);
  //  m->addChart(channel6->getWidth(), 0, channel7);
  m->setMainChart(m);

  // 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())
  //        trackLineLabel(c, trackLineIsAtEnd ? c->getWidth() :
  //        viewer->getPlotAreaMouseX());

  //    // Set the chart image to the QChartViewer
  //    delete viewer->getChart();
  //    viewer->setChart(c);

  if (!viewer->isInMouseMoveEvent()) {
    trackLineLabel(m, (0 == viewer->getChart())
                          ? channel0->getPlotArea()->getRightX()
                          : viewer->getPlotAreaMouseX());
    trackLineLabel(m, (0 == viewer->getChart())
                          ? channel1->getPlotArea()->getRightX()
                          : viewer->getPlotAreaMouseX());
    trackLineLabel(m, (0 == viewer->getChart())
                          ? channel2->getPlotArea()->getRightX()
                          : viewer->getPlotAreaMouseX());
    trackLineLabel(m, (0 == viewer->getChart())
                          ? channel3->getPlotArea()->getRightX()
                          : viewer->getPlotAreaMouseX());
    trackLineLabel(m, (0 == viewer->getChart())
                          ? channel4->getPlotArea()->getRightX()
                          : viewer->getPlotAreaMouseX());
    trackLineLabel(m, (0 == viewer->getChart())
                          ? channel5->getPlotArea()->getRightX()
                          : viewer->getPlotAreaMouseX());
    trackLineLabel(m, (0 == viewer->getChart())
                          ? channel6->getPlotArea()->getRightX()
                          : viewer->getPlotAreaMouseX());
    trackLineLabel(m, (0 == viewer->getChart())
                          ? channel7->getPlotArea()->getRightX()
                          : viewer->getPlotAreaMouseX());
  }
  // Clean up old chart
  MultiChart *previousChart = (MultiChart *)viewer->getChart();
  if (previousChart != 0) {
    for (int i = 0; i < previousChart->getChartCount(); ++i)
      delete previousChart->getChart(i);
    delete previousChart;
  }
  viewer->setChart(m);
}

//
// Draw track cursor when mouse is moving over plotarea
//
void RealTimeMultiThread::onMouseMovePlotArea(QMouseEvent *) {
  // qDebug("----------------------on mouse move-------------");
  double trackLinePos = trackLineLabel((MultiChart *)m_ChartViewer->getChart(),
                                       m_ChartViewer->getPlotAreaMouseX());
  trackLineIsAtEnd = (m_currentIndex <= 0) || (trackLinePos == trackLineEndPos);

  m_ChartViewer->updateDisplay();
}

//
// Draw the track line with data point labels
//
double RealTimeMultiThread::trackLineLabel(MultiChart *m, int mouseX) {

  // qDebug("----------------------on track line-------------");
  // Clear the current dynamic layer and get the DrawArea object to draw on it.
  DrawArea *d = m->initDynamicLayer();
  for (int i = 0; i < m->getChartCount(); ++i) {
    XYChart *c = (XYChart *)m->getChart(i);
    int offsetX = c->getAbsOffsetX();
    int offsetY = c->getAbsOffsetY();
    // 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);
    if (xCoor < plotArea->getLeftX())
      return xValue;

    // Draw a vertical track line at the x-position
    d->vline(plotArea->getTopY() + offsetY, plotArea->getBottomY() + offsetY,
             xCoor + offsetX, 0x888888);

    // Draw a label on the x-axis to show the track line position.
    ostringstream xlabel;
    xlabel << "<*font,bgColor=000000*> "
           << c->xAxis()->getFormattedLabel(xValue + 0.00499, "hh:nn:ss.ff")
           << " <*/font*>";
    TTFText *t = d->text(xlabel.str().c_str(), "arialbd.ttf", 10);

    // Restrict the x-pixel position of the label to make sure it stays inside
    // the chart image.
    int xLabelPos =
        max(0, min(xCoor - t->getWidth() / 2, c->getWidth() - t->getWidth()));
    t->draw(xLabelPos, plotArea->getBottomY() + 6, 0xffffff);
    t->destroy();

    // Iterate through all layers to draw the data labels
    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);
        const char *dataSetName = dataSet->getDataName();

        // Get the color, name and position of the data label
        int color = dataSet->getDataColor();
        int yCoor =
            c->getYCoor(dataSet->getPosition(xIndex), dataSet->getUseYAxis());

        // Draw a track dot with a label next to it for visible data points in
        // the plot area
        if ((yCoor >= plotArea->getTopY()) &&
            (yCoor <= plotArea->getBottomY()) &&
            (color != Chart::Transparent) && dataSetName && *dataSetName) {
          d->circle(xCoor + offsetX, yCoor + +offsetY, 4, 4, color, color);

          ostringstream label;
          label << "<*font,bgColor=" << hex << color << "*> "
                << c->formatValue(dataSet->getValue(xIndex), "{value|P4}")
                << " <*font*>";
          t = d->text(label.str().c_str(), "arialbd.ttf", 10);

          // Draw the label on the right side of the dot if the mouse is on the
          // left side the
          // chart, and vice versa. This ensures the label will not go outside
          // the chart image.
          if (xCoor <= (plotArea->getLeftX() + plotArea->getRightX()) / 2)
            t->draw(xCoor + 6 + offsetX, yCoor + offsetY, 0xffffff,
                    Chart::Left);
          else
            t->draw(xCoor - 6 + offsetX, yCoor + offsetY, 0xffffff,
                    Chart::Right);

          t->destroy();
        }
      }
    }

    return xValue;
  }
}
void RealTimeMultiThread::stopFun() { dataSource->stop(); }

XYChart *RealTimeMultiThread::createXYChart() {
  // qDebug("on XY chart1");
  // Create an XYChart object of size 640 x 350 pixels
  XYChart *c = new XYChart(WindowWidth / 2 * 0.9f, WindowHeight * 0.93f);

  // Set the plotarea at (55, 50) with width 80 pixels less than chart width,
  // and height 80 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, 50, c->getWidth() - 85, c->getHeight() - 650,
      c->linearGradientColor(0, 50, 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 legend box at (55, 25) using horizontal layout. Use 10pt Arial Bold
  // as font. Set the
  // background and border color to transparent and use line style legend key.
  LegendBox *b = c->addLegend(55, 25, false, "arialbd.ttf", 10);
  b->setBackground(Chart::Transparent);
  b->setLineStyleKey();

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

  // Set the y-axis tick length to 0 to disable the tick and put the labels
  // closer to the axis.
  c->yAxis()->setTickLength(0);
  // Add axis title using 12pt Arial Bold Italic font
  // c->yAxis()->setTitle("mV/ms", "arialbd.ttf", 12);
  return c;
}

  Re: can spread the charts from each other ?
Posted by Peter Kwan on Nov-30-2021 01:04
Hi Pmontazer,

In your code, the height of each chart is equal to the window height x 0.93. So the charts that stacked below the first chart must fall outside the window, because the first chart already occupy almost the entire window height. May be you can modify the height to something like WindowHeight / 4 and adjust the other code accordingly (such as how you set the plot area height).

Regards
Peter Kwan