QCustomPlot使用详解(二)

说明

这篇主要介绍QCustomPlo的整体实现思路,理解是非常重要的。

QCustomPlot类

在这里插入图片描述
QCustomPlot类是直接面向用户来使用。具有默认的图层展示。
可以参见其源码:

QCustomPlot::QCustomPlot(QWidget *parent) :
  QWidget(parent),
  xAxis(0),
  yAxis(0),
  xAxis2(0),
  yAxis2(0),
  legend(0),
  mBufferDevicePixelRatio(1.0), // will be adapted to primary screen below
  mPlotLayout(0),
  mAutoAddPlottableToLegend(true),
  mAntialiasedElements(QCP::aeNone),
  mNotAntialiasedElements(QCP::aeNone),
  mInteractions(0),
  mSelectionTolerance(8),
  mNoAntialiasingOnDrag(false),
  mBackgroundBrush(Qt::white, Qt::SolidPattern),
  mBackgroundScaled(true),
  mBackgroundScaledMode(Qt::KeepAspectRatioByExpanding),
  mCurrentLayer(0),
  mPlottingHints(QCP::phCacheLabels|QCP::phImmediateRefresh),
  mMultiSelectModifier(Qt::ControlModifier),
  mSelectionRectMode(QCP::srmNone),
  mSelectionRect(0),
  mOpenGl(false),
  mMouseHasMoved(false),
  mMouseEventLayerable(0),
  mMouseSignalLayerable(0),
  mReplotting(false),
  mReplotQueued(false),
  mOpenGlMultisamples(16),
  mOpenGlAntialiasedElementsBackup(QCP::aeNone),
  mOpenGlCacheLabelsBackup(true)
{
  setAttribute(Qt::WA_NoMousePropagation);
  setAttribute(Qt::WA_OpaquePaintEvent);
  setFocusPolicy(Qt::ClickFocus);
  setMouseTracking(true);
  QLocale currentLocale = locale();
  currentLocale.setNumberOptions(QLocale::OmitGroupSeparator);
  setLocale(currentLocale);
#ifdef QCP_DEVICEPIXELRATIO_SUPPORTED
#  ifdef QCP_DEVICEPIXELRATIO_FLOAT
  setBufferDevicePixelRatio(QWidget::devicePixelRatioF());
#  else
  setBufferDevicePixelRatio(QWidget::devicePixelRatio());
#  endif
#endif
  
  mOpenGlAntialiasedElementsBackup = mAntialiasedElements;
  mOpenGlCacheLabelsBackup = mPlottingHints.testFlag(QCP::phCacheLabels);
  // create initial layers:
  mLayers.append(new QCPLayer(this, QLatin1String("background")));
  mLayers.append(new QCPLayer(this, QLatin1String("grid")));
  mLayers.append(new QCPLayer(this, QLatin1String("main")));
  mLayers.append(new QCPLayer(this, QLatin1String("axes")));
  mLayers.append(new QCPLayer(this, QLatin1String("legend")));
  mLayers.append(new QCPLayer(this, QLatin1String("overlay")));
  updateLayerIndices();
  setCurrentLayer(QLatin1String("main"));
  layer(QLatin1String("overlay"))->setMode(QCPLayer::lmBuffered);
  
  // create initial layout, axis rect and legend:
  mPlotLayout = new QCPLayoutGrid;
  mPlotLayout->initializeParentPlot(this);
  mPlotLayout->setParent(this); // important because if parent is QWidget, QCPLayout::sizeConstraintsChanged will call QWidget::updateGeometry
  mPlotLayout->setLayer(QLatin1String("main"));
  QCPAxisRect *defaultAxisRect = new QCPAxisRect(this, true);
  mPlotLayout->addElement(0, 0, defaultAxisRect);
  xAxis = defaultAxisRect->axis(QCPAxis::atBottom);
  yAxis = defaultAxisRect->axis(QCPAxis::atLeft);
  xAxis2 = defaultAxisRect->axis(QCPAxis::atTop);
  yAxis2 = defaultAxisRect->axis(QCPAxis::atRight);
  legend = new QCPLegend;
  legend->setVisible(false);
  defaultAxisRect->insetLayout()->addElement(legend, Qt::AlignRight|Qt::AlignTop);
  defaultAxisRect->insetLayout()->setMargins(QMargins(12, 12, 12, 12));
  
  defaultAxisRect->setLayer(QLatin1String("background"));
  xAxis->setLayer(QLatin1String("axes"));
  yAxis->setLayer(QLatin1String("axes"));
  xAxis2->setLayer(QLatin1String("axes"));
  yAxis2->setLayer(QLatin1String("axes"));
  xAxis->grid()->setLayer(QLatin1String("grid"));
  yAxis->grid()->setLayer(QLatin1String("grid"));
  xAxis2->grid()->setLayer(QLatin1String("grid"));
  yAxis2->grid()->setLayer(QLatin1String("grid"));
  legend->setLayer(QLatin1String("legend"));
  
  // create selection rect instance:
  mSelectionRect = new QCPSelectionRect(this);
  mSelectionRect->setLayer(QLatin1String("overlay"));
  
  setViewport(rect()); // needs to be called after mPlotLayout has been created
  
  replot(rpQueuedReplot);
}

QcustomPlot类中包含其他功能模块,来进行布局和绘制。需要理解各个类的关系,其中QCPLayer类继承于QObject类。QCPLayout是布局的基类。各个图表的绘制建立在坐标轴之上,其中坐标轴的绘制建立在布局窗口之上,一个QCustomPlot可以有多个布局窗口,一个布局窗口可以包含多个样式坐标轴。

类图

类图
QCPLayerable是所有可绘制对象的基类,包括网格,坐标轴,装饰器,图标。关于具体的实现可以参见官网例子。

布局

先上一张官网图片
在这里插入图片描述
其中最外层是顶层窗口的边缘,每个布局和相邻的窗口都有默认的间隔(每个布局都有自己的索引,布局中可以插入子布局),同样布局中的元素也有默认的间隔(每个元素都有自己的索引)。正是由于源码这种灵活的实现,使得我们可以在布局上有极大的自主权。

数据关系

说一下,它内部是如何实现的。每个图表都有自己对应的数据类:
比如QCPGraph内部维护着QCPGraghData的数组。当我们进行绘制的时候,取出其中的数据进行每个点的绘制。同样当我们想获取某个区域,或者某个像素点的的数据时,可以先进行像素点到坐标轴(参见QCPAxis类函数)的数据转换,然后通过相应图表的接口函数获取当前区域或者点的值。如下

QCPDataSelection selection = graph->selection();
  double sum = 0;
  foreach (QCPDataRange dataRange, selection.dataRanges())
  {
    QCPGraphDataContainer::const_iterator begin = graph->data()->at(dataRange.begin()); // get range begin iterator from index
    QCPGraphDataContainer::const_iterator end = graph->data()->at(dataRange.end()); // get range end iterator from index
    for (QCPGraphDataContainer::const_iterator it=begin; it!=end; ++it)
    {
      // iterator "it" will go through all selected data points, as an example, we calculate the value average
      sum += it->value;
    }
  }
  double average = sum/selection.dataPointCount();

获取选中区域的数据,首先要设置选中属性。这个逻辑可以写在鼠标弹起事件中。

QCPGraphDataContainer::const_iterator it = graph->data()->constEnd();
  QVariant details;
  if (graph->selectTest(QPoint(123, 456), false, &details)) // QPoint could be e.g. event->pos() of a mouse event
  {
    QCPDataSelection dataPoints = details.value<QCPDataSelection>();
    if (dataPoints.dataPointCount() > 0)
      it = graph->data()->at(dataPoints.dataRange().begin());
  }
  // iterator "it" now carries the data point at pixel coordinates (123, 456), or constEnd if no data point was hit.

这个逻辑可以写在鼠标点击事件和鼠标弹起事件中

结束语

如何想要绘制出比较好看的图,还是要图层元素的属性有详细的了解。
附上官网
文档

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值