Esempio n. 1
3
void WindPlot::updatePlot()
{
    clearPlottables();
    clearItems();

    // Return now if plot empty
    if (mMainWindow->dataSize() == 0) return;

    double lower = mMainWindow->rangeLower();
    double upper = mMainWindow->rangeUpper();

    QVector< double > t, x, y;

    double xMin, xMax;
    double yMin, yMax;

    int start = mMainWindow->findIndexBelowT(lower) + 1;
    int end   = mMainWindow->findIndexAboveT(upper);

    bool first = true;
    for (int i = start; i < end; ++i)
    {
        const DataPoint &dp = mMainWindow->dataPoint(i);

        t.append(dp.t);

        if (mMainWindow->units() == PlotValue::Metric)
        {
            x.append(dp.velE * MPS_TO_KMH);
            y.append(dp.velN * MPS_TO_KMH);
        }
        else
        {
            x.append(dp.velE * MPS_TO_MPH);
            y.append(dp.velN * MPS_TO_MPH);
        }

        if (first)
        {
            xMin = xMax = x.back();
            yMin = yMax = y.back();

            first = false;
        }
        else
        {
            if (x.back() < xMin) xMin = x.back();
            if (x.back() > xMax) xMax = x.back();

            if (y.back() < yMin) yMin = y.back();
            if (y.back() > yMax) yMax = y.back();
        }
    }

    QCPCurve *curve = new QCPCurve(xAxis, yAxis);
    curve->setData(t, x, y);
    curve->setPen(QPen(Qt::lightGray, mMainWindow->lineThickness()));

    setViewRange(xMin, xMax, yMin, yMax);

    if (mMainWindow->markActive())
    {
        const DataPoint &dpEnd = mMainWindow->interpolateDataT(mMainWindow->markEnd());

        t.clear();
        x.clear();
        y.clear();

        QVector< double > xMark, yMark;

        if (mMainWindow->units() == PlotValue::Metric)
        {
            xMark.append(dpEnd.velE * MPS_TO_KMH);
            yMark.append(dpEnd.velN * MPS_TO_KMH);
        }
        else
        {
            xMark.append(dpEnd.velE * MPS_TO_MPH);
            yMark.append(dpEnd.velN * MPS_TO_MPH);
        }

        QCPGraph *graph = addGraph();
        graph->setData(xMark, yMark);
        graph->setPen(QPen(Qt::black, mMainWindow->lineThickness()));
        graph->setLineStyle(QCPGraph::lsNone);
        graph->setScatterStyle(QCPScatterStyle::ssDisc);
    }

    updateWind(start, end);

    QVector< double > xMark, yMark;

    if (mMainWindow->units() == PlotValue::Metric)
    {
        xMark.append(mWindE * MPS_TO_KMH);
        yMark.append(mWindN * MPS_TO_KMH);
    }
    else
    {
        xMark.append(mWindE * MPS_TO_MPH);
        yMark.append(mWindN * MPS_TO_MPH);
    }

    QCPGraph *graph = addGraph();
    graph->setData(xMark, yMark);
    graph->setPen(QPen(Qt::red, mMainWindow->lineThickness()));
    graph->setLineStyle(QCPGraph::lsNone);
    graph->setScatterStyle(QCPScatterStyle::ssDisc);

    const double x0 = mWindE;
    const double y0 = mWindN;
    const double r = mVelAircraft;

    QVector< double > tCircle, xCircle, yCircle;

    for (int i = 0; i <= 100; ++i)
    {
        tCircle.append(i);

        const double x = x0 + r * cos((double) i / 100 * 2 * M_PI);
        const double y = y0 + r * sin((double) i / 100 * 2 * M_PI);

        if (mMainWindow->units() == PlotValue::Metric)
        {
            xCircle.append(x * MPS_TO_KMH);
            yCircle.append(y * MPS_TO_KMH);
        }
        else
        {
            xCircle.append(x * MPS_TO_MPH);
            yCircle.append(y * MPS_TO_MPH);
        }
    }

    curve = new QCPCurve(xAxis, yAxis);
    curve->setData(tCircle, xCircle, yCircle);
    curve->setPen(QPen(Qt::red, mMainWindow->lineThickness()));

    // Add label to show best fit
    QCPItemText *textLabel = new QCPItemText(this);

    const double factor = (mMainWindow->units() == PlotValue::Metric) ? MPS_TO_KMH : MPS_TO_MPH;
    const QString units = (mMainWindow->units() == PlotValue::Metric) ? "km/h" : "mph";

    double direction = atan2(-mWindE, -mWindN) / M_PI * 180.0;
    if (direction < 0) direction += 360.0;

    QPainter painter(this);
    double mmPerPix = (double) painter.device()->widthMM() / painter.device()->width();

    double xRatioPerPix = 1.0 / axisRect()->width();
    double xRatioPerMM = xRatioPerPix / mmPerPix;

    double yRatioPerPix = 1.0 / axisRect()->height();
    double yRatioPerMM = yRatioPerPix / mmPerPix;

    textLabel->setPositionAlignment(Qt::AlignBottom|Qt::AlignRight);
    textLabel->setTextAlignment(Qt::AlignRight);
    textLabel->position->setType(QCPItemPosition::ptAxisRectRatio);
    textLabel->position->setCoords(1 - 5 * xRatioPerMM,
                                   1 - 5 * yRatioPerMM);
    textLabel->setText(
                QString("Wind speed = %1 %2\nWind direction = %3 deg\nAircraft speed = %4 %5")
                    .arg(sqrt(mWindE * mWindE + mWindN * mWindN) * factor)
                    .arg(units)
                    .arg(direction)
                    .arg(mVelAircraft * factor)
                    .arg(units));

    replot();
}
Esempio n. 2
0
void AdvancedQCustomPlot::wheelEvent(QWheelEvent *event)
{
    if (event->modifiers() == Qt::ShiftModifier){
        axisRect()->setRangeZoom(Qt::Vertical);
    } else if (event->modifiers() == Qt::ControlModifier){
        axisRect()->setRangeZoom(Qt::Horizontal);
    } else {
        axisRect()->setRangeZoom(Qt::Vertical | Qt::Horizontal);
    }
    isZoomed_ = true;
    QCustomPlot::wheelEvent(event);
}
Esempio n. 3
0
QCustomPlotExt::QCustomPlotExt(QWidget *parent) :
    QCustomPlot(parent),
    marker(NULL)
{
    clearExt();
    setInteractions(QCP::iRangeDrag | QCP::iRangeZoom);
    axisRect()->setRangeDrag(Qt::Horizontal | Qt::Vertical);
    axisRect()->setRangeZoom(Qt::Horizontal | Qt::Vertical);
    connect(this, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(mouseWheel(QWheelEvent*)));
    connect(xAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(xAxisRangeChanged(QCPRange)));
    connect(yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(yAxisRangeChanged(QCPRange)));
}
Esempio n. 4
0
Graph::Graph(QWidget *parent)
: QWidget(parent)
, mPlotArea_(0)
, mHBar_(0)
, mVBar_(0)
, mTitle_("Title")
, mGridX_(0)
, mGridY_(0)
, mDragging_(false)
, mMarker_(0)
, mLabel_(0)
{
    ui.setupUi(this);
    mPaintBuffer_ = QPixmap(size());
    mPlotArea_ = new QFrame(this);
    mPlotArea_->setGeometry(axisRect().adjusted(3, 0, 0, -3));
    mPlotArea_->setFrameStyle(QFrame::Sunken);
    mPlotArea_->setFrameShape(QFrame::Panel);
    mPlotArea_->setLineWidth(2);

    GraphLayer* axisLayer = new GraphLayer();
    GraphAxis* axisX = new GraphAxis(axisRect(), GraphAxis::AT_BOTTOM);
    axisX->setLabel("sec");
    GraphAxis* axisY = new GraphAxis(axisRect(), GraphAxis::AT_LEFT);
    axisY->setLabel("pressure");
    axisX->setLayer(axisLayer);
    axisY->setLayer(axisLayer);
    mAxisXs_.append(axisX);
    mAxisYs_.append(axisY);
    mLayers_.append(axisLayer);

    GraphLayer* gridLayer = new GraphLayer();
    mGridX_ = new GraphGrid(mAxisXs_);
    mGridY_ = new GraphGrid(mAxisYs_);
    mGridX_->setLayer(gridLayer);
    mGridY_->setLayer(gridLayer);
    mLayers_.append(gridLayer);

    GraphLayer* markerLayer = new GraphLayer();
    mMarker_ = new GraphMarker(axisRect());
    mMarker_->setLayer(markerLayer);
    mLayers_.append(markerLayer);

    mHBar_ = new GraphBar(this, *mAxisXs_.first());
    connect(mHBar_, SIGNAL(valueChanged(int)), this, SLOT(hBarValueChanged(int)));

    mVBar_ = new GraphBar(this, *mAxisYs_.first());
    connect(mVBar_, SIGNAL(valueChanged(int)), this, SLOT(vBarValueChanged(int)));

    mLabel_ = new GraphLabel(this, axisRect());
}
Esempio n. 5
0
void WindPlot::setViewRange(
        double xMin,
        double xMax,
        double yMin,
        double yMax)
{
    QPainter painter(this);

    double xMMperPix = (double) painter.device()->widthMM() / painter.device()->width();
    double yMMperPix = (double) painter.device()->heightMM() / painter.device()->height();

    QRect rect = axisRect()->rect();

    double xSpan = (xMax - xMin) * 1.2;
    double ySpan = (yMax - yMin) * 1.2;

    double xScale = xSpan / rect.width() / xMMperPix;
    double yScale = ySpan / rect.height() / yMMperPix;

    double scale = qMax(xScale, yScale);

    double xMid = (xMin + xMax) / 2;
    double yMid = (yMin + yMax) / 2;

    xMin = xMid - rect.width() * xMMperPix * scale / 2;
    xMax = xMid + rect.width() * xMMperPix * scale / 2;

    yMin = yMid - rect.height() * yMMperPix * scale / 2;
    yMax = yMid + rect.height() * yMMperPix * scale / 2;

    xAxis->setRange(xMin, xMax);
    yAxis->setRange(yMin, yMax);
}
Esempio n. 6
0
void TgChart::mouseMoveEvent(QMouseEvent * event)
{
    if (mRubberBand->isVisible())
    {
        mRubberBand->setGeometry(QRect(mOrigin, QPoint(event->pos().x(),axisRect()->bottomRight().y())).normalized());
    }
    QCustomPlot::mouseMoveEvent(event);
}
Esempio n. 7
0
void QCustomPlotExt::mouseWheel(QWheelEvent *event)
{
    if (event->modifiers()& Qt::ShiftModifier)
    {
        axisRect()->setRangeZoom(Qt::Vertical);
    }
    else if (event->delta() < 0 && xAxisRangeOrg == xAxis->range())
    {
        axisRect()->setRangeZoom(0);
    }
    else
    {
        if (event->modifiers()& Qt::ControlModifier)
            axisRect()->setRangeZoom(Qt::Horizontal);
        else
            axisRect()->setRangeZoom(Qt::Horizontal | Qt::Vertical);
    }
}
Esempio n. 8
0
void LiftDragPlot::mousePressEvent(
        QMouseEvent *event)
{
    if (axisRect()->rect().contains(event->pos()))
    {
        mBeginPos = event->pos();
    }

    QCustomPlot::mousePressEvent(event);
}
DrawInteraction::DrawInteraction(QWidget *parent) :
    QCustomPlot(parent)
{
    resize(600,400);

    statusBar = new QStatusBar(this);

    srand(QDateTime::currentDateTime().toTime_t());

    setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectAxes |
                                      QCP::iSelectLegend | QCP::iSelectPlottables);
    xAxis->setRange(-8, 8);
    yAxis->setRange(-5, 5);
    axisRect()->setupFullAxesBox();

    plotLayout()->insertRow(0);
    plotLayout()->addElement(0, 0, new QCPPlotTitle(this, "Interaction Example"));

    xAxis->setLabel("x Axis");
    yAxis->setLabel("y Axis");
    legend->setVisible(true);
    QFont legendFont = font();
    legendFont.setPointSize(10);
    legend->setFont(legendFont);
    legend->setSelectedFont(legendFont);
    legend->setSelectableParts(QCPLegend::spItems); // legend box shall not be selectable, only legend items

    addRandomGraph();
    addRandomGraph();
    addRandomGraph();
    addRandomGraph();

    // connect slot that ties some axis selections together (especially opposite axes):
    connect(this, SIGNAL(selectionChangedByUser()), this, SLOT(selectionChanged()));
    // connect slots that takes care that when an axis is selected, only that direction can be dragged and zoomed:
    connect(this, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(mousePress()));
    connect(this, SIGNAL(mouseWheel(QWheelEvent*)), this, SLOT(mouseWheel()));

    // make bottom and left axes transfer their ranges to top and right axes:
    connect(xAxis, SIGNAL(rangeChanged(QCPRange)), xAxis2, SLOT(setRange(QCPRange)));
    connect(yAxis, SIGNAL(rangeChanged(QCPRange)), yAxis2, SLOT(setRange(QCPRange)));

    // connect some interaction slots:
    connect(this, SIGNAL(titleDoubleClick(QMouseEvent*,QCPPlotTitle*)), this, SLOT(titleDoubleClick(QMouseEvent*,QCPPlotTitle*)));
    connect(this, SIGNAL(axisDoubleClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)), this, SLOT(axisLabelDoubleClick(QCPAxis*,QCPAxis::SelectablePart)));
    connect(this, SIGNAL(legendDoubleClick(QCPLegend*,QCPAbstractLegendItem*,QMouseEvent*)), this, SLOT(legendDoubleClick(QCPLegend*,QCPAbstractLegendItem*)));

    // connect slot that shows a message in the status bar when a graph is clicked:
    connect(this, SIGNAL(plottableClick(QCPAbstractPlottable*,QMouseEvent*)), this, SLOT(graphClicked(QCPAbstractPlottable*)));

    // setup policy and connect slot for context menu popup:
    setContextMenuPolicy(Qt::CustomContextMenu);
    connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenuRequest(QPoint)));
}
Esempio n. 10
0
void QCustomPlotExt::mousePress(QMouseEvent* event)
{
    if (event->button() == Qt::LeftButton)
    {
        if (marker)
        {
            if(axisRect()->rect().contains(event->pos()))
                setMarkerValue(xAxis->pixelToCoord(event->x()));
        }
    }
}
Esempio n. 11
0
void TgChart::mousePressEvent(QMouseEvent * event)
{
    if (mZoomMode)
    {
        if ( event->button() == Qt::MiddleButton)
        {
            mOrigin = QPoint(event->pos().x(),axisRect()->topLeft().y());
            mRubberBand->setGeometry(QRect(mOrigin,
                                           QSize()));
            mRubberBand->show();
        }
    }
    QCustomPlot::mousePressEvent(event);
}
Esempio n. 12
0
void LiftDragPlot::updatePlot()
{
    clearPlottables();
    clearItems();

    xAxis->setLabel(tr("Drag Coefficient"));
    yAxis->setLabel(tr("Lift Coefficient"));

    double lower = mMainWindow->rangeLower();
    double upper = mMainWindow->rangeUpper();

    QVector< double > t, x, y;

    double xMin, xMax;
    double yMin, yMax;

    int start = mMainWindow->findIndexBelowT(lower) + 1;
    int end   = mMainWindow->findIndexAboveT(upper);

    double s10 = 0, s01 = 0, s20 = 0, s11 = 0;
    double s21 = 0, s30 = 0, s40 = 0;

    bool first = true;
    for (int i = start; i < end; ++i)
    {
        const DataPoint &dp = mMainWindow->dataPoint(i);

        t.append(dp.t);
        x.append(dp.drag);
        y.append(dp.lift);

        if (first)
        {
            xMax = x.back();
            yMax = y.back();

            first = false;
        }
        else
        {
            if (x.back() > xMax) xMax = x.back();
            if (y.back() > yMax) yMax = y.back();
        }

        s10 += dp.lift;
        s01 += dp.drag;
        s20 += dp.lift * dp.lift;
        s11 += dp.lift * dp.drag;
        s21 += dp.lift * dp.lift * dp.drag;
        s30 += dp.lift * dp.lift * dp.lift;
        s40 += dp.lift * dp.lift * dp.lift * dp.lift;
    }

    QCPCurve *curve = new QCPCurve(xAxis, yAxis);
    curve->setData(t, x, y);
    curve->setPen(QPen(Qt::lightGray, mMainWindow->lineThickness()));
    curve->setLineStyle(QCPCurve::lsNone);
    curve->setScatterStyle(QCPScatterStyle::ssDisc);
    addPlottable(curve);

    setViewRange(xMax, yMax);

    // Update plot limits
    xMin = xAxis->range().lower;
    xMax = xAxis->range().upper;

    yMin = yAxis->range().lower;
    yMax = yAxis->range().upper;

    if (mMainWindow->markActive())
    {
        int i1 = mMainWindow->findIndexBelowT(mMainWindow->markEnd()) + 1;
        int i2 = mMainWindow->findIndexAboveT(mMainWindow->markEnd()) - 1;

        const DataPoint &dp1 = mMainWindow->dataPoint(i1);
        const DataPoint &dp2 = mMainWindow->dataPoint(i2);

        QVector< double > xMark, yMark;

        if (mMainWindow->markEnd() - dp1.t < dp2.t - mMainWindow->markEnd())
        {
            xMark.append(dp1.drag);
            yMark.append(dp1.lift);
        }
        else
        {
            xMark.append(dp2.drag);
            yMark.append(dp2.lift);
        }

        QCPGraph *graph = addGraph();
        graph->setData(xMark, yMark);
        graph->setPen(QPen(Qt::black, mMainWindow->lineThickness()));
        graph->setLineStyle(QCPGraph::lsNone);
        graph->setScatterStyle(QCPScatterStyle::ssDisc);
    }

    // x = ay^2 + c
    const double m = 1 / mMainWindow->maxLD();
    const double c = mMainWindow->minDrag();
    const double a = m * m / (4 * c);

    // Draw tangent line
    const double yt = sqrt(c / a);

    if (a != 0)
    {
        x.clear();
        y.clear();

        x << m * yMin << m * yMax;
        y << yMin << yMax;

        QCPGraph *graph = addGraph();
        graph->setData(x, y);
        graph->setPen(QPen(Qt::blue, mMainWindow->lineThickness(), Qt::DashLine));
    }

    // Draw minimum drag
    x.clear();
    y.clear();

    x << mMainWindow->minDrag() << mMainWindow->minDrag();
    y << yMin << yMax;

    QCPGraph *graph = addGraph();
    graph->setData(x, y);
    graph->setPen(QPen(Qt::blue, mMainWindow->lineThickness(), Qt::DashLine));

    // Draw maximum lift
    x.clear();
    y.clear();

    x << xMin << xMax;
    y << mMainWindow->maxLift() << mMainWindow->maxLift();

    graph = addGraph();
    graph->setData(x, y);
    graph->setPen(QPen(Qt::blue, mMainWindow->lineThickness(), Qt::DashLine));

    // Draw saved curve
    t.clear();
    x.clear();
    y.clear();

    for (int i = 0; i <= 100; ++i)
    {
        const double yy = yMin + (yMax - yMin) / 100 * i;

        t.append(yy);
        x.append(a * yy * yy + c);
        y.append(yy);
    }

    curve = new QCPCurve(xAxis, yAxis);
    curve->setData(t, x, y);
    curve->setPen(QPen(Qt::red, mMainWindow->lineThickness()));
    addPlottable(curve);

    // Draw dot at maximum L/D
    x.clear();
    y.clear();

    x << a * yt * yt + c;
    y << yt;

    graph = addGraph();
    graph->setData(x, y);
    graph->setPen(QPen(Qt::red, mMainWindow->lineThickness()));
    graph->setLineStyle(QCPGraph::lsNone);
    graph->setScatterStyle(QCPScatterStyle::ssDisc);

    // Add label to show equation for saved curve
    QCPItemText *textLabel = new QCPItemText(this);
    addItem(textLabel);

    QPainter painter(this);
    double mmPerPix = (double) painter.device()->widthMM() / painter.device()->width();

    double xRatioPerPix = 1.0 / axisRect()->width();
    double xRatioPerMM = xRatioPerPix / mmPerPix;

    double yRatioPerPix = 1.0 / axisRect()->height();
    double yRatioPerMM = yRatioPerPix / mmPerPix;

    textLabel->setPositionAlignment(Qt::AlignBottom|Qt::AlignRight);
    textLabel->setTextAlignment(Qt::AlignRight);
    textLabel->position->setType(QCPItemPosition::ptAxisRectRatio);
    textLabel->position->setCoords(1 - 5 * xRatioPerMM,
                                   1 - 5 * yRatioPerMM);
    textLabel->setText(
                QString("Minimum drag = %1\nMaximum lift = %2\nMaximum L/D = %3")
                    .arg(fabs(c))
                    .arg(mMainWindow->maxLift())
                    .arg(1/ m));

    replot();
}
DrawFinancialChart::DrawFinancialChart(QWidget *parent) :
    QCustomPlot(parent)
{
    resize(600,400);

    legend->setVisible(true);

    // generate two sets of random walk data (one for candlestick and one for ohlc chart):
    int n = 500;
    QVector<double> time(n), value1(n), value2(n);
    QDateTime start = QDateTime(QDate(2014, 6, 11));
    start.setTimeSpec(Qt::UTC);
    double startTime = start.toTime_t();
    double binSize = 3600*24; // bin data in 1 day intervals
    time[0] = startTime;
    value1[0] = 60;
    value2[0] = 20;
    qsrand(9);
    for (int i=1; i<n; ++i)
    {
      time[i] = startTime + 3600*i;
      value1[i] = value1[i-1] + (qrand()/(double)RAND_MAX-0.5)*10;
      value2[i] = value2[i-1] + (qrand()/(double)RAND_MAX-0.5)*3;
    }

    // create candlestick chart:
    QCPFinancial *candlesticks = new QCPFinancial(xAxis, yAxis);
    addPlottable(candlesticks);
    QCPFinancialDataMap data1 = QCPFinancial::timeSeriesToOhlc(time, value1, binSize, startTime);
    candlesticks->setName("Candlestick");
    candlesticks->setChartStyle(QCPFinancial::csCandlestick);
    candlesticks->setData(&data1, true);
    candlesticks->setWidth(binSize*0.9);
    candlesticks->setTwoColored(true);
    candlesticks->setBrushPositive(QColor(245, 245, 245));
    candlesticks->setBrushNegative(QColor(0, 0, 0));
    candlesticks->setPenPositive(QPen(QColor(0, 0, 0)));
    candlesticks->setPenNegative(QPen(QColor(0, 0, 0)));

    // create ohlc chart:
    QCPFinancial *ohlc = new QCPFinancial(xAxis, yAxis);
    addPlottable(ohlc);
    QCPFinancialDataMap data2 = QCPFinancial::timeSeriesToOhlc(time, value2, binSize/3.0, startTime); // divide binSize by 3 just to make the ohlc bars a bit denser
    ohlc->setName("OHLC");
    ohlc->setChartStyle(QCPFinancial::csOhlc);
    ohlc->setData(&data2, true);
    ohlc->setWidth(binSize*0.2);
    ohlc->setTwoColored(true);

    // create bottom axis rect for volume bar chart:
    QCPAxisRect *volumeAxisRect = new QCPAxisRect(this);
    plotLayout()->addElement(1, 0, volumeAxisRect);
    volumeAxisRect->setMaximumSize(QSize(QWIDGETSIZE_MAX, 100));
    volumeAxisRect->axis(QCPAxis::atBottom)->setLayer("axes");
    volumeAxisRect->axis(QCPAxis::atBottom)->grid()->setLayer("grid");
    // bring bottom and main axis rect closer together:
    plotLayout()->setRowSpacing(0);
    volumeAxisRect->setAutoMargins(QCP::msLeft|QCP::msRight|QCP::msBottom);
    volumeAxisRect->setMargins(QMargins(0, 0, 0, 0));
    // create two bar plottables, for positive (green) and negative (red) volume bars:
    QCPBars *volumePos = new QCPBars(volumeAxisRect->axis(QCPAxis::atBottom), volumeAxisRect->axis(QCPAxis::atLeft));
    QCPBars *volumeNeg = new QCPBars(volumeAxisRect->axis(QCPAxis::atBottom), volumeAxisRect->axis(QCPAxis::atLeft));
    for (int i=0; i<n/5; ++i)
    {
      int v = qrand()%20000+qrand()%20000+qrand()%20000-10000*3;
      (v < 0 ? volumeNeg : volumePos)->addData(startTime+3600*5.0*i, qAbs(v)); // add data to either volumeNeg or volumePos, depending on sign of v
    }
    setAutoAddPlottableToLegend(false);
    addPlottable(volumePos);
    addPlottable(volumeNeg);
    volumePos->setWidth(3600*4);
    volumePos->setPen(Qt::NoPen);
    volumePos->setBrush(QColor(100, 180, 110));
    volumeNeg->setWidth(3600*4);
    volumeNeg->setPen(Qt::NoPen);
    volumeNeg->setBrush(QColor(180, 90, 90));

    // interconnect x axis ranges of main and bottom axis rects:
    connect(xAxis, SIGNAL(rangeChanged(QCPRange)), volumeAxisRect->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange)));
    connect(volumeAxisRect->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), xAxis, SLOT(setRange(QCPRange)));
    // configure axes of both main and bottom axis rect:
    volumeAxisRect->axis(QCPAxis::atBottom)->setAutoTickStep(false);
    volumeAxisRect->axis(QCPAxis::atBottom)->setTickStep(3600*24*4); // 4 day tickstep
    volumeAxisRect->axis(QCPAxis::atBottom)->setTickLabelType(QCPAxis::ltDateTime);
    volumeAxisRect->axis(QCPAxis::atBottom)->setDateTimeSpec(Qt::UTC);
    volumeAxisRect->axis(QCPAxis::atBottom)->setDateTimeFormat("dd. MMM");
    volumeAxisRect->axis(QCPAxis::atBottom)->setTickLabelRotation(15);
    volumeAxisRect->axis(QCPAxis::atLeft)->setAutoTickCount(3);
    xAxis->setBasePen(Qt::NoPen);
    xAxis->setTickLabels(false);
    xAxis->setTicks(false); // only want vertical grid in main axis rect, so hide xAxis backbone, ticks, and labels
    xAxis->setAutoTickStep(false);
    xAxis->setTickStep(3600*24*4); // 4 day tickstep
    rescaleAxes();
    xAxis->scaleRange(1.025, xAxis->range().center());
    yAxis->scaleRange(1.1, yAxis->range().center());

    // make axis rects' left side line up:
    QCPMarginGroup *group = new QCPMarginGroup(this);
    axisRect()->setMarginGroup(QCP::msLeft|QCP::msRight, group);
    volumeAxisRect->setMarginGroup(QCP::msLeft|QCP::msRight, group);
}