QtCharts:如何用误差线绘制线系列

问题描述

我已经看到有人问过这个问题:

令人惊讶的是,我没有找到解决方案(嗯,qcustomplot 有这个功能,但我们不允许在我的公司使用它)。我的任务是将一些科学数据表示为线图,误差线表示数据标准偏差。我尝试了一些技巧来解决它,但结果对于我们的应用程序来说还不够:

  • 为数据均值创建一个QLineSeries,然后添加N个QLineSeries来表示误差线(见下例中的approachWithMultipleQLineSeries

    results

    );这显然是一种矫枉过正,我的情节在较慢的机器上出现故障(使用 Windows :/)
  • 为数据平均值创建一个QLineSeries,然后添加QBoxPlotSeries来表示误差线(参见下面示例中的approachWithQBoxSet

    results

    );这不再出现故障,但误差线与线图不太对应(例如查看接近于零的值),可能是因为 Boxes 是为稍微不同的 X 轴设计的(带有类别,而不是浮点数);

您能否提出一些更好的方法,其中误差条值实际上与 X 值和平均 Y 值相关联(可能是子类 QXYSeries 或类似的,我没有任何好主意如何准确地做到这一点)?

MCVE 如果有人想尝试:

CMakeLists.txt

cmake_minimum_required(VERSION 3.11.0)
project(QtChartsErrBars CXX)

set(CMAKE_CXX_STANDARD 17)
find_package(Qt5 COMPONENTS Charts Core Widgets REQUIRED)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
add_executable(QtChartsErrBars
  main.cpp
  Demo.cpp
  Demo.hpp
)
target_link_libraries(QtChartsErrBars
  Qt5::Charts
  Qt5::Core
  Qt5::Widgets
)
target_compile_options(QtChartsErrBars PUBLIC -g)

Demo.hpp

#pragma once

/**
 * /author Pawel Ptasznik
 */

#include <QMainWindow>
#include <QWidget>

#include <memory>

class Demo : public QMainWindow
{
  Q_OBJECT
public:
  explicit Demo(QWidget* parent = nullptr);
  ~Demo() override = default;
};

Demo.cpp


#include "Demo.hpp"

#include <QDebug>
#include <QVector>
#include <QtCharts/QChartView>
#include <QtCharts/QLineSeries>
#include <QtCharts/QAreaSeries>
#include <QtCharts/QLegendMarker>
#include <QtCharts/QBoxSet>
#include <QtCharts/QBoxPlotSeries>

#include <algorithm>
#include <random>

static std::random_device rd{};
static std::mt19937 gen{ rd() };

// for each input generate n samples using normal distribution with expected value == x^2
QVector<QVector<double> > generateData(QVector<double> in,int nSamples)
{
  QVector<QVector<double> > results(in.size());
  for (int i = 0; i < in.size(); i++)
  {
    results[i].resize(nSamples);
    std::normal_distribution<double> d{ in[i]*in[i],1 };
    for (auto& sample : results[i])
    {
      sample = d(gen);
    }
  }
  return results;
}

/************* HERE the solutions I have tried and I do not like ************/
namespace
{
class VerticalLine : public QtCharts::QLineSeries
{
public:
  VerticalLine(double x,double yMin,double yMax)
  {
    replace(QVector<QPointF>{ { x,yMin },{ x,yMax } });
  }
};
}
void approachWithMultipleQLineSeries(QtCharts::QChart *chart,const QVector<double>& x,const QVector<double>& yMean,const QVector<double>& yStdDev)
{
  for (int i = 0; i < x.size(); i++)
  {
    auto series = new VerticalLine(x[i],yMean[i]-yStdDev[i],yMean[i]+yStdDev[i]);
    chart->addSeries(series);
    chart->legend()->markers(series)[0]->setVisible(false);
  }
}

void approachWithQBoxSet(QtCharts::QChart *chart,const QVector<double>& yStdDev)
{
  QList<QtCharts::QBoxSet*> boxList;
  for (int i = 0; i < x.size(); i++)
  {
    boxList.push_back(new QtCharts::QBoxSet(yMean[i]-yStdDev[i],yMean[i]-yStdDev[i]/2,yMean[i],yMean[i]+yStdDev[i]/2,yMean[i]+yStdDev[i]));
  }
  auto series = new QtCharts::QBoxPlotSeries();
  series->append(boxList);
  series->setBoxWidth(0.1);
  series->attachAxis(chart->axes(Qt::Horizontal)[0]);
  chart->addSeries(series);
  chart->legend()->markers(series)[0]->setVisible(false);
}

/************* HERE quick example that plots quadratic function over 200 points ************/
Demo::Demo(QWidget* parent): QMainWindow(parent)
{
  QVector<double> xAxisValues(200);  // lets have 200 points between -10 and 10
  std::generate(xAxisValues.begin(),xAxisValues.end(),[x = -10.0]() mutable { return x += 0.1; });
  QVector<double> yAxisMeanValues(xAxisValues.size());
  QVector<double> yAxisStdDev(xAxisValues.size());
  // lets generate 50 samples for each x value
  auto data = generateData(xAxisValues,50);
  // calculating mean values
  std::transform(data.begin(),data.end(),yAxisMeanValues.begin(),[](const auto& samples)
  {
    double sum = 0.0;
    for (auto sample : samples) sum += sample;
    return sum / samples.size();
  });
  // calculating standard deviation
  std::transform(data.begin(),yAxisStdDev.begin(),[](const auto& samples,auto mean)
  {
    double var = 0.0;
    for (auto sample : samples) var += (sample - mean)*(sample - mean);
    var /= samples.size();
    return std::sqrt(var);
  });
  QtCharts::QLineSeries *series = new QtCharts::QLineSeries();
  for (int i = 0; i < xAxisValues.size(); i++)
  {
    series->append(xAxisValues[i],yAxisMeanValues[i]);
  }
  QtCharts::QChart *chart = new QtCharts::QChart();
  chart->addSeries(series);
  // OK,now how do I add the error bars basing on x,yMean,yStdDev?
  // approachWithMultipleQLineSeries(chart,xAxisValues,yAxisMeanValues,yAxisStdDev);
  chart->createDefaultAxes();
  approachWithQBoxSet(chart,yAxisStdDev);
  chart->setTitle("Demo chart");
  QtCharts::QChartView *chartView = new QtCharts::QChartView(chart);
  chartView->setRenderHint(QPainter::Antialiasing);
  setCentralWidget(chartView);
  resize(400,300);
}

main.cpp


#include "Demo.hpp"

#include <QApplication>


int main(int argc,char** argv)
{
  QApplication a(argc,argv);
  Demo mainWindow;
  mainWindow.show();
  return a.exec();
}

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...