Mon, 04 Apr 2022 16:21:38 +0200
Hide not visible fermentables data inside the fermentablesTable so that all data stays together. Format the fermentables json data again after changes. Add confirmation message to delete a fermentable row. Added buttons for a fermentables row editor.
/** * EditRecipe.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 "EditRecipe.h" #include "../ui/ui_EditRecipe.h" #include "MainWindow.h" #include "Utils.h" EditRecipe::EditRecipe(int id, QWidget *parent) : QDialog(parent), ui(new Ui::EditRecipe) { QSqlQuery query; qDebug() << "EditRecipe record:" << id; ui->setupUi(this); this->recno = id; WindowTitle(); ui->typeEdit->addItem(tr("Extract")); ui->typeEdit->addItem(tr("Partial Mash")); ui->typeEdit->addItem(tr("All Grain")); ui->color_methodEdit->addItem("Morey"); ui->color_methodEdit->addItem("Mosher"); ui->color_methodEdit->addItem("Daniels"); ui->color_methodEdit->addItem("Halberstadt"); ui->color_methodEdit->addItem("Naudts"); ui->ibu_methodEdit->addItem("Tinseth"); ui->ibu_methodEdit->addItem("Rager"); ui->ibu_methodEdit->addItem("Daniels"); ui->beerstyleEdit->addItem(""); // First add a dummy query.prepare("SELECT style_guide,style_letter,name FROM profile_styles ORDER BY style_guide,style_letter,name"); query.exec(); query.first(); for (int i = 0; i < query.size(); i++) { ui->beerstyleEdit->addItem(query.value(0).toString()+" "+query.value(1).toString()+" "+query.value(2).toString()); query.next(); } if (id >= 0) { query.prepare("SELECT * FROM recipes WHERE record = :recno"); query.bindValue(":recno", id); query.exec(); query.next(); ui->lockedEdit->setChecked(query.value(2).toInt() ? true:false); ui->st_nameEdit->setText(query.value(3).toString()); ui->st_groupEdit->setText(query.value(4).toString()); ui->st_guideEdit->setText(query.value(5).toString()); ui->st_catEdit->setText(query.value(6).toString()); ui->st_catnrEdit->setText(query.value(7).toString()); ui->st_typeEdit->setText(s_types[query.value(8).toInt()]); ui->nameEdit->setText(query.value(21).toString()); ui->notesEdit->setPlainText(query.value(22).toString()); ui->typeEdit->setCurrentIndex(query.value(23).toInt()); ui->batch_sizeEdit->setValue(query.value(24).toDouble()); ui->boil_sizeEdit->setValue(query.value(25).toDouble()); ui->boil_timeEdit->setValue(query.value(26).toInt()); ui->efficiencyEdit->setValue(query.value(27).toDouble()); ui->est_ogEdit->setValue(query.value(28).toDouble()); ui->est_og2Edit->setValue(query.value(28).toDouble()); ui->est_og3Edit->setValue(query.value(28).toDouble()); ui->est_ogShow->setRange(query.value(9).toDouble(), query.value(10).toDouble()); ui->est_ogShow->setPrecision(3); ui->est_ogShow->setMarkerTextIsValue(true); ui->est_ogShow->setValue(query.value(28).toDouble()); ui->est_fgEdit->setValue(query.value(29).toDouble()); ui->est_fg3Edit->setValue(query.value(29).toDouble()); ui->est_fgShow->setRange(query.value(11).toDouble(), query.value(12).toDouble()); ui->est_fgShow->setPrecision(3); ui->est_fgShow->setMarkerTextIsValue(true); ui->est_fgShow->setValue(query.value(29).toDouble()); ui->est_abvEdit->setValue(query.value(30).toDouble()); ui->est_abv2Edit->setValue(query.value(30).toDouble()); ui->est_abvShow->setRange(query.value(19).toDouble(), query.value(20).toDouble()); ui->est_abvShow->setPrecision(1); ui->est_abvShow->setMarkerTextIsValue(true); ui->est_abvShow->setValue(query.value(30).toDouble()); //QColor color = Utils::ebc_to_color(query.value(31).toInt()); ui->est_colorEdit->setValue(query.value(31).toDouble()); ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(query.value(31).toInt())); ui->est_color2Edit->setValue(query.value(31).toDouble()); ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(query.value(31).toInt())); ui->est_colorShow->setPrecision(0); ui->est_colorShow->setMarkerTextIsValue(true); ui->est_colorShow->setRange(query.value(15).toDouble(), query.value(16).toDouble()); ui->est_colorShow->setValue(query.value(31).toDouble()); ui->color_methodEdit->setCurrentIndex(query.value(32).toInt()); ui->est_ibuEdit->setValue(query.value(33).toDouble()); ui->est_ibu2Edit->setValue(query.value(33).toDouble()); ui->est_ibuShow->setPrecision(0); ui->est_ibuShow->setMarkerTextIsValue(true); ui->est_ibuShow->setRange(query.value(13).toDouble(), query.value(14).toDouble()); ui->est_ibuShow->setValue(query.value(33).toDouble()); ui->ibu_methodEdit->setCurrentIndex(query.value(34).toInt()); ui->est_carbEdit->setValue(query.value(35).toDouble()); ui->est_carbShow->setPrecision(1); ui->est_carbShow->setMarkerTextIsValue(true); ui->est_carbShow->setRange(query.value(17).toDouble(), query.value(18).toDouble()); ui->est_carbShow->setValue(query.value(35).toDouble()); // 36 sparge_temp // 37 sparge_ph // 38 sparge_volume // 39 sparge_source // 40 sparge_acid_type // 41 sparge_acid_perc // 42 sparge_acid_amount // 43 mash_ph // 44 mash_name // 45 calc_acid // 46 w1_name // 47 w1_amount // 48 w1_calcium // 49 w1_sulfate // 50 w1_chloride // 51 w1_sodium // 52 w1_magnesium // 53 w1_total_alkalinity // 54 w1_ph // 55 w1_cost // 56 w2_name // 57 w2_amount // 58 w2_calcium // 59 w2_sulfate // 60 w2_chloride // 61 w2_sodium // 62 w2_magnesium // 63 w2_total_alkalinity // 64 w2_ph // 65 w2_cost // 66 wg_amount // 67 wg_calcium // 68 wg_sulfate // 69 wg_chloride // 70 wg_sodium // 71 wg_magnesium // 72 wg_total_alkalinity // 73 wg_ph // 74 wb_calcium // 75 wb_sulfate // 76 wb_chloride // 77 wb_sodium // 78 wb_magnesium // 79 wb_total_alkalinity // 80 wb_ph // 81 wa_acid_name // 82 wa_acid_perc // 83 wa_base_name QJsonParseError parseError; const auto& f_json = query.value(84).toString(); if (!f_json.trimmed().isEmpty()) { const auto& formattedJson = QString("%1").arg(f_json); this->fermentables = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); if (parseError.error != QJsonParseError::NoError) qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ; } else { qDebug() << "empty fermentables"; } const auto& h_json = query.value(85).toString(); if (!h_json.trimmed().isEmpty()) { const auto& formattedJson = QString("%1").arg(h_json); this->hops = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); if (parseError.error != QJsonParseError::NoError) qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ; } else { qDebug() << "empty hops"; } const auto& m_json = query.value(86).toString(); if (!m_json.trimmed().isEmpty()) { const auto& formattedJson = QString("%1").arg(m_json); this->miscs = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); if (parseError.error != QJsonParseError::NoError) qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ; } else { qDebug() << "empty miscs"; } const auto& y_json = query.value(87).toString(); if (!y_json.trimmed().isEmpty()) { const auto& formattedJson = QString("%1").arg(y_json); this->yeasts = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); if (parseError.error != QJsonParseError::NoError) qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ; } else { qDebug() << "empty yeasts"; } const auto& ma_json = query.value(88).toString(); if (!ma_json.trimmed().isEmpty()) { const auto& formattedJson = QString("%1").arg(ma_json); this->mashs = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); if (parseError.error != QJsonParseError::NoError) qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ; } else { qDebug() << "empty mashs"; } } else { /* Set some defaults */ const auto& formattedJson = QString("[]"); this->fermentables = QJsonDocument::fromJson(formattedJson.toUtf8()); this->hops = QJsonDocument::fromJson(formattedJson.toUtf8()); this->miscs = QJsonDocument::fromJson(formattedJson.toUtf8()); this->yeasts = QJsonDocument::fromJson(formattedJson.toUtf8()); this->mashs = QJsonDocument::fromJson(formattedJson.toUtf8()); } // All signals from tab "Generic" connect(ui->lockedEdit, &QCheckBox::stateChanged, this, &EditRecipe::is_changed); connect(ui->nameEdit, &QLineEdit::textChanged, this, &EditRecipe::is_changed); connect(ui->notesEdit, SIGNAL(textChanged()), this, SLOT(is_changed())); connect(ui->typeEdit, &QComboBox::currentTextChanged, this, &EditRecipe::is_changed); connect(ui->batch_sizeEdit, &QDoubleSpinBox::textChanged, this, &EditRecipe::is_changed); connect(ui->boil_timeEdit, &QSpinBox::textChanged, this, &EditRecipe::is_changed); connect(ui->efficiencyEdit, &QDoubleSpinBox::textChanged, this, &EditRecipe::is_changed); connect(ui->beerstyleEdit, &QComboBox::currentTextChanged, this, &EditRecipe::style_changed); connect(ui->est_ogEdit, &QDoubleSpinBox::textChanged, this, &EditRecipe::is_changed); connect(ui->color_methodEdit, &QComboBox::currentTextChanged, this, &EditRecipe::colormethod_changed); connect(ui->ibu_methodEdit, &QComboBox::currentTextChanged, this, &EditRecipe::is_changed); // All signals from tab "Fermentables" connect(ui->est_og2Edit, &QDoubleSpinBox::textChanged, this, &EditRecipe::is_changed); connect(ui->perc_mashShow, &QProgressBar::valueChanged, this, &EditRecipe::on_perc_mash_valueChanged); connect(ui->perc_sugarsShow, &QProgressBar::valueChanged, this, &EditRecipe::on_perc_sugars_valueChanged); connect(ui->perc_caraShow, &QProgressBar::valueChanged, this, &EditRecipe::on_perc_cara_valueChanged); connect(ui->lintnerShow, &QProgressBar::valueChanged, this, &EditRecipe::on_lintner_valueChanged); connect(ui->fermentablesTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Fermentable_changed(int, int))); // All signals from tab "Hops" // connect(ui->hopsTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Changed(int, int))); // All signals from tab "Miscs" // connect(ui->hopsTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Changed(int, int))); // All signals from tab "Yeasts" // connect(ui->hopsTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Changed(int, int))); // All signals from tab "Mash" // connect(ui->hopsTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Changed(int, int))); // All signals from tab "Water" ui->saveButton->setEnabled(false); ui->deleteButton->setEnabled((id >= 0) ? true:false); emit refreshAll(); } EditRecipe::~EditRecipe() { qDebug() << "EditRecipe done start"; delete ui; emit entry_changed(); qDebug() << "EditRecipe done final"; } void EditRecipe::refreshFermentables() { QString w; QWidget* pWidget; QHBoxLayout* pLayout; QTableWidgetItem *item; QLabel *label; double d; int j; int total = 0; qDebug() << "refreshFermentables" << this->fermentables << this->fermentables.isArray() << this->fermentables.array().size(); /* During filling the table turn off the cellChanged signal because every cell that is filled * triggers the cellChanged signal. The QTableWidget has no better signal to use. */ this->ignoreChanges = true; const QStringList labels({tr("Supplier"), tr("Fermentable"), tr("EBC"), tr("Type"), tr("Graintype"), tr("When"), tr("Yield"), tr("Amount"), tr("Procent"), tr("100%"), tr("Delete"), tr("Edit"), "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30" }); ui->fermentablesTable->setColumnCount(30); ui->fermentablesTable->setColumnWidth(0, 150); /* Supplier */ ui->fermentablesTable->setColumnWidth(1, 200); /* Fermentable */ ui->fermentablesTable->setColumnWidth(2, 50); /* Color */ ui->fermentablesTable->setColumnWidth(3, 75); /* Type */ ui->fermentablesTable->setColumnWidth(4, 75); /* Graintype */ ui->fermentablesTable->setColumnWidth(5, 75); /* Added */ ui->fermentablesTable->setColumnWidth(6, 75); /* Yield */ ui->fermentablesTable->setColumnWidth(7, 75); /* Amount */ ui->fermentablesTable->setColumnWidth(8, 60); /* Procent */ ui->fermentablesTable->setColumnWidth(9, 50); /* 100% */ ui->fermentablesTable->setColumnWidth(10, 80); /* Delete */ ui->fermentablesTable->setColumnWidth(11, 80); /* Edit */ ui->fermentablesTable->setColumnHidden(12, true); ui->fermentablesTable->setColumnHidden(13, true); ui->fermentablesTable->setColumnHidden(14, true); ui->fermentablesTable->setColumnHidden(15, true); ui->fermentablesTable->setColumnHidden(16, true); ui->fermentablesTable->setColumnHidden(17, true); ui->fermentablesTable->setColumnHidden(18, true); ui->fermentablesTable->setColumnHidden(19, true); ui->fermentablesTable->setColumnHidden(20, true); ui->fermentablesTable->setColumnHidden(21, true); ui->fermentablesTable->setColumnHidden(22, true); ui->fermentablesTable->setColumnHidden(23, true); ui->fermentablesTable->setColumnHidden(24, true); ui->fermentablesTable->setColumnHidden(25, true); ui->fermentablesTable->setColumnHidden(26, true); ui->fermentablesTable->setColumnHidden(27, true); ui->fermentablesTable->setColumnHidden(28, true); ui->fermentablesTable->setColumnHidden(29, true); ui->fermentablesTable->setHorizontalHeaderLabels(labels); ui->fermentablesTable->verticalHeader()->hide(); ui->fermentablesTable->setRowCount(this->fermentables.array().size()); if (this->fermentables.isArray()) { for (int i = 0; i < this->fermentables.array().size(); i++) { QJsonObject obj = this->fermentables.array().at(i).toObject(); ui->fermentablesTable->setItem(i, 0, new QTableWidgetItem(obj["f_supplier"].toString())); ui->fermentablesTable->setItem(i, 1, new QTableWidgetItem(obj["f_name"].toString())); w = QString("%1").arg(obj["f_color"].toDouble(), 1, 'f', 0, '0'); item = new QTableWidgetItem(w); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); item->setFlags(item->flags() & ~Qt::ItemIsEditable); ui->fermentablesTable->setItem(i, 2, item); item = new QTableWidgetItem(f_types[obj["f_type"].toInt()]); item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); item->setFlags(item->flags() & ~Qt::ItemIsEditable); ui->fermentablesTable->setItem(i, 3, item); item = new QTableWidgetItem(f_graintypes[obj["f_graintype"].toInt()]); item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); item->setFlags(item->flags() & ~Qt::ItemIsEditable); ui->fermentablesTable->setItem(i, 4, item); item = new QTableWidgetItem(f_added[obj["f_added"].toInt()]); item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); ui->fermentablesTable->setItem(i, 5, item); w = QString("%1%").arg(obj["f_yield"].toDouble(), 2, 'f', 1, '0'); item = new QTableWidgetItem(w); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); item->setFlags(item->flags() & ~Qt::ItemIsEditable); ui->fermentablesTable->setItem(i, 6, item); w = QString("%1 Kg").arg(obj["f_amount"].toDouble(), 4, 'f', 3, '0'); item = new QTableWidgetItem(w); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->fermentablesTable->setItem(i, 7, item); w = QString("%1%").arg(obj["f_percentage"].toDouble(), 2, 'f', 1, '0'); item = new QTableWidgetItem(w); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->fermentablesTable->setItem(i, 8, item); QTableWidgetItem *checkBoxItem = new QTableWidgetItem(); checkBoxItem->setCheckState((obj["f_adjust_to_total_100"].toInt()) ? Qt::Checked : Qt::Unchecked); checkBoxItem->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); ui->fermentablesTable->setItem(i, 9, checkBoxItem); /* Add the Delete row button */ pWidget = new QWidget(); QPushButton* btn_dele = new QPushButton(); btn_dele->setObjectName(QString("%1").arg(i)); /* Send row with the button */ btn_dele->setText(tr("Delete")); connect(btn_dele, SIGNAL(clicked()), this, SLOT(on_deleteFermentRow_clicked())); pLayout = new QHBoxLayout(pWidget); pLayout->addWidget(btn_dele); pLayout->setContentsMargins(5, 0, 5, 0); pWidget->setLayout(pLayout); ui->fermentablesTable->setCellWidget(i, 10, pWidget); pWidget = new QWidget(); QPushButton* btn_edit = new QPushButton(); btn_edit->setObjectName(QString("%1").arg(i)); /* Send row with the button */ btn_edit->setText(tr("Edit")); connect(btn_edit, SIGNAL(clicked()), this, SLOT(on_editFermentRow_clicked())); pLayout = new QHBoxLayout(pWidget); pLayout->addWidget(btn_edit); pLayout->setContentsMargins(5, 0, 5, 0); pWidget->setLayout(pLayout); ui->fermentablesTable->setCellWidget(i, 11, pWidget); /* Hidden columns */ ui->fermentablesTable->setItem(i, 12, new QTableWidgetItem(QString("%1").arg(obj["f_acid_to_ph_57"].toDouble(), 6, 'f', 5, '0'))); ui->fermentablesTable->setItem(i, 13, new QTableWidgetItem(QString("%1").arg(obj["f_add_after_boil"].toInt(), 1, 'f', 0, '0'))); ui->fermentablesTable->setItem(i, 14, new QTableWidgetItem(QString("%1").arg(obj["f_coarse_fine_diff"].toDouble(), 2, 'f', 1, '0'))); ui->fermentablesTable->setItem(i, 15, new QTableWidgetItem(QString("%1").arg(obj["f_cost"].toDouble(), 3, 'f', 2, '0'))); ui->fermentablesTable->setItem(i, 16, new QTableWidgetItem(QString("%1").arg(obj["f_di_ph"].toDouble(), 6, 'f', 5, '0'))); ui->fermentablesTable->setItem(i, 17, new QTableWidgetItem(QString("%1").arg(obj["f_diastatic_power"].toDouble(), 6, 'f', 5, '0'))); ui->fermentablesTable->setItem(i, 18, new QTableWidgetItem(QString("%1").arg(obj["f_dissolved_protein"].toDouble(), 2, 'f', 1, '0'))); ui->fermentablesTable->setItem(i, 19, new QTableWidgetItem(QString("%1").arg(obj["f_moisture"].toDouble(), 2, 'f', 1, '0'))); ui->fermentablesTable->setItem(i, 20, new QTableWidgetItem(obj["f_origin"].toString())); ui->fermentablesTable->setItem(i, 21, new QTableWidgetItem(QString("%1").arg(obj["f_protein"].toDouble(), 2, 'f', 1, '0'))); ui->fermentablesTable->setItem(i, 22, new QTableWidgetItem(QString("%1").arg(obj["f_recommend_mash"].toInt(), 1, 'f', 0, '0'))); /* Again these because the visible ones have human readable strings added. */ ui->fermentablesTable->setItem(i, 23, new QTableWidgetItem(QString("%1").arg(obj["f_amount"].toDouble(), 4, 'f', 3, '0'))); ui->fermentablesTable->setItem(i, 24, new QTableWidgetItem(QString("%1").arg(obj["f_percentage"].toDouble(), 2, 'f', 1, '0'))); ui->fermentablesTable->setItem(i, 25, new QTableWidgetItem(QString("%1").arg(obj["f_yield"].toDouble(), 2, 'f', 1, '0'))); ui->fermentablesTable->setItem(i, 26, new QTableWidgetItem(QString("%1").arg(obj["f_max_in_batch"].toDouble(), 2, 'f', 1, '0'))); ui->fermentablesTable->setItem(i, 27, new QTableWidgetItem(QString("%1").arg(obj["f_type"].toDouble(), 1, 'f', 0, '0'))); ui->fermentablesTable->setItem(i, 28, new QTableWidgetItem(QString("%1").arg(obj["f_graintype"].toDouble(), 1, 'f', 0, '0'))); ui->fermentablesTable->setItem(i, 29, new QTableWidgetItem(QString("%1").arg(obj["f_added"].toDouble(), 1, 'f', 0, '0'))); } } this->ignoreChanges = false; } void EditRecipe::refreshHops() { } void EditRecipe::refreshMiscs() { } void EditRecipe::refreshYeasts() { } void EditRecipe::refreshMashs() { } void EditRecipe::refreshAll() { refreshFermentables(); calcFermentables(); } void EditRecipe::calcFermentables() { int i; bool my_100 = false; double psugar = 0, pcara = 0, d, s = 0, x, color; double vol = 0; // Volume sugars after boil double addedS = 0; // Added sugars after boil double addedmass = 0; // Added mass after boil double mvol = 0; // Mash volume double lintner = 0; // Total recipe lintner double mashkg = 0; double sugarsf = 0; // fermentable sugars mash + boil double sugarsm = 0; // fermentable sugars in mash double sugardensity = 1.611; // kg/l in solution double mashtime = 0; // Total mash time double mashtemp = 0; // Average mash temperature double mashinfuse = 0; // Mash infuse amount double colort = 0; // Colors srm * vol totals double colorh = 0; // Colors ebc * vol * kt double colorn = 0; // Colors ebc * pt * pct QJsonObject obj; qDebug() << "calcFermentables()"; /* * Get average mashtemp and mashtime from the Mash schedule. * It is possible that the schedule is not (yet) present. */ if (this->mashs.array().size() > 0) { for (i = 0; i < this->mashs.array().size(); i++) { obj = this->mashs.array().at(i).toObject(); if (obj["step_type"].toInt() == 0) // Infusion mashinfuse += obj["step_infuse_amount"].toDouble(); if (obj["step_temp"].toDouble() < 75) { // Ignore mashout mashtime += obj["step_time"].toDouble(); mashtemp += obj["step_time"].toDouble() * obj["step_temp"].toDouble(); } } mashtemp = mashtemp / mashtime; mvol = mashinfuse; qDebug() << " mash time" << mashtime << "temp" << mashtemp << "infuse" << mashinfuse; } else { qDebug() << " no mash schedule"; } if (this->fermentables.array().size() < 1) { qDebug() << " no fermentables, return."; return; } for (i = 0; i < this->fermentables.array().size(); i++) { obj = this->fermentables.array().at(i).toObject(); if (obj["f_adjust_to_total_100"].toInt()) { my_100 = true; } if (obj["f_type"].toInt() == 1 && obj["f_added"].toInt() < 4) // Sugars psugar += obj["f_percentage"].toDouble(); if (obj["f_graintype"].toInt() == 2 && obj["f_added"].toInt() < 4) // Crystal/Cara pcara += obj["f_percentage"].toDouble(); d = obj["f_amount"].toDouble() * (obj["f_yield"].toDouble() / 100) * (1 - obj["f_moisture"].toDouble() / 100); if (obj["f_added"].toInt() == 0) { // Mash if (mvol > 0) { // If mash volume is known mvol += obj["f_amount"].toDouble() * obj["f_moisture"].toDouble() / 100; s += d; } d = ui->efficiencyEdit->value() / 100 * d; sugarsm += d; mashkg += obj["f_amount"].toDouble(); } if (obj["f_added"].toInt() == 0 || obj["f_added"].toInt() == 1) // Mash or boil sugarsf += d; if (obj["f_added"].toInt() == 2 || obj["f_added"].toInt() == 3) { // Fermentation or lagering x = (obj["f_yield"].toDouble() / 100) * (1 - obj["f_moisture"].toDouble() / 100); addedS += obj["f_amount"].toDouble() * x; addedmass += obj["f_amount"].toDouble(); vol += (x * sugardensity + (1 - x) * 1) * obj["f_amount"].toDouble(); } if (obj["f_added"].toInt() == 0 && (obj["f_type"].toInt() == 0 || obj["f_type"].toInt() == 4) && obj["f_color"].toDouble() < 50) { lintner += obj["f_diastatic_power"].toDouble() * obj["f_amount"].toDouble(); } if (obj["f_added"].toInt() < 4) { colort += obj["f_amount"].toDouble() * Utils::ebc_to_srm(obj["f_color"].toDouble()); colorh += obj["f_amount"].toDouble() * obj["f_color"].toDouble() * Utils::get_kt(obj["f_color"].toDouble()); colorn += (obj["f_percentage"].toDouble() / 100) * obj["f_color"].toDouble(); // For 8.6 Pt wort. } } qDebug() << " colort" << colort << "colorh" << colorh << "colorn" << colorn; qDebug() << " psugar" << psugar << "pcara" << pcara << "mvol" << mvol; qDebug() << " sugarsf" << sugarsf << "sugarsm" << sugarsm; double og = Utils::estimate_sg(sugarsf + addedS, ui->batch_sizeEdit->value()); qDebug() << " OG" << ui->est_ogEdit->value() << og; ui->est_ogEdit->setValue(og); ui->est_og2Edit->setValue(og); ui->est_og3Edit->setValue(og); ui->est_ogShow->setValue(og); double preboil_sg = Utils::estimate_sg(sugarsm, ui->boil_sizeEdit->value()); qDebug() << " preboil SG" << preboil_sg; /* * Color of the wort */ if (ui->color_methodEdit->currentIndex() == 4) { // Naudts color = round(((Utils::sg_to_plato(og) / 8.6) * colorn) + (ui->boil_timeEdit->value() / 60)); } else if (ui->color_methodEdit->currentIndex() == 3) { // Hans Halberstadt double bv = 0.925; // Beer loss efficiency double sr = 0.95; // Mash and sparge efficiency color = round((4.46 * bv * sr) / ui->batch_sizeEdit->value() * colorh); } else { double cw = colort / ui->batch_sizeEdit->value() * 8.34436; color = Utils::kw_to_ebc(ui->color_methodEdit->currentIndex(), cw); } qDebug() << " color" << ui->est_colorEdit->value() << color; ui->est_colorEdit->setValue(color); ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(color)); ui->est_color2Edit->setValue(color); ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(color)); ui->est_colorShow->setValue(color); /* * We don't have a equipment profile in recipes, * so we assume a certain guessed mashtun size. */ ui->perc_mashShow->setValue(round(mashkg / (ui->boil_sizeEdit->value() / 3) * 100)); ui->perc_sugarsShow->setValue(round(psugar)); ui->perc_caraShow->setValue(round(pcara)); qDebug() << " lintner" << lintner << " mashkg" << mashkg << "final" << round(lintner / mashkg); ui->lintnerShow->setValue(round(lintner / mashkg)); /* * Calculate the apparant attenuation. */ double svg = 0; if (this->yeasts.array().size() > 0) { for (i = 0; i < this->yeasts.array().size(); i++) { obj = this->yeasts.array().at(i).toObject(); if (obj["y_use"].toInt() == 0) { // Used in primary if (obj["y_attenuation"].toDouble() > svg) svg = obj["y_attenuation"].toDouble(); // Take the highest if multiple yeasts. } // TODO: brett or others in secondary. } qDebug() << " SVG" << svg; } if (svg == 0) svg = 77.0; double fg; if (mashkg > 0 && mashinfuse > 0 && mashtime > 0 && mashtemp > 0) fg = Utils::estimate_fg(psugar, pcara, mashinfuse / mashkg, mashtime, mashtemp, svg, og); else fg = Utils::estimate_fg(psugar, pcara, 0, 0, 0, svg, og); qDebug() << " FG" << ui->est_fgEdit->value() << fg; ui->est_fgEdit->setValue(fg); ui->est_fg3Edit->setValue(fg); ui->est_fgShow->setValue(fg); double abv = Utils::abvol(og, fg); qDebug() << " ABV" << ui->est_abvEdit->value() << abv; ui->est_abvEdit->setValue(abv); ui->est_abv2Edit->setValue(abv); ui->est_abvShow->setValue(abv); /* * Calculate kilocalories/liter. Formula from brouwhulp. * Take the alcohol and sugar parts and then combine. */ double alc = 1881.22 * fg * (og - fg) / (1.775 - og); double sug = 3550 * fg * (0.1808 * og + 0.8192 * fg - 1.0004); ui->calEdit->setValue(round((alc + sug) / (12 * 0.0295735296))); // If to_100 then make all amount fields t/o and percent fields r/w } void EditRecipe::on_perc_mash_valueChanged(int value) { if (value < 90) ui->perc_mashShow->setStyleSheet(bar_green); else if (value < 100) ui->perc_mashShow->setStyleSheet(bar_orange); else ui->perc_mashShow->setStyleSheet(bar_red); } void EditRecipe::on_perc_sugars_valueChanged(int value) { if (value < 20) ui->perc_sugarsShow->setStyleSheet(bar_green); else ui->perc_sugarsShow->setStyleSheet(bar_red); } void EditRecipe::on_perc_cara_valueChanged(int value) { if (value < 25) ui->perc_caraShow->setStyleSheet(bar_green); else ui->perc_caraShow->setStyleSheet(bar_red); } void EditRecipe::on_lintner_valueChanged(int value) { if (value < 30) ui->lintnerShow->setStyleSheet(bar_red); else if (value < 40) ui->lintnerShow->setStyleSheet(bar_orange); else ui->lintnerShow->setStyleSheet(bar_green); } /* * Window header, mark any change with '**' */ void EditRecipe::WindowTitle() { QString txt; if (this->recno < 0) { txt = QString(tr("BMSapp - Add new recipe")); } else { txt = QString(tr("BMSapp - Edit recipe %1").arg(this->recno)); } if (this->textIsChanged) { txt.append((QString(" **"))); } setWindowTitle(txt); } void EditRecipe::on_saveButton_clicked() { QSqlQuery query; /* If there are errors in the form, show a message and do "return;" */ if (ui->nameEdit->text().length() < 2) { QMessageBox::warning(this, tr("Edit Recipe"), tr("Name empty or too short.")); return; } if (this->textIsChanged) { if (this->recno == -1) { query.prepare("INSERT INTO recipes SET name=:name, " "uuid = :uuid"); } else { query.prepare("UPDATE recipes SET name=:name " " WHERE record = :recno"); } //query.bindValue(":name", ui->nameEdit->text()); //query.bindValue(":notes", ui->notesEdit->toPlainText()); if (this->recno == -1) { query.bindValue(":uuid", QUuid::createUuid().toString().mid(1, 36)); } else { query.bindValue(":recno", this->recno); } query.exec(); if (query.lastError().isValid()) { qDebug() << "EditRecipe" << query.lastError(); QMessageBox::warning(this, tr("Database error"), tr("MySQL error: %1\n%2\n%3") .arg(query.lastError().nativeErrorCode()) .arg(query.lastError().driverText()) .arg(query.lastError().databaseText())); } else { qDebug() << "EditRecipe Saved"; } } ui->saveButton->setEnabled(false); this->textIsChanged = false; WindowTitle(); } void EditRecipe::on_deleteButton_clicked() { QSqlQuery query; query.prepare("DELETE FROM recipes WHERE record = :recno"); query.bindValue(":recno", this->recno); query.exec(); if (query.lastError().isValid()) { qDebug() << "EditRecipe" << query.lastError(); QMessageBox::warning(this, tr("Database error"), tr("MySQL error: %1\n%2\n%3") .arg(query.lastError().nativeErrorCode()) .arg(query.lastError().driverText()) .arg(query.lastError().databaseText())); } else { qDebug() << "EditRecipe Deleted" << this->recno; } this->close(); this->setResult(1); } void EditRecipe::is_changed() { ui->saveButton->setEnabled(true); ui->deleteButton->setEnabled((this->recno >= 0) ? true:false); this->textIsChanged = true; WindowTitle(); } /* * New beerstyle is selected. */ void EditRecipe::style_changed() { QSqlQuery query; if (ui->beerstyleEdit->currentIndex() < 1) return; query.prepare("SELECT * FROM profile_styles ORDER BY style_guide,style_letter,name"); query.exec(); query.first(); // Skip to the record index. for (int i = 0; i < (ui->beerstyleEdit->currentIndex() - 1); i++) { query.next(); } // Set relevant fields and update ranges. ui->st_nameEdit->setText(query.value(1).toString()); ui->st_catEdit->setText(query.value(2).toString()); ui->st_catnrEdit->setText(query.value(3).toString()); ui->st_groupEdit->setText(query.value(4).toString()); ui->st_guideEdit->setText(query.value(5).toString()); ui->st_typeEdit->setText(s_types[query.value(6).toInt()]); ui->est_ogShow->setRange(query.value(7).toDouble(), query.value(8).toDouble()); ui->est_fgShow->setRange(query.value(9).toDouble(), query.value(10).toDouble()); ui->est_ibuShow->setRange(query.value(11).toDouble(), query.value(12).toDouble()); ui->est_colorShow->setRange(query.value(13).toDouble(), query.value(14).toDouble()); ui->est_carbShow->setRange(query.value(15).toDouble(), query.value(16).toDouble()); ui->est_abvShow->setRange(query.value(17).toDouble(), query.value(18).toDouble()); is_changed(); } void EditRecipe::colormethod_changed() { calcFermentables(); is_changed(); } void EditRecipe::time_changed() { is_changed(); } void EditRecipe::fermentable_Json() { QTableWidgetItem *item; QJsonArray array; ui->fermentablesTable->sortItems(23, Qt::DescendingOrder); // Sort on amount. for (int i = 0; i < ui->fermentablesTable->rowCount(); i++) { QJsonObject obj; item = ui->fermentablesTable->item(i, 12); obj.insert("f_acid_to_ph_57", item->text().toDouble()); item = ui->fermentablesTable->item(i, 13); obj.insert("f_add_after_boil", item->text().toDouble()); item = ui->fermentablesTable->item(i, 29); obj.insert("f_added", item->text().toDouble()); item = ui->fermentablesTable->item(i, 9); obj.insert("f_adjust_to_total_100", item->text().toDouble()); item = ui->fermentablesTable->item(i, 23); obj.insert("f_amount", item->text().toDouble()); item = ui->fermentablesTable->item(i, 14); obj.insert("f_coarse_fine_diff", item->text().toDouble()); item = ui->fermentablesTable->item(i, 2); obj.insert("f_color", item->text().toDouble()); item = ui->fermentablesTable->item(i, 15); obj.insert("f_cost", item->text().toDouble()); item = ui->fermentablesTable->item(i, 16); obj.insert("f_di_ph", item->text().toDouble()); item = ui->fermentablesTable->item(i, 17); obj.insert("f_diastatic_power", item->text().toDouble()); item = ui->fermentablesTable->item(i, 18); obj.insert("f_dissolved_protein", item->text().toDouble()); item = ui->fermentablesTable->item(i, 28); obj.insert("f_graintype", item->text().toDouble()); item = ui->fermentablesTable->item(i, 26); obj.insert("f_max_in_batch", item->text().toDouble()); item = ui->fermentablesTable->item(i, 19); obj.insert("f_moisture", item->text().toDouble()); item = ui->fermentablesTable->item(i, 1); obj.insert("f_name", item->text()); item = ui->fermentablesTable->item(i, 20); obj.insert("f_origin", item->text()); item = ui->fermentablesTable->item(i, 24); obj.insert("f_percentage", item->text().toDouble()); item = ui->fermentablesTable->item(i, 21); obj.insert("f_protein", item->text().toDouble()); item = ui->fermentablesTable->item(i, 22); obj.insert("f_recommend_mash", item->text().toDouble()); item = ui->fermentablesTable->item(i, 0); obj.insert("f_supplier", item->text()); item = ui->fermentablesTable->item(i, 27); obj.insert("f_type", item->text().toDouble()); item = ui->fermentablesTable->item(i, 25); obj.insert("f_yield", item->text().toDouble()); // qDebug() << "fermentable_Json" << i << obj; array.append(obj); /* Append this object */ } qDebug() << array; /* Copy to the global array and refresh */ this->fermentables.setArray(array); is_changed(); emit refreshAll(); } void EditRecipe::cell_Fermentable_changed(int nRow, int nCol) { if (this->ignoreChanges) return; qDebug() << "Cell at row " + QString::number(nRow) + " column " + QString::number(nCol) + " was changed."; if (nCol == 9) { // 100% checkbox this->ignoreChanges = true; if (ui->fermentablesTable->item(nRow, nCol)->checkState() == Qt::Checked) { /* * This row is checked. Remove any other checked item. */ for (int i = 0; i < ui->fermentablesTable->rowCount(); i++) { if (i != nRow) { QTableWidgetItem *checkBoxItem = ui->fermentablesTable->item(i, nCol); checkBoxItem->setCheckState(Qt::Unchecked); ui->fermentablesTable->setItem(i, nCol, checkBoxItem); } } } else { /* * Unchecked, start working with amounts instead of percentages. */ } qDebug() << ui->fermentablesTable->item(nRow, nCol)->checkState(); this->ignoreChanges = false; } // TODO: some checks and auto fixes. // make_Json(); } void EditRecipe::on_deleteFermentRow_clicked() { QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender()); int row = pb->objectName().toInt(); qDebug() << "Delete fermentable row" << row; QJsonObject obj = this->fermentables.array().at(row).toObject(); qDebug() << obj; int rc = QMessageBox::warning(this, tr("Delete fermentable"), tr("Delete %1").arg(obj["f_name"].toString()), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (rc == QMessageBox::No) return; ui->fermentablesTable->removeRow(row); // recalculate percentages fermentable_Json(); } void EditRecipe::on_editFermentRow_clicked() { QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender()); int row = pb->objectName().toInt(); qDebug() << "Edit fermentable row" << row; // init fermentrow and fermentbackup. // connect popup to refreshfermentables and calcFermentables // popup editor // on abort, restore fermentbackup // on accept, fermentrow to final // disconnect // return } void EditRecipe::on_quitButton_clicked() { if (this->textIsChanged) { int rc = QMessageBox::warning(this, tr("Recipe changed"), tr("The recipe has been modified. Save changes?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save); switch (rc) { case QMessageBox::Save: on_saveButton_clicked(); break; /* Saved and then Quit */ case QMessageBox::Discard: break; /* Quit without Save */ case QMessageBox::Cancel: return; /* Return to the editor page */ } } this->close(); this->setResult(1); }