QCustomPlot 之柱状图,多个参数并排显示数值

QCustomPlot 之柱状图

一、使用环境

  1. Qt+vs2017+QCustomPlot
  2. QCustomPlot的使用,只需引入.cpp和.h即可
  3. 遇到的问题:
    Link2019 “__declspec(dllimport) public: __cdecl QPrinter::QPrinter(enum QPrinter::PrinterMode)” (_imp??0QPrinter@@QEAA@W4Printe
    后来发现是少导入模块
    QCustomPlot 之柱状图,多个参数并排显示数值_第1张图片
    二、可以去官网参考demo
    链接: https://www.qcustomplot.com/index.php/demos/barchartdemo
    效果图:
    QCustomPlot 之柱状图,多个参数并排显示数值_第2张图片
    ps demo例子给的是堆砌在一起的

三、自己改写代码

  1. 目的:在官网的例子上,改成多个柱子并排的

  2. 需要引入一个类 :QCPBars类
    简单使用(2中方法):

    QCPBars *bar = new QCPBars(this);
    QCPBars *bar1 = new QCPBars(this);
    QCPBarsGroup *group = new QCPBarsGroup(customPlot);
    bar->setBarsGroup(group);
    bar1->setBarsGroup(group);
    //设置大小
    bar->setWidth(0.3);
    bar1->setWidth(0.3);
    
    QCPBarsGroup *group = new QCPBarsGroup(customPlot);  
    QList bars;
    bars << fossil << nuclear << regen;
    
    foreach (QCPBars *bar, bars) {
      // 设置柱状图的宽度类型为以key坐标轴计算宽度的大小,其实默认就是这种方式
      bar->setWidthType(QCPBars::wtPlotCoords);
      bar->setWidth(bar->width() / bars.size()); // 设置柱状图的宽度大小
      group->append(bar);  // 将柱状图加入柱状图分组中
    }
    
  3. 先放最后的图
    QCustomPlot 之柱状图,多个参数并排显示数值_第3张图片

  4. 上代码

    .h
    #pragma once
    
    #include <QtWidgets/QWidget>
    #include "ui_barchart.h"
    #include "qcustomplot.h"
    #include "custombars.h"
    
    class BarChart : public QWidget
    {
    	Q_OBJECT
    
    public:
    	BarChart(QWidget *parent = 0);
    
    private:
    	Ui::BarChartClass ui;
    	QCustomPlot *customPlot;
    	CustomBars *regen, *nuclear, *fossil;
    };
    
    
    .cpp
    #include "barchart.h"
    
    BarChart::BarChart(QWidget *parent)
    	: QWidget(parent)
    {
    	ui.setupUi(this);
    	//整体布局
    	QHBoxLayout *m_h = new QHBoxLayout(this);
    	customPlot = new QCustomPlot(this);
    	m_h->addWidget(customPlot);
    
    
    	// 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:
    	regen = new CustomBars(customPlot->xAxis, customPlot->yAxis);
    	nuclear = new CustomBars(customPlot->xAxis, customPlot->yAxis);
    	fossil = new CustomBars(customPlot->xAxis, customPlot->yAxis);
    	QCPBarsGroup *group = new QCPBarsGroup(customPlot);
    	group->setSpacingType(QCPBarsGroup::stAbsolute);  // 设置组内柱状图的间距,按像素
    	group->setSpacing(1);     // 设置较小的间距值,这样看起来更紧凑
    	regen->setBarsGroup(group);
    	nuclear->setBarsGroup(group);
    	fossil->setBarsGroup(group);
    	
    	//设置大小
    	regen->setWidth(0.3);
    	nuclear->setWidth(0.3);
    	fossil->setWidth(0.3);
    	//设置抗锯齿
    	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<double> ticks;
    	QVector<QString> labels;
    	ticks << 1 << 3 << 5 << 7 << 9 << 11 << 13;
    	labels << "USA" << "Japan" << "Germany" << "France" << "UK" << "Italy" << "Canada";
    	QSharedPointer<QCPAxisTickerText> 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,15);
    	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<double> 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);
    }
    
    

    显示数值的功能,是借助另外一个类去继承QCPBars

    .h
    
    #pragma once
    
    #include 
    #include "qcustomplot.h"
    class CustomBars : public QCPBars
    {
    	Q_OBJECT
    
    public:
    	explicit CustomBars(QCPAxis *keyAxis, QCPAxis *valueAxis);
    
    	Qt::Alignment textAligment() const { return mTextAlignment; }
    	double spacing() const { return mSpacing; }
    	QFont font() const { return mFont; }
    
    	void setTextAlignment(Qt::Alignment alignment);
    	void setSpacing(double spacing);
    	void setFont(const QFont &font);
    protected:
    	Qt::Alignment mTextAlignment;   // 文字对齐方式
    	double mSpacing;                // 文字与柱状图的间距,这里按像素大小
    	QFont mFont;                    // 文字使用的字体
    
    	virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;
    };
    
    

    在这类中改写了QCPBars的draw函数
    ps 注意一点 QRectF barRect = getBarRect(it->key, it->value); //自己加的

    // 源文件CustomBars.cpp
    #include "custombars.h"
    CustomBars::CustomBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
    	: QCPBars(keyAxis, valueAxis),
    	mTextAlignment(Qt::AlignCenter),
    	mSpacing(5),
    	mFont(QFont(QLatin1String("sans serif"), 12))
    {
    
    }
    
    void CustomBars::setTextAlignment(Qt::Alignment alignment)
    {
    	mTextAlignment = alignment;
    }
    
    void CustomBars::setSpacing(double spacing)
    {
    	mSpacing = spacing;
    }
    
    void CustomBars::setFont(const QFont &font)
    {
    	mFont = font;
    }
    void CustomBars::draw(QCPPainter *painter)
    {
    	if (!mKeyAxis || !mValueAxis) { qDebug() << Q_FUNC_INFO << "invalid key or value axis"; return; }
    	if (mDataContainer->isEmpty()) return;
    
    	QCPBarsDataContainer::const_iterator visibleBegin, visibleEnd;
    	getVisibleDataBounds(visibleBegin, visibleEnd);
    
    	// loop over and draw segments of unselected/selected data:
    	QList selectedSegments, unselectedSegments, allSegments;
    	getDataSegments(selectedSegments, unselectedSegments);
    	allSegments << unselectedSegments << selectedSegments;
    	for (int i = 0; i < allSegments.size(); ++i)
    	{
    		bool isSelectedSegment = i >= unselectedSegments.size();
    		QCPBarsDataContainer::const_iterator begin = visibleBegin;
    		QCPBarsDataContainer::const_iterator end = visibleEnd;
    		mDataContainer->limitIteratorsToDataRange(begin, end, allSegments.at(i));
    		if (begin == end)
    			continue;
    
    		for (QCPBarsDataContainer::const_iterator it = begin; it != end; ++it)
    		{
    			// draw bar:
    			if (isSelectedSegment && mSelectionDecorator)
    			{
    				mSelectionDecorator->applyBrush(painter);
    				mSelectionDecorator->applyPen(painter);
    			}
    			else
    			{
    				painter->setBrush(mBrush);
    				painter->setPen(mPen);
    			}
    			applyDefaultAntialiasingHint(painter);
    			QRectF barRect = getBarRect(it->key, it->value);   //自己加的
    			painter->drawPolygon(barRect);
    			// 以上是拷贝的源码部分
    			painter->drawPolygon(barRect);
    
    			// 我们仅需在 painter->drawPolygon(barRect); 这行下增加以下的内容即可
    
    			// 计算文字的位置
    			painter->setFont(mFont);                     // 设置字体
    			QString text  = QString::number(it->value, 'g', 2);   // 取得当前value轴的值,保留两位精度
    
    			QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip | mTextAlignment, text);  // 计算文字所占用的大小
    
    			if (mKeyAxis.data()->orientation() == Qt::Horizontal) {    // 当key轴为水平轴的时候
    				if (mKeyAxis.data()->axisType() == QCPAxis::atTop)     // 上轴,移动文字到柱状图下面
    					textRect.moveTopLeft(barRect.bottomLeft() + QPointF(0, mSpacing));
    				else                                                   // 下轴,移动文字到柱状图上面
    					textRect.moveBottomLeft(barRect.topLeft() - QPointF(0, mSpacing));
    				textRect.setWidth(barRect.width());
    				painter->drawText(textRect, Qt::TextDontClip | mTextAlignment, text);
    			}
    			else {                                                  // 当key轴为竖直轴的时候
    				if (mKeyAxis.data()->axisType() == QCPAxis::atLeft)   // 左轴,移动文字到柱状图右边
    					textRect.moveTopLeft(barRect.topRight() + QPointF(mSpacing, 0));
    				else                                                  // 右轴,移动文字到柱状图左边
    					textRect.moveTopRight(barRect.topLeft() - QPointF(mSpacing, 0));
    				textRect.setHeight(barRect.height());
    				painter->drawText(textRect, Qt::TextDontClip | mTextAlignment, text);
    			}
    
    		}
    	}
    	
    }
    

四、总结和感想

  1. 个人认为官网给的例子其实是蛮丰富的,可以进行二次改写,多看看。
  2. 有时候别人的代码在自己这边总是运行不了,不知道你有没有这样的困惑,其实主要还是要理解代码,不能盲目的抄,换一种方法,我自己在参考别人的方法的时候也碰到过,多一点耐心。
  3. 当然,我个人水平有限,有多代码也是进行一些整合和自己的想法,如果有更好的写法,欢迎交流!

你可能感兴趣的:(Qt,c++,qt)