# HG changeset patch # User Michiel Broek # Date 1658838410 -7200 # Node ID d03a426e0b6b6f089dbf0c90ebbdf6511be8c8eb # Parent a730825bc5e424be4e66735591bc8abb7d105fb1 On the fermenter, iSpindel, carbonation and brewday chart a tooltip with values is shown wheren hovering over the most important data lines. Changed the vertical to horzontal screen layout and added a save button to save the graph as .png image. diff -r a730825bc5e4 -r d03a426e0b6b src/ChartCarbonate.cpp --- a/src/ChartCarbonate.cpp Tue Jul 26 11:15:37 2022 +0200 +++ b/src/ChartCarbonate.cpp Tue Jul 26 14:26:50 2022 +0200 @@ -15,6 +15,7 @@ * along with this program. If not, see . */ #include "ChartCarbonate.h" +#include "callout.h" #include "MainWindow.h" @@ -31,16 +32,20 @@ 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->setGeometry(QRect(40, 565, 944, 36)); - buttonBox->setLayoutDirection(Qt::LeftToRight); - buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setOrientation(Qt::Vertical); buttonBox->setStandardButtons(QDialogButtonBox::Ok); - buttonBox->setCenterButtons(true); + buttonBox->addButton(saveButton,QDialogButtonBox::ActionRole); - QSplineSeries *temperature = new QSplineSeries(); - QSplineSeries *pressure = new QSplineSeries(); + temperature = new QSplineSeries(); + pressure = new QSplineSeries(); query.prepare("SELECT * FROM log_co2pressure WHERE code=:code ORDER BY datetime"); query.bindValue(":code", code); @@ -58,7 +63,7 @@ pen.setWidth(3); pressure->setPen(pen); - QChart *chart = new QChart(); + chart = new QChart(); chart->setTitle(QString("%1 \"%2\"").arg(code).arg(name)); chart->addSeries(temperature); chart->addSeries(pressure); @@ -90,14 +95,20 @@ chart->addAxis(axisYP, Qt::AlignLeft); pressure->attachAxis(axisYP); - QChartView *chartView = new QChartView(chart); + chart->setAcceptHoverEvents(true); + + connect(temperature, &QSplineSeries::hovered, this, &ChartCarbonate::tooltip); + connect(pressure, &QSplineSeries::hovered, this, &ChartCarbonate::tooltip); + + chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); - dialog->setLayout(new QVBoxLayout); + 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(); @@ -106,3 +117,43 @@ ChartCarbonate::~ChartCarbonate() {} + +void ChartCarbonate::savePNG() +{ + QString path = QFileDialog::getSaveFileName(this, tr("Save Image"), QDir::homePath() + "/carbonation.png", tr("Image (*.png)")); + if (path.isEmpty()) { + QMessageBox::warning(this, tr("Save File"), tr("No image file selected.")); + return; + } + + 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 ChartCarbonate::tooltip(QPointF point, bool state) +{ + QAbstractSeries *series = qobject_cast(sender()); + + if (t_tooltip == 0) + t_tooltip = new Callout(chart, series); + + if (state) { + QDateTime timeis = QDateTime::fromMSecsSinceEpoch(point.x()); + QString suffix = (series == temperature) ? "°C":" bar"; + t_tooltip->setSeries(series); + t_tooltip->setText(QString("%1\n%2%3").arg(timeis.toString("dd-MM-yyyy hh:mm")).arg(point.y(), 2, 'f', 1).arg(suffix)); + t_tooltip->setAnchor(point); + t_tooltip->setZValue(11); + t_tooltip->updateGeometry(); + t_tooltip->show(); + } else { + t_tooltip->hide(); + } +} + diff -r a730825bc5e4 -r d03a426e0b6b src/ChartCarbonate.h --- a/src/ChartCarbonate.h Tue Jul 26 11:15:37 2022 +0200 +++ b/src/ChartCarbonate.h Tue Jul 26 14:26:50 2022 +0200 @@ -1,9 +1,12 @@ #ifndef _CHARTCARBONATE_H #define _CHARTCARBONATE_H +#include "MainWindow.h" + #include #include +class Callout; namespace Ui { class ChartCarbonate; @@ -13,10 +16,21 @@ { Q_OBJECT +private slots: + void savePNG(); + +public slots: + void tooltip(QPointF point, bool state); + public: explicit ChartCarbonate(QString code, QString name, QWidget *parent = 0); ~ChartCarbonate(); +private: + QChartView *chartView; + QChart *chart; + Callout *t_tooltip = 0; + QSplineSeries *temperature, *pressure; }; #endif diff -r a730825bc5e4 -r d03a426e0b6b src/ChartFermenter.cpp --- a/src/ChartFermenter.cpp Tue Jul 26 11:15:37 2022 +0200 +++ b/src/ChartFermenter.cpp Tue Jul 26 14:26:50 2022 +0200 @@ -15,6 +15,7 @@ * along with this program. If not, see . */ #include "ChartFermenter.h" +#include "callout.h" #include "MainWindow.h" @@ -31,13 +32,17 @@ 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->setGeometry(QRect(40, 565, 944, 36)); - buttonBox->setLayoutDirection(Qt::LeftToRight); - buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setOrientation(Qt::Vertical); buttonBox->setStandardButtons(QDialogButtonBox::Ok); - buttonBox->setCenterButtons(true); + buttonBox->addButton(saveButton,QDialogButtonBox::ActionRole); QSplineSeries *pv_air = new QSplineSeries(); QSplineSeries *pv_beer = new QSplineSeries(); @@ -75,7 +80,7 @@ pwr_heat->setOpacity(0.25); pwr_heat->setColor(QColorConstants::Red); - QChart *chart = new QChart(); + chart = new QChart(); chart->setTitle(QString("%1 \"%2\"").arg(code).arg(name)); chart->addSeries(pwr_cool); chart->addSeries(pwr_heat); @@ -114,16 +119,19 @@ pwr_cool->attachAxis(axisYR); pwr_heat->attachAxis(axisYR); - + connect(pv_air, &QSplineSeries::hovered, this, &ChartFermenter::tooltip); + connect(pv_beer, &QSplineSeries::hovered, this, &ChartFermenter::tooltip); + connect(pv_chiller, &QSplineSeries::hovered, this, &ChartFermenter::tooltip); - QChartView *chartView = new QChartView(chart); + chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); - dialog->setLayout(new QVBoxLayout); + 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(); @@ -132,3 +140,44 @@ ChartFermenter::~ChartFermenter() {} + +void ChartFermenter::savePNG() +{ + QString path = QFileDialog::getSaveFileName(this, tr("Save Image"), QDir::homePath() + "/fermenter.png", tr("Image (*.png)")); + if (path.isEmpty()) { + QMessageBox::warning(this, tr("Save File"), tr("No image file selected.")); + return; + } + + 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 ChartFermenter::tooltip(QPointF point, bool state) +{ + QAbstractSeries *series = qobject_cast(sender()); + + if (t_tooltip == 0) + t_tooltip = new Callout(chart, series); + + if (state) { + QDateTime timeis = QDateTime::fromMSecsSinceEpoch(point.x()); + //qDebug() << "tooltip" << QString("%1 %2°C").arg( timeis.toString("dd-MM-yyyy hh:mm") ).arg(point.y(), 2, 'f', 1); + + t_tooltip->setSeries(series); + t_tooltip->setText(QString("%1\n%2 %3°C").arg(timeis.toString("dd-MM-yyyy hh:mm")).arg(series->name()).arg(point.y(), 2, 'f', 1)); + t_tooltip->setAnchor(point); + t_tooltip->setZValue(11); + t_tooltip->updateGeometry(); + t_tooltip->show(); + } else { + t_tooltip->hide(); + } +} + diff -r a730825bc5e4 -r d03a426e0b6b src/ChartFermenter.h --- a/src/ChartFermenter.h Tue Jul 26 11:15:37 2022 +0200 +++ b/src/ChartFermenter.h Tue Jul 26 14:26:50 2022 +0200 @@ -1,9 +1,12 @@ #ifndef _CHARTFERMENTER_H #define _CHARTFERMENTER_H +#include "MainWindow.h" + #include #include +class Callout; namespace Ui { class ChartFermenter; @@ -13,10 +16,20 @@ { Q_OBJECT +private slots: + void savePNG(); + +public slots: + void tooltip(QPointF point, bool state); + public: explicit ChartFermenter(QString code, QString name, QWidget *parent = 0); ~ChartFermenter(); +private: + QChartView *chartView; + QChart *chart; + Callout *t_tooltip = 0; }; #endif diff -r a730825bc5e4 -r d03a426e0b6b src/ChartiSpindel.cpp --- a/src/ChartiSpindel.cpp Tue Jul 26 11:15:37 2022 +0200 +++ b/src/ChartiSpindel.cpp Tue Jul 26 14:26:50 2022 +0200 @@ -15,6 +15,7 @@ * along with this program. If not, see . */ #include "ChartiSpindel.h" +#include "callout.h" #include "MainWindow.h" @@ -31,17 +32,21 @@ 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->setGeometry(QRect(40, 565, 944, 36)); - buttonBox->setLayoutDirection(Qt::LeftToRight); - buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setOrientation(Qt::Vertical); buttonBox->setStandardButtons(QDialogButtonBox::Ok); - buttonBox->setCenterButtons(true); + buttonBox->addButton(saveButton,QDialogButtonBox::ActionRole); - QSplineSeries *temperature = new QSplineSeries(); - QSplineSeries *density = new QSplineSeries(); - QSplineSeries *battery = new QSplineSeries(); + temperature = new QSplineSeries(); + density = new QSplineSeries(); + battery = new QSplineSeries(); query.prepare("SELECT * FROM log_ispindel WHERE code=:code ORDER BY datetime"); query.bindValue(":code", code); @@ -53,7 +58,7 @@ battery ->append(timestamp, query.value("battery").toDouble()); } - temperature->setName(tr("Temp °C")); + temperature->setName(tr("Temperature")); temperature->setColor(QColorConstants::Svg::red); density->setName(tr("SG")); QPen pen(QColorConstants::Svg::navy); @@ -62,7 +67,7 @@ battery->setName(tr("Battery")); battery->setColor(QColorConstants::Svg::limegreen); - QChart *chart = new QChart(); + chart = new QChart(); chart->setTitle(QString("%1 \"%2\"").arg(code).arg(name)); chart->addSeries(battery); chart->addSeries(temperature); @@ -102,14 +107,19 @@ chart->addAxis(axisYB, Qt::AlignRight); battery->attachAxis(axisYB); - QChartView *chartView = new QChartView(chart); + connect(temperature, &QSplineSeries::hovered, this, &ChartiSpindel::tooltip); + connect(density, &QSplineSeries::hovered, this, &ChartiSpindel::tooltip); + connect(battery, &QSplineSeries::hovered, this, &ChartiSpindel::tooltip); + + chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); - dialog->setLayout(new QVBoxLayout); + 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(); @@ -118,3 +128,47 @@ ChartiSpindel::~ChartiSpindel() {} + +void ChartiSpindel::savePNG() +{ + QString path = QFileDialog::getSaveFileName(this, tr("Save Image"), QDir::homePath() + "/ispindel.png", tr("Image (*.png)")); + if (path.isEmpty()) { + QMessageBox::warning(this, tr("Save File"), tr("No image file selected.")); + return; + } + + 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(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("%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("%1\nDensity %2 SG").arg(timeis.toString("dd-MM-yyyy hh:mm")).arg(point.y(), 5, 'f', 4)); + else if (series == battery) + t_tooltip->setText(QString("%1\nBattery %2 volt").arg(timeis.toString("dd-MM-yyyy hh:mm")).arg(point.y(), 3, 'f', 2)); + t_tooltip->setAnchor(point); + t_tooltip->setZValue(11); + t_tooltip->updateGeometry(); + t_tooltip->show(); + } else { + t_tooltip->hide(); + } +} + diff -r a730825bc5e4 -r d03a426e0b6b src/ChartiSpindel.h --- a/src/ChartiSpindel.h Tue Jul 26 11:15:37 2022 +0200 +++ b/src/ChartiSpindel.h Tue Jul 26 14:26:50 2022 +0200 @@ -1,9 +1,12 @@ #ifndef _CHARTISPINDEL_H #define _CHARTISPINDEL_H +#include "MainWindow.h" + #include #include +class Callout; namespace Ui { class ChartiSpindel; @@ -13,10 +16,21 @@ { Q_OBJECT +private slots: + void savePNG(); + +public slots: + void tooltip(QPointF point, bool state); + public: explicit ChartiSpindel(QString code, QString name, QWidget *parent = 0); ~ChartiSpindel(); +private: + QChartView *chartView; + QChart *chart; + Callout *t_tooltip = 0; + QSplineSeries *temperature, *density, *battery; }; #endif diff -r a730825bc5e4 -r d03a426e0b6b src/EditProduct.cpp --- a/src/EditProduct.cpp Tue Jul 26 11:15:37 2022 +0200 +++ b/src/EditProduct.cpp Tue Jul 26 14:26:50 2022 +0200 @@ -15,6 +15,7 @@ * along with this program. If not, see . */ #include "MainWindow.h" +#include "callout.h" #include "EditProduct.h" #include "PrinterDialog.h" #include "ChartCarbonate.h" diff -r a730825bc5e4 -r d03a426e0b6b src/EditProduct.h --- a/src/EditProduct.h Tue Jul 26 11:15:37 2022 +0200 +++ b/src/EditProduct.h Tue Jul 26 14:26:50 2022 +0200 @@ -1,6 +1,8 @@ #ifndef _EDITPRODUCT_H #define _EDITPRODUCT_H +#include "MainWindow.h" + #include #include #include @@ -19,6 +21,8 @@ #include "global.h" +class Callout; + struct StepResult { double svol; double irate; @@ -42,6 +46,9 @@ explicit EditProduct(int id, QWidget *parent = 0); ~EditProduct(); +public slots: + void tooltip(QPointF point, bool state); + private slots: void on_saveButton_clicked(); void on_quitButton_clicked(); @@ -190,6 +197,7 @@ void brew_trubloss_changed(double val); void brew_topupwater_changed(double val); void brew_log_button(); + void savePNG(); void brix_changed(double val); void primary_start_changed(double val); void primary_peak_changed(double val); @@ -273,6 +281,9 @@ QLabel *htimeLabel, *mtimeLabel, *mamountLabel, *yamountLabel, *ivolLabel, *itmpLabel; QTableWidget *splitTable; QPushButton *split_addButton, *split_delButton; + QChart *chart; + QChartView *chartView; + Callout *t_tooltip = 0; void to100Fermentables(int row); static bool ferment_sort_test(const Fermentables &D1, const Fermentables &D2); diff -r a730825bc5e4 -r d03a426e0b6b src/EditProductTab9.cpp --- a/src/EditProductTab9.cpp Tue Jul 26 11:15:37 2022 +0200 +++ b/src/EditProductTab9.cpp Tue Jul 26 14:26:50 2022 +0200 @@ -463,13 +463,18 @@ QDialog* dialog = new QDialog(this); 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->setGeometry(QRect(40, 565, 944, 36)); - buttonBox->setLayoutDirection(Qt::LeftToRight); - buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setOrientation(Qt::Vertical); buttonBox->setStandardButtons(QDialogButtonBox::Ok); - buttonBox->setCenterButtons(true); + buttonBox->addButton(saveButton,QDialogButtonBox::ActionRole); QLineSeries *pv_mlt = new QLineSeries(); pv_mlt->setName("MLT"); @@ -501,7 +506,7 @@ pwm_mlt->setName("MLT pwr"); pwm_mlt->setOpacity(0.25); - QChart *chart = new QChart(); + chart = new QChart(); chart->setTitle(QString("%1 \"%2\"").arg(product->code).arg(product->name)); chart->addSeries(pwm_mlt); // Order is important, first drawn is lowest layer. chart->addSeries(sp_mlt); @@ -531,15 +536,59 @@ sp_mlt->attachAxis(axisY); pv_hlt->attachAxis(axisY); - QChartView *chartView = new QChartView(chart); + connect(pv_mlt, &QLineSeries::hovered, this, &EditProduct::tooltip); + connect(pv_hlt, &QLineSeries::hovered, this, &EditProduct::tooltip); + + chartView = new QChartView(chart); chartView->setRenderHint(QPainter::Antialiasing); - dialog->setLayout(new QVBoxLayout); + dialog->setLayout(new QHBoxLayout); dialog->layout()->addWidget(chartView); dialog->layout()->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); + connect(saveButton, SIGNAL(clicked()), this, SLOT(savePNG())); + dialog->setModal(true); dialog->exec(); } +void EditProduct::savePNG() +{ + QString path = QFileDialog::getSaveFileName(this, tr("Save Image"), QDir::homePath() + "/brewday.png", tr("Image (*.png)")); + if (path.isEmpty()) { + QMessageBox::warning(this, tr("Save File"), tr("No image file selected.")); + return; + } + + 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 EditProduct::tooltip(QPointF point, bool state) +{ + QAbstractSeries *series = qobject_cast(sender()); + + if (t_tooltip == 0) + t_tooltip = new Callout(chart, series); + + if (state) { + QDateTime timeis = QDateTime::fromMSecsSinceEpoch(point.x()); + t_tooltip->setSeries(series); + t_tooltip->setText(QString("%1\n%2 %3°C").arg(timeis.toString("dd-MM-yyyy hh:mm")).arg(series->name()).arg(point.y(), 2, 'f', 1)); + t_tooltip->setAnchor(point); + t_tooltip->setZValue(11); + t_tooltip->updateGeometry(); + t_tooltip->show(); + } else { + t_tooltip->hide(); + } +} + +