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


    前言

      前一篇介绍了横向柱图图。本篇将介绍基础饼图使用,并将其封装一层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文件。

    html>
    <html>
      <head>
        <meta charset="utf-8" />
        <title>EChartstitle>
        <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 
    #include 
    #include 
    #include 
    #include 
    
    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 
    #include 
    #include 
    
    // QtCreator在msvc下设置编码也或有一些乱码,直接一刀切,避免繁琐的设置
    //#define MSVC
    #ifdef MSVC
    #define QSTRING(s)  QString::fromLocal8Bit(s)
    #else
    #define QSTRING(s)  QString(s)
    #endif
    
    #include 
    #include 
    //#define LOG qDebug()<<__FILE__<<__LINE__
    //#define LOG qDebug()<<__FILE__<<__LINE__<<__FUNCTION__
    //#define LOG qDebug()<<__FILE__<<__LINE__<
    //#define LOG qDebug()<<__FILE__<<__LINE__<
    #define LOG qDebug()<<__FILE__<<__LINE__<"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显示混合显示。

  • 相关阅读:
    算法-栈操作
    2022_08_10_106期__二叉树
    阿里云记录保存
    树莓派系统镜像备份
    一文读懂 HTTP/2 特性
    2023年最新版Apollo保姆级使用手册(超级详尽版本)
    计算机毕业设计Java高校企业实训系统(源码+系统+mysql数据库+Lw文档)
    【win11内存占用高优化】未运行程序,系统内存占用50以上
    【Docker】二、docker镜像的制作运行发布
    Python数据攻略-Pandas与API数据交互
  • 原文地址:https://www.cnblogs.com/qq21497936/p/16587463.html