Qt+ECharts開發筆記(三):ECharts的柱狀圖介紹、基礎使用和Qt封裝Demo

21497936 發表於 2022-07-13
QT

前言

  上一篇成功是EChart隨著Qt視窗變化而變化,本篇將開始正式介紹柱狀圖介紹、基礎使用,並將其封裝一層Qt。
  本篇的demo實現了隱藏js程式碼的方式,實現了一個條形圖的基本互動方式,即Qt呼叫js指令碼操作html。

Demo演示

   請新增圖片描述

ECharts除錯工具

  ECharts提供的純JS程式碼編輯開發除錯工具,可編輯js,並且檢視執行效果:
  
   在這裡插入圖片描述
  開發過程中對於屬性的查詢和除錯ECharts也提供了配置幫助。
  官方配置手冊:
   在這裡插入圖片描述

目標

  隨便找一個預期目標:
   在這裡插入圖片描述

ECharts介面靜態方式

  使用EChart除錯工具開發,先除錯出基礎框架。
  這裡貼出的全部程式碼:

<!DOCTYPE html><html>
  <head>
    <meta charset="utf-8" />
    <title>ECharts</title>
    <!-- 引入剛剛下載的 ECharts 檔案 -->
    <!--<script src="echarts.js"></script>-->
    <script src="./echarts.js"></script>
    <!--<script src="D:/qtProject/echartsDemo/echartsDemo/modules/barEChartWidget/html/echarts.js"></script>-->
    <!--<script src="echarts.min.js"></script>-->
    <!--<script src="./echarts.min.js"></script>-->
    <!--<script src="./html/echarts.min.js"></script>-->
    <!--<script src="D:/qtProject/echartsDemo/echartsDemo/modules/barEChartWidget/html/echarts.min.js"></script>-->
  </head>
  <body>
    <!--設定body跟隨查u哪個口,main填充body-->
    <style>
        #main,
        html,
        body{
            width: 100%;
            height: 100%;
            overflow: hidden;
        }
        #main {
            width: 100%;
            height: 100%;
        }
    </style>
    <div id="main"></div>
    <script type="text/javascript">
        // 基於準備好的dom,初始化echarts例項
        var myChart = echarts.init(document.getElementById('main'));
        // 視窗高度變化設定
        window.onresize = function() {
            myChart.resize();
        };
        // 指定圖表的配置項和資料
        var option = {
            title: {
                text: 'ECharts 入門示例'
            },
            tooltip: {},
            legend: {
                data: ['銷量']
            },
            xAxis: {
            data: ['襯衫', '羊毛衫', '雪紡衫', '褲子', '高跟鞋', '襪子']
            },
            yAxis: {},
            series: [
                {
                    name: '銷量',
                    type: 'bar',
                    data: [5, 20, 36, 10, 10, 20]
                }
            ]
        };
        // 使用剛指定的配置項和資料顯示圖表。
        myChart.setOption(option);
        function initJs() {
            var myChart = echarts.init(document.getElementById('main'));
            var option;
            option = {
                tooltip: {
                    trigger: 'axis'
                },
                grid: {
                    left: '3%',
                    right: '4%',
                    bottom: '50',
                    containLabel: true
                },
                legend: {
                    orient: 'horizontal',
                    x: 'center',
                    y: 'bottom',
                    itemGap: 100
                },
                xAxis: {
                    type: 'value'
                },
                yAxis: {
                    type: 'category',
                    data: ['專案1', '專案2', '專案3']
                },
                series: [
                    {
                        name: '變數1',
                        type: 'bar',
                        stack: 'totla',
                        label: {
                            show: true
                        },
                        data: [11, 12, 13]
                    },
                    {
                        name: '子專案1',
                        type: 'bar',
                        stack: 'totla',
                        label: {
                            show: true
                        },
                        data: [24, 20, 21]
                    },
                    {
                        name: '變數3',
                        type: 'bar',
                        stack: 'totla',
                        label: {
                            show: true
                        },
                        data: [95, 87, 55]
                    }
                ]
            };
        };
        initJs();
    </script>
  </body></html>

ECharts介面動態方式

  動態方式,最初目的是為了動態實現資料的js操作,在開發過程中發現動態方式還能 隱藏echarts程式碼,使echarts程式碼不被抓取,最後決定全部改為動態載入方式。

步驟一:修改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>

步驟二:初始化

  新增了loadFinished訊號槽關聯。

void BarEChartWidget::initControl(){
    _pWebEngineView = new QWebEngineView(this);
    _pWebEnginePage = new QWebEnginePage(this);
    _pWebChannel = new QWebChannel(this);
    QString filePath;#if 1
    filePath = QString("%1/%2").arg(_htmlDir).arg(_indexFileName);#else
    filePath = "qrc:/barEChartWidget/html/barEChartWidget.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);}

步驟三:載入完成頁面後進行初始化

void BarEChartWidget::slot_loadFinished(bool result){
    if(result)
    {
        initJs();
    }}void BarEChartWidget::initJs(){
    _initJsStr = QSTRING(
            "var option;"
            "option = {"
            "    tooltip: {"
            "        trigger: 'axis'"
            "    },"
            "    grid: {"
            "        left: '10',"
            "        right: '10',"
            "        top: '10',"
            "        bottom: 30,"
            "        containLabel: true"
            "    },"
            "    legend: {"
            "        orient: 'horizontal',"
            "        x: 'center',"
            "        y: 'bottom',"
            "        itemGap: 20"
            "    },"
            "    xAxis: {"
            "        type: 'value'"
            "    },"
            "    yAxis: {"
            "        type: 'category',"
            "        data: ['專案1', '專案2', '專案3']"
            "    },"
            "    series: ["
            "        {"
            "            name: '變數1',"
            "            type: 'bar',"
            "            stack: 'totla',"
            "            label: {"
            "                show: true"
            "            },"
            "            data: [11, 12, 13]"
            "        },"
            "        {"
            "            name: '變數2',"
            "            type: 'bar',"
            "            stack: 'totla',"
            "            label: {"
            "                show: true"
            "            },"
            "            data: [24, 20, 21]"
            "        },"
            "        {"
            "            name: '變數3',"
            "            type: 'bar',"
            "            stack: 'totla',"
            "            label: {"
            "                show: true"
            "            },"
            "            data: [95, 87, 55]"
            "        }"
            "    ]"
            "};"
            "myChart.setOption(option);");
    runJsScript(_initJsStr);}void BarEChartWidget::runJsScript(QString str){
    if(_pWebEnginePage)
    {
        _pWebEnginePage->runJavaScript(str);
    }}

步驟四:動態執行js操作

重置

void BarEChartWidget::on_pushButton_reset_clicked(){
    initJs();}

重新整理

void BarEChartWidget::on_pushButton_flush_clicked(){
    QString jsStr =
            "var empty = {};"
            "myChart.setOption(empty, true);"
            "myChart.setOption(option, true);";
    runJsScript(jsStr);}

清空

void BarEChartWidget::on_pushButton_clear_clicked(){
    QString jsStr =
            "option.series[0].data = [];"
            "option.series[1].data = [];"
            "option.series[2].data = [];"
            "myChart.setOption(option, true);";
    runJsScript(jsStr);}

隨機生成(使用js程式碼)

void BarEChartWidget::on_pushButton_createRandom_clicked(){
    QString jsStr =
    "var min = 0;"
    "var max = 100;"
    "for(var i = 0; i < option.series.length; i++)"
    "{"
    "    for(var j = 0; j < option.yAxis.data.length; j++)"
    "    {"
    "        option.series[i].data[j] = Math.floor(Math.random() * (max - min)) + min;"
    "    }"
    "}"
    "myChart.setOption(option, true);";
    runJsScript(jsStr);}

  後續用會將資料進行Qt的一份快取資料,由Qt的資料去生成ecahrts的js指令碼。

Demo

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>

  在程式碼中使用js初始化echarts:
BarECartWidget.h

#ifndef BARECHARTWIDGET_H#define BARECHARTWIDGET_H#include <QWidget>#include <QWebEngineView>#include <QWebEnginePage>#include <QWebChannel>namespace Ui {class BarEChartWidget;}class BarEChartWidget : public QWidget{
    Q_OBJECTpublic:
    explicit BarEChartWidget(QWidget *parent = 0);
    ~BarEChartWidget();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();private:
    Ui::BarEChartWidget *ui;private:
    QWebEngineView *_pWebEngineView;            // 瀏覽器視窗
    QWebEnginePage *_pWebEnginePage;            // 瀏覽器頁面
    QWebChannel *_pWebChannel;                  // 瀏覽器js互動
    QString _htmlDir;                           // html資料夾路徑
    QString _indexFileName;                     // html檔案
    QString _initJsStr;                         // 第一次初始化的表格};#endif // BARECHARTWIDGET_H

BarEChartWidget.cpp

#include "BarEChartWidget.h"#include "ui_BarEChartWidget.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")BarEChartWidget::BarEChartWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::BarEChartWidget),
    _pWebEngineView(0),
    _pWebEnginePage(0),
    _pWebChannel(0),
    _htmlDir("D:/qtProject/echartsDemo/echartsDemo/modules/barEChartWidget/html"),    // 使用了絕對路徑,引到html資料夾
    _indexFileName("barEChartWidget.html"){
    ui->setupUi(this);
    QString version = "v1.0.0";
    setWindowTitle(QString("基於Qt的ECharts條狀圖Demo %1(長沙紅胖子 QQ:21497936 WX:15173255813 blog:hpzwl.blog.csdn.net").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();}BarEChartWidget::~BarEChartWidget(){
    delete ui;}void BarEChartWidget::initControl(){
    _pWebEngineView = new QWebEngineView(this);
    _pWebEnginePage = new QWebEnginePage(this);
    _pWebChannel = new QWebChannel(this);
    QString filePath;#if 1
    filePath = QString("%1/%2").arg(_htmlDir).arg(_indexFileName);#else
    filePath = "qrc:/barEChartWidget/html/barEChartWidget.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);}void BarEChartWidget::slot_loadFinished(bool result){
    if(result)
    {
        initJs();
    }}void BarEChartWidget::initJs(){
    _initJsStr = QSTRING(
            "var option;"
            "option = {"
            "    tooltip: {"
            "        trigger: 'axis'"
            "    },"
            "    grid: {"
            "        left: '10',"
            "        right: '10',"
            "        top: '10',"
            "        bottom: 30,"
            "        containLabel: true"
            "    },"
            "    legend: {"
            "        orient: 'horizontal',"
            "        x: 'center',"
            "        y: 'bottom',"
            "        itemGap: 20"
            "    },"
            "    xAxis: {"
            "        type: 'value'"
            "    },"
            "    yAxis: {"
            "        type: 'category',"
            "        data: ['專案1', '專案2', '專案3']"
            "    },"
            "    series: ["
            "        {"
            "            name: '變數1',"
            "            type: 'bar',"
            "            stack: 'totla',"
            "            label: {"
            "                show: true"
            "            },"
            "            data: [11, 12, 13]"
            "        },"
            "        {"
            "            name: '變數2',"
            "            type: 'bar',"
            "            stack: 'totla',"
            "            label: {"
            "                show: true"
            "            },"
            "            data: [24, 20, 21]"
            "        },"
            "        {"
            "            name: '變數3',"
            "            type: 'bar',"
            "            stack: 'totla',"
            "            label: {"
            "                show: true"
            "            },"
            "            data: [95, 87, 55]"
            "        }"
            "    ]"
            "};"
            "myChart.setOption(option);");
    runJsScript(_initJsStr);}void BarEChartWidget::runJsScript(QString str){
    if(_pWebEnginePage)
    {
        _pWebEnginePage->runJavaScript(str);
    }}void BarEChartWidget::resizeEvent(QResizeEvent *event){
    if(_pWebEngineView)
    {
        _pWebEngineView->setGeometry(ui->label_echarts->geometry());
    }}void BarEChartWidget::on_pushButton_clear_clicked(){
    QString jsStr =
            "option.series[0].data = [];"
            "option.series[1].data = [];"
            "option.series[2].data = [];"
            "myChart.setOption(option, true);";
    runJsScript(jsStr);}void BarEChartWidget::on_pushButton_flush_clicked(){
    QString jsStr =
            "var empty = {};"
            "myChart.setOption(empty, true);"
            "myChart.setOption(option, true);";
    runJsScript(jsStr);}void BarEChartWidget::on_pushButton_createRandom_clicked(){
    QString jsStr =
    "var min = 0;"
    "var max = 100;"
    "for(var i = 0; i < option.series.length; i++)"
    "{"
    "    for(var j = 0; j < option.yAxis.data.length; j++)"
    "    {"
    "        option.series[i].data[j] = Math.floor(Math.random() * (max - min)) + min;"
    "    }"
    "}"
    "myChart.setOption(option, true);";
    runJsScript(jsStr);}void BarEChartWidget::on_pushButton_reset_clicked(){
    initJs();}

入坑

入坑一:呼叫js函式失敗

問題

  通過qt程式碼跑js呼叫函式失敗。
   在這裡插入圖片描述

   在這裡插入圖片描述

原理

  先要等頁面載入完成後才能呼叫定義的函式。

解決

  發現是呼叫成功了,原來只定義也會報這個錯誤。
    在這裡插入圖片描述
  由此可以看出,在qt中去初始化需要等待頁面載入完成才行。

入坑二:Qt的msvc編譯器少數中文亂碼

問題

  少數中文亂碼,加空格等都無法解決。
   在這裡插入圖片描述

解決

  最終解決了,嘗試了QTextCodec等各種都不行,最終:
   在這裡插入圖片描述
   在這裡插入圖片描述

工程模板v1.2.0

   在這裡插入圖片描述


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