QCustomPlot之绘图的基础

来源:互联网 发布:生态科技成大数据企业 编辑:程序博客网 时间:2024/05/16 19:38
使用QCustomPlot绘图的基础
首先你用QCustomPlot::addGraph创建一个曲线图然后你给曲线图赋一些数据点(一对QVector<double>为x,y的值)并且定义曲线图的外观(线型,分散图案,颜色,线笔)子厚调用QCustomPlot::replot。注意replot应该被自动调用当widget被重新改变大小的时候或者内建的用户交互被触发的时候(拖拽坐标轴范围用鼠标或者齿轮),莫ten情况下QCustomPlot有四个类型为QCPAxis的坐标轴:xAxis,yAxis,xAxis2,yAxis2。组成了下,左,上,右四个坐标轴。他们的范围(QCPAxis::setRange)定义了Plot当前可见的部分。


这有一个小例子customPlot是一个QCustomPlot widget的指针。
// generate some data:QVector<double> x(101), y(101); // initialize with entries 0..100for (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();


输出应该看起来跟下面一样。注意刻度线的位置是被自动选择的。然而乜可以完全控制刻度步长甚至单一刻度位置通过调用setTickStep或者setTickVector在各自的轴上。为了禁用和可用自动化,电泳setAutoTickStep或
</pre>者setAutoTicks。如果你想提供自己的刻度步长或者刻度vector,禁用自动化将是必须的。如果你只是想在可见的范围里改变合适的刻度数,可以使用setAutoTickCount.你将看到坐标轴的刻度标签没有被裁剪甚至它们变宽了.这是因为自动化的边缘计算由默认的数据决定。如果你不希望axis的margin自动决定,用过调用customPlot->axisRect()->setAutoMargins(QCP::msNone)来关闭。然后可以通过QCPAxisRect::setMargins来手动调整margins.改变外观graph有很多影响外观的因素,所有的都可以被修改。这有重要的几点Line style:调用QCPGraph::setLineStyle。对所有可能的Line Style你可以查看LineStyle文档。Line Pen:绘图框架的所有Pen都是可获取的,solid,dashed,dotted,不同的宽度,颜色,透明度,等等。通过QCPGraph::setPen设置配置好的Pen.Scatter symbol调用QCPGraph::setScatterStyle来改变分散点符号的外观。所以可能的scatter style可以查看QCPScatterStyle文档。Fills under graph or between two graphs:绘图框架的所有画刷都可以被用来填充graph:solid,各种各样的模式,纹理,渐变,颜色,透明度等等通过QCPGraph::setBrush来设置配置好的Brush.坐标轴的外观可以被修改通过改变绘图的画笔和它们label的字体。看一看QCPAxis文档就不言而喻了。这有一个很快的总结:setBasePen,setTickPen,setTickLength,setSubTickLength,setSubTickPen,setTickLabelFont,setLabelFont,setTickLabelPadding,setLabelPadding。你可以反转一个坐标轴使用setRangeReversed.栅格可以被修改通过使用单独的QCPGrid实例。每一个坐标轴都有自己的栅格实例QCPAxis::grid()因此改变水平的栅格线可以通过访问customPlot->yAxis->grid()来实现。栅格线外观的基础地的画笔可以使用QCPGrid::setPen来设置。0刻度可以使用不同的画笔绘出来,使用QCPGrid::setZeroLinePen来配置。如果你不想画0刻度用特殊的笔只需要设置为Qt::NoPen并且栅格线将会使用普通的画笔绘制。Examples有两个曲线图的Plot这有一个例子创建一个衰减的cosin函数图像.<span style="white-space:pre"></span><pre name="code" class="cpp">// add two new graphs and set their look:customPlot->addGraph();customPlot->graph(0)->setPen(QPen(Qt::blue)); // line color blue for first graphcustomPlot->graph(0)->setBrush(QBrush(QColor(0, 0, 255, 20))); // first graph will be filled with translucent bluecustomPlot->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:// (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);


正如你看到的,应用填充凸显跟设置一个画刷一样容易但不是Qt::NoBrush.填充从图像开始到与坐标轴平行的0值线。如果我们想要在一个graph和另一个graph之间填充的通道可以调用QCPGraph::setChannelFillGraph(otherGraph).为了删除通道填充,只需要将零值线作为另一个图形并且填充将可以达到所有的方式来把之前的作为零值线。为了完全移除填充调用QCPGraph::setBrush(Qt::NoBrush);
Plotting with multiple axes and more advanced styleing
绘制多轴和更高级的样式
现在看一些复杂的例子为了创建演示截图,在四个坐标轴上包括了五个graphs,纹理填充,垂直误差,图例,点作为小数分隔符等等。
ustomPlot->setLocale(QLocale(QLocale::English, QLocale::UnitedKingdom)); // period as decimal separator and comma as thousand separatorcustomPlot->legend->setVisible(true);QFont legendFont = font();  // start out with MainWindow's font..legendFont.setPointSize(9); // and make a bit smaller for legendcustomPlot->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 functioncustomPlot->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-imagecustomPlot->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 functioncustomPlot->addGraph();customPlot->graph(1)->setPen(QPen(Qt::red));customPlot->graph(1)->setBrush(QBrush(QPixmap("./dali.png"))); // same fill as we used for graph 0customPlot->graph(1)->setLineStyle(QCPGraph::lsStepCenter);customPlot->graph(1)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, Qt::red, Qt::white, 7));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 2customPlot->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 perturbancecustomPlot->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<double> x0(25), y0(25);QVector<double> x1(15), y1(15), y1err(15);QVector<double> x2(250), y2(250);QVector<double> x3(250), y3(250);QVector<double> x4(250), y4(250);for (int i=0; i<25; ++i) // data for graph 0{  x0[i] = 3*i/25.0;  y0[i] = exp(-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] = 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);// add title layout element:customPlot->plotLayout()->insertRow(0);customPlot->plotLayout()->addElement(0, 0, new QCPPlotTitle(customPlot, "Way too many graphs in one plot"));// 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);



正如你所见,你可以自由的定义那个轴作为对graph起作用。例子中索引为0的graph使用了左轴作为它的key并且下轴作为它的值。因此graph是朝着左轴向上的。
为了对graph1应用误差线,我们需要确保它们设置了QCPGraph::setErrorType().它常用来指定是否为这个值,键,或者两者或者都不显示误差线。然后我们可以调用QCPGraph::setData函数将传递我们想要的参数。这有keys(x1),value(y1)和误差值(ylerr)。为了进一步的解释这个方法的使用可以查看文档。
Plotting date and time data
下面,我们将看到如何绘制日期和时间关联的数据。它基本可以归结为两个额外的函数调用来通知坐标轴,它应该输出标签作为日期或者时间。
// 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 secondscustomPlot->xAxis->setSubTickCount(3);// apply manual tick and tick label for left axis:customPlot->yAxis->setAutoTicks(false);customPlot->yAxis->setAutoTickLabels(false);customPlot->yAxis->setTickVector(QVector<double>() << 5 << 55);customPlot->yAxis->setTickVectorLabels(QVector<QString>() << "Not so\nhigh" << "Very\nhigh");// 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:customPlot->legend->setVisible(true);



你可以给QCPAxis::setDateTimeFormate()传递一个字符串有相同的格式选项跟QDateTime::toString。所有的date/time都被作为秒来控制从1970.1.1 凌晨12点开始。这就是你使用的格式,当调用QDateTime::toTime_t或者setTime_t在Qt date/time类中。为了达到次秒级的精度,你可以使用QDateTime::toMSecsSinceEpoch()/1000.0结果是double值代表与toTime_t返回一样的时间间隔,但是是毫秒级的精度。
Beyond Graphs:Curves,Bar Charts,Statistical Box Plot...
到目前位置我们只看了Graph.因为它们是这样一个占主导地位的用例,QCustomPlot为他们提供一个特化的接口。我们始终使用它QCustomPlot::addGraph,QCustomPlot::graph.这不是故事的全部。QCustomPlot有更一般的接口来为不同的对象在Plot里绘制数据。我可以称它们为Plottables.这个接口围绕抽象基类QCPAbstractPlottable被创建。所有的Plottables派生自这个类并且跟QCPGraph这个类相似。QCustomPlot提供了很多其他的Plottable类。
QCPGraph
这是一个plottable类,显示一些列数据点作为graph.
QCPCurve
跟QCPGraph类似用不同的参数化的曲线组成。不像函数graph它们有封闭的环形。
QCPBar
一个柱状图。带有一系列的数据点和代表它们的柱状图。如果这有多个QCPBars plottables.他们可以堆叠在彼此顶方。
QCPStatisticalBox
一个统计框。带着五个数字(minium,lower quartile,midian,upper quartile,maximum)。
QCPColorMap
一个2D map可视化第三维度的数据通过使用一个渐变颜色。
QCPFinancial
一个plottable可以被用来可视化像股票的开盘价,最高,最低,收盘价通过使用Candlesticks或者OHLC bars


不像graphs,其他的plottables需要通过使用外面的QCustomPlot来创建并且把它们添加到里面QCustomPlot::addPlottable.这意味着这没有addCurver或者addBar函数。QCustomPlot传递plottable的所有权。已经存在的plottables可以通过QCustomPlot::plottable(int index)来访问。plottable包括graph的数量可以使用QCustomPlot::plottableCount来检索。这有一个很简单的例子创建一个带有三个柱状的柱状图。
QCPBars *myBars = new QCPBars(customPlot->xAxis, customPlot->yAxis);customPlot->addPlottable(myBars);// now we can modify properties of myBars:myBars->setName("Bars Series 1");QVector<double> keyData;QVector<double> valueData;keyData << 1 << 2 << 3;valueData << 2 << 4 << 8;myBars->setData(keyData, valueData);customPlot->rescaleAxes();customPlot->replot();



关于其他plottables更多的细节可以在实例里面被发现。更进一步,每一个plottable类型都在文档里有详细的描述。
当然,这是绝对可以的编写你自己的plottable来显示你需要的任意数据。你应该查看QCPAbstractPlottable文档来学习如何子类化它。你也可以查看已经存在的plottables来看他们如何工作。为了这个目的我建议从QCPBar或者QCPCurve开始。QCPGraph特征很多不适合作为一个开始。
0 0