Qt+ECharts開發筆記(四):ECharts的餅圖介紹、基礎使用和Qt封裝百分比圖Demo

21497936發表於2022-08-15

前言

  前一篇介紹了橫向柱圖圖。本篇將介紹基礎餅圖使用,並將其封裝一層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顯示混合顯示。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70010283/viewspace-2910423/,如需轉載,請註明出處,否則將追究法律責任。

相關文章