QT QChart QPieSeries 空心饼图2

 前段时间的一个项目中有一个细节,就是要做一个展示续报率的控件。第一眼看到设计图(下图),就是一个空心的饼状图,然后中间还有百分比数字来显示进度。这个很简单的控件,使用JS来实现,那就是几行代码的问题。但是对于使用C++的小伙伴来讲,就是一件比较头疼的事情了。来实现这样自定义的控件,要花费很大的功夫。我们也常听到这样的声音:“C++就做底层的逻辑、数据处理还行,对于这样好看的自定义控件,想想就好。要么让交互老师改设计,改成你们可以做的样子。”

        到底是不是这样子呢?答案是“不全对!虽然c++实现起来确实很麻烦,因为没有现成的控件,不过也不是不能实现,只不过这些需要我们自己去绘制而已,或者在基础控件上进行一些组装,形成新的自定义的控件”。

 

 接下来,我们一起来还原一下,遇到上面的项目,我们应该怎么来一步一步的实现的。

 我们先来分析一下这个控件,其实有三部分:饼图 + 百分比数字 + 最下面的标题(续报率)

 我们先来我们知道,QT的普通控件肯定不能实现这些,我们可以有两种可能得方法来进行实现需求:

    1. 使用 QPainter 手动画出来。 

    2. 使用 QT 的新功能 QChart 来实现自定义。

        对于第一点使用QPainter来说,讲道理,是可以实现的。方法就是需要使用QPainterPath,画两个arc(弧线),取他们的差集,进行绘画。这里由于有两种颜色,所以需要两个QPainterPath来画。这里画圆弧的时候,需要注意初始角度。这样也能实现,不过缺点,就是实现起来比较复杂,计算量大,每次刷新都要重新计算,效率稍微低一些。

        对于第二点使用QT的新功能QChart来实现这个功能,我们先了解一下为什么说QChart,还说自定义呢,因为QChart 只提供了一个原始的饼状图(如下):

 我们可以看到,原始的图形虽然有基础功能,但是效果是远远达不到我们的需求。我们需要做什么,才能达到我们的效果呢?经过分析我们发现,从原始图形到我们的需求图形,有三点问题需要解决:

       1. 将饼图变成空心。

        2. 初始角度不应该在12点钟方向

        3. 将百分比显示在中心。

 我们来一步一步的解决这些问题,看最后能不能达到我们的要求。

        1. 将饼图变为空心

        经过阅读文档发现,想要饼状图变成空心,其实官方是留有接口的我们可以调用QPieSeries中的setHoleSize来设置空心的大小。

series->setHoleSize(0.25);//饼图中间空心的大小

 这里设置孔大小范围是0-1。0是孔最小,也就是没有孔。1就是孔最大。这里我们有必要了解一下QPieSeries的另一个方法,就是设置饼图的大小函数。

series->setPieSize(0.5);//饼图的大小

这个设置饼图大小的范围也是0-1.0表示充满整个chartView。设置0.5的意思也就是饼图占据整个显示页面的一半。经过多轮参数调试,我们最终将饼图大小确定为0.8,将空心大小设置为0.66。这样后,我们一起看看效果:

这样我们的第一部的效果就达到了。一起来看看接下来需要解决什么问题。

 2. 第二个问题:使用饼状图总是0度开始

        对于初始角度,官方对其描述为,开始的角度是0,结束的角度是360。 0度与360度重合,也就是在12点钟方向。

  startAngle : qreal

This property defines the starting angle of the pie.

Full pie is 360 degrees where 0 degrees is at 12 a'clock.

Default is value is 0.

  那需求是空白的地方应该均匀的分配到12点钟的两侧。那么我们需要解决对于饼状图开始的角度与结束的角度进行限制约束。这里我们假设饼状图的有颜色部分占比是 int percent;//0-100,然后我们需要计算出来空白的部分一共占了多少度:

int remainAngle = (1.0 - percent / 100.0) * 360;  //每1个百分比,占有多少度

 那么开始的角度应该是剩余角度的一半:

int halfRemainAngle = remainAngle / 2; //剩余角度的一半

 那么我们就可以得出开始的角度了:

 int startAngle = halfRemainAngle; //开始角度

  由于一圈应该是360度,结束角度应该是开始角度startAngle+360度:

int endAngle = startAngle + 360;  //结束角度

  需要对QPieSeries加上角度限制就行了,我们看一下效果:

    series->setPieStartAngle(startAngle);
    series->setPieEndAngle(endAngle);

好了,我们第二个问题也顺利解决。到现在为止,都是控件提供了一些方法,我们能够方便的实现了前两个功能。

        3. 但是对于第三个问题也就是最棘手的了:在空心中显示百分比文字。

        经过查阅API,我们并没有发现官方有关设置。那么这个问题我们就需要自己来解决了。我的解决思路是这样的:在 chartView 中手动创建一个 label 用来显示放在圆孔的中央。在创建图形与窗体发生改变的时候同时将 label 显示出来并移动到圆孔中心。在构造函数创建label,并设置样式表,最终隐藏。

    m_centerLabel = new QLabel(ui->chartViewPie);
    m_centerLabel->setStyleSheet(QString("background:transparent;font-family:\"微软雅黑\";
    "font-size:30px; color:#1564FF; font-weight:900;"));
    m_centerLabel->hide();

那么我们都应该知道,这个label 应该放到chartview 最中心的位置。不过我们什么时候放置呢?可能有小伙伴不假思索的就能给出答案:“初始化的时候就应该放到chartview中心!”答案是正确的,不过是不够的。

        我们来假设这样的情况:我们将饼图显示出来了,然后也在初始化的时候将百分比的 label 移动到了中心,这时候,我们如果缩放这个控件,那么整个控件的大小都会改变,中心位置也会相应改变。这时候如果不更改中心百分比label的位置,就会造成百分比的label不会在chartview的中心。所以我们需要在窗体的resizeEvent 的函数里动手脚。每次窗体大小发生变化,我们都要重新计算百分比label的位置。

void MainWindow::resizeEvent(QResizeEvent *event)
{
    m_centerLabel->move((ui->chartViewPie->width()-m_centerLabel->width())/2,
                        (ui->chartViewPie->height()-m_centerLabel->height())/2-5);
}

上面我们说了,我们需要在创建label的时候,就需要计算百分比label的位置。也就是在构建图形的时候,进行显示label并执行resize函数:

   m_centerLabel->show();
   resizeEvent(NULL);

    最终得到下面的效果:

到这里,已经完成的差不多了,对比ui,我们还需要进行颜色设置,和最下面的标签的位置就可以了:

 QPieSlice *slice0 = new QPieSlice();
  slice0->setValue(percent);
  slice0->setColor(QColor(21, 100, 255));
 
  QPieSlice *slice1 = new QPieSlice();
  slice1->setValue(100 - percent);
  slice1->setColor(QColor(231, 238, 251));
 
  series->append(slice0);
  series->append(slice1);
 
  chart->addSeries(series);
  chart->legend()->setVisible(false);

写在最后

        本文主要介绍,我们拿到一个需求,从哪些角度来分析需求,意识到我们该有几种解决方案,并且每种方案的优缺点,最终能够选择一种最优的解决方案并实施。选定方案后,我们要分析,我们有没有可利用的资源、技术基础等,在现有的基础上,我们要清晰地认识到,我们要实现最终的方案,需要做哪些改变,每种改变需要用什么方式,有什么难,并一步一步的按照解决思路来逐步实现我们的方案。

 

最后直接贴源码

#pragma execution_character_set("utf-8")
#include "PieForm.h"
#include "ui_PieForm.h"

PieForm::PieForm(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::PieForm)
{
    ui->setupUi(this);

    initPieChart();

    m_centerLabel = new QLabel(ui->chartView);
    m_centerLabel->setStyleSheet(QString("background:transparent;font-family:\"微软雅黑\";"
                                         "font-size:20px; color:#1564FF; font-weight:900;"));
    m_centerLabel->hide();

    m_bottomLabel = new QLabel(ui->chartView);
    m_bottomLabel->setStyleSheet(QString("background:transparent;font-family:\"Microsoft YaHei\";"
                                         "font-size:14px; color:#333333;"));

    bulidPieChart(75);
    setTitle("小组平均续报率");
}

PieForm::~PieForm()
{
    delete ui;
}

void PieForm::setTitle(const QString &title)
{
    m_bottomLabel->setText(title);
    resizeEvent(NULL);
}

void PieForm::resizeEvent(QResizeEvent *event)
{
    Q_UNUSED(event)

    m_centerLabel->move((ui->chartView->width()-m_centerLabel->width())/2,
                        (ui->chartView->height()-m_centerLabel->height())/2);

    m_bottomLabel->move((ui->chartView->width()-m_bottomLabel->width())/2,
                        ui->chartView->height() - m_bottomLabel->height() - 15);
}

void PieForm::initPieChart()
{
    QChart *chart = new QChart();

    chart->setAnimationOptions(QChart::SeriesAnimations);
    ui->chartView->setChart(chart);
    ui->chartView->setRenderHint(QPainter::Antialiasing);
}

void PieForm::bulidPieChart(int percent)
{
    m_centerLabel->setText(QString("%1%").arg(percent));
    QChart *chart = ui->chartView->chart();
    chart->removeAllSeries();

    ui->chartView->setRenderHint(QPainter::NonCosmeticDefaultPen);
    QPieSeries *series = new QPieSeries();//创建饼图序列
    series->setHoleSize(0.66);//饼图中间空心的大小
    series->setPieSize(0.8);//饼图的大小

    QPieSlice *slice0 = new QPieSlice();
    slice0->setValue(percent);
    slice0->setColor(QColor(21, 100, 255));

    QPieSlice *slice1 = new QPieSlice();
    slice1->setValue(100 - percent);
    slice1->setColor(QColor(231, 238, 251));

    series->append(slice0);
    series->append(slice1);

    chart->addSeries(series);
    chart->legend()->setVisible(false);

    m_centerLabel->show();
    resizeEvent(NULL);
}

没有积分的小伙伴,评论留下你的邮箱,看到后第一时间发送源码。

  • 7
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值