欢迎光临散文网 会员登陆 & 注册

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

2022-08-15 11:00 作者:红胖子_AAA红模仿  | 我要投稿

前言

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

Demo演示

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\n{d}%',        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)'          }        }      ]    }  ]};

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());}

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_OBJECTpublic:    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\\n{d}%',"                "        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\\n{d}%',"                "        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());}

工程模板v1.3.0

  

入坑

入坑一: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的评论 (共 条)

分享到微博请遵守国家法律