/*************************************************************************** ** ** ** QCustomPlot, an easy to use, modern plotting widget for Qt ** ** Copyright (C) 2011-2021 Emanuel Eichhammer ** ** ** ** This program is free software: you can redistribute it and/or modify ** ** it under the terms of the GNU General Public License as published by ** ** the Free Software Foundation, either version 3 of the License, or ** ** (at your option) any later version. ** ** ** ** This program is distributed in the hope that it will be useful, ** ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** ** GNU General Public License for more details. ** ** ** ** You should have received a copy of the GNU General Public License ** ** along with this program. If not, see http://www.gnu.org/licenses/. ** ** ** **************************************************************************** ** Author: Emanuel Eichhammer ** ** Website/Contact: http://www.qcustomplot.com/ ** ** Date: 29.03.21 ** ** Version: 2.1.0 ** ****************************************************************************/ /************************************************************************************************************ ** ** ** This is the example code for QCustomPlot. ** ** ** ** It demonstrates basic and some advanced capabilities of the widget. The interesting code is inside ** ** the "setup(...)Demo" functions of MainWindow. ** ** ** ** In order to see a demo in action, call the respective "setup(...)Demo" function inside the ** ** MainWindow constructor. Alternatively you may call setupDemo(i) where i is the index of the demo ** ** you want (for those, see MainWindow constructor comments). All other functions here are merely a ** ** way to easily create screenshots of all demos for the website. I.e. a timer is set to successively ** ** setup all the demos and make a screenshot of the window area and save it in the ./screenshots ** ** directory. ** ** ** *************************************************************************************************************/ #include "mainwindow.h" #include "ui_mainwindow.h" #include #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) # include #endif #include #include #include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); setGeometry(400, 250, 542, 390); //setupDemo(0); setupDemo(20); //setupPlayground(ui->customPlot); // 0: setupQuadraticDemo(ui->customPlot); // 1: setupSimpleDemo(ui->customPlot); // 2: setupSincScatterDemo(ui->customPlot); // 3: setupScatterStyleDemo(ui->customPlot); // 4: setupScatterPixmapDemo(ui->customPlot); // 5: setupLineStyleDemo(ui->customPlot); // 6: setupDateDemo(ui->customPlot); // 7: setupTextureBrushDemo(ui->customPlot); // 8: setupMultiAxisDemo(ui->customPlot); // 9: setupLogarithmicDemo(ui->customPlot); // 10: setupRealtimeDataDemo(ui->customPlot); // 11: setupParametricCurveDemo(ui->customPlot); // 12: setupBarChartDemo(ui->customPlot); // 13: setupStatisticalDemo(ui->customPlot); // 14: setupSimpleItemDemo(ui->customPlot); // 15: setupItemDemo(ui->customPlot); // 16: setupStyledDemo(ui->customPlot); // 17: setupAdvancedAxesDemo(ui->customPlot); // 18: setupColorMapDemo(ui->customPlot); // 19: setupFinancialDemo(ui->customPlot); // 20: setupPolarPlotDemo(ui->customPlot); // for making screenshots of the current demo or all demos (for website screenshots): //QTimer::singleShot(1500, this, SLOT(allScreenShots())); //QTimer::singleShot(4000, this, SLOT(screenShot())); } void MainWindow::setupDemo(int demoIndex) { switch (demoIndex) { case 0: setupQuadraticDemo(ui->customPlot); break; case 1: setupSimpleDemo(ui->customPlot); break; case 2: setupSincScatterDemo(ui->customPlot); break; case 3: setupScatterStyleDemo(ui->customPlot); break; case 4: setupScatterPixmapDemo(ui->customPlot); break; case 5: setupLineStyleDemo(ui->customPlot); break; case 6: setupDateDemo(ui->customPlot); break; case 7: setupTextureBrushDemo(ui->customPlot); break; case 8: setupMultiAxisDemo(ui->customPlot); break; case 9: setupLogarithmicDemo(ui->customPlot); break; case 10: setupRealtimeDataDemo(ui->customPlot); break; case 11: setupParametricCurveDemo(ui->customPlot); break; case 12: setupBarChartDemo(ui->customPlot); break; case 13: setupStatisticalDemo(ui->customPlot); break; case 14: setupSimpleItemDemo(ui->customPlot); break; case 15: setupItemDemo(ui->customPlot); break; case 16: setupStyledDemo(ui->customPlot); break; case 17: setupAdvancedAxesDemo(ui->customPlot); break; case 18: setupColorMapDemo(ui->customPlot); break; case 19: setupFinancialDemo(ui->customPlot); break; case 20: setupPolarPlotDemo(ui->customPlot); break; } setWindowTitle("QCustomPlot: "+demoName); statusBar()->clearMessage(); currentDemoIndex = demoIndex; ui->customPlot->replot(); } void MainWindow::setupQuadraticDemo(QCustomPlot *customPlot) { demoName = "Quadratic Demo"; // generate some data: QVector x(101), y(101); // initialize with entries 0..100 for (int i=0; i<101; ++i) { x[i] = i/50.0 - 1; // x goes from -1 to 1 y[i] = x[i]*x[i]; // let's plot a quadratic function } // create graph and assign data to it: customPlot->addGraph(); customPlot->graph(0)->setData(x, y); // give the axes some labels: customPlot->xAxis->setLabel("x"); customPlot->yAxis->setLabel("y"); // set axes ranges, so we see all data: customPlot->xAxis->setRange(-1, 1); customPlot->yAxis->setRange(0, 1); } void MainWindow::setupSimpleDemo(QCustomPlot *customPlot) { demoName = "Simple Demo"; // add two new graphs and set their look: customPlot->addGraph(); customPlot->graph(0)->setPen(QPen(Qt::blue)); // line color blue for first graph customPlot->graph(0)->setBrush(QBrush(QColor(0, 0, 255, 20))); // first graph will be filled with translucent blue customPlot->addGraph(); customPlot->graph(1)->setPen(QPen(Qt::red)); // line color red for second graph // generate some points of data (y0 for first, y1 for second graph): QVector x(251), y0(251), y1(251); for (int i=0; i<251; ++i) { x[i] = i; y0[i] = qExp(-i/150.0)*qCos(i/10.0); // exponentially decaying cosine y1[i] = qExp(-i/150.0); // exponential envelope } // configure right and top axis to show ticks but no labels: // (see QCPAxisRect::setupFullAxesBox for a quicker method to do this) customPlot->xAxis2->setVisible(true); customPlot->xAxis2->setTickLabels(false); customPlot->yAxis2->setVisible(true); customPlot->yAxis2->setTickLabels(false); // make left and bottom axes always transfer their ranges to right and top axes: connect(customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->xAxis2, SLOT(setRange(QCPRange))); connect(customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->yAxis2, SLOT(setRange(QCPRange))); // pass data points to graphs: customPlot->graph(0)->setData(x, y0); customPlot->graph(1)->setData(x, y1); // let the ranges scale themselves so graph 0 fits perfectly in the visible area: customPlot->graph(0)->rescaleAxes(); // same thing for graph 1, but only enlarge ranges (in case graph 1 is smaller than graph 0): customPlot->graph(1)->rescaleAxes(true); // Note: we could have also just called customPlot->rescaleAxes(); instead // Allow user to drag axis ranges with mouse, zoom with mouse wheel and select graphs by clicking: customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); } void MainWindow::setupSincScatterDemo(QCustomPlot *customPlot) { demoName = "Sinc Scatter Demo"; customPlot->legend->setVisible(true); customPlot->legend->setFont(QFont("Helvetica",9)); // set locale to english, so we get english decimal separator: customPlot->setLocale(QLocale(QLocale::English, QLocale::UnitedKingdom)); // add confidence band graphs: customPlot->addGraph(); QPen pen; pen.setStyle(Qt::DotLine); pen.setWidth(1); pen.setColor(QColor(180,180,180)); customPlot->graph(0)->setName("Confidence Band 68%"); customPlot->graph(0)->setPen(pen); customPlot->graph(0)->setBrush(QBrush(QColor(255,50,30,20))); customPlot->addGraph(); customPlot->legend->removeItem(customPlot->legend->itemCount()-1); // don't show two confidence band graphs in legend customPlot->graph(1)->setPen(pen); customPlot->graph(0)->setChannelFillGraph(customPlot->graph(1)); // add theory curve graph: customPlot->addGraph(); pen.setStyle(Qt::DashLine); pen.setWidth(2); pen.setColor(Qt::red); customPlot->graph(2)->setPen(pen); customPlot->graph(2)->setName("Theory Curve"); // add data point graph: customPlot->addGraph(); customPlot->graph(3)->setPen(QPen(Qt::blue)); customPlot->graph(3)->setName("Measurement"); customPlot->graph(3)->setLineStyle(QCPGraph::lsNone); customPlot->graph(3)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCross, 4)); // add error bars: QCPErrorBars *errorBars = new QCPErrorBars(customPlot->xAxis, customPlot->yAxis); errorBars->removeFromLegend(); errorBars->setAntialiased(false); errorBars->setDataPlottable(customPlot->graph(3)); errorBars->setPen(QPen(QColor(180,180,180))); // generate ideal sinc curve data and some randomly perturbed data for scatter plot: QVector x0(250), y0(250); QVector yConfUpper(250), yConfLower(250); for (int i=0; i<250; ++i) { x0[i] = (i/249.0-0.5)*30+0.01; // by adding a small offset we make sure not do divide by zero in next code line y0[i] = qSin(x0[i])/x0[i]; // sinc function yConfUpper[i] = y0[i]+0.15; yConfLower[i] = y0[i]-0.15; x0[i] *= 1000; } QVector x1(50), y1(50), y1err(50); for (int i=0; i<50; ++i) { // generate a gaussian distributed random number: double tmp1 = rand()/(double)RAND_MAX; double tmp2 = rand()/(double)RAND_MAX; double r = qSqrt(-2*qLn(tmp1))*qCos(2*M_PI*tmp2); // box-muller transform for gaussian distribution // set y1 to value of y0 plus a random gaussian pertubation: x1[i] = (i/50.0-0.5)*30+0.25; y1[i] = qSin(x1[i])/x1[i]+r*0.15; x1[i] *= 1000; y1err[i] = 0.15; } // pass data to graphs and let QCustomPlot determine the axes ranges so the whole thing is visible: customPlot->graph(0)->setData(x0, yConfUpper); customPlot->graph(1)->setData(x0, yConfLower); customPlot->graph(2)->setData(x0, y0); customPlot->graph(3)->setData(x1, y1); errorBars->setData(y1err); customPlot->graph(2)->rescaleAxes(); customPlot->graph(3)->rescaleAxes(true); // setup look of bottom tick labels: customPlot->xAxis->setTickLabelRotation(30); customPlot->xAxis->ticker()->setTickCount(9); customPlot->xAxis->setNumberFormat("ebc"); customPlot->xAxis->setNumberPrecision(1); customPlot->xAxis->moveRange(-10); // make top right axes clones of bottom left axes. Looks prettier: customPlot->axisRect()->setupFullAxesBox(); } void MainWindow::setupScatterStyleDemo(QCustomPlot *customPlot) { demoName = "Scatter Style Demo"; customPlot->legend->setVisible(true); customPlot->legend->setFont(QFont("Helvetica", 9)); customPlot->legend->setRowSpacing(-3); QVector shapes; shapes << QCPScatterStyle::ssCross; shapes << QCPScatterStyle::ssPlus; shapes << QCPScatterStyle::ssCircle; shapes << QCPScatterStyle::ssDisc; shapes << QCPScatterStyle::ssSquare; shapes << QCPScatterStyle::ssDiamond; shapes << QCPScatterStyle::ssStar; shapes << QCPScatterStyle::ssTriangle; shapes << QCPScatterStyle::ssTriangleInverted; shapes << QCPScatterStyle::ssCrossSquare; shapes << QCPScatterStyle::ssPlusSquare; shapes << QCPScatterStyle::ssCrossCircle; shapes << QCPScatterStyle::ssPlusCircle; shapes << QCPScatterStyle::ssPeace; shapes << QCPScatterStyle::ssCustom; QPen pen; // add graphs with different scatter styles: for (int i=0; iaddGraph(); pen.setColor(QColor(qSin(i*0.3)*100+100, qSin(i*0.6+0.7)*100+100, qSin(i*0.4+0.6)*100+100)); // generate data: QVector x(10), y(10); for (int k=0; k<10; ++k) { x[k] = k/10.0 * 4*3.14 + 0.01; y[k] = 7*qSin(x[k])/x[k] + (shapes.size()-i)*5; } customPlot->graph()->setData(x, y); customPlot->graph()->rescaleAxes(true); customPlot->graph()->setPen(pen); customPlot->graph()->setName(QCPScatterStyle::staticMetaObject.enumerator(QCPScatterStyle::staticMetaObject.indexOfEnumerator("ScatterShape")).valueToKey(shapes.at(i))); customPlot->graph()->setLineStyle(QCPGraph::lsLine); // set scatter style: if (shapes.at(i) != QCPScatterStyle::ssCustom) { customPlot->graph()->setScatterStyle(QCPScatterStyle(shapes.at(i), 10)); } else { QPainterPath customScatterPath; for (int i=0; i<3; ++i) customScatterPath.cubicTo(qCos(2*M_PI*i/3.0)*9, qSin(2*M_PI*i/3.0)*9, qCos(2*M_PI*(i+0.9)/3.0)*9, qSin(2*M_PI*(i+0.9)/3.0)*9, 0, 0); customPlot->graph()->setScatterStyle(QCPScatterStyle(customScatterPath, QPen(Qt::black, 0), QColor(40, 70, 255, 50), 10)); } } // set blank axis lines: customPlot->rescaleAxes(); customPlot->xAxis->setTicks(false); customPlot->yAxis->setTicks(false); customPlot->xAxis->setTickLabels(false); customPlot->yAxis->setTickLabels(false); // make top right axes clones of bottom left axes: customPlot->axisRect()->setupFullAxesBox(); } void MainWindow::setupLineStyleDemo(QCustomPlot *customPlot) { demoName = "Line Style Demo"; customPlot->legend->setVisible(true); customPlot->legend->setFont(QFont("Helvetica", 9)); QPen pen; QStringList lineNames; lineNames << "lsNone" << "lsLine" << "lsStepLeft" << "lsStepRight" << "lsStepCenter" << "lsImpulse"; // add graphs with different line styles: for (int i=QCPGraph::lsNone; i<=QCPGraph::lsImpulse; ++i) { customPlot->addGraph(); pen.setColor(QColor(qSin(i*1+1.2)*80+80, qSin(i*0.3+0)*80+80, qSin(i*0.3+1.5)*80+80)); customPlot->graph()->setPen(pen); customPlot->graph()->setName(lineNames.at(i-QCPGraph::lsNone)); customPlot->graph()->setLineStyle((QCPGraph::LineStyle)i); customPlot->graph()->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 5)); // generate data: QVector x(15), y(15); for (int j=0; j<15; ++j) { x[j] = j/15.0 * 5*3.14 + 0.01; y[j] = 7*qSin(x[j])/x[j] - (i-QCPGraph::lsNone)*5 + (QCPGraph::lsImpulse)*5 + 2; } customPlot->graph()->setData(x, y); customPlot->graph()->rescaleAxes(true); } // zoom out a bit: customPlot->yAxis->scaleRange(1.1, customPlot->yAxis->range().center()); customPlot->xAxis->scaleRange(1.1, customPlot->xAxis->range().center()); // set blank axis lines: customPlot->xAxis->setTicks(false); customPlot->yAxis->setTicks(true); customPlot->xAxis->setTickLabels(false); customPlot->yAxis->setTickLabels(true); // make top right axes clones of bottom left axes: customPlot->axisRect()->setupFullAxesBox(); } void MainWindow::setupScatterPixmapDemo(QCustomPlot *customPlot) { demoName = "Scatter Pixmap Demo"; customPlot->axisRect()->setBackground(QPixmap("./solarpanels.jpg")); customPlot->addGraph(); customPlot->graph()->setLineStyle(QCPGraph::lsLine); QPen pen; pen.setColor(QColor(255, 200, 20, 200)); pen.setStyle(Qt::DashLine); pen.setWidthF(2.5); customPlot->graph()->setPen(pen); customPlot->graph()->setBrush(QBrush(QColor(255,200,20,70))); customPlot->graph()->setScatterStyle(QCPScatterStyle(QPixmap("./sun.png"))); // set graph name, will show up in legend next to icon: customPlot->graph()->setName("Data from Photovoltaic\nenergy barometer 2011"); // set data: QVector year, value; year << 2005 << 2006 << 2007 << 2008 << 2009 << 2010 << 2011; value << 2.17 << 3.42 << 4.94 << 10.38 << 15.86 << 29.33 << 52.1; customPlot->graph()->setData(year, value); // set title of plot: customPlot->plotLayout()->insertRow(0); customPlot->plotLayout()->addElement(0, 0, new QCPTextElement(customPlot, "Regenerative Energies", QFont("sans", 12, QFont::Bold))); // axis configurations: customPlot->xAxis->setLabel("Year"); customPlot->yAxis->setLabel("Installed Gigawatts of\nphotovoltaic in the European Union"); customPlot->xAxis2->setVisible(true); customPlot->yAxis2->setVisible(true); customPlot->xAxis2->setTickLabels(false); customPlot->yAxis2->setTickLabels(false); customPlot->xAxis2->setTicks(false); customPlot->yAxis2->setTicks(false); customPlot->xAxis2->setSubTicks(false); customPlot->yAxis2->setSubTicks(false); customPlot->xAxis->setRange(2004.5, 2011.5); customPlot->yAxis->setRange(0, 52); // setup legend: customPlot->legend->setFont(QFont(font().family(), 7)); customPlot->legend->setIconSize(50, 20); customPlot->legend->setVisible(true); customPlot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignLeft | Qt::AlignTop); } void MainWindow::setupDateDemo(QCustomPlot *customPlot) { demoName = "Date Demo"; // set locale to english, so we get english month names: customPlot->setLocale(QLocale(QLocale::English, QLocale::UnitedKingdom)); // seconds of current time, we'll use it as starting point in time for data: double now = QDateTime::currentDateTime().toMSecsSinceEpoch()/1000.0; srand(8); // set the random seed, so we always get the same random data // create multiple graphs: for (int gi=0; gi<5; ++gi) { customPlot->addGraph(); QColor color(20+200/4.0*gi,70*(1.6-gi/4.0), 150, 150); customPlot->graph()->setLineStyle(QCPGraph::lsLine); customPlot->graph()->setPen(QPen(color.lighter(200))); customPlot->graph()->setBrush(QBrush(color)); // generate random walk data: QVector timeData(250); for (int i=0; i<250; ++i) { timeData[i].key = now + 24*3600*i; if (i == 0) timeData[i].value = (i/50.0+1)*(rand()/(double)RAND_MAX-0.5); else timeData[i].value = qFabs(timeData[i-1].value)*(1+0.02/4.0*(4-gi)) + (i/50.0+1)*(rand()/(double)RAND_MAX-0.5); } customPlot->graph()->data()->set(timeData); } // configure bottom axis to show date instead of number: QSharedPointer dateTicker(new QCPAxisTickerDateTime); dateTicker->setDateTimeFormat("d. MMMM\nyyyy"); customPlot->xAxis->setTicker(dateTicker); // configure left axis text labels: QSharedPointer textTicker(new QCPAxisTickerText); textTicker->addTick(10, "a bit\nlow"); textTicker->addTick(50, "quite\nhigh"); customPlot->yAxis->setTicker(textTicker); // set a more compact font size for bottom and left axis tick labels: customPlot->xAxis->setTickLabelFont(QFont(QFont().family(), 8)); customPlot->yAxis->setTickLabelFont(QFont(QFont().family(), 8)); // set axis labels: customPlot->xAxis->setLabel("Date"); customPlot->yAxis->setLabel("Random wobbly lines value"); // make top and right axes visible but without ticks and labels: customPlot->xAxis2->setVisible(true); customPlot->yAxis2->setVisible(true); customPlot->xAxis2->setTicks(false); customPlot->yAxis2->setTicks(false); customPlot->xAxis2->setTickLabels(false); customPlot->yAxis2->setTickLabels(false); // set axis ranges to show all data: customPlot->xAxis->setRange(now, now+24*3600*249); customPlot->yAxis->setRange(0, 60); // show legend with slightly transparent background brush: customPlot->legend->setVisible(true); customPlot->legend->setBrush(QColor(255, 255, 255, 150)); } void MainWindow::setupTextureBrushDemo(QCustomPlot *customPlot) { demoName = "Texture Brush Demo"; // add two graphs with a textured fill: customPlot->addGraph(); QPen redDotPen; redDotPen.setStyle(Qt::DotLine); redDotPen.setColor(QColor(170, 100, 100, 180)); redDotPen.setWidthF(2); customPlot->graph(0)->setPen(redDotPen); customPlot->graph(0)->setBrush(QBrush(QPixmap("./balboa.jpg"))); // fill with texture of specified image customPlot->addGraph(); customPlot->graph(1)->setPen(QPen(Qt::red)); // activate channel fill for graph 0 towards graph 1: customPlot->graph(0)->setChannelFillGraph(customPlot->graph(1)); // generate data: QVector x(250); QVector y0(250), y1(250); for (int i=0; i<250; ++i) { // just playing with numbers, not much to learn here x[i] = 3*i/250.0; y0[i] = 1+qExp(-x[i]*x[i]*0.8)*(x[i]*x[i]+x[i]); y1[i] = 1-qExp(-x[i]*x[i]*0.4)*(x[i]*x[i])*0.1; } // pass data points to graphs: customPlot->graph(0)->setData(x, y0); customPlot->graph(1)->setData(x, y1); // activate top and right axes, which are invisible by default: customPlot->xAxis2->setVisible(true); customPlot->yAxis2->setVisible(true); // make tick labels invisible on top and right axis: customPlot->xAxis2->setTickLabels(false); customPlot->yAxis2->setTickLabels(false); // set ranges: customPlot->xAxis->setRange(0, 2.5); customPlot->yAxis->setRange(0.9, 1.6); // assign top/right axes same properties as bottom/left: customPlot->axisRect()->setupFullAxesBox(); } void MainWindow::setupMultiAxisDemo(QCustomPlot *customPlot) { customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); demoName = "Multi Axis Demo"; customPlot->setLocale(QLocale(QLocale::English, QLocale::UnitedKingdom)); // period as decimal separator and comma as thousand separator customPlot->legend->setVisible(true); QFont legendFont = font(); // start out with MainWindow's font.. legendFont.setPointSize(9); // and make a bit smaller for legend customPlot->legend->setFont(legendFont); customPlot->legend->setBrush(QBrush(QColor(255,255,255,230))); // by default, the legend is in the inset layout of the main axis rect. So this is how we access it to change legend placement: customPlot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignBottom|Qt::AlignRight); // setup for graph 0: key axis left, value axis bottom // will contain left maxwell-like function customPlot->addGraph(customPlot->yAxis, customPlot->xAxis); customPlot->graph(0)->setPen(QPen(QColor(255, 100, 0))); customPlot->graph(0)->setBrush(QBrush(QPixmap("./balboa.jpg"))); // fill with texture of specified image customPlot->graph(0)->setLineStyle(QCPGraph::lsLine); customPlot->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 5)); customPlot->graph(0)->setName("Left maxwell function"); // setup for graph 1: key axis bottom, value axis left (those are the default axes) // will contain bottom maxwell-like function with error bars customPlot->addGraph(); customPlot->graph(1)->setPen(QPen(Qt::red)); customPlot->graph(1)->setBrush(QBrush(QPixmap("./balboa.jpg"))); // same fill as we used for graph 0 customPlot->graph(1)->setLineStyle(QCPGraph::lsStepCenter); customPlot->graph(1)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::red, Qt::white, 7)); customPlot->graph(1)->setName("Bottom maxwell function"); QCPErrorBars *errorBars = new QCPErrorBars(customPlot->xAxis, customPlot->yAxis); errorBars->removeFromLegend(); errorBars->setDataPlottable(customPlot->graph(1)); // setup for graph 2: key axis top, value axis right // will contain high frequency sine with low frequency beating: customPlot->addGraph(customPlot->xAxis2, customPlot->yAxis2); customPlot->graph(2)->setPen(QPen(Qt::blue)); customPlot->graph(2)->setName("High frequency sine"); // setup for graph 3: same axes as graph 2 // will contain low frequency beating envelope of graph 2 customPlot->addGraph(customPlot->xAxis2, customPlot->yAxis2); QPen blueDotPen; blueDotPen.setColor(QColor(30, 40, 255, 150)); blueDotPen.setStyle(Qt::DotLine); blueDotPen.setWidthF(4); customPlot->graph(3)->setPen(blueDotPen); customPlot->graph(3)->setName("Sine envelope"); // setup for graph 4: key axis right, value axis top // will contain parabolically distributed data points with some random perturbance customPlot->addGraph(customPlot->yAxis2, customPlot->xAxis2); customPlot->graph(4)->setPen(QColor(50, 50, 50, 255)); customPlot->graph(4)->setLineStyle(QCPGraph::lsNone); customPlot->graph(4)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, 4)); customPlot->graph(4)->setName("Some random data around\na quadratic function"); // generate data, just playing with numbers, not much to learn here: QVector x0(25), y0(25); QVector x1(15), y1(15), y1err(15); QVector x2(250), y2(250); QVector x3(250), y3(250); QVector x4(250), y4(250); for (int i=0; i<25; ++i) // data for graph 0 { x0[i] = 3*i/25.0; y0[i] = qExp(-x0[i]*x0[i]*0.8)*(x0[i]*x0[i]+x0[i]); } for (int i=0; i<15; ++i) // data for graph 1 { x1[i] = 3*i/15.0;; y1[i] = qExp(-x1[i]*x1[i])*(x1[i]*x1[i])*2.6; y1err[i] = y1[i]*0.25; } for (int i=0; i<250; ++i) // data for graphs 2, 3 and 4 { x2[i] = i/250.0*3*M_PI; x3[i] = x2[i]; x4[i] = i/250.0*100-50; y2[i] = qSin(x2[i]*12)*qCos(x2[i])*10; y3[i] = qCos(x3[i])*10; y4[i] = 0.01*x4[i]*x4[i] + 1.5*(rand()/(double)RAND_MAX-0.5) + 1.5*M_PI; } // pass data points to graphs: customPlot->graph(0)->setData(x0, y0); customPlot->graph(1)->setData(x1, y1); errorBars->setData(y1err); customPlot->graph(2)->setData(x2, y2); customPlot->graph(3)->setData(x3, y3); customPlot->graph(4)->setData(x4, y4); // activate top and right axes, which are invisible by default: customPlot->xAxis2->setVisible(true); customPlot->yAxis2->setVisible(true); // set ranges appropriate to show data: customPlot->xAxis->setRange(0, 2.7); customPlot->yAxis->setRange(0, 2.6); customPlot->xAxis2->setRange(0, 3.0*M_PI); customPlot->yAxis2->setRange(-70, 35); // set pi ticks on top axis: customPlot->xAxis2->setTicker(QSharedPointer(new QCPAxisTickerPi)); // add title layout element: customPlot->plotLayout()->insertRow(0); customPlot->plotLayout()->addElement(0, 0, new QCPTextElement(customPlot, "Way too many graphs in one plot", QFont("sans", 12, QFont::Bold))); // set labels: customPlot->xAxis->setLabel("Bottom axis with outward ticks"); customPlot->yAxis->setLabel("Left axis label"); customPlot->xAxis2->setLabel("Top axis label"); customPlot->yAxis2->setLabel("Right axis label"); // make ticks on bottom axis go outward: customPlot->xAxis->setTickLength(0, 5); customPlot->xAxis->setSubTickLength(0, 3); // make ticks on right axis go inward and outward: customPlot->yAxis2->setTickLength(3, 3); customPlot->yAxis2->setSubTickLength(1, 1); } void MainWindow::setupLogarithmicDemo(QCustomPlot *customPlot) { demoName = "Logarithmic Demo"; customPlot->setNoAntialiasingOnDrag(true); // more performance/responsiveness during dragging customPlot->addGraph(); QPen pen; pen.setColor(QColor(255,170,100)); pen.setWidth(2); pen.setStyle(Qt::DotLine); customPlot->graph(0)->setPen(pen); customPlot->graph(0)->setName("x"); customPlot->addGraph(); customPlot->graph(1)->setPen(QPen(Qt::red)); customPlot->graph(1)->setBrush(QBrush(QColor(255, 0, 0, 20))); customPlot->graph(1)->setName("-sin(x)exp(x)"); customPlot->addGraph(); customPlot->graph(2)->setPen(QPen(Qt::blue)); customPlot->graph(2)->setBrush(QBrush(QColor(0, 0, 255, 20))); customPlot->graph(2)->setName(" sin(x)exp(x)"); customPlot->addGraph(); pen.setColor(QColor(0,0,0)); pen.setWidth(1); pen.setStyle(Qt::DashLine); customPlot->graph(3)->setPen(pen); customPlot->graph(3)->setBrush(QBrush(QColor(0,0,0,15))); customPlot->graph(3)->setLineStyle(QCPGraph::lsStepCenter); customPlot->graph(3)->setName("x!"); const int dataCount = 200; const int dataFactorialCount = 21; QVector dataLinear(dataCount), dataMinusSinExp(dataCount), dataPlusSinExp(dataCount), dataFactorial(dataFactorialCount); for (int i=0; igraph(0)->data()->set(dataLinear); customPlot->graph(1)->data()->set(dataMinusSinExp); customPlot->graph(2)->data()->set(dataPlusSinExp); customPlot->graph(3)->data()->set(dataFactorial); customPlot->yAxis->grid()->setSubGridVisible(true); customPlot->xAxis->grid()->setSubGridVisible(true); customPlot->yAxis->setScaleType(QCPAxis::stLogarithmic); customPlot->yAxis2->setScaleType(QCPAxis::stLogarithmic); QSharedPointer logTicker(new QCPAxisTickerLog); customPlot->yAxis->setTicker(logTicker); customPlot->yAxis2->setTicker(logTicker); customPlot->yAxis->setNumberFormat("eb"); // e = exponential, b = beautiful decimal powers customPlot->yAxis->setNumberPrecision(0); // makes sure "1*10^4" is displayed only as "10^4" customPlot->xAxis->setRange(0, 19.9); customPlot->yAxis->setRange(1e-2, 1e10); // make range draggable and zoomable: customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); // make top right axes clones of bottom left axes: customPlot->axisRect()->setupFullAxesBox(); // connect signals so top and right axes move in sync with bottom and left axes: connect(customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->xAxis2, SLOT(setRange(QCPRange))); connect(customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->yAxis2, SLOT(setRange(QCPRange))); customPlot->legend->setVisible(true); customPlot->legend->setBrush(QBrush(QColor(255,255,255,150))); customPlot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignLeft|Qt::AlignTop); // make legend align in top left corner or axis rect } void MainWindow::setupRealtimeDataDemo(QCustomPlot *customPlot) { demoName = "Real Time Data Demo"; // include this section to fully disable antialiasing for higher performance: /* customPlot->setNotAntialiasedElements(QCP::aeAll); QFont font; font.setStyleStrategy(QFont::NoAntialias); customPlot->xAxis->setTickLabelFont(font); customPlot->yAxis->setTickLabelFont(font); customPlot->legend->setFont(font); */ customPlot->addGraph(); // blue line customPlot->graph(0)->setPen(QPen(QColor(40, 110, 255))); customPlot->addGraph(); // red line customPlot->graph(1)->setPen(QPen(QColor(255, 110, 40))); QSharedPointer timeTicker(new QCPAxisTickerTime); timeTicker->setTimeFormat("%h:%m:%s"); customPlot->xAxis->setTicker(timeTicker); customPlot->axisRect()->setupFullAxesBox(); customPlot->yAxis->setRange(-1.2, 1.2); // make left and bottom axes transfer their ranges to right and top axes: connect(customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->xAxis2, SLOT(setRange(QCPRange))); connect(customPlot->yAxis, SIGNAL(rangeChanged(QCPRange)), customPlot->yAxis2, SLOT(setRange(QCPRange))); // setup a timer that repeatedly calls MainWindow::realtimeDataSlot: connect(&dataTimer, SIGNAL(timeout()), this, SLOT(realtimeDataSlot())); dataTimer.start(0); // Interval 0 means to refresh as fast as possible } void MainWindow::setupParametricCurveDemo(QCustomPlot *customPlot) { demoName = "Parametric Curves Demo"; // create empty curve objects: QCPCurve *fermatSpiral1 = new QCPCurve(customPlot->xAxis, customPlot->yAxis); QCPCurve *fermatSpiral2 = new QCPCurve(customPlot->xAxis, customPlot->yAxis); QCPCurve *deltoidRadial = new QCPCurve(customPlot->xAxis, customPlot->yAxis); // generate the curve data points: const int pointCount = 500; QVector dataSpiral1(pointCount), dataSpiral2(pointCount), dataDeltoid(pointCount); for (int i=0; idata()->set(dataSpiral1, true); fermatSpiral2->data()->set(dataSpiral2, true); deltoidRadial->data()->set(dataDeltoid, true); // color the curves: fermatSpiral1->setPen(QPen(Qt::blue)); fermatSpiral1->setBrush(QBrush(QColor(0, 0, 255, 20))); fermatSpiral2->setPen(QPen(QColor(255, 120, 0))); fermatSpiral2->setBrush(QBrush(QColor(255, 120, 0, 30))); QRadialGradient radialGrad(QPointF(310, 180), 200); radialGrad.setColorAt(0, QColor(170, 20, 240, 100)); radialGrad.setColorAt(0.5, QColor(20, 10, 255, 40)); radialGrad.setColorAt(1,QColor(120, 20, 240, 10)); deltoidRadial->setPen(QPen(QColor(170, 20, 240))); deltoidRadial->setBrush(QBrush(radialGrad)); // set some basic customPlot config: customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables); customPlot->axisRect()->setupFullAxesBox(); customPlot->rescaleAxes(); } void MainWindow::setupBarChartDemo(QCustomPlot *customPlot) { demoName = "Bar Chart Demo"; // set dark background gradient: QLinearGradient gradient(0, 0, 0, 400); gradient.setColorAt(0, QColor(90, 90, 90)); gradient.setColorAt(0.38, QColor(105, 105, 105)); gradient.setColorAt(1, QColor(70, 70, 70)); customPlot->setBackground(QBrush(gradient)); // create empty bar chart objects: QCPBars *regen = new QCPBars(customPlot->xAxis, customPlot->yAxis); QCPBars *nuclear = new QCPBars(customPlot->xAxis, customPlot->yAxis); QCPBars *fossil = new QCPBars(customPlot->xAxis, customPlot->yAxis); regen->setAntialiased(false); // gives more crisp, pixel aligned bar borders nuclear->setAntialiased(false); fossil->setAntialiased(false); regen->setStackingGap(1); nuclear->setStackingGap(1); fossil->setStackingGap(1); // set names and colors: fossil->setName("Fossil fuels"); fossil->setPen(QPen(QColor(111, 9, 176).lighter(170))); fossil->setBrush(QColor(111, 9, 176)); nuclear->setName("Nuclear"); nuclear->setPen(QPen(QColor(250, 170, 20).lighter(150))); nuclear->setBrush(QColor(250, 170, 20)); regen->setName("Regenerative"); regen->setPen(QPen(QColor(0, 168, 140).lighter(130))); regen->setBrush(QColor(0, 168, 140)); // stack bars on top of each other: nuclear->moveAbove(fossil); regen->moveAbove(nuclear); // prepare x axis with country labels: QVector ticks; QVector labels; ticks << 1 << 2 << 3 << 4 << 5 << 6 << 7; labels << "USA" << "Japan" << "Germany" << "France" << "UK" << "Italy" << "Canada"; QSharedPointer textTicker(new QCPAxisTickerText); textTicker->addTicks(ticks, labels); customPlot->xAxis->setTicker(textTicker); customPlot->xAxis->setTickLabelRotation(60); customPlot->xAxis->setSubTicks(false); customPlot->xAxis->setTickLength(0, 4); customPlot->xAxis->setRange(0, 8); customPlot->xAxis->setBasePen(QPen(Qt::white)); customPlot->xAxis->setTickPen(QPen(Qt::white)); customPlot->xAxis->grid()->setVisible(true); customPlot->xAxis->grid()->setPen(QPen(QColor(130, 130, 130), 0, Qt::DotLine)); customPlot->xAxis->setTickLabelColor(Qt::white); customPlot->xAxis->setLabelColor(Qt::white); // prepare y axis: customPlot->yAxis->setRange(0, 12.1); customPlot->yAxis->setPadding(5); // a bit more space to the left border customPlot->yAxis->setLabel("Power Consumption in\nKilowatts per Capita (2007)"); customPlot->yAxis->setBasePen(QPen(Qt::white)); customPlot->yAxis->setTickPen(QPen(Qt::white)); customPlot->yAxis->setSubTickPen(QPen(Qt::white)); customPlot->yAxis->grid()->setSubGridVisible(true); customPlot->yAxis->setTickLabelColor(Qt::white); customPlot->yAxis->setLabelColor(Qt::white); customPlot->yAxis->grid()->setPen(QPen(QColor(130, 130, 130), 0, Qt::SolidLine)); customPlot->yAxis->grid()->setSubGridPen(QPen(QColor(130, 130, 130), 0, Qt::DotLine)); // Add data: QVector fossilData, nuclearData, regenData; fossilData << 0.86*10.5 << 0.83*5.5 << 0.84*5.5 << 0.52*5.8 << 0.89*5.2 << 0.90*4.2 << 0.67*11.2; nuclearData << 0.08*10.5 << 0.12*5.5 << 0.12*5.5 << 0.40*5.8 << 0.09*5.2 << 0.00*4.2 << 0.07*11.2; regenData << 0.06*10.5 << 0.05*5.5 << 0.04*5.5 << 0.06*5.8 << 0.02*5.2 << 0.07*4.2 << 0.25*11.2; fossil->setData(ticks, fossilData); nuclear->setData(ticks, nuclearData); regen->setData(ticks, regenData); // setup legend: customPlot->legend->setVisible(true); customPlot->axisRect()->insetLayout()->setInsetAlignment(0, Qt::AlignTop|Qt::AlignHCenter); customPlot->legend->setBrush(QColor(255, 255, 255, 100)); customPlot->legend->setBorderPen(Qt::NoPen); QFont legendFont = font(); legendFont.setPointSize(10); customPlot->legend->setFont(legendFont); customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); } void MainWindow::setupStatisticalDemo(QCustomPlot *customPlot) { demoName = "Statistical Demo"; QCPStatisticalBox *statistical = new QCPStatisticalBox(customPlot->xAxis, customPlot->yAxis); QBrush boxBrush(QColor(60, 60, 255, 100)); boxBrush.setStyle(Qt::Dense6Pattern); // make it look oldschool statistical->setBrush(boxBrush); // specify data: statistical->addData(1, 1.1, 1.9, 2.25, 2.7, 4.2); statistical->addData(2, 0.8, 1.6, 2.2, 3.2, 4.9, QVector() << 0.7 << 0.34 << 0.45 << 6.2 << 5.84); // provide some outliers as QVector statistical->addData(3, 0.2, 0.7, 1.1, 1.6, 2.9); // prepare manual x axis labels: customPlot->xAxis->setSubTicks(false); customPlot->xAxis->setTickLength(0, 4); customPlot->xAxis->setTickLabelRotation(20); QSharedPointer textTicker(new QCPAxisTickerText); textTicker->addTick(1, "Sample 1"); textTicker->addTick(2, "Sample 2"); textTicker->addTick(3, "Control Group"); customPlot->xAxis->setTicker(textTicker); // prepare axes: customPlot->yAxis->setLabel(QString::fromUtf8("O₂ Absorption [mg]")); customPlot->rescaleAxes(); customPlot->xAxis->scaleRange(1.7, customPlot->xAxis->range().center()); customPlot->yAxis->setRange(0, 7); customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); } void MainWindow::setupSimpleItemDemo(QCustomPlot *customPlot) { demoName = "Simple Item Demo"; customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); // add the text label at the top: QCPItemText *textLabel = new QCPItemText(customPlot); textLabel->setPositionAlignment(Qt::AlignTop|Qt::AlignHCenter); textLabel->position->setType(QCPItemPosition::ptAxisRectRatio); textLabel->position->setCoords(0.5, 0); // place position at center/top of axis rect textLabel->setText("Text Item Demo"); textLabel->setFont(QFont(font().family(), 16)); // make font a bit larger textLabel->setPen(QPen(Qt::black)); // show black border around text // add the arrow: QCPItemLine *arrow = new QCPItemLine(customPlot); arrow->start->setParentAnchor(textLabel->bottom); arrow->end->setCoords(4, 1.6); // point to (4, 1.6) in x-y-plot coordinates arrow->setHead(QCPLineEnding::esSpikeArrow); } void MainWindow::setupItemDemo(QCustomPlot *customPlot) { demoName = "Item Demo"; customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); QCPGraph *graph = customPlot->addGraph(); int n = 500; double phase = 0; double k = 3; QVector x(n), y(n); for (int i=0; isetData(x, y); graph->setPen(QPen(Qt::blue)); graph->rescaleKeyAxis(); customPlot->yAxis->setRange(-1.45, 1.65); customPlot->xAxis->grid()->setZeroLinePen(Qt::NoPen); // add the bracket at the top: QCPItemBracket *bracket = new QCPItemBracket(customPlot); bracket->left->setCoords(-8, 1.1); bracket->right->setCoords(8, 1.1); bracket->setLength(13); // add the text label at the top: QCPItemText *wavePacketText = new QCPItemText(customPlot); wavePacketText->position->setParentAnchor(bracket->center); wavePacketText->position->setCoords(0, -10); // move 10 pixels to the top from bracket center anchor wavePacketText->setPositionAlignment(Qt::AlignBottom|Qt::AlignHCenter); wavePacketText->setText("Wavepacket"); wavePacketText->setFont(QFont(font().family(), 10)); // add the phase tracer (red circle) which sticks to the graph data (and gets updated in bracketDataSlot by timer event): QCPItemTracer *phaseTracer = new QCPItemTracer(customPlot); itemDemoPhaseTracer = phaseTracer; // so we can access it later in the bracketDataSlot for animation phaseTracer->setGraph(graph); phaseTracer->setGraphKey((M_PI*1.5-phase)/k); phaseTracer->setInterpolating(true); phaseTracer->setStyle(QCPItemTracer::tsCircle); phaseTracer->setPen(QPen(Qt::red)); phaseTracer->setBrush(Qt::red); phaseTracer->setSize(7); // add label for phase tracer: QCPItemText *phaseTracerText = new QCPItemText(customPlot); phaseTracerText->position->setType(QCPItemPosition::ptAxisRectRatio); phaseTracerText->setPositionAlignment(Qt::AlignRight|Qt::AlignBottom); phaseTracerText->position->setCoords(1.0, 0.95); // lower right corner of axis rect phaseTracerText->setText("Points of fixed\nphase define\nphase velocity vp"); phaseTracerText->setTextAlignment(Qt::AlignLeft); phaseTracerText->setFont(QFont(font().family(), 9)); phaseTracerText->setPadding(QMargins(8, 0, 0, 0)); // add arrow pointing at phase tracer, coming from label: QCPItemCurve *phaseTracerArrow = new QCPItemCurve(customPlot); phaseTracerArrow->start->setParentAnchor(phaseTracerText->left); phaseTracerArrow->startDir->setParentAnchor(phaseTracerArrow->start); phaseTracerArrow->startDir->setCoords(-40, 0); // direction 30 pixels to the left of parent anchor (tracerArrow->start) phaseTracerArrow->end->setParentAnchor(phaseTracer->position); phaseTracerArrow->end->setCoords(10, 10); phaseTracerArrow->endDir->setParentAnchor(phaseTracerArrow->end); phaseTracerArrow->endDir->setCoords(30, 30); phaseTracerArrow->setHead(QCPLineEnding::esSpikeArrow); phaseTracerArrow->setTail(QCPLineEnding(QCPLineEnding::esBar, (phaseTracerText->bottom->pixelPosition().y()-phaseTracerText->top->pixelPosition().y())*0.85)); // add the group velocity tracer (green circle): QCPItemTracer *groupTracer = new QCPItemTracer(customPlot); groupTracer->setGraph(graph); groupTracer->setGraphKey(5.5); groupTracer->setInterpolating(true); groupTracer->setStyle(QCPItemTracer::tsCircle); groupTracer->setPen(QPen(Qt::green)); groupTracer->setBrush(Qt::green); groupTracer->setSize(7); // add label for group tracer: QCPItemText *groupTracerText = new QCPItemText(customPlot); groupTracerText->position->setType(QCPItemPosition::ptAxisRectRatio); groupTracerText->setPositionAlignment(Qt::AlignRight|Qt::AlignTop); groupTracerText->position->setCoords(1.0, 0.20); // lower right corner of axis rect groupTracerText->setText("Fixed positions in\nwave packet define\ngroup velocity vg"); groupTracerText->setTextAlignment(Qt::AlignLeft); groupTracerText->setFont(QFont(font().family(), 9)); groupTracerText->setPadding(QMargins(8, 0, 0, 0)); // add arrow pointing at group tracer, coming from label: QCPItemCurve *groupTracerArrow = new QCPItemCurve(customPlot); groupTracerArrow->start->setParentAnchor(groupTracerText->left); groupTracerArrow->startDir->setParentAnchor(groupTracerArrow->start); groupTracerArrow->startDir->setCoords(-40, 0); // direction 30 pixels to the left of parent anchor (tracerArrow->start) groupTracerArrow->end->setCoords(5.5, 0.4); groupTracerArrow->endDir->setParentAnchor(groupTracerArrow->end); groupTracerArrow->endDir->setCoords(0, -40); groupTracerArrow->setHead(QCPLineEnding::esSpikeArrow); groupTracerArrow->setTail(QCPLineEnding(QCPLineEnding::esBar, (groupTracerText->bottom->pixelPosition().y()-groupTracerText->top->pixelPosition().y())*0.85)); // add dispersion arrow: QCPItemCurve *arrow = new QCPItemCurve(customPlot); arrow->start->setCoords(1, -1.1); arrow->startDir->setCoords(-1, -1.3); arrow->endDir->setCoords(-5, -0.3); arrow->end->setCoords(-10, -0.2); arrow->setHead(QCPLineEnding::esSpikeArrow); // add the dispersion arrow label: QCPItemText *dispersionText = new QCPItemText(customPlot); dispersionText->position->setCoords(-6, -0.9); dispersionText->setRotation(40); dispersionText->setText("Dispersion with\nvp < vg"); dispersionText->setFont(QFont(font().family(), 10)); // setup a timer that repeatedly calls MainWindow::bracketDataSlot: connect(&dataTimer, SIGNAL(timeout()), this, SLOT(bracketDataSlot())); dataTimer.start(0); // Interval 0 means to refresh as fast as possible } void MainWindow::setupStyledDemo(QCustomPlot *customPlot) { demoName = "Styled Demo"; // prepare data: QVector x1(20), y1(20); QVector x2(100), y2(100); QVector x3(20), y3(20); QVector x4(20), y4(20); for (int i=0; iaddGraph(); graph1->setData(x1, y1); graph1->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, QPen(Qt::black, 1.5), QBrush(Qt::white), 9)); graph1->setPen(QPen(QColor(120, 120, 120), 2)); QCPGraph *graph2 = customPlot->addGraph(); graph2->setData(x2, y2); graph2->setPen(Qt::NoPen); graph2->setBrush(QColor(200, 200, 200, 20)); graph2->setChannelFillGraph(graph1); QCPBars *bars1 = new QCPBars(customPlot->xAxis, customPlot->yAxis); bars1->setWidth(9/(double)x3.size()); bars1->setData(x3, y3); bars1->setPen(Qt::NoPen); bars1->setBrush(QColor(10, 140, 70, 160)); QCPBars *bars2 = new QCPBars(customPlot->xAxis, customPlot->yAxis); bars2->setWidth(9/(double)x4.size()); bars2->setData(x4, y4); bars2->setPen(Qt::NoPen); bars2->setBrush(QColor(10, 100, 50, 70)); bars2->moveAbove(bars1); // move bars above graphs and grid below bars: customPlot->addLayer("abovemain", customPlot->layer("main"), QCustomPlot::limAbove); customPlot->addLayer("belowmain", customPlot->layer("main"), QCustomPlot::limBelow); graph1->setLayer("abovemain"); customPlot->xAxis->grid()->setLayer("belowmain"); customPlot->yAxis->grid()->setLayer("belowmain"); // set some pens, brushes and backgrounds: customPlot->xAxis->setBasePen(QPen(Qt::white, 1)); customPlot->yAxis->setBasePen(QPen(Qt::white, 1)); customPlot->xAxis->setTickPen(QPen(Qt::white, 1)); customPlot->yAxis->setTickPen(QPen(Qt::white, 1)); customPlot->xAxis->setSubTickPen(QPen(Qt::white, 1)); customPlot->yAxis->setSubTickPen(QPen(Qt::white, 1)); customPlot->xAxis->setTickLabelColor(Qt::white); customPlot->yAxis->setTickLabelColor(Qt::white); customPlot->xAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine)); customPlot->yAxis->grid()->setPen(QPen(QColor(140, 140, 140), 1, Qt::DotLine)); customPlot->xAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine)); customPlot->yAxis->grid()->setSubGridPen(QPen(QColor(80, 80, 80), 1, Qt::DotLine)); customPlot->xAxis->grid()->setSubGridVisible(true); customPlot->yAxis->grid()->setSubGridVisible(true); customPlot->xAxis->grid()->setZeroLinePen(Qt::NoPen); customPlot->yAxis->grid()->setZeroLinePen(Qt::NoPen); customPlot->xAxis->setUpperEnding(QCPLineEnding::esSpikeArrow); customPlot->yAxis->setUpperEnding(QCPLineEnding::esSpikeArrow); QLinearGradient plotGradient; plotGradient.setStart(0, 0); plotGradient.setFinalStop(0, 350); plotGradient.setColorAt(0, QColor(80, 80, 80)); plotGradient.setColorAt(1, QColor(50, 50, 50)); customPlot->setBackground(plotGradient); QLinearGradient axisRectGradient; axisRectGradient.setStart(0, 0); axisRectGradient.setFinalStop(0, 350); axisRectGradient.setColorAt(0, QColor(80, 80, 80)); axisRectGradient.setColorAt(1, QColor(30, 30, 30)); customPlot->axisRect()->setBackground(axisRectGradient); customPlot->rescaleAxes(); customPlot->yAxis->setRange(0, 2); } void MainWindow::setupAdvancedAxesDemo(QCustomPlot *customPlot) { demoName = "Advanced Axes Demo"; // configure axis rect: customPlot->plotLayout()->clear(); // clear default axis rect so we can start from scratch QCPAxisRect *wideAxisRect = new QCPAxisRect(customPlot); wideAxisRect->setupFullAxesBox(true); wideAxisRect->axis(QCPAxis::atRight, 0)->setTickLabels(true); wideAxisRect->addAxis(QCPAxis::atLeft)->setTickLabelColor(QColor("#6050F8")); // add an extra axis on the left and color its numbers QCPLayoutGrid *subLayout = new QCPLayoutGrid; customPlot->plotLayout()->addElement(0, 0, wideAxisRect); // insert axis rect in first row customPlot->plotLayout()->addElement(1, 0, subLayout); // sub layout in second row (grid layout will grow accordingly) //customPlot->plotLayout()->setRowStretchFactor(1, 2); // prepare axis rects that will be placed in the sublayout: QCPAxisRect *subRectLeft = new QCPAxisRect(customPlot, false); // false means to not setup default axes QCPAxisRect *subRectRight = new QCPAxisRect(customPlot, false); subLayout->addElement(0, 0, subRectLeft); subLayout->addElement(0, 1, subRectRight); subRectRight->setMaximumSize(100, 100); // make bottom right axis rect size fixed 100x100 subRectRight->setMinimumSize(100, 100); // make bottom right axis rect size fixed 100x100 // setup axes in sub layout axis rects: subRectLeft->addAxes(QCPAxis::atBottom | QCPAxis::atLeft); subRectRight->addAxes(QCPAxis::atBottom | QCPAxis::atRight); subRectLeft->axis(QCPAxis::atLeft)->ticker()->setTickCount(2); subRectRight->axis(QCPAxis::atRight)->ticker()->setTickCount(2); subRectRight->axis(QCPAxis::atBottom)->ticker()->setTickCount(2); subRectLeft->axis(QCPAxis::atBottom)->grid()->setVisible(true); // synchronize the left and right margins of the top and bottom axis rects: QCPMarginGroup *marginGroup = new QCPMarginGroup(customPlot); subRectLeft->setMarginGroup(QCP::msLeft, marginGroup); subRectRight->setMarginGroup(QCP::msRight, marginGroup); wideAxisRect->setMarginGroup(QCP::msLeft | QCP::msRight, marginGroup); // move newly created axes on "axes" layer and grids on "grid" layer: foreach (QCPAxisRect *rect, customPlot->axisRects()) { foreach (QCPAxis *axis, rect->axes()) { axis->setLayer("axes"); axis->grid()->setLayer("grid"); } } // prepare data: QVector dataCos(21), dataGauss(50), dataRandom(100); QVector x3, y3; std::srand(3); for (int i=0; iaddGraph(wideAxisRect->axis(QCPAxis::atBottom), wideAxisRect->axis(QCPAxis::atLeft)); mainGraphCos->data()->set(dataCos); mainGraphCos->valueAxis()->setRange(-1, 1); mainGraphCos->rescaleKeyAxis(); mainGraphCos->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, QPen(Qt::black), QBrush(Qt::white), 6)); mainGraphCos->setPen(QPen(QColor(120, 120, 120), 2)); QCPGraph *mainGraphGauss = customPlot->addGraph(wideAxisRect->axis(QCPAxis::atBottom), wideAxisRect->axis(QCPAxis::atLeft, 1)); mainGraphGauss->data()->set(dataGauss); mainGraphGauss->setPen(QPen(QColor("#8070B8"), 2)); mainGraphGauss->setBrush(QColor(110, 170, 110, 30)); mainGraphCos->setChannelFillGraph(mainGraphGauss); mainGraphCos->setBrush(QColor(255, 161, 0, 50)); mainGraphGauss->valueAxis()->setRange(0, 1000); mainGraphGauss->rescaleKeyAxis(); QCPGraph *subGraphRandom = customPlot->addGraph(subRectLeft->axis(QCPAxis::atBottom), subRectLeft->axis(QCPAxis::atLeft)); subGraphRandom->data()->set(dataRandom); subGraphRandom->setLineStyle(QCPGraph::lsImpulse); subGraphRandom->setPen(QPen(QColor("#FFA100"), 1.5)); subGraphRandom->rescaleAxes(); QCPBars *subBars = new QCPBars(subRectRight->axis(QCPAxis::atBottom), subRectRight->axis(QCPAxis::atRight)); subBars->setWidth(3/(double)x3.size()); subBars->setData(x3, y3); subBars->setPen(QPen(Qt::black)); subBars->setAntialiased(false); subBars->setAntialiasedFill(false); subBars->setBrush(QColor("#705BE8")); subBars->keyAxis()->setSubTicks(false); subBars->rescaleAxes(); // setup a ticker for subBars key axis that only gives integer ticks: QSharedPointer intTicker(new QCPAxisTickerFixed); intTicker->setTickStep(1.0); intTicker->setScaleStrategy(QCPAxisTickerFixed::ssMultiples); subBars->keyAxis()->setTicker(intTicker); } void MainWindow::setupColorMapDemo(QCustomPlot *customPlot) { demoName = "Color Map Demo"; // configure axis rect: customPlot->setInteractions(QCP::iRangeDrag|QCP::iRangeZoom); // this will also allow rescaling the color scale by dragging/zooming customPlot->axisRect()->setupFullAxesBox(true); customPlot->xAxis->setLabel("x"); customPlot->yAxis->setLabel("y"); // set up the QCPColorMap: QCPColorMap *colorMap = new QCPColorMap(customPlot->xAxis, customPlot->yAxis); int nx = 200; int ny = 200; colorMap->data()->setSize(nx, ny); // we want the color map to have nx * ny data points colorMap->data()->setRange(QCPRange(-4, 4), QCPRange(-4, 4)); // and span the coordinate range -4..4 in both key (x) and value (y) dimensions // now we assign some data, by accessing the QCPColorMapData instance of the color map: double x, y, z; for (int xIndex=0; xIndexdata()->cellToCoord(xIndex, yIndex, &x, &y); double r = 3*qSqrt(x*x+y*y)+1e-2; z = 2*x*(qCos(r+2)/r-qSin(r+2)/r); // the B field strength of dipole radiation (modulo physical constants) colorMap->data()->setCell(xIndex, yIndex, z); } } // add a color scale: QCPColorScale *colorScale = new QCPColorScale(customPlot); customPlot->plotLayout()->addElement(0, 1, colorScale); // add it to the right of the main axis rect colorScale->setType(QCPAxis::atRight); // scale shall be vertical bar with tick/axis labels right (actually atRight is already the default) colorMap->setColorScale(colorScale); // associate the color map with the color scale colorScale->axis()->setLabel("Magnetic Field Strength"); // set the color gradient of the color map to one of the presets: colorMap->setGradient(QCPColorGradient::gpPolar); // we could have also created a QCPColorGradient instance and added own colors to // the gradient, see the documentation of QCPColorGradient for what's possible. // rescale the data dimension (color) such that all data points lie in the span visualized by the color gradient: colorMap->rescaleDataRange(); // make sure the axis rect and color scale synchronize their bottom and top margins (so they line up): QCPMarginGroup *marginGroup = new QCPMarginGroup(customPlot); customPlot->axisRect()->setMarginGroup(QCP::msBottom|QCP::msTop, marginGroup); colorScale->setMarginGroup(QCP::msBottom|QCP::msTop, marginGroup); // rescale the key (x) and value (y) axes so the whole color map is visible: customPlot->rescaleAxes(); } void MainWindow::setupFinancialDemo(QCustomPlot *customPlot) { demoName = "Financial Charts Demo"; customPlot->legend->setVisible(true); // generate two sets of random walk data (one for candlestick and one for ohlc chart): int n = 500; QVector time(n), value1(n), value2(n); QDateTime start(QDate(2014, 6, 11), QTime(0, 0)); start.setTimeSpec(Qt::UTC); double startTime = start.toMSecsSinceEpoch()/1000.0; double binSize = 3600*24; // bin data in 1 day intervals time[0] = startTime; value1[0] = 60; value2[0] = 20; std::srand(9); for (int i=1; ixAxis, customPlot->yAxis); candlesticks->setName("Candlestick"); candlesticks->setChartStyle(QCPFinancial::csCandlestick); candlesticks->data()->set(QCPFinancial::timeSeriesToOhlc(time, value1, binSize, startTime)); candlesticks->setWidth(binSize*0.9); candlesticks->setTwoColored(true); candlesticks->setBrushPositive(QColor(245, 245, 245)); candlesticks->setBrushNegative(QColor(40, 40, 40)); candlesticks->setPenPositive(QPen(QColor(0, 0, 0))); candlesticks->setPenNegative(QPen(QColor(0, 0, 0))); // create ohlc chart: QCPFinancial *ohlc = new QCPFinancial(customPlot->xAxis, customPlot->yAxis); ohlc->setName("OHLC"); ohlc->setChartStyle(QCPFinancial::csOhlc); ohlc->data()->set(QCPFinancial::timeSeriesToOhlc(time, value2, binSize/3.0, startTime)); // divide binSize by 3 just to make the ohlc bars a bit denser ohlc->setWidth(binSize*0.2); ohlc->setTwoColored(true); // create bottom axis rect for volume bar chart: QCPAxisRect *volumeAxisRect = new QCPAxisRect(customPlot); customPlot->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: customPlot->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: customPlot->setAutoAddPlottableToLegend(false); 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; iaddData(startTime+3600*5.0*i, qAbs(v)); // add data to either volumeNeg or volumePos, depending on sign of v } 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(customPlot->xAxis, SIGNAL(rangeChanged(QCPRange)), volumeAxisRect->axis(QCPAxis::atBottom), SLOT(setRange(QCPRange))); connect(volumeAxisRect->axis(QCPAxis::atBottom), SIGNAL(rangeChanged(QCPRange)), customPlot->xAxis, SLOT(setRange(QCPRange))); // configure axes of both main and bottom axis rect: QSharedPointer dateTimeTicker(new QCPAxisTickerDateTime); dateTimeTicker->setDateTimeSpec(Qt::UTC); dateTimeTicker->setDateTimeFormat("dd. MMMM"); volumeAxisRect->axis(QCPAxis::atBottom)->setTicker(dateTimeTicker); volumeAxisRect->axis(QCPAxis::atBottom)->setTickLabelRotation(15); customPlot->xAxis->setBasePen(Qt::NoPen); customPlot->xAxis->setTickLabels(false); customPlot->xAxis->setTicks(false); // only want vertical grid in main axis rect, so hide xAxis backbone, ticks, and labels customPlot->xAxis->setTicker(dateTimeTicker); customPlot->rescaleAxes(); customPlot->xAxis->scaleRange(1.025, customPlot->xAxis->range().center()); customPlot->yAxis->scaleRange(1.1, customPlot->yAxis->range().center()); // make axis rects' left side line up: QCPMarginGroup *group = new QCPMarginGroup(customPlot); customPlot->axisRect()->setMarginGroup(QCP::msLeft|QCP::msRight, group); volumeAxisRect->setMarginGroup(QCP::msLeft|QCP::msRight, group); } void MainWindow::setupPolarPlotDemo(QCustomPlot *customPlot) { // Warning: Polar plots are a still a tech preview customPlot->plotLayout()->clear(); QCPPolarAxisAngular *angularAxis = new QCPPolarAxisAngular(customPlot); customPlot->plotLayout()->addElement(0, 0, angularAxis); /* This is how we could set the angular axis to show pi symbols instead of degree numbers: QSharedPointer ticker(new QCPAxisTickerPi); ticker->setPiValue(180); ticker->setTickCount(8); polarAxis->setTicker(ticker); */ customPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); angularAxis->setRangeDrag(false); angularAxis->setTickLabelMode(QCPPolarAxisAngular::lmUpright); angularAxis->radialAxis()->setTickLabelMode(QCPPolarAxisRadial::lmUpright); angularAxis->radialAxis()->setTickLabelRotation(0); angularAxis->radialAxis()->setAngle(45); angularAxis->grid()->setAngularPen(QPen(QColor(200, 200, 200), 0, Qt::SolidLine)); angularAxis->grid()->setSubGridType(QCPPolarGrid::gtAll); QCPPolarGraph *g1 = new QCPPolarGraph(angularAxis, angularAxis->radialAxis()); QCPPolarGraph *g2 = new QCPPolarGraph(angularAxis, angularAxis->radialAxis()); g2->setPen(QPen(QColor(255, 150, 20))); g2->setBrush(QColor(255, 150, 20, 50)); g1->setScatterStyle(QCPScatterStyle::ssDisc); for (int i=0; i<100; ++i) { g1->addData(i/100.0*360.0, qSin(i/100.0*M_PI*8)*8+1); g2->addData(i/100.0*360.0, qSin(i/100.0*M_PI*6)*2); } angularAxis->setRange(0, 360); angularAxis->radialAxis()->setRange(-10, 10); } void MainWindow::realtimeDataSlot() { static QTime timeStart = QTime::currentTime(); // calculate two new data points: double key = timeStart.msecsTo(QTime::currentTime())/1000.0; // time elapsed since start of demo, in seconds static double lastPointKey = 0; if (key-lastPointKey > 0.002) // at most add point every 2 ms { // add data to lines: ui->customPlot->graph(0)->addData(key, qSin(key)+std::rand()/(double)RAND_MAX*1*qSin(key/0.3843)); ui->customPlot->graph(1)->addData(key, qCos(key)+std::rand()/(double)RAND_MAX*0.5*qSin(key/0.4364)); // rescale value (vertical) axis to fit the current data: //ui->customPlot->graph(0)->rescaleValueAxis(); //ui->customPlot->graph(1)->rescaleValueAxis(true); lastPointKey = key; } // make key axis range scroll with the data (at a constant range size of 8): ui->customPlot->xAxis->setRange(key, 8, Qt::AlignRight); ui->customPlot->replot(); // calculate frames per second: static double lastFpsKey; static int frameCount; ++frameCount; if (key-lastFpsKey > 2) // average fps over 2 seconds { ui->statusBar->showMessage( QString("%1 FPS, Total Data points: %2") .arg(frameCount/(key-lastFpsKey), 0, 'f', 0) .arg(ui->customPlot->graph(0)->data()->size()+ui->customPlot->graph(1)->data()->size()) , 0); lastFpsKey = key; frameCount = 0; } } void MainWindow::bracketDataSlot() { double secs = QCPAxisTickerDateTime::dateTimeToKey(QDateTime::currentDateTime()); // update data to make phase move: int n = 500; double phase = secs*5; double k = 3; QVector x(n), y(n); for (int i=0; icustomPlot->graph()->setData(x, y); itemDemoPhaseTracer->setGraphKey((8*M_PI+fmod(M_PI*1.5-phase, 6*M_PI))/k); ui->customPlot->replot(); // calculate frames per second: double key = secs; static double lastFpsKey; static int frameCount; ++frameCount; if (key-lastFpsKey > 2) // average fps over 2 seconds { ui->statusBar->showMessage( QString("%1 FPS, Total Data points: %2") .arg(frameCount/(key-lastFpsKey), 0, 'f', 0) .arg(ui->customPlot->graph(0)->data()->size()) , 0); lastFpsKey = key; frameCount = 0; } } void MainWindow::setupPlayground(QCustomPlot *customPlot) { Q_UNUSED(customPlot) } MainWindow::~MainWindow() { delete ui; } void MainWindow::screenShot() { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) QPixmap pm = QPixmap::grabWindow(qApp->desktop()->winId(), this->x()+2, this->y()+2, this->frameGeometry().width()-4, this->frameGeometry().height()-4); #elif QT_VERSION < QT_VERSION_CHECK(5, 5, 0) QPixmap pm = qApp->primaryScreen()->grabWindow(qApp->desktop()->winId(), this->x()+2, this->y()+2, this->frameGeometry().width()-4, this->frameGeometry().height()-4); #elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QPixmap pm = qApp->primaryScreen()->grabWindow(qApp->desktop()->winId(), this->x()-7, this->y()-7, this->frameGeometry().width()+14, this->frameGeometry().height()+14); #else QPixmap pm = qApp->primaryScreen()->grabWindow(0, this->x()-7, this->y()-7, this->frameGeometry().width()+14, this->frameGeometry().height()+14); #endif QString fileName = demoName.toLower()+".png"; fileName.replace(" ", ""); pm.save("./screenshots/"+fileName); qApp->quit(); } void MainWindow::allScreenShots() { #if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) QPixmap pm = QPixmap::grabWindow(qApp->desktop()->winId(), this->x()+2, this->y()+2, this->frameGeometry().width()-4, this->frameGeometry().height()-4); #elif QT_VERSION < QT_VERSION_CHECK(5, 5, 0) QPixmap pm = qApp->primaryScreen()->grabWindow(qApp->desktop()->winId(), this->x()+2, this->y()+2, this->frameGeometry().width()-4, this->frameGeometry().height()-4); #elif QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QPixmap pm = qApp->primaryScreen()->grabWindow(qApp->desktop()->winId(), this->x()-7, this->y()-7, this->frameGeometry().width()+14, this->frameGeometry().height()+14); #else QPixmap pm = qApp->primaryScreen()->grabWindow(0, this->x()-7, this->y()-7, this->frameGeometry().width()+14, this->frameGeometry().height()+14); #endif QString fileName = demoName.toLower()+".png"; fileName.replace(" ", ""); pm.save("./screenshots/"+fileName); if (currentDemoIndex < 19) { if (dataTimer.isActive()) dataTimer.stop(); dataTimer.disconnect(); delete ui->customPlot; ui->customPlot = new QCustomPlot(ui->centralWidget); ui->verticalLayout->addWidget(ui->customPlot); setupDemo(currentDemoIndex+1); // setup delay for demos that need time to develop proper look: int delay = 250; if (currentDemoIndex == 10) // Next is Realtime data demo delay = 12000; else if (currentDemoIndex == 15) // Next is Item demo delay = 5000; QTimer::singleShot(delay, this, SLOT(allScreenShots())); } else { qApp->quit(); } }