Qt+ECharts开发笔记:ECharts的饼图介绍基础使用和Qt封装百分比图Demo

Posted 长沙红胖子Qt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt+ECharts开发笔记:ECharts的饼图介绍基础使用和Qt封装百分比图Demo相关的知识,希望对你有一定的参考价值。

前言

  前一篇介绍了横向柱图图。本篇将介绍基础饼图使用,并将其封装一层Qt。
  本篇的demo使用隐藏js代码的方式,实现了一个饼图的基本交互方式,并预留了Qt模块对外的基础接口。

<br>

Demo演示

<br>

ECharts代码效果调试

  使用ECharts的在线调试器,先调试出大致预期的效果。

option = 
  legend: 
    top: 90%,
    show: false
  ,
  series: [
    
      selectedMode: single,    // 选择模式
      selectedOffset: 10,       // 选取后偏移,需要先设置选择模式才生效
      type: pie,               // 图例类型
      radius: [60%, 90%],     // 同心圆双边界区域
      itemStyle:                // 数据项样式
        borderRadius: 0,       // 边界圆角
        borderColor: #FF0000,  // 边界颜色
        borderWidth: 0        // 边界宽度
      ,
      label: 
        show: true,
        fontSize: 32,
        fontWeight: bold,
        formatter: b\\n\\nd%,
        position: center
      ,
      emphasis: 
        // 高亮状态的扇区和标签样式
        label: 
          show: false,
          fontSize: 32,
          fontWeight: bold
        
      ,
      labelLine: 
        show: true
      ,
      data: [
        
          value: 5.6,
          name: 开机率,
          itemStyle: 
            color: rgb(41, 235, 255),
            shadowBlur: 10,      // 外阴影
            shadowOffsetX: 0,    // 外阴影x轴偏移
            shadowOffsetY: 0,    // 外阴影y轴偏移
            shadowColor: rgb(41, 235, 255) // 外阴影颜色
          
        ,
        
          value: 5.2,
          name: ,
          itemStyle: 
            color: rgba(45,62,113),
            shadowBlur: 10,
            shadowOffsetX: 0,
            shadowOffsetY: 0,
            shadowColor: rgba(45,62,113)
          
        
      ]
    
  ]
;

<br>

Qt封装动态ECharts

步骤一:静态html

  此系列的标准html文件。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>ECharts</title>
    <script src="./echarts.js"></script>
  </head>
  <body>
    <style>
        #main,
        html,
        body
            width: 100%;
            height: 100%;
            overflow: hidden;
        
        #main 
            width: 95%;
            height: 95%;
        
    </style>
    <div id="main"></div>
    <script type="text/javascript">
        var myChart = echarts.init(document.getElementById(main));
        window.onresize = function() 
            myChart.resize();
        ;
    </script>
  </body>
</html>

步骤二:初始化

  这里是我们不让鼠标点击,只用于观看,鼠标相关的效果EChart4和EChart5也有一些不同,ECharts4交互的坑,查看本文章最后“入坑“章节。

void PieEChartWidget::initControl()

    _pLabelCenterUp = new QLabel(this);
    _pLabelCenterUp->raise();
    _pLabelCenterUp->setAlignment(Qt::AlignBottom | Qt::AlignHCenter);
    _pLabelCenterUp->setText(QSTRING("开机率"));
    _pLabelCenterUp->setStyleSheet("font-size: 36px;"
                                   "font-weight: bold;"
                                   "align: top ;"
                                   "color: rgb(41, 235, 255);"
                                   "padding: 0 30 10 0;");
    _pLabelCenterDown = new QLabel(this);
    _pLabelCenterDown->raise();
    _pLabelCenterDown->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
    _pLabelCenterDown->setText(QSTRING("%1%").arg(0));
    _pLabelCenterDown->setStyleSheet("font-size: 64px;"
                                     "font-weight: bold;"
                                     "align: center;"
                                     "color: rgb(41, 235, 255);"
                                     "padding: 0 30 0 0;");

    _pWebEngineView = new QWebEngineView(this);
    _pWebEnginePage = new QWebEnginePage(this);
    _pWebChannel = new QWebChannel(this);
    QString filePath;
#if 0
    // 使用绝对路径
    filePath = QString("%1/%2").arg(_htmlDir).arg(_indexFileName);
#else
    // 使用资源路径
    filePath = "qrc:/pieEChartWidget/html/eChartWidget.html";
#endif
    LOG << "file exist:" << QFile::exists(filePath) << filePath;
#if 0
    // 打印html文件内容
    QFile file(_indexFilePath);
    file.open(QIODevice::ReadOnly);
    LOG << QString(file.readAll());
    file.close();
#endif
    connect(_pWebEnginePage, SIGNAL(loadFinished(bool)), this, SLOT(slot_loadFinished(bool)));
    _pWebEnginePage->load(QUrl(filePath));
    _pWebEnginePage->setWebChannel(_pWebChannel);
    _pWebEngineView->setPage(_pWebEnginePage);

    // 背景透明
//    _pWebEngineView->setStyleSheet("background-color: transparent");
    _pWebEnginePage->setBackgroundColor(Qt::transparent);

    // 鼠标穿透
    _pWebEngineView->setAttribute(Qt::WA_TransparentForMouseEvents, true);

步骤三:将需要的接口预留

  设置百分数,文本上要注意位置偏移,手动进行校准:

void PieEChartWidget::setPercent(double percent)

    if(percent < 0 || percent > 100)
    
        return;
    
    LOG << percent;
    if(percent == 100)
    
        _pLabelCenterDown->setText(QSTRING("100%"));
    else
        if(_percent < 10)
        
            _pLabelCenterDown->setText(QSTRING("%1%").arg(percent, 0, g, 2));
        else
            _pLabelCenterDown->setText(QSTRING(" %1%").arg(percent, 0, g, 2));
        
    

    QString jsStr = QSTRING(
                            "option.series[0].data[0].value = %1;"
                            "option.series[0].data[1].value = %2;"
                            "myChart.setOption(option, true);")
                            .arg(percent)
                            .arg(100 - percent);
    LOG << jsStr;

    _percent = percent;

    runJsScript(jsStr);

步骤四:动态操作

重置

void PieEChartWidget::on_pushButton_reset_clicked()

    initJs();

刷新

void PieEChartWidget::on_pushButton_flush_clicked()

    QString jsStr =
            "var empty = ;"
            "myChart.setOption(empty, true);"
            "myChart.setOption(option, true);";
    runJsScript(jsStr);

随机生成(使用Qt代码)

void PieEChartWidget::on_pushButton_createRandom_clicked()

    float value = qrand() % 10001 / 100;
    setPercent(value);

清除数据

void PieEChartWidget::on_pushButton_clear_clicked()

    setPercent(0.0f);

指定值

void PieEChartWidget::on_doubleSpinBox_valueChanged(double arg1)

    setPercent(ui->doubleSpinBox->value());

<br>

Demo源码

PieEChartWidget.h

#ifndef PIEECHARTWIDGET_H
#define PIEECHARTWIDGET_H

#include <QWidget>
#include <QWebEngineView>
#include <QWebEnginePage>
#include <QWebChannel>
#include <QLabel>

namespace Ui 
class PieEChartWidget;


class PieEChartWidget : public QWidget

    Q_OBJECT

public:
    explicit PieEChartWidget(QWidget *parent = 0);
    ~PieEChartWidget();

public:
    void setPercent(double percent);

protected:
    void initControl();

protected slots:
    void slot_loadFinished(bool result);

protected:
    void initJs();

protected:
    void runJsScript(QString str);

protected:
    void resizeEvent(QResizeEvent *event);

private slots:
    void on_pushButton_clear_clicked();
    void on_pushButton_flush_clicked();
    void on_pushButton_createRandom_clicked();
    void on_pushButton_reset_clicked();
    void on_doubleSpinBox_valueChanged(double arg1);

private:
    Ui::PieEChartWidget *ui;

private:
    QWebEngineView *_pWebEngineView;            // 浏览器窗口
    QWebEnginePage *_pWebEnginePage;            // 浏览器页面
    QWebChannel *_pWebChannel;                  // 浏览器js交互

    QString _htmlDir;                           // html文件夹路径
    QString _indexFileName;                     // html文件

    QLabel *_pLabelCenterUp;                    // 显示文字的控件
    QLabel *_pLabelCenterDown;                  // 显示百分比的控件

    QString _initJsStr;                         // 初始化的js字符串
    QString _initValueJsStr;                    // 设置值的js字符串

private:
    double _percent;                            // 百分比(0~100)

;

#endif // PIEECHARTWIDGET_H

PieEChartWidget.cpp

#include "PieEChartWidget.h"
#include "ui_PieEChartWidget.h"

#include <QFile>
#include <QMessageBox>
#include <QTimer>

// QtCreator在msvc下设置编码也或有一些乱码,直接一刀切,避免繁琐的设置
//#define MSVC
#ifdef MSVC
#define QSTRING(s)  QString::fromLocal8Bit(s)
#else
#define QSTRING(s)  QString(s)
#endif

#include <QDebug>
#include <QDateTime>
//#define LOG qDebug()<<__FILE__<<__LINE__
//#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
//#define LOG qDebug()<<__FILE__<<__LINE__<<QThread()::currentThread()
//#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd")
#define LOG qDebug()<<__FILE__<<__LINE__<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss:zzz")

PieEChartWidget::PieEChartWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::PieEChartWidget),
    _pWebEngineView(0),
    _pWebEnginePage(0),
    _pWebChannel(0),
    _htmlDir("D:/qtProject/echartsDemo/echartsDemo/modules/PieEChartWidget/html"),    // 使用了绝对路径,引到html文件夹
    _indexFileName("PieEChartWidget.html"),
    _pLabelCenterUp(0),
    _pLabelCenterDown(0)

    ui->setupUi(this);

    QString version = "v1.0.0";
    setWindowTitle(QString("基于Qt的EChartb饼状图Demo %1(长沙红胖子Qt").arg(version));

    // 设置无边框,以及背景透明
    // 背景透明,在界面构架时,若为本窗口为其他窗口提升为本窗口时,
    // 则再qss会在主窗口第一级添加frame_all,防止其他窗口提升本窗口而冲掉qss设置
//    setWindowFlag(Qt::FramelessWindowHint);
//    setAttribute(Qt::WA_TranslucentBackground, true);

#if 0
    // 这是方法一:让滚动条不出来(通过大小),还有一个方法是在html设置body的overflow: hidden
//    resize(600 + 20, 400 + 20);
#endif

    initControl();


PieEChartWidget::~PieEChartWidget()

    delete ui;


void PieEChartWidget::setPercent(double percent)

    if(percent < 0 || percent > 100)
    
        return;
    
    LOG << percent;
    if(percent == 100)
    
        _pLabelCenterDown->setText(QSTRING("100%"));
    else
        if(_percent < 10)
        
            _pLabelCenterDown->setText(QSTRING("%1%").arg(percent, 0, g, 2));
        else
            _pLabelCenterDown->setText(QSTRING(" %1%").arg(percent, 0, g, 2));
        
    

    QString jsStr = QSTRING(
                            "option.series[0].data[0].value = %1;"
                            "option.series[0].data[1].value = %2;"
                            "myChart.setOption(option, true);")
                            .arg(percent)
                            .arg(100 - percent);
    LOG << jsStr;

    _percent = percent;

    runJsScript(jsStr);


void PieEChartWidget::initControl()

    _pLabelCenterUp = new QLabel(this);
    _pLabelCenterUp->raise();
    _pLabelCenterUp->setAlignment(Qt::AlignBottom | Qt::AlignHCenter);
    _pLabelCenterUp->setText(QSTRING("开机率"));
    _pLabelCenterUp->setStyleSheet("font-size: 36px;"
                                   "font-weight: bold;"
                                   "align: top ;"
                                   "color: rgb(41, 235, 255);"
                                   "padding: 0 30 10 0;");
    _pLabelCenterDown = new QLabel(this);
    _pLabelCenterDown->raise();
    _pLabelCenterDown->setAlignment(Qt::AlignTop | Qt::AlignHCenter);
    _pLabelCenterDown->setText(QSTRING("%1%").arg(0));
    _pLabelCenterDown->setStyleSheet("font-size: 64px;"
                                     "font-weight: bold;"
                                     "align: center;"
                                     "color: rgb(41, 235, 255);"
                                     "padding: 0 30 0 0;");

    _pWebEngineView = new QWebEngineView(this);
    _pWebEnginePage = new QWebEnginePage(this);
    _pWebChannel = new QWebChannel(this);
    QString filePath;
#if 0
    // 使用绝对路径
    filePath = QString("%1/%2").arg(_htmlDir).arg(_indexFileName);
#else
    // 使用资源路径
    filePath = "qrc:/pieEChartWidget/html/eChartWidget.html";
#endif
    LOG << "file exist:" << QFile::exists(filePath) << filePath;
#if 0
    // 打印html文件内容
    QFile file(_indexFilePath);
    file.open(QIODevice::ReadOnly);
    LOG << QString(file.readAll());
    file.close();
#endif
    connect(_pWebEnginePage, SIGNAL(loadFinished(bool)), this, SLOT(slot_loadFinished(bool)));
    _pWebEnginePage->load(QUrl(filePath));
    _pWebEnginePage->setWebChannel(_pWebChannel);
    _pWebEngineView->setPage(_pWebEnginePage);

    // 背景透明
//    _pWebEngineView->setStyleSheet("background-color: transparent");
    _pWebEnginePage->setBackgroundColor(Qt::transparent);

    // 鼠标穿透
    _pWebEngineView->setAttribute(Qt::WA_TransparentForMouseEvents, true);


void PieEChartWidget::slot_loadFinished(bool result)

    if(result)
    
        initJs();
        resizeEvent(0);
    


void PieEChartWidget::initJs()

    _initJsStr = QSTRING(
                "var option;"
                "option = "
                "  legend: "
                "    top: 90%,"
                "    show: false"
                "  ,"
                "  series: ["
                "    "
                "      selectedMode: single,      /* 选择模式 */"
                "      selectedOffset: 0,           /* 选取后偏移,需要先设置选择模式才生效 */"
                "      type: pie,                 /* 图例类型 */"
                "      radius: [60%, 90%],      /* 同心圆双边界区域 */"
                "      itemStyle:                  /* 数据项样式 */"
                "        borderRadius: 0,           /* 边界圆角 */"
                "        borderColor: #FF0000,    /* 边界颜色 */"
                "        borderWidth: 0             /* 边界宽度 */"
                "      ,"
                "      avoidLabelOverlap: true,"
                "      label: "
                "        show: false,"
                "        fontSize: 32,"
                "        fontWeight: bold,"
                "        formatter: b\\\\n\\\\nd%,"
                "        position: center"
                "      ,"
                "      emphasis:                   /* 高亮状态的扇区和标签样式 */"
                "        label: "
                "          show: false,"
                "          fontSize: 32,"
                "          fontWeight: bold"
                "        "
                "      ,"
                "      labelLine: "
                "        show: false"
                "      ,"
                "      data: ["
                "        "
                "          value: 0,"
                "          name: 开机率,"
                "          selected: true,"
                "          itemStyle: "
                "            color: rgba(41, 235, 255, 255),"
                "            shadowColor:rgba(41, 235, 255, 255),"
                "            shadowBlur: 10,"
                "            shadowOffsetX: 0,"
                "            shadowOffsetY: 0,"
                "          "
                "        ,"
                "        "
                "          value: 100,"
                "          name: 11,"
                "          itemStyle: "
                "            color: rgba(45,62,113,255),"
                "            shadowColor:rgba(45,62,113,255),"
                "            shadowBlur: 10,"
                "            shadowOffsetX: 0,"
                "            shadowOffsetY: 0,"
                "          "
                "        "
                "      ]"
                "    "
                "  ]"
                ";"
                "myChart.setOption(option);"
                );
    
        _initValueJsStr = QSTRING(
                "var option;"
                "option = "
                "  legend: "
                "    top: 90%,"
                "    show: false"
                "  ,"
                "  series: ["
                "    "
                "      selectedMode: single,      /* 选择模式 */"
                "      selectedOffset: 0,           /* 选取后偏移,需要先设置选择模式才生效 */"
                "      type: pie,                 /* 图例类型 */"
                "      radius: [60%, 90%],      /* 同心圆双边界区域 */"
                "      itemStyle:                  /* 数据项样式 */"
                "        borderRadius: 0,           /* 边界圆角 */"
                "        borderColor: #FF0000,    /* 边界颜色 */"
                "        borderWidth: 0             /* 边界宽度 */"
                "      ,"
                "      avoidLabelOverlap: true,"
                "      label: "
                "        show: false,"
                "        fontSize: 32,"
                "        fontWeight: bold,"
                "        formatter: b\\\\n\\\\nd%,"
                "        position: center"
                "      ,"
                "      emphasis:                   /* 高亮状态的扇区和标签样式 */"
                "        label: "
                "          show: false,"
                "          fontSize: 32,"
                "          fontWeight: bold"
                "        "
                "      ,"
                "      labelLine: "
                "        show: false"
                "      ,"
                "      data: ["
                "        "
                "          value: %1,"
                "          name: 开机率,"
                "          selected: true,"
                "          itemStyle: "
                "            color: rgba(41, 235, 255, 255),"
                "            shadowColor:rgba(41, 235, 255, 255),"
                "            shadowBlur: 10,"
                "            shadowOffsetX: 0,"
                "            shadowOffsetY: 0,"
                "          "
                "        ,"
                "        "
                "          value: %2,"
                "          name: ,"
                "          itemStyle: "
                "            color: rgba(45, 62, 113, 255),"
                "            shadowColor:rgba(45,62,113,255),"
                "            shadowBlur: 10,"
                "            shadowOffsetX: 0,"
                "            shadowOffsetY: 0,"
                "          "
                "        "
                "      ]"
                "    "
                "  ]"
                ";"
                "myChart.setOption(option);"
                );
    
    setPercent(0);
    runJsScript(_initJsStr);


void PieEChartWidget::runJsScript(QString str)

    if(_pWebEnginePage)
    
        _pWebEnginePage->runJavaScript(str);
    


void PieEChartWidget::resizeEvent(QResizeEvent *event)

    if(_pWebEngineView)
    
        _pWebEngineView->setGeometry(ui->label_echarts->geometry());
    
    if(_pLabelCenterUp)
    
        QRect echarRect = ui->label_echarts->geometry();
        _pLabelCenterUp->setGeometry(echarRect.x(),
                                     echarRect.y(),
                                     echarRect.width(),
                                     echarRect.height()/2);
    
    if(_pLabelCenterDown)
    
        QRect echarRect = ui->label_echarts->geometry();
        _pLabelCenterDown->setGeometry(echarRect.x(),
                                       echarRect.y() + echarRect.height()/2,
                                       echarRect.width(),
                                       echarRect.height()/2);
    


void PieEChartWidget::on_pushButton_clear_clicked()

    setPercent(0.0f);


void PieEChartWidget::on_pushButton_flush_clicked()

    QString jsStr =
            "var empty = ;"
            "myChart.setOption(empty, true);"
            "myChart.setOption(option, true);";
    runJsScript(jsStr);


void PieEChartWidget::on_pushButton_createRandom_clicked()

    float value = qrand() % 10001 / 100;
    setPercent(value);


void PieEChartWidget::on_pushButton_reset_clicked()

    initJs();


void PieEChartWidget::on_doubleSpinBox_valueChanged(double arg1)

    setPercent(ui->doubleSpinBox->value());

<br>

工程模板v1.3.0

  

<br>

入坑

入坑一:js出现错误“unexpected token”

问题

  

原理

  判断传入编码转换或者语法规则有问题
  

解决方法

  从字符串这种方式,只能使用/**/,如下图:
  

入坑二:出现Invalid or unexpected token错误

问题

  

原理

  
  判断输入在定义label格式的时候,输入了特殊字符,导致整条字符串没有达到预期转义

解决方法

  发现打印出来是对的也不行,主要是给换行符加上,换行符qt的直接换行展示为\\n,到浏览器那边估计是直接换行了,导致不在一行了。
  改掉即可,给\\n改成\\n,建议可打印出来看一看即可。
  

入坑三:嵌入Qt中的显示不对

问题

  

原理

  重新一条一条递增添加js语句找到问题为样式部分的问题。
  

解决

  rgba改a即可,这是之前测试过rgba,rgba中的a是有效果的。

入坑四:Qt中实际饼图的默认Label显示不对

问题

  Label显示不对

  

原理

  版本相关,但qt无法嵌入echart5无法显示(具体原因查看本系列第一篇)。

其他尝试

  最起码笔者使用的这个版本是有问题的。
  直接加载js文件,也是如此:
  

解决

  绕开,用QLabel显示混合显示。

以上是关于Qt+ECharts开发笔记:ECharts的饼图介绍基础使用和Qt封装百分比图Demo的主要内容,如果未能解决你的问题,请参考以下文章

Qt+ECharts开发笔记:ECharts的饼图介绍基础使用和Qt封装百分比图Demo

echarts插件的饼图怎么把外围边框去掉

如何解决,小程序里echarts画的饼图在安卓手机上效果不正常

小程序里echarts画的饼图在安卓手机上效果不正常?

我用的echarts做的饼图,一个页面中写了2个 ,页面没法往下拉,怎么解决?

Echarts如何画一个空心的饼图