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

2022-10-05 15:00:14

前言

  上一篇的demo使用隱藏js程式碼的方式,實現了一個餅圖的基本互動方式,並預留了Qt模組對外的基礎介面。
  本篇的demo實現了自動排序的柱狀圖,實現了一個自動排序柱狀圖的基本互動方式,即Qt呼叫js指令碼操作html。
  本篇demo使用Qt定時器方式,實現資料定時重新整理自增,並預留出了定時器間隔引數。
  像巨量資料網頁常看的人口增長時間圖,收入年度增長時間圖等都是這一類。

 

Demo演示

  請新增圖片描述

 

ECharts程式碼效果偵錯

  使用ECharts的線上偵錯程式,先偵錯出大致預期的效果。

option = {
  xAxis: {
    max: 'dataMax'
  },
  yAxis: {
    type: 'category',
    data: ['特斯拉', '賓士', '寶馬', '理想', '蔚來'],
    inverse: true,
    animationDuration: 300,
    animationDurationUpdate: 300,
    max: 4
  },
  series: [
    {
      realtimeSort: true,
      name: 'X',
      type: 'bar',
      data: [10,20,50,10,30],
      label: {
        show: true,
        position: 'right',
        valueAnimation: true
      },
      itemStyle: {
            color: function(params) {
              var colorList = ['#EE14FF', '#F092FF', '#FF61FE', '#A02F99', '#F00682'];  /* 注意1:需要分號 */
              return colorList[params.dataIndex];    /* 注意2:需要dataIndex,獲取序號 */
         }
      }
    },
  ],
  graphic: {
    elements: [    /* 時間標誌 */
      {
        type: 'text', 
        right: 160,
        bottom: 100,
        style: {
          text: '1970-01',
          font: 'bolder 100px monospace',
          fill: 'rgba(100, 100, 100, 0.25)'
        },
        z: 100
      }
    ]
  },
  legend: {
    show: false,
  },
  animationDuration: 0,
  animationDurationUpdate: 1000,
  animationEasing: 'linear',
  animationEasingUpdate: 'linear'
};

  在這裡插入圖片描述

 

Qt封裝動態ECharts

步驟一:靜態html

  此係列的標準html檔案,因為是標準的所以對檔名進行了調整,改為eChartWidget.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>

步驟二:初始化

void BarAutoSortEChartWidget::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:/barAutoSortEChartWidget/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);
}

步驟三:動態操作

  在這裡插入圖片描述

重置

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

重新整理

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

開始統計(使用Qt程式碼)

  這裡預留了定時器間隔。

void BarAutoSortEChartWidget::on_pushButton_start_clicked()
{
    if(_timerId == -1)
    {
        LOG << ui->lineEdit_interval->text().toInt();
        _timerId = startTimer(ui->lineEdit_interval->text().toInt());
        _dateTime.setSecsSinceEpoch(0);
        QString jsStr = QString(
                "option.series[0].data[0] = 0;"
                "option.series[0].data[1] = 0;"
                "option.series[0].data[2] = 0;"
                "option.series[0].data[3] = 0;"
                "option.series[0].data[4] = 0;"
                "option.graphic.elements[0].style.text= '%1';"
                "myChart.setOption(option, true);"
                )
                .arg(_dateTime.toString("yyyy-MM"));
        runJsScript(jsStr);

        ui->pushButton_start->setText("停止統計");
    }else{
        if(_timerId != -1)
        {
            killTimer(_timerId);
            _timerId = -1;
        }
        ui->pushButton_start->setText("開始統計");
    }
}
void BarAutoSortEChartWidget::timerEvent(QTimerEvent *event)
{
    _dateTime = _dateTime.addMonths(1);
    if(_dateTime >= QDateTime::currentDateTime())
    {
        if(_timerId != -1)
        {
            killTimer(_timerId);
            _timerId = -1;
        }
    }
    QString jsStr = QString(
            "option.series[0].data[0] = option.series[0].data[0] + %1;"
            "option.series[0].data[1] = option.series[0].data[1] + %2;"
            "option.series[0].data[2] = option.series[0].data[2] + %3;"
            "option.series[0].data[3] = option.series[0].data[3] + %4;"
            "option.series[0].data[4] = option.series[0].data[4] + %5;"
            "option.graphic.elements[0].style.text= '%6';"
            "myChart.setOption(option, true);"
            )
            .arg(qrand()%100)
            .arg(qrand()%100)
            .arg(qrand()%100)
            .arg(qrand()%100)
            .arg(qrand()%100)
            .arg(_dateTime.toString("yyyy-MM"));
    runJsScript(jsStr);
}

清除資料

void BarAutoSortEChartWidget::on_pushButton_clear_clicked()
{
    _dateTime.setSecsSinceEpoch(0);
    QString jsStr = QString(
            "option.series[0].data[0] = 0;"
            "option.series[0].data[1] = 0;"
            "option.series[0].data[2] = 0;"
            "option.series[0].data[3] = 0;"
            "option.series[0].data[4] = 0;"
            "option.graphic.elements[0].style.text= '%1';"
            "myChart.setOption(option, true);"
            )
            .arg(_dateTime.toString("yyyy-MM"));
    runJsScript(jsStr);
}
 

Demo原始碼

BarAutoSortEChartWidget.h

#ifndef BARAUTOSORTECHARTWIDGET_H
#define BARAUTOSORTECHARTWIDGET_H

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

namespace Ui {
class BarAutoSortEChartWidget;
}

class BarAutoSortEChartWidget : public QWidget
{
    Q_OBJECT

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

protected:
    void initControl();

protected slots:
    void slot_loadFinished(bool result);

protected:
    void initJs();

protected:
    void runJsScript(QString str);

protected:
    void resizeEvent(QResizeEvent *event);
    void timerEvent(QTimerEvent *event);

private slots:
    void on_pushButton_clear_clicked();
    void on_pushButton_flush_clicked();
    void on_pushButton_start_clicked();
    void on_pushButton_reset_clicked();


private:
    Ui::BarAutoSortEChartWidget *ui;

private:
    QWebEngineView *_pWebEngineView;            // 瀏覽器視窗
    QWebEnginePage *_pWebEnginePage;            // 瀏覽器頁面
    QWebChannel *_pWebChannel;                  // 瀏覽器js互動

    QString _htmlDir;                           // html資料夾路徑
    QString _indexFileName;                     // html檔案

    QString _initJsStr;                         // 第一次初始化的表格

private:
    int _timerId;
    QDateTime _dateTime;
};

#endif // BARAUTOSORTECHARTWIDGET_H

BarAutoSortEChartWidget.cpp

#include "BarAutoSortEChartWidget.h"
#include "ui_BarAutoSortEChartWidget.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")

BarAutoSortEChartWidget::BarAutoSortEChartWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::BarAutoSortEChartWidget),
    _pWebEngineView(0),
    _pWebEnginePage(0),
    _pWebChannel(0),
    _htmlDir("D:/qtProject/echartsDemo/echartsDemo/modules/barAutoSortEChartWidget/html"),    // 使用了絕對路徑,引到html資料夾
    _indexFileName("eChartWidget.html"),
    _timerId(-1)
{
    ui->setupUi(this);

    QString version = "v1.0.0";
    setWindowTitle(QString("基於Qt的ECharts條狀圖(自動排序)Demo %1(長沙紅胖子).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();
}

BarAutoSortEChartWidget::~BarAutoSortEChartWidget()
{
    delete ui;
}


void BarAutoSortEChartWidget::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:/barAutoSortEChartWidget/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);
}

void BarAutoSortEChartWidget::slot_loadFinished(bool result)
{
    if(result)
    {
        initJs();
        // 因為使用佈局,在沒有完全構造之前,其大小是不可預期的,等構造完成後,佈局的大小才會形成,此時再初始化一次
        resizeEvent(0);
    }
}

void BarAutoSortEChartWidget::initJs()
{
    _initJsStr = QSTRING(
            "option = {"
            "  xAxis: {"
            "    max: 'dataMax'"
            "  },"
            "  yAxis: {"
            "    type: 'category',"
            "    data: ['特斯拉', '賓士', '寶馬', '理想', '蔚來'],"
            "    inverse: true,"
            "    animationDuration: 300,"
            "    animationDurationUpdate: 300,"
            "    max: 4"
            "  },"
            "  series: ["
            "    {"
            "      realtimeSort: true,"
            "      name: 'X',"
            "      type: 'bar',"
            "      data: [10,20,50,10,30],"
            "      label: {"
            "        show: true,"
            "        position: 'right',"
            "        valueAnimation: true"
            "      },"
            "      itemStyle: {"
            "            color: function(params) {"
            "              var colorList = ['#EE14FF', '#F092FF', '#FF61FE', '#A02F99', '#F00682'];  /* 注意1:需要分號 */"
            "              return colorList[params.dataIndex];    /* 注意2:需要dataIndex,獲取序號 */"
            "         }"
            "      }"
            "    },"
            "  ],"
            "  graphic: {"
            "    elements: [    /* 時間標誌 */"
            "      {"
            "        type: 'text', "
            "        right: 160,"
            "        bottom: 100,"
            "        style: {"
            "          text: '1970-01',"
            "          font: 'bolder 100px monospace',"
            "          fill: 'rgba(100, 100, 100, 0.25)'"
            "        },"
            "        z: 100"
            "      }"
            "    ]"
            "  },"
            "  legend: {"
            "    show: false,"
            "  },"
            "  animationDuration: 0,"
            "  animationDurationUpdate: 1000,"
            "  animationEasing: 'linear',"
            "  animationEasingUpdate: 'linear'"
            "};"
            "myChart.setOption(option);");
    runJsScript(_initJsStr);
}

void BarAutoSortEChartWidget::runJsScript(QString str)
{
    if(_pWebEnginePage)
    {
        _pWebEnginePage->runJavaScript(str);
    }
}

void BarAutoSortEChartWidget::resizeEvent(QResizeEvent *event)
{
    if(_pWebEngineView)
    {
        _pWebEngineView->setGeometry(ui->label_echarts->geometry());
        LOG << ui->label_echarts->geometry();
    }
}

void BarAutoSortEChartWidget::timerEvent(QTimerEvent *event)
{
    _dateTime = _dateTime.addMonths(1);
    if(_dateTime >= QDateTime::currentDateTime())
    {
        if(_timerId != -1)
        {
            killTimer(_timerId);
            _timerId = -1;
        }
    }
    QString jsStr = QString(
            "option.series[0].data[0] = option.series[0].data[0] + %1;"
            "option.series[0].data[1] = option.series[0].data[1] + %2;"
            "option.series[0].data[2] = option.series[0].data[2] + %3;"
            "option.series[0].data[3] = option.series[0].data[3] + %4;"
            "option.series[0].data[4] = option.series[0].data[4] + %5;"
            "option.graphic.elements[0].style.text= '%6';"
            "myChart.setOption(option, true);"
            )
            .arg(qrand()%100)
            .arg(qrand()%100)
            .arg(qrand()%100)
            .arg(qrand()%100)
            .arg(qrand()%100)
            .arg(_dateTime.toString("yyyy-MM"));
    runJsScript(jsStr);
}

void BarAutoSortEChartWidget::on_pushButton_clear_clicked()
{
    _dateTime.setSecsSinceEpoch(0);
    QString jsStr = QString(
            "option.series[0].data[0] = 0;"
            "option.series[0].data[1] = 0;"
            "option.series[0].data[2] = 0;"
            "option.series[0].data[3] = 0;"
            "option.series[0].data[4] = 0;"
            "option.graphic.elements[0].style.text= '%1';"
            "myChart.setOption(option, true);"
            )
            .arg(_dateTime.toString("yyyy-MM"));
    runJsScript(jsStr);
}

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

void BarAutoSortEChartWidget::on_pushButton_start_clicked()
{
    if(_timerId == -1)
    {
        LOG << ui->lineEdit_interval->text().toInt();
        _timerId = startTimer(ui->lineEdit_interval->text().toInt());
        _dateTime.setSecsSinceEpoch(0);
        QString jsStr = QString(
                "option.series[0].data[0] = 0;"
                "option.series[0].data[1] = 0;"
                "option.series[0].data[2] = 0;"
                "option.series[0].data[3] = 0;"
                "option.series[0].data[4] = 0;"
                "option.graphic.elements[0].style.text= '%1';"
                "myChart.setOption(option, true);"
                )
                .arg(_dateTime.toString("yyyy-MM"));
        runJsScript(jsStr);

        ui->pushButton_start->setText("停止統計");
    }else{
        if(_timerId != -1)
        {
            killTimer(_timerId);
            _timerId = -1;
        }
        ui->pushButton_start->setText("開始統計");
    }
}

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

工程模板v1.4.0

  在這裡插入圖片描述

 

入坑

入坑一:排序圖問題無法自動排序

問題

  沒有排序:
  在這裡插入圖片描述

原理

  這裡之前我們已經遇見各種坑了,所以直接上偵錯工具,將Qt的js初始化程式碼在偵錯工具當中跑,如下圖,web偵錯網頁效果:
  在這裡插入圖片描述

解決方法

  自己調整序號,交換資料可以實現,但是無法實現上下條交換的動畫了。