Sun, 15 Oct 2023 13:30:24 +0200
Added MySQL query example in the source.
/** * ChartiSpindel.cpp is part of bmsapp. * * bmsapp is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * bmsapp is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "ChartiSpindel.h" #include "callout.h" #include "MainWindow.h" #include "Utils.h" ChartiSpindel::ChartiSpindel(QString code, QString name, QWidget *parent) : QDialog(parent) { QSqlQuery query; double timestamp; double sg_min = 1.2, sg_max = 0.8, mg, sg; double temp_min = 100, temp_max = 0, temp; double batt_min = 4.5, batt_max = 3.0, batt; qDebug() << "ChartiSpindel:" << code << name; QDialog* dialog = new QDialog(parent); dialog->setWindowTitle(tr("BMSapp - iSpindel ") + "\"" + name + "\""); dialog->setObjectName(QString::fromUtf8("ChartiSpindel")); dialog->setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint); dialog->resize(1024, 600); QPushButton *saveButton = new QPushButton(tr("Save")); saveButton->setAutoDefault(false); QIcon icon1; icon1.addFile(QString::fromUtf8(":icons/silk/disk.png"), QSize(), QIcon::Normal, QIcon::Off); saveButton->setIcon(icon1); QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); buttonBox->setObjectName(QString::fromUtf8("buttonBox")); buttonBox->setOrientation(Qt::Vertical); buttonBox->setStandardButtons(QDialogButtonBox::Ok); buttonBox->addButton(saveButton,QDialogButtonBox::ActionRole); temperature = new QLineSeries(); density = new QLineSeries(); battery = new QLineSeries(); query.prepare("SELECT * FROM log_ispindel WHERE code=:code ORDER BY datetime"); query.bindValue(":code", code); query.exec(); while (query.next()) { timestamp = query.value("datetime").toDateTime().toSecsSinceEpoch() * 1000; mg = query.value("sg").toDouble(); temp = query.value("temperature").toDouble(); if (ceil(temp) > temp_max) temp_max = ceil(temp); if (floor(temp) < temp_min) temp_min = floor(temp); /* * Correct the SG plotline for temperature. */ sg = Utils::HydroCorrection(mg, temp, 20.0); if ((ceil(sg * 100) / 100) > sg_max) sg_max = ceil(sg * 100) / 100; if ((floor(sg * 100) / 100) < sg_min) sg_min = floor(sg * 100) / 100; batt = round(query.value("battery").toDouble() * 500) / 500; if ((ceil(batt * 10) / 10) > batt_max) batt_max = ceil(batt * 10) / 10; if ((floor(batt * 10) / 10) < batt_min) batt_min = floor(batt * 10) / 10; temperature->append(timestamp, temp); density->append(timestamp, sg); battery ->append(timestamp, batt); } temperature->setName(tr("Temperature")); temperature->setColor(QColorConstants::Svg::red); density->setName(tr("SG")); QPen pen(QColorConstants::Svg::navy); pen.setWidth(2); density->setPen(pen); battery->setName(tr("Battery")); battery->setColor(QColorConstants::Svg::limegreen); chart = new QChart(); chart->setTitle(QString("%1 \"%2\"").arg(code).arg(name)); chart->addSeries(battery); chart->addSeries(temperature); chart->addSeries(density); QDateTimeAxis *axisX = new QDateTimeAxis; axisX->setTickCount(10); axisX->setFormat("dd MMM"); axisX->setTitleText(tr("Date")); axisX->setLabelsFont(QFont("Helvetica", 8, QFont::Normal)); chart->addAxis(axisX, Qt::AlignBottom); battery->attachAxis(axisX); temperature->attachAxis(axisX); density->attachAxis(axisX); QValueAxis *axisYT = new QValueAxis; axisYT->setRange(temp_min, temp_max); axisYT->setTickCount(10); axisYT->setLabelFormat("%.1f"); axisYT->setTitleText(tr("Temperature °C")); axisYT->setLabelsFont(QFont("Helvetica", 8, QFont::Normal)); chart->addAxis(axisYT, Qt::AlignRight); temperature->attachAxis(axisYT); QValueAxis *axisYD = new QValueAxis; axisYD->setRange(sg_min, sg_max); axisYD->setTickCount(10); axisYD->setLabelFormat("%.3f"); axisYD->setTitleText("SG"); axisYD->setLabelsFont(QFont("Helvetica", 8, QFont::Normal)); chart->addAxis(axisYD, Qt::AlignLeft); density->attachAxis(axisYD); QValueAxis *axisYB = new QValueAxis; axisYB->setRange(batt_min, batt_max); axisYB->setTickCount(10); axisYB->setLabelFormat("%.2f"); axisYB->setTitleText(tr("Battery volt")); axisYB->setLabelsFont(QFont("Helvetica", 8, QFont::Normal)); chart->addAxis(axisYB, Qt::AlignRight); battery->attachAxis(axisYB); connect(temperature, &QLineSeries::hovered, this, &ChartiSpindel::tooltip); connect(density, &QLineSeries::hovered, this, &ChartiSpindel::tooltip); connect(battery, &QLineSeries::hovered, this, &ChartiSpindel::tooltip); chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); dialog->setLayout(new QHBoxLayout); dialog->layout()->addWidget(chartView); dialog->layout()->addWidget(buttonBox); QObject::connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); QObject::connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); QObject::connect(saveButton, SIGNAL(clicked()), this, SLOT(savePNG())); dialog->setModal(true); dialog->exec(); } ChartiSpindel::~ChartiSpindel() {} void ChartiSpindel::savePNG() { QSettings settings(QSettings::IniFormat, QSettings::UserScope, "mbse", "bmsapp"); QString dirName; /* * First check if the directory stored in the settings file exists. * It might be on a removable media that was last used ... * If so, fallback to the user's home directory. */ dirName = settings.value("paths/download").toString(); if (! QDir(dirName).exists()) { dirName = QDir::homePath(); } QString path = QFileDialog::getSaveFileName(this, tr("Save Image"), dirName + "/ispindel.png", tr("Image (*.png)")); if (path.isEmpty()) { QMessageBox::warning(this, tr("Save File"), tr("No image file selected.")); return; } /* * Update to current selected path */ settings.setValue("paths/download", QFileInfo(path).absolutePath()); QImage img((chartView->size()), QImage::Format_ARGB32); QPainter painter; painter.begin(&img); chartView->render(&painter); painter.setRenderHint(QPainter::Antialiasing); painter.end(); img.save(path); } void ChartiSpindel::tooltip(QPointF point, bool state) { QAbstractSeries *series = qobject_cast<QAbstractSeries *>(sender()); if (t_tooltip == 0) t_tooltip = new Callout(chart, series); if (state) { QDateTime timeis = QDateTime::fromMSecsSinceEpoch(point.x()); t_tooltip->setSeries(series); if (series == temperature) t_tooltip->setText(QString(tr("%1\nTemperature %2°C")).arg(timeis.toString("dd-MM-yyyy hh:mm")).arg(point.y(), 2, 'f', 1)); else if (series == density) t_tooltip->setText(QString(tr("%1\nDensity %2 SG")).arg(timeis.toString("dd-MM-yyyy hh:mm")).arg(point.y(), 5, 'f', 4)); else if (series == battery) { const float battery_max = 4.13; const float battery_min = 3.43; double batt = ((point.y() - battery_min) / (battery_max - battery_min)) * 100; if (batt < 0) batt = 0; if (batt > 100) batt = 100; t_tooltip->setText(QString(tr("%1\nBattery %2 volt\nCapacity %3%")).arg(timeis.toString("dd-MM-yyyy hh:mm")) .arg(point.y(), 3, 'f', 2).arg(batt, 2, 'f', 1, '0')); } t_tooltip->setAnchor(point); t_tooltip->setZValue(11); t_tooltip->updateGeometry(); t_tooltip->show(); } else { t_tooltip->hide(); } }