QCustomPlot 之柱状图
一、使用环境
- Qt+vs2017+QCustomPlot
- QCustomPlot的使用,只需引入.cpp和.h即可
- 遇到的问题:
Link2019 “__declspec(dllimport) public: __cdecl QPrinter::QPrinter(enum QPrinter::PrinterMode)” (_imp??0QPrinter@@QEAA@W4Printe
后来发现是少导入模块
二、可以去官网参考demo
链接: https://www.qcustomplot.com/index.php/demos/barchartdemo
效果图:
ps demo例子给的是堆砌在一起的
三、自己改写代码
-
目的:在官网的例子上,改成多个柱子并排的
-
需要引入一个类 :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<QCPBars*> bars; bars << fossil << nuclear << regen; foreach (QCPBars *bar, bars) { // 设置柱状图的宽度类型为以key坐标轴计算宽度的大小,其实默认就是这种方式 bar->setWidthType(QCPBars::wtPlotCoords); bar->setWidth(bar->width() / bars.size()); // 设置柱状图的宽度大小 group->append(bar); // 将柱状图加入柱状图分组中 }
-
先放最后的图
-
上代码
.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 <QWidget> #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<QCPDataRange> 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); } } } }
四、总结和感想
- 个人认为官网给的例子其实是蛮丰富的,可以进行二次改写,多看看。
- 有时候别人的代码在自己这边总是运行不了,不知道你有没有这样的困惑,其实主要还是要理解代码,不能盲目的抄,换一种方法,我自己在参考别人的方法的时候也碰到过,多一点耐心。
- 当然,我个人水平有限,有多代码也是进行一些整合和自己的想法,如果有更好的写法,欢迎交流!