前言

  上一篇成功是EChart随着Qt窗口变化而变化,本篇将开始正式介绍柱状图介绍、基础使用,并将其封装一层Qt。
  本篇的demo实现了隐藏js代码的方式,实现了一个条形图的基本交互方式,即Qt调用js脚本操作html。

 

Demo演示

  

 

ECharts调试工具

  ECharts提供的纯JS代码编辑开发调试工具,可编辑js,并且查看运行效果:
  https://echarts.apache.org/examples/zh/editor.html
  
  开发过程中对于属性的查询和调试ECharts也提供了配置帮助。
  官方配置手册:https://echarts.apache.org/zh/option.html
  

 

目标

  随便找一个预期目标:
  

 

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_OBJECT public:
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

  

 

后话

  条形图将会根据实际需求持续升级版本,下一篇会继续折线图的基础封装。

Qt+ECharts开发笔记(三):ECharts的柱状图介绍、基础使用和Qt封装Demo的更多相关文章

  1. Django开发笔记三

    Django开发笔记一 Django开发笔记二 Django开发笔记三 Django开发笔记四 Django开发笔记五 Django开发笔记六 1.基于类的方式重写登录:views.py: from ...

  2. Qt+ECharts开发笔记(五):ECharts的动态排序柱状图介绍、基础使用和Qt封装Demo

    前言   上一篇的demo使用隐藏js代码的方式,实现了一个饼图的基本交互方式,并预留了Qt模块对外的基础接口.  本篇的demo实现了自动排序的柱状图,实现了一个自动排序柱状图的基本交互方式,即Qt ...

  3. Qt+ECharts开发笔记(二):Qt窗口动态调整大小,使ECharts跟随Qt窗口大小变换而变换大小

    前言   上一篇将ECharts嵌入Qt中,在开始ECharts使用之前,还有一个很重要的功能,就是在窗口变换大小的时候,ECharts的图表尺寸也要跟随Qt窗口变换大小而变换大小.   Demo演示 ...

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

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

  5. TERSUS无代码开发(笔记01)-按装下载和基础语法

    1.中国官网 https://tersus.cn/ 2.下载:https://tersus.cn/download/ 3.开发文档:https://tersus.cn/docs/ 4.基本元件说明 图 ...

  6. echarts使用笔记三:柱子对比

    app.title = '坐标轴刻度与标签对齐'; option = { title : { //标题 x : 'center', y : 5, text : '对比图' //换行用 \n }, le ...

  7. 麒麟系统开发笔记(二):国产麒麟系统搭建Qt开发环境安装Qt5.12

    前言   开发国产应用,使用到银河麒麟V4,V10,本篇以V10记录,参照上一篇可安装V4.V7.V10三个版本,麒麟V4系自带了Qt,麒麟V10没有自带Qt,需要自己编译搭建环境.   银河麒麟V1 ...

  8. ECharts学习总结(三):ECharts图表对象的初始化(init)详解以及注意事项

    一.相关js文件的引入 这里我们采用标签式引入文件的方式,我们引入两个js文件,一个是esl.js文件和一个echarts.js. <script src="js/esl.js&quo ...

  9. RBL开发笔记三

    2014-08-26 20:06:24 今天就是在开发这个EPOLL来处理网络事件 封装较为健壮的EPOLL模型来处理基本的网络IO 1) 超时这个主题先没有弄 在开发EPOLL包括select/po ...

随机推荐

  1. k8s入门之ConfigMap(九)

    ConfigMap是k8s的配置管理工具,通常用来保存明文的配置信息,以key-value形式传递配置. 一.使用命令创建ConfigMap对象 1.通过--from-literal参数创建 kube ...

  2. 图解KMP字符串匹配算法+代码实现

    kmp算法跟之前讲的bm算法思想有一定的相似性.之前提到过,bm算法中有个好后缀的概念,而在kmp中有个好前缀的概念,什么是好前缀,我们先来看下面这个例子. 观察上面这个例子,已经匹配的abcde称为 ...

  3. axios源码解析 - 请求拦截器

    axios请求拦截器,也就是在请求发送之前执行自定义的函数. axios源码版本 - ^0.27.2 (源码是精简版) 平时在业务中会这样去写请求拦截器,代码如下: // 创建一个新的实例 var s ...

  4. 【算法】堆排序(Heap Sort)(七)

    堆排序(Heap Sort) 堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法.堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父 ...

  5. 好客租房29-从jsx中抽离事件处理程序

    从jsx中抽离过多js逻辑代码 会显得非常混乱 推荐:将逻辑抽离到单独的方法中 保证jsx结构清晰 //导入react     import React from 'react'           ...

  6. SpringBoot实现基于token的登录验证

    一.SpringBoot实现基于token的登录验证 基于token的登录验证实现原理:客户端通过用户名和密码调用登录接口,当验证数据库中存在该用户后,将用户的信息按照token的生成规则,生成一个字 ...

  7. 从头创建一个新的vue项目------用npm|yarn下载vue-cli|vue-ui创建vue

    1.下载node或者是nvm node可以直接去node官网下载,http://nodejs.cn/,默认是长期维护的版本 如果想管理node的版本,可以下载nvm.这个是可选的.但是作为一个前端工程 ...

  8. vuex+Es6语法补充-Promise

    Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,采用 集中式存储管理 单页面的状态管理/多页面状态管理 使用步骤: // 1.导入 import Vuex from 'vuex' // ...

  9. 用t-SNE进行流形学习(digits数据集)

    流行学习算法: 是一类用于可视化的算法,它允许进行更复杂的映射,通常也可以给出更好的可视化. t-SNE算法是其中一种. PCA是用于变换数据的首选方法,也可以进行可视化,但它的性质(先旋转然后减少方 ...

  10. CF1625D - Binary Spiders[trie树优化dp]

    官方题解 题意:给数列a[],选择尽量多的数满足任意两个异或起来<=k 1625D - Binary Spiders 思路:首先,将数列排序得到,然后升序取得的值的任意两个最小值为相邻两个异或的 ...