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