void QCustomPlotExt::addMarker(const QPen &pen) { const QVector<double> markerValues = QVector<double>() << 0 << 0; marker = new QCPCurve(xAxis, yAxis); marker->setPen(pen); marker->setData(markerValues, markerValues); addPlottable(marker); connect(this, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(mousePress(QMouseEvent*))); markerValue = 0.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(); }
void ccHistogramWindow::refresh() { // set ranges appropriate to show data double minVal = m_minVal; double maxVal = m_maxVal; if (m_sfInteractionMode && m_associatedSF) { double minSat = m_associatedSF->saturationRange().min(); double maxSat = m_associatedSF->saturationRange().max(); minVal = std::min(minVal,minSat); maxVal = std::max(maxVal,maxSat); } xAxis->setRange(minVal, maxVal); yAxis->setRange(0, m_maxHistoVal); if (!m_titleStr.isEmpty()) { // add title layout element if (!m_titlePlot) { //add a row for the title plotLayout()->insertRow(0); } else { //remove previous title plotLayout()->remove(m_titlePlot); m_titlePlot = 0; } m_titlePlot = new QCPPlotTitle(this, QString("%0 [%1 classes]").arg(m_titleStr).arg(m_histoValues.size())); //title font m_renderingFont.setPointSize(ccGui::Parameters().defaultFontSize); m_titlePlot->setFont(m_renderingFont); plotLayout()->addElement(0, 0, m_titlePlot); } //clear previous display m_histogram = 0; m_vertBar = 0; m_overlayCurve = 0; m_areaLeft = 0; m_areaRight = 0; m_arrowLeft = 0; m_arrowRight = 0; this->clearGraphs(); this->clearPlottables(); if (m_histoValues.empty()) return; //default color scale to be used for display ccColorScale::Shared colorScale = (m_colorScale ? m_colorScale : ccColorScalesManager::GetDefaultScale()); //histogram int histoSize = static_cast<int>(m_histoValues.size()); double totalSum = 0; double partialSum = 0; if (histoSize > 0) { m_histogram = new QCPColoredBars(xAxis, yAxis); addPlottable(m_histogram); // now we can modify properties of myBars: m_histogram->setWidth((m_maxVal - m_minVal) / histoSize); m_histogram->setAntialiasedFill(false); QVector<double> keyData(histoSize); QVector<double> valueData(histoSize); HISTOGRAM_COLOR_SCHEME colorScheme = m_colorScheme; switch(colorScheme) { case USE_SOLID_COLOR: m_histogram->setBrush(QBrush(m_solidColor,Qt::SolidPattern)); m_histogram->setPen(QPen(m_solidColor)); break; case USE_CUSTOM_COLOR_SCALE: //nothing to do break; case USE_SF_SCALE: if (m_associatedSF && m_associatedSF->getColorScale()) { //we use the SF's color scale colorScale = m_associatedSF->getColorScale(); } else { //we'll use the default one... assert(false); colorScheme = USE_CUSTOM_COLOR_SCALE; } break; default: assert(false); colorScheme = USE_CUSTOM_COLOR_SCALE; break; } QVector<QColor> colors; if (colorScheme != USE_SOLID_COLOR) { colors.resize(histoSize); } for (int i=0; i<histoSize; ++i) { //we take the 'normalized' value at the middle of the class double normVal = (static_cast<double>(i)+0.5) / histoSize; totalSum += m_histoValues[i]; if (normVal < m_verticalIndicatorPositionPercent) partialSum += m_histoValues[i]; keyData[i] = m_minVal + normVal * (m_maxVal - m_minVal); valueData[i] = m_histoValues[i]; //import color for the current bin if (colorScheme != USE_SOLID_COLOR) { const ColorCompType* col = 0; if (colorScheme == USE_SF_SCALE) { //equivalent SF value assert(m_associatedSF); col = m_associatedSF->getColor(static_cast<ScalarType>(keyData[i])); } else if (colorScheme == USE_CUSTOM_COLOR_SCALE) { //use default gradient assert(colorScale); col = colorScale->getColorByRelativePos(normVal); } if (!col) //hidden values may have no associated color! col = ccColor::lightGrey.rgba; colors[i] = QColor(col[0],col[1],col[2]); } } if (!colors.isEmpty()) m_histogram->setData(keyData, valueData, colors); else m_histogram->setData(keyData, valueData); } //overlay curve? int curveSize = static_cast<int>(m_curveValues.size()); if (curveSize > 1) { QVector<double> x(curveSize), y(curveSize); double step = (m_maxVal - m_minVal) / (curveSize-1); for (int i=0; i<curveSize; ++i) { x[i] = m_minVal + (static_cast<double>(i)/*+0.5*/) * step; y[i] = m_curveValues[i]; } // create graph and assign data to it: m_overlayCurve = addGraph(); m_overlayCurve->setData(x, y); m_overlayCurve->setName("OverlayCurve"); //set pen color const ccColor::Rgba& col = ccColor::darkGrey; QPen pen(QColor(col.r,col.g,col.b)); m_overlayCurve->setPen(pen); //set width updateOverlayCurveWidth(rect().width(),rect().height()); } //sf interaction mode if (m_sfInteractionMode && m_associatedSF) { const ccScalarField::Range& dispRange = m_associatedSF->displayRange(); m_areaLeft = new QCPHiddenArea(true,xAxis, yAxis); m_areaLeft->setRange(dispRange.min(),dispRange.max()); m_areaLeft->setCurrentVal(dispRange.start()); addPlottable(m_areaLeft); m_areaRight = new QCPHiddenArea(false,xAxis, yAxis); m_areaRight->setRange(dispRange.min(),dispRange.max()); m_areaRight->setCurrentVal(dispRange.stop()); addPlottable(m_areaRight); const ccScalarField::Range& satRange = m_associatedSF->saturationRange(); m_arrowLeft = new QCPArrow(xAxis, yAxis); m_arrowLeft->setRange(satRange.min(),satRange.max()); m_arrowLeft->setCurrentVal(satRange.start()); if (colorScale) { const ColorCompType* col = colorScale->getColorByRelativePos(m_associatedSF->symmetricalScale() ? 0.5 : 0,m_associatedSF->getColorRampSteps()); if (col) m_arrowLeft->setColor(col[0],col[1],col[2]); } addPlottable(m_arrowLeft); m_arrowRight = new QCPArrow(xAxis, yAxis); m_arrowRight->setRange(satRange.min(),satRange.max()); m_arrowRight->setCurrentVal(satRange.stop()); if (colorScale) { const ColorCompType* col = colorScale->getColorByRelativePos(1.0,m_associatedSF->getColorRampSteps()); if (col) m_arrowRight->setColor(col[0],col[1],col[2]); } addPlottable(m_arrowRight); } else if (m_drawVerticalIndicator) //vertical hint { m_vertBar = new QCPBarsWithText(xAxis, yAxis); addPlottable(m_vertBar); // now we can modify properties of vertBar m_vertBar->setName("VertLine"); m_vertBar->setWidth(0/*(m_maxVal - m_minVal) / histoSize*/); m_vertBar->setBrush(QBrush(Qt::red)); m_vertBar->setPen(QPen(Qt::red)); m_vertBar->setAntialiasedFill(false); QVector<double> keyData(1); QVector<double> valueData(1); //horizontal position keyData[0] = m_minVal + (m_maxVal-m_minVal) * m_verticalIndicatorPositionPercent; valueData[0] = m_maxHistoVal; m_vertBar->setData(keyData,valueData); //precision (same as color scale) int precision = static_cast<int>(ccGui::Parameters().displayedNumPrecision); unsigned bin = static_cast<unsigned>(m_verticalIndicatorPositionPercent * m_histoValues.size()); QString valueStr = QString("bin %0").arg(bin); m_vertBar->setText(valueStr); valueStr = QString("< %0 %").arg(100.0*static_cast<double>(partialSum)/static_cast<double>(totalSum),0,'f',3); m_vertBar->appendText(valueStr); valueStr = QString("val = %0").arg(m_minVal+(m_maxVal-m_minVal)*m_verticalIndicatorPositionPercent,0,'f',precision); m_vertBar->appendText(valueStr); m_vertBar->setTextAlignment(m_verticalIndicatorPositionPercent > 0.5); } //rescaleAxes(); // redraw 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); }