找回密码
 立即注册
收起左侧

QCustomPlot之柱状图(三)

7
回复
4967
查看
[复制链接]
累计签到:10 天
连续签到:1 天
来源: 原创 2020-3-16 20:55:26 显示全部楼层 |阅读模式
本帖最后由 maxlogo 于 2020-3-20 09:50 编辑

在QCustomPlot中,柱状图需要使用QCPBars类, QCPBars的使用很简单

创建一个柱状图,并且设置它的风格
  1. QCPAxis *keyAxis = customPlot->xAxis;
  2. QCPAxis *valueAxis = customPlot->yAxis;
  3. QCPBars *fossil = new QCPBars(keyAxis, valueAxis);  // 使用xAxis作为柱状图的key轴,yAxis作为value轴

  4. fossil->setAntialiased(false); // 为了更好的边框效果,关闭抗齿锯
  5. fossil->setName("Fossil fuels"); // 设置柱状图的名字,可在图例中显示
  6. fossil->setPen(QPen(QColor(0, 168, 140).lighter(130))); // 设置柱状图的边框颜色
  7. fossil->setBrush(QColor(0, 168, 140));  // 设置柱状图的画刷颜色
复制代码


之后,我们为柱状图的key轴设置一个文字类型的轴
  1. // 为柱状图设置一个文字类型的key轴,ticks决定了轴的范围,而labels决定了轴的刻度文字的显示
  2. QVector<double> ticks;
  3. QVector<QString> labels;
  4. ticks << 1 << 2 << 3 << 4 << 5 << 6 << 7;
  5. labels << "USA" << "Japan" << "Germany" << "France" << "UK" << "Italy" << "Canada";
  6. QSharedPointer<QCPAxisTickerText> textTicker(new QCPAxisTickerText);
  7. textTicker->addTicks(ticks, labels);

  8. keyAxis->setTicker(textTicker);        // 设置为文字轴
复制代码

设置柱状图轴范围,并且为其添加数据数据
  1. keyAxis->setTickLabelRotation(60);     // 轴刻度文字旋转60度
  2. keyAxis->setSubTicks(false);           // 不显示子刻度
  3. keyAxis->setTickLength(0, 4);          // 轴内外刻度的长度分别是0,4,也就是轴内的刻度线不显示
  4. keyAxis->setRange(0, 8);               // 设置范围
  5. keyAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);

  6. valueAxis->setRange(0, 12.1);
  7. valueAxis->setPadding(35);             // 轴的内边距,可以到QCustomPlot之开始(一)看图解
  8. valueAxis->setLabel("Power Consumption in\nKilowatts per Capita (2007)");
  9. valueAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);
  10. QVector<double> fossilData;
  11. 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;
  12. fossil->setData(ticks, fossilData);
复制代码



竖向柱状图


横向柱状图也很简单,只要设置key轴为y轴,value轴为x轴即可
  1. QCPAxis *keyAxis = customPlot->yAxis;
  2. QCPAxis *valueAxis = customPlot->xAxis;
复制代码


横向柱状图

为了展示堆积图,我们再添加了两个柱状图
  1. QCPBars *regen = new QCPBars(keyAxis, valueAxis);
  2. QCPBars *nuclear = new QCPBars(keyAxis, valueAxis);
  3. QCPBars *fossil = new QCPBars(keyAxis, valueAxis);  // 使用xAxis作为柱状图的key轴,yAxis作为value轴
复制代码

设置它们的数据
  1. QVector<double> fossilData, nuclearData, regenData;
  2. 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;
  3. 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;
  4. 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;
  5. fossil->setData(ticks, fossilData);
  6. nuclear->setData(ticks, nuclearData);
  7. regen->setData(ticks, regenData);
复制代码

接下来设置堆积方式
  1. regen->setStackingGap(1);    // 设置堆积在其它柱状图上时的间距(像素)
  2. nuclear->setStackingGap(1);

  3. nuclear->moveAbove(fossil);  // 将nuclear移到fossil之上
  4. regen->moveAbove(nuclear);
复制代码


柱状堆积图

柱状分组图需要引入一个新的类:QCPBarsGroup,首先注释掉上面的堆积方式的代码
  1. QCPBarsGroup *group = new QCPBarsGroup(customPlot);  

  2. QList<QCPBars*> bars;
  3. bars << fossil << nuclear << regen;

  4. foreach (QCPBars *bar, bars) {
  5.   // 设置柱状图的宽度类型为以key坐标轴计算宽度的大小,其实默认就是这种方式
  6.   bar->setWidthType(QCPBars::wtPlotCoords);
  7.   bar->setWidth(bar->width() / bars.size()); // 设置柱状图的宽度大小
  8.   group->append(bar);  // 将柱状图加入柱状图分组中
  9. }

  10. group->setSpacingType(QCPBarsGroup::stAbsolute);  // 设置组内柱状图的间距,按像素
  11. group->setSpacing(2);     // 设置较小的间距值,这样看起来更紧凑
复制代码




柱状分组图


柱状图宽度类型和柱状分组图间距类型

柱状图有三种宽度类型,分别是:
  • QCPBars::wtAbsolute 宽度是绝对像素大小,即setWidth设置为多少就是多少
  • QCPBars::wtAxisRectRatio 宽度大小是以QCPAxisRect的大小决定的,当key轴为水平的时候,宽度大小为setWidth设置的比例值乘以QCPAxisRect的宽度;key轴为垂直的时候,是乘以QCPAxisRect的高度
  • QCPBars::wtPlotCoords 宽度大小是以key坐标轴刻度位置以及setWidth设置的值确定,宽度的计算方式为当前key±width
柱状分组图的间距类型和柱状图的宽度类型是类似的

在柱状图上显示值
有时候我们需要在柱状图上显示具体的值,这时我们仅需继承QCPBars
  1. // 头文件CustomBars.h
  2. class CustomBars : public QCPBars
  3. {
  4. public:
  5.     explicit CustomBars(QCPAxis *keyAxis, QCPAxis *valueAxis);

  6.     Qt::Alignment textAligment() const { return mTextAlignment; }
  7.     double spacing() const { return mSpacing; }
  8.     QFont font() const { return mFont; }

  9.     void setTextAlignment(Qt::Alignment alignment);
  10.     void setSpacing(double spacing);
  11.     void setFont(const QFont &font);

  12. protected:
  13.     Qt::Alignment mTextAlignment;   // 文字对齐方式
  14.     double mSpacing;                // 文字与柱状图的间距,这里按像素大小
  15.     QFont mFont;                    // 文字使用的字体

  16.     virtual void draw(QCPPainter *painter) Q_DECL_OVERRIDE;
  17. };
复制代码
  1. // 源文件CustomBars.cpp
  2. CustomBars::CustomBars(QCPAxis *keyAxis, QCPAxis *valueAxis)
  3.     : QCPBars (keyAxis, valueAxis),
  4.       mTextAlignment(Qt::AlignCenter),
  5.       mSpacing(5),
  6.       mFont(QFont(QLatin1String("sans serif"), 12))
  7. {

  8. }

  9. void CustomBars::setTextAlignment(Qt::Alignment alignment)
  10. {
  11.     mTextAlignment = alignment;
  12. }

  13. void CustomBars::setSpacing(double spacing)
  14. {
  15.     mSpacing = spacing;
  16. }

  17. void CustomBars::setFont(const QFont &font)
  18. {
  19.     mFont = font;
  20. }
复制代码
draw函数的绘制 我们直接拷贝QCPBars的draw函数源码过来,在其上面修改即可
  1. // 源文件CustomBars.cpp
  2. void CustomBars::draw(QCPPainter *painter)
  3. {

  4.     // 以上是拷贝的源码部分
  5.     painter->drawPolygon(barRect);

  6.     // 我们仅需在 painter->drawPolygon(barRect); 这行下增加以下的内容即可
  7.    
  8.     // 计算文字的位置
  9.     painter->setFont(mFont);                     // 设置字体
  10.     QString text = QString text = QString::number(it->value, 'g', 2);   // 取得当前value轴的值,保留两位精度

  11.     QRectF textRect = painter->fontMetrics().boundingRect(0, 0, 0, 0, Qt::TextDontClip | mTextAlignment, text);  // 计算文字所占用的大小

  12.     if (mKeyAxis.data()->orientation() == Qt::Horizontal) {    // 当key轴为水平轴的时候
  13.         if (mKeyAxis.data()->axisType() == QCPAxis::atTop)     // 上轴,移动文字到柱状图下面
  14.             textRect.moveTopLeft(barRect.bottomLeft() + QPointF(0, mSpacing));
  15.         else                                                   // 下轴,移动文字到柱状图上面
  16.             textRect.moveBottomLeft(barRect.topLeft() - QPointF(0, mSpacing));
  17.         textRect.setWidth(barRect.width());
  18.         painter->drawText(textRect, Qt::TextDontClip | mTextAlignment, text);
  19.     } else {                                                  // 当key轴为竖直轴的时候
  20.         if (mKeyAxis.data()->axisType() == QCPAxis::atLeft)   // 左轴,移动文字到柱状图右边
  21.             textRect.moveTopLeft(barRect.topRight() + QPointF(mSpacing, 0));
  22.         else                                                  // 右轴,移动文字到柱状图左边
  23.             textRect.moveTopRight(barRect.topLeft() - QPointF(mSpacing, 0));
  24.         textRect.setHeight(barRect.height());
  25.         painter->drawText(textRect, Qt::TextDontClip | mTextAlignment, text);
  26.     }
  27.    
  28.     // 以下是拷贝的源码部分
  29. }
复制代码


使用很简单,仅需将QCPBars替换成CustomBars
  1. CustomBars *regen = new CustomBars(keyAxis, valueAxis);
  2. CustomBars *nuclear = new CustomBars(keyAxis, valueAxis);
  3. CustomBars *fossil = new CustomBars(keyAxis, valueAxis);  // 使用xAxis作为柱状图的key轴,yAxis作为value轴
复制代码

在柱状图上显示数值

我们可以通过setBaseValue函数设置柱状图的起点位置,这个函数仅对最底下的柱状图有效果(即其没有堆积在别的柱状图之上)
  1. foreach (QCPBars *bar, bars) {
  2.   // 设置柱状图的宽度类型为以key坐标轴计算宽度的大小,其实默认就是这种方式
  3.   bar->setBaseValue(2.5);     // 设置柱状图的起点为value轴值为2.5的位置
  4. }
复制代码

设置柱状图起点位置




本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

累计签到:10 天
连续签到:1 天
2020-3-16 20:58:17 显示全部楼层
论坛发贴功能太不友好了,放弃了

点评

建议使用markdown,发帖的时候有插件  详情 回复 发表于 2020-3-16 21:32
回复 支持 反对

使用道具 举报

累计签到:1568 天
连续签到:1 天
2020-3-16 21:32:02 显示全部楼层
maxlogo 发表于 2020-3-16 20:58
论坛发贴功能太不友好了,放弃了

建议使用markdown,发帖的时候有插件
回复 支持 反对

使用道具 举报

累计签到:10 天
连续签到:1 天
2020-3-16 22:56:47 显示全部楼层
yafeilinux 发表于 2020-3-16 21:32
建议使用markdown,发帖的时候有插件

就是使用markdown啊,出来这个效果,因为图片在markdown中也是一个网络链接,所以。。。发不了图片

点评

嗯。这里图片需要单独上传。  详情 回复 发表于 2020-3-19 22:14
回复 支持 反对

使用道具 举报

累计签到:1568 天
连续签到:1 天
2020-3-19 22:14:31 显示全部楼层
maxlogo 发表于 2020-3-16 22:56
就是使用markdown啊,出来这个效果,因为图片在markdown中也是一个网络链接,所以。。。发不了图片 ...

嗯。这里图片需要单独上传。
回复 支持 反对

使用道具 举报

累计签到:10 天
连续签到:1 天
2020-3-20 00:10:28 显示全部楼层
yafeilinux 发表于 2020-3-19 22:14
嗯。这里图片需要单独上传。

嗯上传了还是不行,上传后markdown中图片链接还是h t t p ://qter.org这样子开头的,然后还是提示无法发布url链接
回复 支持 反对

使用道具 举报

累计签到:1568 天
连续签到:1 天
2020-3-21 14:50:35 显示全部楼层
maxlogo 发表于 2020-3-20 00:10
嗯上传了还是不行,上传后markdown中图片链接还是h t t p ://qter.org这样子开头的,然后还是提示无法发 ...

看看这个效果,这就是markdown里面插入图片

print("Hello, World!");

下面是图片




下面是操作说明:
可以先贴markdown内容,在需要图片的地方,使用“纯文本”模式,上传图片,然后插入图片到文章。如下面图片所示:






本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复 支持 反对

使用道具 举报

累计签到:10 天
连续签到:1 天
2020-3-21 21:05:43 显示全部楼层
yafeilinux 发表于 2020-3-21 14:50
[md]## 看看这个效果,这就是markdown里面插入图片
```
print("Hello, World!");

好,重新编辑一下了。。。。。。。。。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了