因为博客园越来越不好用,所以就此转战csdn。

之所以要制作这个可视化平台呢,是因为了完成【数据可视化】课程的大作业,一篇综述和聆心云平台的数据展示。

好吧,现在先来看作业要求:

各位同学好,就本课程的期末期末考试说明如下: 
本课程的期末考试不采用闭卷考试的形式,而是采用平时大作业+文献综述的形式。具体要求如下: 
1. 写一篇最新的可视化理论研究、技术研究、产品开发等方面的综述文章(成绩占比50%) 
。 
要求(1)阅读10篇左右相关中外文论文,(2)论文篇幅在5000字的(图文并茂)
2. 开发一个可视化系统(成绩占比50%)。 
利用自己比较熟悉的可视化编程工具(推荐Python,Java+ECharts或Pyecharts)等,开发一个基于“聆心云3D在线数字化沙盘游戏”的自我沙盘游戏数据的可视化系统。
要求(1)注册聆心云账号;
(2)做30个沙盘游戏,保存在个人账号里;
(3)数据的获取、整理、存储;
(4)可视化展示:要求展示不少于3种可视化形式(在所有作品使用沙具名称的词云;使用频率最高的10个沙具的使用次数的柱状图;10个最多的操作行为的占比饼图;等)
(5)撰写系统设计文档要求:a. 需求分析;b.设计规划:数据来源和获取方法,数据存取和处理形式;c.可视化作品的编码实现过程,采用什么工具,如何编码实现;d. 心得体会。 
3. 最后提交作业的形式:作业1为电子文档,作业2为可软件系统代码+设计文档和系统运行结果。 
4. 每个作业都独立完成,提交截止日期2023.2.20。

综述,前段时间放了两周疫情假,趁假期搞定了已经,而这个可视化系统,花了我将近一周的时间。

首先看最终效果:

视频效果

接下来我们来看下怎么制作的。

需求分析

根据作业需求,我们需要从聆心云数据接口爬取数据、数据持久化与读取、数据展示等几大步骤。

在数据爬取上,根据接口要求,我们需要才用http get的方式爬取数据

在数据持久化和读取上,起初我考虑的是mysql\mongo\redis等数据库,但考虑到这些方式都需要客户机上特别安装数据库并配置,不利于程序的传播,所以我决定使用文件的方式进行数据持久化。

进行数据展示的时候,我们可以使用多种语言工具进行编程,比如Python\Java+ECharts或Pyecharts等,但考虑到我个人的代码熟悉情况,我决定使用Qt进行编程。

设计规划

数据来源和获取方法

1、用户需要注册聆心云账号,并进行一定数量的沙盘游戏

2、可通过

https://lingxinyun.cn/sp/getIdByAuth?mobile=用户名&passwd=密码

获取用户所有游戏局数的步数和对应游戏ID,结果如下

​
​​[{"id":25930,"cnt":47},{"id":25933,"cnt":349},{"id":25934,"cnt":473},{"id":26308,"cnt":131},{"id":26311,"cnt":276},{"id":26318,"cnt":91},{"id":26324,"cnt":371},{"id":26909,"cnt":443},{"id":26915,"cnt":279},{"id":26940,"cnt":166},{"id":26944,"cnt":326},{"id":26945,"cnt":224},{"id":26946,"cnt":619},{"id":26972,"cnt":747},{"id":26973,"cnt":1196},{"id":27524,"cnt":1156},{"id":27525,"cnt":555},{"id":27545,"cnt":621},{"id":27569,"cnt":807},{"id":27570,"cnt":1622},{"id":27571,"cnt":737},{"id":27572,"cnt":844},{"id":27573,"cnt":907},{"id":27574,"cnt":795},{"id":27645,"cnt":398}]​

3、可通过

https://lingxinyun.cn/sp/getSandPlay?id=游戏ID

获取对应游戏局数的详细操作以及道具,结果如下

["创建沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","创建沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","创建沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","创建沙具(草坪)","移动沙具(草坪)","删除沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","创建沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","创建沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","移动沙具(草坪)","创建沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","缩放沙具(草坪)","移动沙具(草坪)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(草丛)","移动沙具(草丛)","创建沙具(兰)","移动沙具(兰)","创建沙具(菊)","移动沙具(菊)","创建沙具(蜜蜂)","移动沙具(蜜蜂)","缩放沙具(蜜蜂)","移动沙具(蜜蜂)","移动沙具(蜜蜂)","旋转沙具(蜜蜂)","旋转沙具(蜜蜂)","移动沙具(草坪)","移动沙具(草坪)","创建沙具(草房)","移动沙具(草房)","缩放沙具(草房)","创建沙具(女学生)","移动沙具(女学生)","移动沙具(女学生)","移动沙具(女学生)","创建沙具(母鸡)","移动沙具(母鸡)","缩放沙具(母鸡)","创建沙具(母鸡)","移动沙具(母鸡)","缩放沙具(母鸡)","旋转沙具(母鸡)","移动沙具(母鸡)","创建沙具(兔)","移动沙具(兔)","缩放沙具(兔)","旋转沙具(兔)","移动沙具(兔)","移动沙具(兔)","旋转沙具(兔)","移动沙具(兔)","移动沙具(兔)","创建沙具(苹果树)","移动沙具(苹果树)","缩放沙具(苹果树)","缩放沙具(草坪)","缩放沙具(草坪)","缩放沙具(苹果树)"]

4、进行json解析、字符串和统计操作,即可拆出所需的所有数据

数据存储与处理形式

起初我考虑的是mysql\mongo\redis等数据库,但考虑到这些方式都需要客户机上特别安装数据库并配置,不利于程序的传播,所以我决定使用文件的方式进行数据持久化。

单个文件大概如下

序列化存储和反序列化读取规则如下

序列化的时候单局数据与单局数据隔断,使用|符号,单局内ID与json隔断,使用&符号,这样反序列化的时候,可以直接使用split函数,方便的进行数据拆解

可视化作品的编程实现过程

1、总体思路:

①通过接口爬取沙盘数据,并用文件进行存储;

②使用C++对沙盘数据进行分析;

③分析结果用QT制作应用程序进行展示。

2、作业结构:

3、爬取并持久化沙盘数据

①获取所有沙盘Id并根据沙盘ID获取具体数据

void UserInfoMgr::doLogin(QString userName,QString userCryptPass)
{QString targetUrl = QString("https://lingxinyun.cn/sp/getIdByAuth?mobile=%1&passwd=%2").arg(userName, userCryptPass);m_userName = userName;m_userPass = userCryptPass;QHttpMgr::me()->httpGet(targetUrl);
}void UserInfoMgr::onLoginSuccess(QString url, QByteArray data)
{// 解析参数auto parseRes = parseUri(url.toStdString().c_str());std::string absPath = std::get<0>(parseRes);auto paramMap = std::get<1>(parseRes);// 获取roleID和typestd::string mobileNo = getParamValue(paramMap,"mobile");if (mobileNo != ""){if (data.length() == 0){qInfo("数据获取失败,url:%s", url.toStdString().c_str());emit onAllDataLoaded(1);return;}QJsonDocument jsonDoc = QJsonDocument::fromJson(QString(data).toUtf8());if(!jsonDoc.isArray()){qInfo("数据解析失败,data:%s", QString(data).toStdString().c_str());emit onAllDataLoaded(2);return;}m_jsonIDArray = jsonDoc.array();for (int i=0;i < m_jsonIDArray.size();i ++){QJsonObject jsonTmp = m_jsonIDArray[i].toObject();QString targetUrl = QString("https://lingxinyun.cn/sp/getSandPlay?id=%1").arg(jsonTmp["id"].toInt());QHttpMgr::me()->httpGet(targetUrl);}} else {std::string szId = getParamValue(paramMap,"id");if (szId != ""){if (data.length() == 0){qInfo("数据获取失败,url:%s", url.toStdString().c_str());emit onAllDataLoaded(3);return;}QJsonDocument jsonDoc = QJsonDocument::fromJson(QString(data).toUtf8());if(!jsonDoc.isArray()){qInfo("数据解析失败,data:%s", QString(data).toStdString().c_str());emit onAllDataLoaded(4);return;}QJsonArray tmpArray;tmpArray = jsonDoc.array();uint32_t uID = std::stoi(szId);m_detailMap[uID] = tmpArray;size_t currentMapNum = m_detailMap.size();if (currentMapNum >= (uint32_t)m_jsonIDArray.size()){QString szFullFilePath = QCoreApplication::applicationDirPath() + "/" + m_userName + ".lxcloud";this->saveFile(szFullFilePath);this->doAnalyze();this->createWordCloudPng();}} else {emit onAllDataLoaded(5);return;}}
}

②存储沙盘数据

void UserInfoMgr::saveFile(QString filename)
{//用IODevice方式保存文本文件QFile aFile(filename);//aFile.setFileName(aFileName);if (!aFile.open(QIODevice::WriteOnly | QIODevice::Text)){QString strInfo = "写文件失败,请检查磁盘空间大小或联系开发人员。";qWarning(strInfo.toStdString().c_str());return;}for (auto it = m_detailMap.begin(); it != m_detailMap.end(); ++it){uint32_t uID = it->first;QJsonArray jsonTmp = it->second;QJsonDocument document;document.setArray(jsonTmp);QByteArray byteArray = document.toJson(QJsonDocument::Compact);QString strJson = QString(byteArray);QString strWrite = QString("%1&%2|").arg(uID).arg(strJson);QByteArray  strBytes=strWrite.toUtf8();aFile.write(strBytes, strBytes.length());  //写入文件}aFile.close();
}

4、数据展示

这里的沙盘操作是经过了拆分的,比如"移动沙具(草丛)"会被拆分成"移动沙具"和"草丛"

为了方便做数据统计,这里另外存储了另一种格式的沙盘数据,这里存储的是完整的操作

统计沙盘数据

void UserInfoMgr::doAnalyze()
{for (auto it = m_detailMap.begin(); it != m_detailMap.end(); ++it){QJsonArray jsonTmp = it->second;for (int i = 0; i < jsonTmp.size(); i++){QString szTmp = jsonTmp[i].toString();int32_t leftBracketPos = szTmp.indexOf('(');QRegExp rx("[0-9\)]");int32_t rightBracketPos = szTmp.indexOf(rx);QString szOperate = szTmp.mid(0,leftBracketPos);QString szItem = szTmp.mid(leftBracketPos + 1,rightBracketPos - leftBracketPos - 1);if (leftBracketPos == -1){szOperate = szTmp;szItem = "";}auto itOperate = m_operateMap.find(szOperate);if (itOperate != m_operateMap.end()){uint32_t currNum = itOperate->second;itOperate->second = currNum + 1;} else {m_operateMap[szOperate] = 1;m_strOperateWC = m_strOperateWC + " " + szOperate;}auto itItem = m_itemMap.find(szItem);if (itItem != m_itemMap.end() && szItem!=""){uint32_t currNum = itItem->second;itItem->second = currNum + 1;} else {m_itemMap[szItem] = 1;m_strItemWC = m_strItemWC + " " + szItem;}}}this->doItemMapSort();
}

沙盘数据可视化

饼图:

这里使用的技术,主要是Qt自带的QtCharts,可以很方便的制作饼图,并且根据需求,我只展示了前10种最多的操作和道具,其余的,都被我归并为了“其它”类

以下是代码:

#ifndef PIECHARTFORM_H
#define PIECHARTFORM_H#include <QWidget>namespace Ui {
class PieChartForm;
}class PieChartForm : public QWidget
{Q_OBJECTpublic:explicit PieChartForm(QWidget *parent = nullptr);~PieChartForm();private:Ui::PieChartForm *ui;
private:void createItemPie();void createOperatePie();
};#endif // PIECHARTFORM_H
#include "PieChartform.h"
#include "ui_PieChartform.h"#include "userInfoMgr.h"#include "drilldownchart.h"
#include "drilldownslice.h"
#include <QtCore/QRandomGenerator>
#include <QtCharts/QChartView>
#include <QtCharts/QLegend>
#include <QtCharts/QPieSeries>PieChartForm::PieChartForm(QWidget *parent) :QWidget(parent),ui(new Ui::PieChartForm)
{ui->setupUi(this);createItemPie();createOperatePie();
}PieChartForm::~PieChartForm()
{delete ui;
}void PieChartForm::createItemPie()
{auto& itemVec = UserInfoMgr::me()->getSortedItemVec();QStringList labels;for (auto it = itemVec.begin();it != itemVec.end();it++){labels.push_back(it->name);}DrilldownChart *chart = new DrilldownChart();chart->setTheme(QChart::ChartThemeDark);chart->setAnimationOptions(QChart::AllAnimations);chart->legend()->setVisible(true);chart->legend()->setAlignment(Qt::AlignTop);QPieSeries *yearSeries = new QPieSeries();yearSeries->setName("道具分布图");yearSeries->setHoleSize(0.3);for (int i=0;i < labels.size();i++) {QString name = labels[i];uint32_t nNum = itemVec[i].num;QPieSeries *series = new QPieSeries();*yearSeries << new DrilldownSlice(nNum, name, series);}chart->changeSeries(yearSeries);QChartView *chartView = new QChartView(chart);chartView->setRenderHint(QPainter::Antialiasing);ui->verticalLayout_4->addWidget(chartView);
}void PieChartForm::createOperatePie()
{auto& operateMap = UserInfoMgr::me()->getOperateMap();QStringList labels;for (auto it = operateMap.begin();it != operateMap.end();it++){labels.push_back(it->first);}DrilldownChart *chart = new DrilldownChart();chart->setTheme(QChart::ChartThemeLight);chart->setAnimationOptions(QChart::AllAnimations);chart->legend()->setVisible(true);chart->legend()->setAlignment(Qt::AlignTop);QPieSeries *yearSeries = new QPieSeries();yearSeries->setName("操作分布图");for (const QString &name : labels) {QPieSeries *series = new QPieSeries();*yearSeries << new DrilldownSlice(operateMap[name], name, series);}chart->changeSeries(yearSeries);QChartView *chartView = new QChartView(chart);chartView->setRenderHint(QPainter::Antialiasing);ui->verticalLayout_3->addWidget(chartView);
}

以下是实际效果:

柱状图

考虑到Qt自带的QtCharts在柱状图方面,表现不如第三方插件好用,所以我才用了第三方插件QCustomPlot来制作。

以下是代码:

#ifndef BARCHARTFORM_H
#define BARCHARTFORM_H#include <QWidget>namespace Ui {
class BarChartForm;
}class BarChartForm : public QWidget
{Q_OBJECTpublic:explicit BarChartForm(QWidget *parent = nullptr);~BarChartForm();private:Ui::BarChartForm *ui;
private:void generateOpChart();void generateItemChart();
};#endif // BARCHARTFORM_H
#include "BarChartForm.h"
#include "ui_BarChartForm.h"#include "userInfoMgr.h"#include "qcustomplot.h"BarChartForm::BarChartForm(QWidget *parent) :QWidget(parent),ui(new Ui::BarChartForm)
{ui->setupUi(this);generateOpChart();generateItemChart();
}BarChartForm::~BarChartForm()
{delete ui;
}void BarChartForm::generateOpChart()
{uint32_t maxOpNum = 0;auto& operateMap = UserInfoMgr::me()->getOperateMap();QCPAxis *keyAxis = ui->widgetOperate->xAxis;QCPAxis *valueAxis = ui->widgetOperate->yAxis;QCPBars *fossil = new QCPBars(keyAxis, valueAxis);  // 使用xAxis作为柱状图的key轴,yAxis作为value轴fossil->setAntialiased(false); // 为了更好的边框效果,关闭抗齿锯fossil->setName("Fossil fuels"); // 设置柱状图的名字,可在图例中显示fossil->setPen(QPen(QColor(0, 168, 140).lighter(130))); // 设置柱状图的边框颜色fossil->setBrush(QColor(0, 168, 140));  // 设置柱状图的画刷颜色// 为柱状图设置一个文字类型的key轴,ticks决定了轴的范围,而labels决定了轴的刻度文字的显示QVector<double> ticks;QVector<QString> labels;uint32_t index = 1;for (auto it = operateMap.begin();it != operateMap.end();it++){labels.push_back(it->first);maxOpNum = it->second > maxOpNum ? it->second : maxOpNum;ticks << index;index++;}maxOpNum *= 1.1;QSharedPointer<QCPAxisTickerText> textTicker(new QCPAxisTickerText);textTicker->addTicks(ticks, labels);keyAxis->setTicker(textTicker);        // 设置为文字轴keyAxis->setTickLabelRotation(60);     // 轴刻度文字旋转60度keyAxis->setSubTicks(false);           // 不显示子刻度keyAxis->setTickLength(0, 4);          // 轴内外刻度的长度分别是0,4,也就是轴内的刻度线不显示keyAxis->setRange(0, ticks.size()+1);               // 设置范围keyAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);valueAxis->setRange(0, maxOpNum);valueAxis->setPadding(35);             // 轴的内边距,可以到QCustomPlot之开始(一)看图解valueAxis->setLabel("操作柱状图");valueAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);QVector<double> fossilData;for (int i=0; i<labels.size(); i++) {QString name = labels[i];fossilData << operateMap[name];}fossil->setData(ticks, fossilData);
}void BarChartForm::generateItemChart()
{uint32_t maxOpNum = 0;auto& itemVec = UserInfoMgr::me()->getSortedItemVec();QCPAxis *keyAxis = ui->widgetItem->yAxis;QCPAxis *valueAxis = ui->widgetItem->xAxis;QCPBars *fossil = new QCPBars(keyAxis, valueAxis);  // 使用xAxis作为柱状图的key轴,yAxis作为value轴fossil->setAntialiased(false); // 为了更好的边框效果,关闭抗齿锯fossil->setName("Fossil fuels"); // 设置柱状图的名字,可在图例中显示fossil->setPen(QPen(QColor(0, 168, 140).lighter(130))); // 设置柱状图的边框颜色fossil->setBrush(QColor(0, 168, 140));  // 设置柱状图的画刷颜色// 为柱状图设置一个文字类型的key轴,ticks决定了轴的范围,而labels决定了轴的刻度文字的显示QVector<double> ticks;QVector<QString> labels;uint32_t index = 1;for (auto it = itemVec.begin();it != itemVec.end();it++){labels.push_back(it->name);maxOpNum = it->num > maxOpNum ? it->num : maxOpNum;ticks << index;index++;}maxOpNum *= 1.1;QSharedPointer<QCPAxisTickerText> textTicker(new QCPAxisTickerText);textTicker->addTicks(ticks, labels);keyAxis->setTicker(textTicker);        // 设置为文字轴keyAxis->setTickLabelRotation(60);     // 轴刻度文字旋转60度keyAxis->setSubTicks(false);           // 不显示子刻度keyAxis->setTickLength(0, 4);          // 轴内外刻度的长度分别是0,4,也就是轴内的刻度线不显示keyAxis->setRange(0, ticks.size()+1);               // 设置范围keyAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);valueAxis->setRange(0, maxOpNum);valueAxis->setPadding(35);             // 轴的内边距,可以到QCustomPlot之开始(一)看图解valueAxis->setLabel("操作柱状图");valueAxis->setUpperEnding(QCPLineEnding::esSpikeArrow);QVector<double> fossilData;for (int i=0; i<labels.size(); i++) {QString name = labels[i];fossilData << itemVec[i].num;}fossil->setData(ticks, fossilData);
}

以下是效果:

词云

词云这里最开始我曾考虑过使用opencv上手硬写,但是几经尝试,虽然我做出来了,但是效果在颜色和形状上有点不尽人意,于是决定直接使用python自带的wordcloud库。

QT本身是支持C++和python的混合编程的,所以将python文件导入qt,直接代码中调用即可。

以下是对应的Python文件

# This Python file uses the following encoding: utf-8# if __name__ == "__main__":
#     pass
import imageio
import wordcloud as wcdef generateWordcloud(words_list, back_img_path, file_path): #根据文本文件生成词云background_image=imageio.imread(back_img_path)  #读取背景图片w=wc.WordCloud(background_color='white',# 设置背景颜色font_path = './fonts/simfang.ttf',# 设置字体mask=background_image # 设置背景图片)w.generate(words_list)    #生成词云w.to_file(file_path) # 生成图片print("生成词云成功!")return back_img_path# generateWordcloud("挖沙子 创建沙具 移动沙具 缩放沙具 移动沙具 缩放沙具 移动沙具 创建沙具 移动沙具 缩放沙具 删除沙具 挖沙子 创建沙具 移动沙具 创建沙具 移动沙具 创建沙具 移动沙具 创建沙具 移动沙具 创建沙具 移动沙具 删除沙具 删除沙具 删除沙具 删除沙具 删除沙具 创建沙具 移动沙具 缩放沙具 创建沙具 移动沙具 创建沙具 移动沙具 创建沙具 移动沙具 创建沙具 移动沙具 创建沙具 移动沙具 创建沙具 移动沙具 删除沙具 缩放沙具 缩放沙具 旋转沙具 移动沙具 创建沙具 移动沙具 缩放沙具 创建沙具 移动沙具 创建沙具 移动沙具 创建沙具 移动沙具", "./images/shoucang.png", "D:/work/build-LingXinCloud-Desktop_Qt_5_15_2_MinGW_64_bit-Debug/debug/wc_operate.png")

以下是效果:

其它功能

登陆

这里将用户输入的用户名密码记录,用于填入接口取数据。

至于记录用户名这个小功能,则是通过注册表存取实现的。

void LoginDialog::readLocalData()
{QSettings settings(BMI_GROUP_NAME,BMI_PROJECT_NAME);m_isSaved = settings.value(LOCAL_DATA_IS_SAVED,0).toInt();m_user = settings.value(LOCAL_DATA_USER_NAME,"").toString();m_password = settings.value(LOCAL_DATA_USER_PASS,"").toString();if (m_isSaved > 0) {ui->lineEditUserName->setText(m_user);}if (m_isSaved > 0) {ui->checkBoxRemberPass->setChecked(true);}
}void LoginDialog::writeLocalData()
{QSettings settings(BMI_GROUP_NAME,BMI_PROJECT_NAME);settings.setValue(LOCAL_DATA_USER_NAME,m_user);// password need to cryptsettings.setValue(LOCAL_DATA_USER_PASS,m_password);settings.setValue(LOCAL_DATA_IS_SAVED,m_isSaved);
}

关于聆心云

一个QMessageBox搞定

void MainWindow::on_action_BMI_triggered()
{QMessageBox::about(this, tr("关于聆心云"),tr("<b>聆心云心理健康平台</b>是由山东大学、山东中医药大学、齐鲁师范学院等多家高校联合开发的一款沙盘游戏,""我们致力于通过构建沙游世界,映射内心镜像,释放青少年无限潜能,共同创作美好未来。"));
}

最后放出程序的下载链接

下载链接

用QT制作聆心云数据可视化平台相关推荐

  1. 使用python生成词云——聆心云心理健康服务平台数据可视分析和可视化

    实验题目:聆心云心理健康服务平台数据可视分析和可视化 实验目的和要求:统计出在聆心云平台做沙盘游戏的次数.根据各次沙盘游戏所使用的沙具和进行的操作数据进行词云可视化,掌握Python词云制作方法 实验 ...

  2. 自开发数据可视化平台

    自开发数据可视化平台,目前只是一个简单的Demo版. 可以动态的设置数据源,动态绑定字段, 目前自适应布局逻辑还没想出来,是通过代码写死 在实现过程中遇到的难点和亮点工作 1.Combobox自定义搜 ...

  3. 如何设计数据可视化平台

    最近在项目上常常听到这样的话:"我想要一个酷炫的数据大屏","设计一定要有科技感","这个可视化设计没有重点"--每当听到这些需求,作为设计 ...

  4. 世界人口钟实时数据_用数字孪生重新定义智慧城市,美象VR推出MxDATA智慧城市CIM数据可视化平台...

    [猎云网(微信:ilieyun)成都]10月11日报道(文/尹子璇) 根据联合国发布的<世界城镇化展望>显示,预计到2050年,全世界将近70%的人口将居住在城市.然而,我们真的了解自己亲 ...

  5. vue 可视化布局工具_GitDataV一款Github的'大数据可视化平台'

    简介 GitDataV基于Vue框架构建的github数据可视化平台,是一个github"大数据可视化平台",通过它你可以更直观的看到你在github里的一些数据: 个人信息(✔) ...

  6. emqx 使用端口_数据传输、存储、展现,EMQ X + TDengine 搭建 MQTT 物联网数据可视化平台...

    物联网数据采集涉及到大量设备接入.海量的时序数据传输,EMQ X 消息中间件与 TDengine 大数据平台的组合技术栈完全能够胜任场景中的海量时间序列监测数据的传输.存储和计算. 数据入库后,往往需 ...

  7. java数据可视化平台初步构想

    2019独角兽企业重金招聘Python工程师标准>>> java数据可视化平台初步构想 平台架构 权限系统-负责后台用户权限 后台管理系统(oss)-负责后台运营配置相关操作 前端展 ...

  8. 数据可视化平台由哪些要素组成

    随着互联网行业的飞速发展,越来越多的企业意识到BI工具对企业的业务发展有很大的推动作用,使得工作效率更高更强.BI工具作为目前最炙手可热的数据分析工具,在数据分析结果的展示方式上大都选择了数据可视化, ...

  9. 大数据可视化平台有什么特点

    数据可视化是当下最热门的大数据应用技术,数据可视化就是将数据或者数据分析结果以图表的形式展示在各种平台上.这要求大数据分析平台有着强大的数据图表渲染功能,并且要内置丰富的可视化效果,以满足用户的不同展 ...

最新文章

  1. 开发日记-20190417 关键词 WheelView(视图绘制)
  2. PHP的pcntl进程控制教程二(pcntl_wait)
  3. 音视频技术开发周刊 | 170
  4. c malloc 头文件_C/C++求职者必备 23 道面试题,一道试题一份信心!
  5. java条码识别技术_条码识别示例代码
  6. android怎么阿看手机是移动联通还是电信的网络,GSM是联通、移动还是电信的网络?...
  7. 经典机器学习系列(十四)PAC-Learning
  8. 利用after和before伪元素在文字两边写横线
  9. Android 意图(Intent) 理论详解
  10. linux vi 软件 下载,在Linux系统中下载安装Vim 8
  11. 《圆明园的毁灭》教学设计方案
  12. TAOCP中1.1习题1解答与思考
  13. [ 资料分享 ] Vue 源码分析与讲解 - 附下载地址
  14. 文件对比软件 Beyond Compare 4 破解
  15. 世界银行公布各国1996-2018年GDP及GDP增长率
  16. 王者荣耀服务器维护5月22,王者荣耀5月22日更新维护公告 更新内容汇总
  17. Python爬虫-网贷之家P2P机构排名查询
  18. zigbee终端入网
  19. i386, x86, x86_64, IA-32, IA-64, 安腾, AMD64 的关系是什么?
  20. 无向图的最大独立集和最大团

热门文章

  1. 济南信息安全服务资质认证申请流程
  2. 业务流程图 VS 功能流程图 VS 页面流程图
  3. platform基础介绍
  4. 机器学习中的标签数据和无标签数据(监督、非监督和半监督学习)
  5. Android 使用百度鹰眼实现运动轨迹功能
  6. flex布局水平垂直居中
  7. Apriori算法简介---关联规则的频繁项集算法
  8. 分享小黄人商务唯美通用PPT模板
  9. blink不能发,这里记录下
  10. 【python】pop()栈函数介绍