Qt plotting widget

So you just want a simple plotting widget for Qt.

QCustomPlot is a rather compact Qt widget for plotting, with the possibility of extension to your needs. It has no further dependencies and is fully documented (including internal functions). If you use QtCreator you just drop a QWidget on your form as a placeholder and promote it to a QCustomPlot, and it just works. All others can create QCustomPlot widgets like any other widget in their application.

This library concentrates on making good looking, publication quality 2D plots, and offering high performance for realtime visualization applications. If you are searching for 3D plots, have a look at QwtPlot3D and MathGL.

The default license is the GPL. If you need a different license like LGPL, please contact me via E-Mail, I’m sure we’ll find an arrangement.

New version released on 9.6.2012, see the change notes and documentation for changes!

Demo Screenshots

Here are some demo screenshots showing what can be achieved with QCustomPlot in only a few lines of code. You can get the code of all those plots in the download section below, as an example project for QtCreator. People who don’t use QtCreator can profit from them equally, just look at the mainwindow.cpp file.

 

QCustomPlot supports exporting its content as a vectorized PDF or PS file and rasterized image formats like PNG, JPG and BMP. All outputs look identical and the generated PDF files can be imported and edited by vector editors like Inkscape, without trouble. So QCustomPlot is useful for both displaying of e.g. realtime data inside the application as well as producing publication quality plots for other media.

 Setup

  • Get the latest version of QCustomPlot from the download section at the bottom of this page.
  • Use the qcustomplot.h and .cpp file like any other ordinary class file

For QtCreator users

  • Right click on your root project directory in the left sidebar, click the “Add existing file” (or similar) menu item. In the appearing file dialog, select the qcustomplot.h and .cpp file, to add them to your project
  • place a QWidget on your form in the desired location. Right click on it and promote it to QCustomPlot. You won’t see any immediate visual changes in QtCreator, but while running the application, you should see an empty plot with axis and grid.

As shared library .so/.dll

If you rather want to use QCustomPlot as a shared library, i.e. don’t want to include the .h/.cpp file into your project directly but link with an external .so or .dll file, this is possible, too. QCustomPlot is ready to be built as a shared library by setting the compiler define QCUSTOMPLOT_COMPILE_LIBRARY. To use the shared library, set the define QCUSTOMPLOT_USE_LIBRARY before including the header. The sharedlib package (see download section) includes two projects that demonstrate this: one compiles the shared QCustomPlot library and the other uses the shared library.

Running the examples

The QCustomPlot.tar.gz package in the download section contains the QCustomPlot source files and the example projects ready to be compiled. Just extract the whole package to a new directory, navigate inside the example directories and run qmake; make. Alternatively you can of course open the .pro files in QtCreator and run them from there.

Documentation

The complete API documentation is available either online, or as a package in the download section. The package contains the documentation as a HTML hierarchy (the same you access online) and as a qch-help file for QtCreator/Assistant integration. If you use QtCreator or Assistant, you should definetly consider using the qch-help file, it’s great!
The integration of the qch file is pretty straight forward: Copy the qcustomplot.qch file to a place where it should be stored (e.g. the local QtCreator config directory). In QtCreator, go to the program settings and find the help section. In the tab Documentation or similar, you see a list of loaded documentation modules and some buttons to add/remove modules. Click the add button and select the qcustomplot.qch file. That’s it!
Now, when you place the cursor on any QCustomPlot related class or function, press F1 and you find help just like you know it from Qt components.

Basic usage

Here’s the concept: QCustomPlot has four axes: xAxis, yAxis, xAxis2, yAxis2 of type QCPAxis, corresponding to bottom, left, top and right axis.
You first create a graph (with QCustomPlot::addGraph), then you assign the graph some data points (e.g. as a pair of QVector<double> for x and y values) and define the look of the graph (line style, scatter symbol, color, line pen, scatter size, filling…).
finally, call QCustomPlot::replot. Note that replot will be called automatically when the widget is resized and when the built-in user interactions are triggered (basically dragging axis ranges with mouse and zooming with mouse wheel).

Here’s a minimal example, customPlot is a pointer to our QCustomPlot widget:

// generate some data:
QVector<double> 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);
customPlot->replot();

The output should look something like shown below. Note that the choice at what x and y positions the tick marks are, is done automatically. QCustomPlot takes into account, that multiples of two as well as numbers alternating between five and zero in the lowest digit are nice to look at. You can take full control over the tick-step and even the single tick mark positions by calling setTickStep or setTickVector. For disabling/enabling the automation, call setAutoTickStep or setAutoTicks. If you just want to change the approximate tick count in the visible range, while leaving the details to the algorithm, use setAutoTickCount.

You’ll see that the tick labels of the axes are not truncated even when the numbers get larger. This is due to the automatic margin calculation, which is turned on by default. If you don’t wish that the axis margin (the distance between the widget border and the axis base line) is determined automatically, switch it off with QCustomPlot::setAutoMargin. Then you can adjust the margin manually via for example QCustomPlot::setMarginLeft. analog functions exist for the top, right and bottom margins, or for all sides in one function as setMargin(left, right, top, bottom).

Changing the look

The look of the graph is characterized by many factors, all of which can be modified. Here are the most important ones:

  • Line style: Call QCPGraph::setLineStyle(QCPGraph::LineStyle ls). For all possible line styles, see the LineStyle documentation or the line style demo screenshot above.
  • Scatter symbol style: Call QCPGraph::setScatterStyle(QCP::ScatterStyle ss). For all possible scatter styles, see the ScatterStyle documentation or the scatter style demo screenshot above (ssPixmap style not shown there).
  • Scatter symbol size: floating point pixel size of the scatter symbols. QCPGraph::setScatterSize(double size)
  • Line specifics: all pens the QPainter-framework provides are available, e.g. solid, dashed, dotted, different widths, colors, transparency, etc. Set the configured pen via QCPGraph::setPen(const QPen &p)
  • Fills under graph or between two graphs: All brushes the QPainter-framework provides, solid, various patterns, textures, gradients, colors, transpareny, etc. Set the configured brush via QCPGraph::setBrush(const QBrush &b)

The look of the axis and grid lines can also be modified by changing the pens they are painted with and the fonts their labels use. A look at the documentation of QCPAxis should be self-explanatory now, here’s a quick summary of the most important properties: setBasePen, setGridPen, setSubGridPen, setZeroLinePen, setTickPen, setTickLength, setSubTickLength, setSubTickPen, setTickLabelFont, setLabelFont, setTickLabelPadding, setLabelPadding.
You can reverse an axis (e.g. make the values decrease instead of increase from left to right on a horizontal axis) with setRangeReversed.

Examples

Simple plot of two graphs

Here’s an example which creates the image of the decaying cosine function with its exponential envelope, as shown in the Demo Screenshots section.

// For simplicity we'll just setup all data and plotting options here
// 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<double> x(250), y0(250), y1(250);
for (int i=0; i<250; ++i)
{
  x[i] = i;
  y0[i] = exp(-i/150.0)*cos(i/10.0); // exponentially decaying cosine
  y1[i] = exp(-i/150.0);             // exponential envelope
}
// configure right and top axis to show ticks but no labels (could've also just called customPlot->setupFullAxesBox):
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
// make range moveable by mouse interaction (click and drag):
customPlot->setRangeDrag(Qt::Horizontal | Qt::Vertical);
customPlot->setRangeZoom(Qt::Horizontal | Qt::Vertical);
customPlot->setInteraction(QCustomPlot::iSelectPlottables); // allow selection of graphs via mouse click
customPlot->replot();

As you can see, applying a fill to a graph is as easy as setting a brush that is not Qt::NoBrush. The fill will go from the graph (here graph 0) to the zero-value-line parallel to the key (here x) axis. If we wanted a channel fill between two graphs, we would additionally call QCPGraph::setChannelFillGraph(otherGraph), where otherGraph is, you guessed it, the other graph ;) . To remove the channel fill, just pass 0 as other graph, and the fill will reach all the way to the zero-value-line as before. To remove the fill completely, call QCPGraph::setBrush(Qt::NoBrush).
For opaque channel fills you will get best looking results if the graph on which the setChannelFillGraph function is called, was created before the other graph. Because then it’s guaranteed, the other graph’s line is drawn above the border of the fill. Else, the fill might obscure half the line of the other graph, which doesn’t look very nice.

Plotting with multiple axes and more advanced styling

Now, let’s look at a more complex example for creating the demo screenshot which contains the five graphs on four axes, textured filling, vertical error bars, a legend, dots as decimal separators etc.

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->setPositionStyle(QCPLegend::psBottomRight);
customPlot->legend->setBrush(QBrush(QColor(255,255,255,230)));

// 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("./dali.png"))); // fill with texture of specified png-image
customPlot->graph(0)->setLineStyle(QCPGraph::lsLine);
customPlot->graph(0)->setScatterStyle(QCP::ssDisc);
customPlot->graph(0)->setScatterSize(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
customPlot->addGraph();
customPlot->graph(1)->setPen(QPen(Qt::red));
customPlot->graph(1)->setBrush(QBrush(QPixmap("./dali.png"))); // same fill as we used for graph 0
customPlot->graph(1)->setLineStyle(QCPGraph::lsStepCenter);
customPlot->graph(1)->setScatterStyle(QCP::ssCircle);
customPlot->graph(1)->setErrorType(QCPGraph::etValue);
customPlot->graph(1)->setName("Bottom maxwell function");

// 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(QCP::ssPlus);
customPlot->graph(4)->setScatterSize(4);
customPlot->graph(4)->setName("Some random data around\na quadratic function");

// generate data, just playing with numbers, not much to learn here:
QVector<double> x0(50), y0(50);
QVector<double> x1(25), y1(25), y1err(25);
QVector<double> x2(250), y2(250);
QVector<double> x3(250), y3(250);
QVector<double> x4(250), y4(250);
for (int i=0; i<50; ++i) // data for graph 0
{
  x0[i] = 5*i/50.0;
  y0[i] = exp(-x0[i]*x0[i]*0.8)*(x0[i]*x0[i]+x0[i]);
}
for (int i=0; i<25; ++i) // data for graph 1
{
  x1[i] = 5*i/25.0;;
  y1[i] = exp(-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] = sin(x2[i]*12)*cos(x2[i])*10;
  y3[i] = cos(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)->setDataValueError(x1, y1, 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:
QVector<double> piTicks;
QVector<QString> piLabels;
piTicks << 0  << 0.5*M_PI << M_PI << 1.5*M_PI << 2*M_PI << 2.5*M_PI << 3*M_PI;
piLabels << "0" << QString::fromUtf8("½π") << QString::fromUtf8("π") << QString::fromUtf8("1½π") << QString::fromUtf8("2π") << QString::fromUtf8("2½π") << QString::fromUtf8("3π");
customPlot->xAxis2->setAutoTicks(false);
customPlot->xAxis2->setAutoTickLabels(false);
customPlot->xAxis2->setTickVector(piTicks);
customPlot->xAxis2->setTickVectorLabels(piLabels);
// set labels:
customPlot->setTitle("Way too many graphs in one plot");
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);

As you can see, you can define freely which axis should play which role for a graph. Graph with index 0 for example uses the left axis (yAxis) as its key and the bottom axis (xAxis) as its value. Consequently the graph is standing upward against the left axis, see the screenshot from the gallery.
In order to apply error bars for graph 1, we need to enable the plotting of the corresponding error bars via QCPGraph::setErrorType which takes an argument whether the error bars are for the value, the key, both or none dimensions. Then we call one of the many QCPGraph::setData functions which take the arguments we want. Here they are keys (x1), values (y1) and value errors (y1err). For further explanation of the used methods, I once again refer to the documentation.

Plotting date/time data

Next, we’ll look at how to plot date and/or time related data, as shown in the screenshots section. It basically comes down to two additional function calls to tell an axis, it should output the labels as dates/times in lines 28 and 29 :

// 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().toTime_t();
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();
  QPen pen;
  pen.setColor(QColor(0, 0, 255, 200));
  customPlot->graph()->setLineStyle(QCPGraph::lsLine);
  customPlot->graph()->setPen(pen);
  customPlot->graph()->setBrush(QBrush(QColor(255/4.0*gi,160,50,150)));
  // generate random walk data:
  QVector<double> time(250), value(250);
  for (int i=0; i<250; ++i)
  {
    time[i] = now + 24*3600*i;
    if (i == 0)
      value[i] = (i/50.0+1)*(rand()/(double)RAND_MAX-0.5);
    else
      value[i] = fabs(value[i-1])*(1+0.02/4.0*(4-gi)) + (i/50.0+1)*(rand()/(double)RAND_MAX-0.5);
  }
  customPlot->graph()->setData(time, value);
}
// configure bottom axis to show date and time instead of number:
customPlot->xAxis->setTickLabelType(QCPAxis::ltDateTime);
customPlot->xAxis->setDateTimeFormat("MMMM\nyyyy");
// 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 a fixed tick-step to one tick per month:
customPlot->xAxis->setAutoTickStep(false);
customPlot->xAxis->setTickStep(2628000); // one month in seconds
customPlot->xAxis->setSubTickCount(3);
// apply manual tick and tick label for left axis:
QVector<double> tickVector;
QVector<QString> tickLabels;
tickVector << 5 << 55;
tickLabels << "Not so\nhigh" << "Very\nhigh";
customPlot->yAxis->setAutoTicks(false);
customPlot->yAxis->setAutoTickLabels(false);
customPlot->yAxis->setTickVector(tickVector);
customPlot->yAxis->setTickVectorLabels(tickLabels);
// 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);
// activate legend and position it in top left corner:
customPlot->legend->setVisible(true);
customPlot->legend->setPositionStyle(QCPLegend::psTopLeft);

The string you pass to QCPAxis::setDateTimeFormat() has the same date formatting options as the string passed to QDateTime::toString(), see Qt docs.
All date/times are handled as seconds since midnight 1. January 1970, UTC. This is the format you use, when calling QDateTime::toTime_t or setTime_t on the Qt date/time classes. For sub-second accuracy, you can use QDateTime::toMSecsSinceEpoch()/1000.0, which results in a double value representing the same timespan as toTime_t returns, but with millisecond accuracy.

For more of those demo codes, see the examples package in the download section below.

Beyond Graphs: Curves, Bar Charts, Statistical Box Plot,…

Up to now we’ve only looked at graphs. Since they are such a dominant use case, QCustomPlot offers a specialized interface for them. We’ve been using it all the time: QCustomPlot::addGraph, QCustomPlot::graph etc. But that’s not the the whole story.

QCustomPlot has a more general interface for classes that draw data inside the plot, I call them Plottables. This interface is built around the abstract base class QCPAbstractPlottable. All Plottables derive from this class, also our familiar QCPGraph class.

Currently, QCustomPlot offers four different plottable classes:

  • QCPGraph: That’s the plottable class we’ve been using. Displays a series of data points as a graph with different line styles, filling, scatters and error bars.
  • QCPCurve: Similar to QCPGraph with the difference that it’s made for displaying parametric curves. Unlike function graphs, they may have loops.
  • QCPBars: A Bar Chart. Takes a series of data points and represents them with bars. If there are multiple QCPBars plottables in the plot, they can be stacked on top of each other, as shown in the screenshot section.
  • QCPStatisticalBox: A Statistical Box Plot. Takes a five-number-summary (minimum, lower quartile, median, upper quartile, maximum) and represents it as a statistical box. Outliers can also be displayed.

Unlike graphs, other plottables need to be created with new outside of QCustomPlot and then added with QCustomPlot::addPlottable. (I.e. there is no addCurve or addBars function as there is an addGraph function.) QCustomPlot takes ownership of the passed plottable. Existing plottables can be accessed with QCustomPlot::plottable(int index), the total number of plottables in the plot (including graphs) can be retrieved with QCustomPlot::plottableCount.

Here’s a quick example that creates a bar chart with three bars:

QCPBars *myBars = new QCPBars(customPlot->xAxis, customPlot->yAxis);
customPlot->addPlottable(myBars);
// now we can modify properties of myBars:
myBars->setName("Pretty bars");
QVector<double> keyData;
QVector<double> valueData;
keyData << 1 << 2 << 3;
valueData << 2 << 4 << 8;
myBars->setData(keyData, valueData);
customPlot->rescaleAxes();
customPlot->replot();

More examples can be found in the example project. Further, each plottable type has a more detailed description on the documentation page of the respective class.

Of course, it’s absolutely possible to write your own plottable to make any data look exactly the way you need it. You should look at the QCPAbstractPlottable documentation for a guide how to start subclassing it. Further, you can look at the other plottables to see how they work. For that purpose, I recommend QCPBars or QCPCurve for a start. QCPGraph is quite feature rich and thus probably not suitable as a starting point.

Items

QCustomPlot allows placing and anchoring of graphical elements such as text, arrows, lines, rectangles, arbitrary pixmaps etc. on the plot. Items of this kind are based on the abstract base class QCPAbstractItem. A detailed description of the item mechanism and what built-in items are currently available can be found in the documentation of QCPAbstractItem.

Here’s a simple example showing how to create a text label that always is positioned at the top of the axis rect and an arrow that connects a point in plot coordinates with that label.

customPlot->setRangeDrag(Qt::Horizontal|Qt::Vertical);
customPlot->setRangeZoom(Qt::Horizontal|Qt::Vertical);

// add the text label at the top:
QCPItemText *textLabel = new QCPItemText(customPlot);
customPlot->addItem(textLabel);
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);
customPlot->addItem(arrow);
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);

As with plottables, it is easy to create own items, too, by making your own subclass of QCPAbstractItem. See the subclassing section in the documentation there.

User interaction

QCustomPlot offers multiple built-in user interactions. They include axis range dragging as mentioned earlier, but also detecting clicks/double clicks on objects as well as a complete selection mechanism for plottables, axes, axes ticks, axes labels, legend, legend items etc.

The availability of these interactions is controlled with QCustomPlot::setInteractions. For details about the interaction system, see the documentation there. QCustomPlot always emits corresponding signals, when objects are clicked or double clicked. See the signals plottableClick, plottableDoubleClick and axisClick for example.

The examples include one project which makes extensive use of various aspects of the interaction system, and explains how to fine tune the behaviour to fit ones needs.

Unfinished but planned features

  • A Candlesticks plottable or other financial charting
  • Plottables for other data representation concepts, like bubble charts.
  • Plottables for two-dimensional data as color map, vector map and contour lines.

But the first and foremost aim of QCustomPlot is being easy and intuitive to use, as well as extendable by anybody who wants to. Because then its versatility is much greater than by implementing every thinkable feature. If I ever feel a feature would conflict with this aim and make the library less user-friendly, I won’t implement it.

Download

Release Date: 09.06.2012
QCustomPlot source and example files: QCustomPlot.tar.gz
QCustomPlot source only: QCustomPlot-source.tar.gz
QCustomPlot shared library: QCustomPlot-sharedlib.tar.gz
QCustomPlot documentation: QCustomPlot-doc.tar.gz
QCustomPlot change notes: changenotes.txt

Older releases

Release Date: 31.03.2012
QCustomPlot source and example files: QCustomPlot-12-03-31.tar.gz
QCustomPlot source only: QCustomPlot-source-12-03-31.tar.gz
QCustomPlot documentation: QCustomPlot-doc-12-03-31.tar.gz

Quo Vadis QCP?

The QCustomPlot library is maturing fast and is already being used by many companies, universities, research centers and individual developers.

The next release will concentrate on consolidation, expanding the unit tests, cleaning up and some refactoring. It will be QCustomPlot version 1.0.0, replacing the previous versioning scheme following release dates. At the same time the code base will move from my local repository to a public git repository on gitorious.

514 comments on “Qt plotting widget

    • first, in the designer view, right click on a your button on the form, “go to slot”, “clicked()”.

      if you want to get a new graph:
      void mainWindow::pushbuttonClicked(){
      QCPGraph* graph = ui->plot->addGraph();
      graph->addData(…);

      ui->plot->replot();
      }

      if you want a new dot on a previous graph:
      void mainWindow::pushbuttonClicked(){
      QCPGraph* graph = ui->plot->graph();
      graph->addData(x, y);

      ui->plot->replot();
      }

  1. Hello, at first I want to say that this is a great way to plot data in QT. I have a question.
    Is qcustomplot thread safe?
    And when do you plan to release qcustomplot for qt 5.0.x?
    Thank you

  2. Hello DerManu, i need to do a polar graph i am trying to do it by drawning lines with spikearrows in the end.
    I need the graph area to be a square so i don’t have scale problems. Any idea on how to do it? I tried to set the customplot geometry to a square but the graph area , as expected, doesnt stay square.

  3. Dear DerManu,
    I have read your 14 pages of comments and there is a lot of useful information! It would be good to have it as a FAQ in your page (hehe it would probably be a lot of work :P )

    Anyway i have some performance issue in my embedded system when plotting too many points. I am declaring 8 QCBars each of them with their own dataset, they have 768 points each and only 4 can be visible at given time.

    I’ve already done this steps:
    -setNoAntialiasingOnDrag
    -Use pen width as 1

    Do you have other suggestions? It seems to me that the problem is that even though i set only 1 QCBar as visible all of them still get reploted when i call myGraph->replot(), since the rescale function uses the higher range of my qcpbars (even when they are not visibile)

    Another problem i see is that i can drag for ever the graph, the drag function doesnt stop in my highest X or Y value, is there any turnaround for this? I can drag for ever even if there is no points.

    Thank you very much for your support and work!

    • Plottables (in your case bars) that have their visibility disabled aren’t drawn and don’t cost performance. The fact that you see the scaling as if they were all visible is just the way QCustomPlot::rescaleAxes works in the current release (the upcoming release will change this). If you want to prevent that, just go through all visible bars and call QCPBars::rescaleAxes on them individually.
      To the performance: Depending on the power of the embedded system different measures may improve your performance. Do you use semi-transparent fills under the bars? That can be a performance limitation.

      Limiting the range dragging is up to you. You could for example connect a slot to the beforeReplot signal and just limit the range of the respective axes to whatever interval you like. Or you could directly connect such a slot to the rangeChanged signal of the respective axes.

  4. The last suggestion you gave me worked pretty well.
    Is there anyway to plot the QCBars side by side instead of stacked on the top of each other? The example doesnt show how (if there is a way of course)

    thanks

  5. Very useful and very easy to use ! QCP is a must have !

    I was wondering if it is possible to adjust properties of scatter according to a third set of data. For example, I would like to set the size a scatter according to a f(x,y).
    I suppose I can do it by using multiple graphs, but if you have another solution…

    Keep on doing what you do, that’s worth it ;)

    Kevin (Yeah, I Know !)

    • In my opinion that’s a classical case for a custom plottable. Because this requires the data point to store values of that extra dimension, which can’t be done flexibly in a general way (It would require each data point to be stored in a base class with virtual functions – that kills performance). So in the cases where you wish to display so strongly different things from what existing plottables offer, you should make your own, specialized plottable.

  6. DerManu thanks for your attention and there goes my last question.
    I am doing a bar chart with a lot of QBars, some of them have phase valors and others have magnitude.
    The range of values is very different and when i setVisibile(false) for magnitude QCBars the autoscale doesnt scale for the phase values, what can i do about this? Any suggestions?

    Magnitude values are around 100 and phase from -3.14 to 3.14

    • Use the individual rescale functions on the bars. So if you only want the rescale to happen for the bars that show phase values, call rescaleAxes on that QCPBars instance.

      The next QCustomPlot release will have a QCustomPlot::rescaleAxes function that takes a parameter onlyVisible, which will make this slightly more comfortable.

  7. Hello i have another doubt, i am trying to fill a QCPbar with the same color if the QPen but it doesnt work, it stays black always, is it a bug?

    barras_mag_Ua = new QCPBars(this->ui->grafico->xAxis, this->ui->grafico->yAxis);
    QPen pen_ua(Qt::red, 2, Qt::SolidLine);
    QBrush qb_ua(Qt::SolidPattern);
    qb_ua.setColor(Qt::red);
    barras_ang_Ua->setPen(pen_ua);
    barras_ang_Ua->setBrush(qb_ub);

    By doing this it fills with black color.
    Thank you very much for your help, your library is very good!

  8. DerManu, how can i zoom without using the mouse wheel? I want to port my application to a touchscreen beaglebone and there is no such thing on a touchscreen.

    In QWTPLOT i can do an area zoom by clicking somewhere and dragging to other place, pretty much like a smartphone.

    Thank you very much

    • On touch devices you usually get signals for e.g. pinching touch events. You can react to those and set the axis ranges accordingly, to fit the desired effect. See QTouchEvent.

      • I emulated your wheel zoom in the onclick event when setzoomonclick is enabled (a method i introduced in ur library), works like a charm hehe.

Leave a Comment

Email address is optional and will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>