Fix UTF-8 issue with CSV files opened in Excel

This commit is contained in:
Alex Spataru 2020-12-25 12:53:56 -06:00
parent d2690de4f9
commit cf839fd8e7
12 changed files with 177 additions and 82 deletions

View File

@ -42,6 +42,8 @@ Widgets.Window {
//
Settings {
category: "Device Manager"
property alias dmAuto: commAuto.checked
property alias dmManual: commManual.checked
property alias dmParity: parity.currentIndex
property alias dmStopBits: stopBits.currentIndex
property alias dmBaudRate: baudRate.currentIndex

View File

@ -30,5 +30,9 @@ import Dataset 1.0
import "../Widgets"
Item {
AccelerometerDelegate {
width: 250
height: 250
anchors.centerIn: parent
}
}

View File

@ -127,15 +127,10 @@ Page {
Layout.fillHeight: true
}
Item {
Components.WidgetGrid {
id: widgetGrid
Layout.fillWidth: true
Layout.fillHeight: true
Widgets.MapDelegate {
id: gpsMap
anchors.fill: parent
anchors.margins: app.spacing * 2
}
}
}

View File

@ -21,7 +21,91 @@
*/
import QtQuick 2.12
import QtQuick.Layouts 1.0
Item {
import Group 1.0
import Dataset 1.0
RowLayout {
id: accel
spacing: app.spacing
//
// Custom properties
//
property int index: 0
property real accX: 0
property real accY: 0
property real accZ: 0
property string title: ""
property real meanGForce: 0
property real gConstant: 9.80665
//
// Calculates the mean g force for all three axes using the pythagorean theorem
//
function calculateMeanGForce() {
if (CppWidgets.accelerometerGroupCount() > accel.index) {
accel.accX = CppWidgets.accelerometerX(accel.index)
accel.accY = CppWidgets.accelerometerY(accel.index)
accel.accZ = CppWidgets.accelerometerZ(accel.index)
accel.title = CppWidgets.accelerometerGroupAt(accel.index).title
}
else {
accel.accX = 0
accel.accY = 0
accel.accZ = 0
accel.title = 0
}
var gX = accel.accX / accel.gConstant
var gY = accel.accY / accel.gConstant
var gZ = accel.accZ / accel.gConstant
accel.meanGForce = Math.sqrt(Math.pow(gX, 2) + Math.pow(gY, 2) + Math.pow(gZ, 2))
}
//
// Accelerometer circle
//
Rectangle {
width: height
color: "#444"
border.width: 2
radius: width / 2
border.color: "#bebebe"
Layout.fillHeight: true
}
//
// Indicator controls
//
ColumnLayout {
spacing: app.spacing
Layout.fillHeight: true
Item {
Layout.fillHeight: true
}
LED {
onColor: "#f00"
text: qsTr("Cont. value")
}
LED {
onColor: "#0f0"
text: qsTr("Max. value")
}
LED {
onColor: "#00f"
text: qsTr("Min. value")
}
Item {
Layout.fillHeight: true
}
}
}

View File

@ -60,29 +60,6 @@ ColumnLayout {
//
readonly property var gpsCoordinates: QtPositioning.coordinate(latitude, longitude)
//
// Center map when connecting with CanSat
//
Connections {
target: CppQmlBridge
function onUpdated() {
if (oldCoordinates === QtPositioning.coordinate(0,0)) {
map.center = gpsCoordinates
oldCoordinates = gpsCoordinates
}
gps.latitude = CppQmlBridge.gpsLatitude
gps.longitude = CppQmlBridge.gpsLongitude
}
} Connections {
target: CppSerialManager
function onPortChanged() {
oldCoordinates = QtPositioning.coordinate(0,0)
}
}
//
// Centers the map to Queretaro if the GPS is not working,
// otherwise, centers the map to the CanSat's position

View File

@ -106,6 +106,9 @@ ApplicationWindow {
Timer {
id: timer
interval: 500
onTriggered: app.visible = true
onTriggered: {
app.visible = true
CppJsonParser.readSettings()
}
}
}

View File

@ -138,6 +138,8 @@ void Export::closeFile()
writeValues();
m_csvFile.close();
m_textStream.setDevice(Q_NULLPTR);
emit openChanged();
}
}
@ -228,7 +230,7 @@ void Export::writeValues()
// Open file
m_csvFile.setFileName(dir.filePath(fileName));
if (!m_csvFile.open(QFile::WriteOnly))
if (!m_csvFile.open(QIODevice::WriteOnly | QIODevice::Text))
{
QMessageBox::critical(Q_NULLPTR, tr("CSV File Error"), tr("Cannot open CSV file for writing!"),
QMessageBox::Ok);
@ -236,14 +238,17 @@ void Export::writeValues()
break;
}
// Add cell titles
// Add cell titles & force UTF-8 codec
m_textStream.setDevice(&m_csvFile);
m_textStream.setCodec("UTF-8");
m_textStream.setGenerateByteOrderMark(true);
for (int i = 0; i < titles.count(); ++i)
{
m_csvFile.write(titles.at(i).toUtf8());
m_textStream << titles.at(i).toUtf8();
if (i < titles.count() - 1)
m_csvFile.write(",");
m_textStream << ",";
else
m_csvFile.write("\n");
m_textStream << "\n";
}
// Update UI
@ -253,11 +258,11 @@ void Export::writeValues()
// Write cell values
for (int i = 0; i < values.count(); ++i)
{
m_csvFile.write(values.at(i).toUtf8());
m_textStream << values.at(i).toUtf8();
if (i < values.count() - 1)
m_csvFile.write(",");
m_textStream << ",";
else
m_csvFile.write("\n");
m_textStream << "\n";
}
// Remove JSON from list

View File

@ -47,15 +47,16 @@ private:
public slots:
void openCsv();
void closeFile();
void openCurrentCsv();
private slots:
void closeFile();
void writeValues();
void updateValues();
private:
QFile m_csvFile;
QTextStream m_textStream;
QList<QPair<QDateTime, QJsonObject>> m_jsonList;
};

View File

@ -139,7 +139,7 @@ void JsonParser::loadJsonMap()
/**
* Opens, validates & loads into memory the JSON file in the given @a path.
*/
void JsonParser::loadJsonMap(const QString &path)
void JsonParser::loadJsonMap(const QString &path, const bool silent)
{
// Validate path
if (path.isEmpty())
@ -163,23 +163,26 @@ void JsonParser::loadJsonMap(const QString &path)
if (error.error != QJsonParseError::NoError)
{
m_jsonMap.close();
writeSettings("");
NiceMessageBox(tr("JSON parse error"), error.errorString());
}
// JSON contains no errors
// JSON contains no errors, load data & save settings
else
{
writeSettings(path);
m_jsonMapData = QString::fromUtf8(data);
NiceMessageBox(tr("JSON map file loaded successfully!"),
tr("File \"%1\" loaded into memory").arg(jsonMapFilename()));
if (!silent)
NiceMessageBox(tr("JSON map file loaded successfully!"),
tr("File \"%1\" loaded into memory").arg(jsonMapFilename()));
}
}
// Open error
else
{
NiceMessageBox(tr("Cannot read file contents"),
tr("Failed to read contents of \"%1\", check file permissions").arg(jsonMapFilename()));
writeSettings("");
NiceMessageBox(tr("Cannot read JSON file"), tr("Please check file permissions & location"));
m_jsonMap.close();
}
@ -204,6 +207,24 @@ void JsonParser::setOperationMode(const OperationMode mode)
emit operationModeChanged();
}
/**
* Loads the last saved JSON map file (if any)
*/
void JsonParser::readSettings()
{
auto path = m_settings.value("json_map_location", "").toString();
if (!path.isEmpty())
loadJsonMap(path, true);
}
/**
* Saves the location of the last valid JSON map file that was opened (if any)
*/
void JsonParser::writeSettings(const QString &path)
{
m_settings.setValue("json_map_location", path);
}
/**
* Tries to parse the given data as a JSON document according to the selected
* operation mode.

View File

@ -25,6 +25,7 @@
#include <QFile>
#include <QObject>
#include <QSettings>
#include <QQmlEngine>
#include <QJsonArray>
#include <QJsonValue>
@ -62,17 +63,22 @@ public:
public slots:
void loadJsonMap();
void loadJsonMap(const QString &path);
void setOperationMode(const OperationMode mode);
void loadJsonMap(const QString &path, const bool silent = false);
private:
JsonParser();
public slots:
void readSettings();
void writeSettings(const QString &path);
private slots:
void readData(const QByteArray &data);
private:
QFile m_jsonMap;
QSettings m_settings;
QString m_jsonMapData;
OperationMode m_opMode;
QJsonDocument m_document;

View File

@ -43,8 +43,5 @@ ModuleManager::ModuleManager()
*/
void ModuleManager::deleteModules()
{
Export::getInstance()->deleteLater();
QmlBridge::getInstance()->deleteLater();
GraphProvider::getInstance()->deleteLater();
SerialManager::getInstance()->deleteLater();
Export::getInstance()->closeFile();
}

View File

@ -44,40 +44,40 @@ public:
QList<Group *> gaugeGroup() const;
QList<Group *> accelerometerGroup() const;
int barGroupCount() const;
int mapGroupCount() const;
int gyroGroupCount() const;
int tankGroupCount() const;
int gaugeGroupCount() const;
int accelerometerGroupCount() const;
Q_INVOKABLE int barGroupCount() const;
Q_INVOKABLE int mapGroupCount() const;
Q_INVOKABLE int gyroGroupCount() const;
Q_INVOKABLE int tankGroupCount() const;
Q_INVOKABLE int gaugeGroupCount() const;
Q_INVOKABLE int accelerometerGroupCount() const;
Group *barGroupAt(const int index);
Group *mapGroupAt(const int index);
Group *gyroGroupAt(const int index);
Group *tankGroupAt(const int index);
Group *gaugeGroupAt(const int index);
Group *accelerometerGroupAt(const int index);
Q_INVOKABLE Group *barGroupAt(const int index);
Q_INVOKABLE Group *mapGroupAt(const int index);
Q_INVOKABLE Group *gyroGroupAt(const int index);
Q_INVOKABLE Group *tankGroupAt(const int index);
Q_INVOKABLE Group *gaugeGroupAt(const int index);
Q_INVOKABLE Group *accelerometerGroupAt(const int index);
double gyroX(const int index);
double gyroY(const int index);
double gyroZ(const int index);
Q_INVOKABLE double gyroX(const int index);
Q_INVOKABLE double gyroY(const int index);
Q_INVOKABLE double gyroZ(const int index);
double accelerometerX(const int index);
double accelerometerY(const int index);
double accelerometerZ(const int index);
Q_INVOKABLE double accelerometerX(const int index);
Q_INVOKABLE double accelerometerY(const int index);
Q_INVOKABLE double accelerometerZ(const int index);
double bar(const int index);
double tank(const int index);
double gauge(const int index);
double barMin(const int index);
double barMax(const int index);
double tankMin(const int index);
double tankMax(const int index);
double gaugeMin(const int index);
double gaugeMax(const int index);
Q_INVOKABLE double bar(const int index);
Q_INVOKABLE double tank(const int index);
Q_INVOKABLE double gauge(const int index);
Q_INVOKABLE double barMin(const int index);
Q_INVOKABLE double barMax(const int index);
Q_INVOKABLE double tankMin(const int index);
Q_INVOKABLE double tankMax(const int index);
Q_INVOKABLE double gaugeMin(const int index);
Q_INVOKABLE double gaugeMax(const int index);
double mapLatitude(const int index);
double mapLongitude(const int index);
Q_INVOKABLE double mapLatitude(const int index);
Q_INVOKABLE double mapLongitude(const int index);
private slots:
void updateModels();