Fri, 13 Jan 2023 12:00:58 +0100
Added a button to automatic recreate the yeast starter steps. Some code cleanup
/** * EditProduct.cpp is part of bmsapp. * * tab 7, mash. * * 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/>. */ void EditProduct::refreshMashs() { QString w; QWidget* pWidget; QHBoxLayout* pLayout; QTableWidgetItem *item; QIcon down_icon, up_icon, del_icon, ed_icon; down_icon.addFile(QString::fromUtf8(":/icons/silk/bullet_arrow_down.png"), QSize(), QIcon::Normal, QIcon::Off); up_icon.addFile(QString::fromUtf8(":/icons/silk/bullet_arrow_up.png"), QSize(), QIcon::Normal, QIcon::Off); del_icon.addFile(QString::fromUtf8(":/icons/silk/delete.png"), QSize(), QIcon::Normal, QIcon::Off); ed_icon.addFile(QString::fromUtf8(":/icons/silk/pencil.png"), QSize(), QIcon::Normal, QIcon::Off); const QStringList labels({tr("Step name"), tr("Type"), tr("Start"), tr("End"), tr("Rest"), tr("Ramp"), tr("Inf/dec"), tr("Inf/dec"), tr("Volume"), tr("W/G ratio"), tr("SG"), tr("pH"), "", "", "", "" }); ui->mashsTable->setColumnCount(16); ui->mashsTable->setColumnWidth(0, 189); /* Step name */ ui->mashsTable->setColumnWidth(1, 100); /* Type */ ui->mashsTable->setColumnWidth(2, 70); /* Start temp */ ui->mashsTable->setColumnWidth(3, 70); /* End temp */ ui->mashsTable->setColumnWidth(4, 70); /* Rest time */ ui->mashsTable->setColumnWidth(5, 70); /* Ramp time */ ui->mashsTable->setColumnWidth(6, 70); /* Infusion vol */ ui->mashsTable->setColumnWidth(7, 70); /* Infusion tmp */ ui->mashsTable->setColumnWidth(8, 70); /* Volume */ ui->mashsTable->setColumnWidth(9, 80); /* W/G ratio */ ui->mashsTable->setColumnWidth(10, 50); /* SG */ ui->mashsTable->setColumnWidth(11, 50); /* pH */ ui->mashsTable->setColumnWidth(12, 30); /* Up button */ ui->mashsTable->setColumnWidth(13, 30); /* Down button */ ui->mashsTable->setColumnWidth(14, 30); /* Delete */ ui->mashsTable->setColumnWidth(15, 30); /* Edit */ ui->mashsTable->setHorizontalHeaderLabels(labels); ui->mashsTable->verticalHeader()->hide(); ui->mashsTable->setRowCount(product->mashs.size()); for (int i = 0; i < product->mashs.size(); i++) { ui->mashsTable->setItem(i, 0, new QTableWidgetItem(product->mashs.at(i).step_name)); item = new QTableWidgetItem(QCoreApplication::translate("StepType", g_step_types[product->mashs.at(i).step_type])); item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); ui->mashsTable->setItem(i, 1, item); item = new QTableWidgetItem(QString("%1 °C").arg(product->mashs.at(i).step_temp, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(i, 2, item); item = new QTableWidgetItem(QString("%1 °C").arg(product->mashs.at(i).end_temp, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(i, 3, item); item = new QTableWidgetItem(QString("%1 min").arg(product->mashs.at(i).step_time, 1, 'f', 0, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(i, 4, item); item = new QTableWidgetItem(QString("%1 min").arg(product->mashs.at(i).ramp_time, 1, 'f', 0, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(i, 5, item); if (product->mashs.at(i).step_infuse_amount > 0) { item = new QTableWidgetItem(QString("%1 L").arg(product->mashs.at(i).step_infuse_amount, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(i, 6, item); item = new QTableWidgetItem(QString("%1 °C").arg(product->mashs.at(i).step_infuse_temp, 3, 'f', 2, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(i, 7, item); } else { ui->mashsTable->removeCellWidget(i, 6); ui->mashsTable->removeCellWidget(i, 7); } item = new QTableWidgetItem(QString("%1 L").arg(product->mashs.at(i).step_volume, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(i, 8, item); item = new QTableWidgetItem(QString("%1 L/kg").arg(product->mashs.at(i).step_wg_ratio, 3, 'f', 2, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(i, 9, item); item = new QTableWidgetItem(QString("%1").arg(product->mashs.at(i).step_sg, 4, 'f', 3, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(i, 10, item); item = new QTableWidgetItem(QString("%1").arg(product->mashs.at(i).step_ph, 3, 'f', 2, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(i, 11, item); if (i > 0 && product->stage <= PROD_STAGE_BREW) { pWidget = new QWidget(); QPushButton* btn_up = new QPushButton(); btn_up->setObjectName(QString("%1").arg(i)); /* Send row with the button */ btn_up->setIcon(up_icon); connect(btn_up, SIGNAL(clicked()), this, SLOT(upMashRow_clicked())); pLayout = new QHBoxLayout(pWidget); pLayout->addWidget(btn_up); pLayout->setContentsMargins(5, 0, 5, 0); pWidget->setLayout(pLayout); ui->mashsTable->setCellWidget(i, 12, pWidget); } else { ui->mashsTable->removeCellWidget(i, 12); } if (i < (product->mashs.size() - 1) && product->stage <= PROD_STAGE_BREW) { pWidget = new QWidget(); QPushButton* btn_down = new QPushButton(); btn_down->setObjectName(QString("%1").arg(i)); /* Send row with the button */ btn_down->setIcon(down_icon); connect(btn_down, SIGNAL(clicked()), this, SLOT(downMashRow_clicked())); pLayout = new QHBoxLayout(pWidget); pLayout->addWidget(btn_down); pLayout->setContentsMargins(5, 0, 5, 0); pWidget->setLayout(pLayout); ui->mashsTable->setCellWidget(i, 13, pWidget); } else { ui->mashsTable->removeCellWidget(i, 13); } if (product->stage <= PROD_STAGE_BREW) { pWidget = new QWidget(); QToolButton* btn_dele = new QToolButton(); btn_dele->setObjectName(QString("%1").arg(i)); /* Send row with the button */ btn_dele->setIcon(del_icon); connect(btn_dele, SIGNAL(clicked()), this, SLOT(deleteMashRow_clicked())); pLayout = new QHBoxLayout(pWidget); pLayout->addWidget(btn_dele); pLayout->setContentsMargins(5, 0, 5, 0); pWidget->setLayout(pLayout); ui->mashsTable->setCellWidget(i, 14, pWidget); pWidget = new QWidget(); QToolButton* btn_edit = new QToolButton(); btn_edit->setObjectName(QString("%1").arg(i)); /* Send row with the button */ btn_edit->setIcon(ed_icon); connect(btn_edit, SIGNAL(clicked()), this, SLOT(editMashRow_clicked())); pLayout = new QHBoxLayout(pWidget); pLayout->addWidget(btn_edit); pLayout->setContentsMargins(5, 0, 5, 0); pWidget->setLayout(pLayout); ui->mashsTable->setCellWidget(i, 15, pWidget); } else { ui->mashsTable->removeCellWidget(i, 14); ui->mashsTable->removeCellWidget(i, 15); } } } double EditProduct::infusionVol(double step_infused, double step_mashkg, double infuse_temp, double step_temp, double last_temp) { double a = last_temp * (product->eq_tun_weight * product->eq_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt); double b = step_temp * (product->eq_tun_weight * product->eq_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt); double vol = round(((b - a) / ((infuse_temp - step_temp) * SpecificHeatWater)) * 100.0) / 100.0; if (vol < 0) vol = 0; qDebug() << " infusionVol(" << step_infused << "," << step_mashkg << "," << infuse_temp <<"," << step_temp << "," << last_temp << "):" << vol; return vol; } double EditProduct::decoctionVol(double step_volume, double step_temp, double prev_temp) { double a = (product->eq_tun_weight * product->eq_tun_specific_heat + step_volume * SpecificHeatWater) * (step_temp - prev_temp); double b = SpecificHeatWater * (99 - step_temp); double vol = 0; if (b > 0) vol = round((a / b) * 1000000.0) / 1000000.0; qDebug() << " decoctionVol(" << step_volume << "," << step_temp << "," << prev_temp << "):" << vol; return vol; } void EditProduct::calcMash() { double infused = 0, vol, a, b, temp; int i, j, n; double lasttemp = 18.0; double graintemp = 18.0; double tuntemp = 18.0; product->mashs_time = 0; if (product->mashs.size() && product->mashs_kg > 0) { qDebug() << "calcMash()"; for (i = 0; i < product->mashs.size(); i++) { if (product->mashs.at(i).step_type == 0) { // Infusion if (i == 0) { // First mash step, temperature from the mashtun and malt. n = 20; // tun is preheated. tuntemp = product->mashs.at(i).step_temp; for (j = 0; j < n; j++) { a = product->mashs_kg * graintemp * SpecificHeatMalt + product->eq_tun_weight * tuntemp * product->eq_tun_specific_heat; b = product->mashs[i].step_temp * (product->eq_tun_weight * product->eq_tun_specific_heat + product->mashs.at(i).step_infuse_amount * SpecificHeatWater + product->mashs_kg * SpecificHeatMalt) - SlakingHeat * product->mashs_kg; if (product->mashs.at(i).step_infuse_amount > 0) { temp = (b - a) / (product->mashs.at(i).step_infuse_amount * SpecificHeatWater); } else { temp = 99; } tuntemp += (temp - tuntemp) / 2; product->mashs[i].step_infuse_temp = round(temp * 1000000.0) / 1000000.0; } qDebug() << " init infuse temp:" << product->mashs.at(i).step_infuse_temp; } else { // Calculate amount of infusion water. product->mashs[i].step_infuse_amount = infusionVol(infused, product->mashs_kg, product->mashs.at(i).step_infuse_temp, product->mashs.at(i).step_temp, lasttemp); qDebug() << i << " vol:" << product->mashs.at(i).step_infuse_amount << "temp:" << product->mashs.at(i).step_infuse_temp; } infused += product->mashs.at(i).step_infuse_amount; } else if (product->mashs.at(i).step_type == 1) { // Temperature if (i > 0) product->mashs[i].step_infuse_amount = 0; product->mashs[i].step_infuse_temp = 0; } else if (product->mashs.at(i).step_type == 2) { // Decoction product->mashs[i].step_infuse_amount = decoctionVol(infused, product->mashs.at(i).step_temp, lasttemp); product->mashs[i].step_infuse_temp = 99; } product->mashs[i].step_volume = infused; //qDebug() << i << " type:" << product->mashs.at(i).step_type << "volume:" << product->mashs.at(i).step_infuse_amount << "temp:" << product->mashs.at(i).step_infuse_temp; lasttemp = product->mashs.at(i).step_temp; product->mashs_time += product->mashs.at(i).step_time; if (i > 0) product->mashs_time += product->mashs.at(i).ramp_time; product->mashs[i].step_wg_ratio = round((infused / product->mashs_kg) * 1000000.0) / 1000000.0; } } ui->mash_volEdit->setValue(product->mashs_kg * MaltVolume + infused); /* Show the calculated total mash time. */ ui->mash_timeEdit->setText(QString("%1:%2").arg(product->mashs_time / 60).arg(product->mashs_time % 60, 2, 'f', 0, '0')); /* Estimated needed sparge water corrected for the temperature. */ product->brew_sparge_est = round((product->boil_size - infused + (product->mashs_kg * my_grain_absorption) + product->eq_lauter_deadspace) * 1.03 * 1000) / 1000; ui->brew_spargeestShow->setValue(product->brew_sparge_est); } void EditProduct::addMashRow_clicked() { MashSteps newm; for (int i = 0; i < product->mashs.size(); i++) { if (product->mashs.at(i).step_time == 0) return; // Add only one at a time. } newm.step_name = "Name me"; newm.step_temp = newm.end_temp = 67.0; newm.step_time = 20; newm.ramp_time = 10; if (product->mashs.size()) { newm.step_volume = product->mashs.at(product->mashs.size() - 1).step_volume; newm.step_wg_ratio = product->mashs.at(product->mashs.size() - 1).step_wg_ratio; newm.step_type = product->mashs.at(product->mashs.size() - 1).step_type; } else { newm.step_volume = 0; newm.step_wg_ratio = 0; newm.step_type = 1; } newm.step_infuse_amount = newm.step_infuse_temp = 0; product->mashs.append(newm); is_changed(); emit refreshAll(); } void EditProduct::deleteMashRow_clicked() { if (product->locked || product->mashs.size() < 1) return; QToolButton *pb = qobject_cast<QToolButton *>(QObject::sender()); int row = pb->objectName().toInt(); qDebug() << "Delete mash row" << row << product->mashs.size(); int rc = QMessageBox::warning(this, tr("Delete mash step"), tr("Delete %1").arg(product->mashs.at(row).step_name), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if (rc == QMessageBox::No) return; product->mashs.removeAt(row); is_changed(); emit refreshAll(); } void EditProduct::upMashRow_clicked() { if (product->locked || product->mashs.size() < 1) return; QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender()); int row = pb->objectName().toInt(); qDebug() << "Move up mash row" << row << product->mashs.size(); MashSteps temp; temp = product->mashs[row - 1]; product->mashs[row - 1] = product->mashs[row]; product->mashs[row] = temp; is_changed(); emit refreshAll(); } void EditProduct::downMashRow_clicked() { if (product->locked || product->mashs.size() < 1) return; QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender()); int row = pb->objectName().toInt(); qDebug() << "Move down mash row" << row << product->mashs.size(); MashSteps temp; temp = product->mashs[row + 1]; product->mashs[row + 1] = product->mashs[row]; product->mashs[row] = temp; is_changed(); emit refreshAll(); } void EditProduct::step_name_changed(QString val) { product->mashs[product->mashs_row].step_name = val; ui->mashsTable->setItem(product->mashs_row, 0, new QTableWidgetItem(val)); is_changed(); } void EditProduct::step_type_changed(int val) { qDebug() << "step_type_changed" << product->mashs_row << val; product->mashs[product->mashs_row].step_type = val; ivolLabel->setVisible(product->mashs.at(product->mashs_row).step_type == 0); stepivolEdit->setVisible(product->mashs.at(product->mashs_row).step_type == 0); itmpLabel->setVisible(product->mashs.at(product->mashs_row).step_type == 0); stepitmpEdit->setVisible(product->mashs.at(product->mashs_row).step_type == 0); is_changed(); emit refreshAll(); } void EditProduct::step_temp_changed(double val) { qDebug() << "step_temp_changed" << product->mashs_row << val; product->mashs[product->mashs_row].step_temp = val; QTableWidgetItem *item = new QTableWidgetItem(QString("%1 °C").arg(val, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(product->mashs_row, 2, item); is_changed(); emit refreshAll(); } void EditProduct::end_temp_changed(double val) { qDebug() << "end_temp_changed" << product->mashs_row << val; product->mashs[product->mashs_row].end_temp = val; QTableWidgetItem *item = new QTableWidgetItem(QString("%1 °C").arg(val, 2, 'f', 1, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(product->mashs_row, 3, item); is_changed(); emit refreshAll(); } void EditProduct::step_time_changed(double val) { qDebug() << "step_time_changed" << product->mashs_row << val; product->mashs[product->mashs_row].step_time = val; QTableWidgetItem *item = new QTableWidgetItem(QString("%1 min").arg(val, 1, 'f', 0, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(product->mashs_row, 4, item); is_changed(); emit refreshAll(); } void EditProduct::ramp_time_changed(double val) { qDebug() << "ramp_time_changed" << product->mashs_row << val; product->mashs[product->mashs_row].ramp_time = val; QTableWidgetItem *item = new QTableWidgetItem(QString("%1 min").arg(val, 1, 'f', 0, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(product->mashs_row, 5, item); is_changed(); emit refreshAll(); } void EditProduct::infuse_changed(double val) { qDebug() << "infuse_changed" << product->mashs_row << val; product->mashs[product->mashs_row].step_infuse_amount = val; QTableWidgetItem *item = new QTableWidgetItem(QString("%1 L").arg(val, 1, 'f', 0, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(product->mashs_row, 6, item); /* * Recalculate water volumes */ double volume = 0; for (int i = 0; i < product->mashs.size(); i++) { if (product->mashs.at(i).step_type == 0) { volume += product->mashs.at(i).step_infuse_amount; } product->mashs[i].step_volume = volume; } product->w1_amount = volume - product->w2_amount; product->wg_amount = volume; ui->w1_volEdit->setValue(product->w1_amount); check_waters(); is_changed(); emit refreshAll(); } void EditProduct::stepph_changed(double val) { qDebug() << "stepph_changed" << product->mashs_row << val; if (product->mashs.at(product->mashs_row).step_ph == 0) { if (product->mashs_row == 0) { /* * Jump start at 5.0pH */ product->mashs[product->mashs_row].step_ph = 5.0; } else { /* * Start with pH from previous step. */ product->mashs[product->mashs_row].step_ph = product->mashs.at(product->mashs_row - 1).step_ph; } const QSignalBlocker blocker1(stepphEdit); stepphEdit->setValue(product->mashs.at(product->mashs_row).step_ph); } else { product->mashs[product->mashs_row].step_ph = val; } QTableWidgetItem *item = new QTableWidgetItem(QString("%1").arg(product->mashs.at(product->mashs_row).step_ph, 3, 'f', 2, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(product->mashs_row, 11, item); is_changed(); } void EditProduct::stepsg_changed(double val) { qDebug() << "stepsg_changed" << product->mashs_row << val; if (product->mashs.at(product->mashs_row).step_sg == 0) { if (product->mashs_row > 0) { /* Start with value from previous step. */ product->mashs[product->mashs_row].step_sg = product->mashs.at(product->mashs_row - 1).step_sg; } else { /* Start at 1.001 */ product->mashs[product->mashs_row].step_sg = 1.001; } const QSignalBlocker blocker1(stepsgEdit); stepsgEdit->setValue(product->mashs.at(product->mashs_row).step_sg); } else { product->mashs[product->mashs_row].step_sg = val; } const QSignalBlocker blocker2(brixEdit); if (product->mashs.at(product->mashs_row).step_sg >= 1.000) brixEdit->setValue(round(Utils::sg_to_brix(product->mashs.at(product->mashs_row).step_sg) * 10.0) / 10.0); else brixEdit->setValue(0); qDebug() << "brix" << brixEdit->value(); QTableWidgetItem *item = new QTableWidgetItem(QString("%1").arg(product->mashs.at(product->mashs_row).step_sg, 4, 'f', 3, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(product->mashs_row, 10, item); is_changed(); } void EditProduct::stepbrix_changed(double val) { qDebug() << "stepbrix_changed" << product->mashs_row << val << "SG" << Utils::brix_to_sg(val); if ((product->mashs.at(product->mashs_row).step_sg == 0) && (product->mashs_row > 0)) { /* If not the first step, and SG was not set, pickup previous step. */ val = Utils::sg_to_brix(product->mashs.at(product->mashs_row - 1).step_sg); const QSignalBlocker blocker2(brixEdit); brixEdit->setValue(val); } product->mashs[product->mashs_row].step_sg = Utils::brix_to_sg(val); const QSignalBlocker blocker1(stepsgEdit); stepsgEdit->setValue(product->mashs.at(product->mashs_row).step_sg); QTableWidgetItem *item = new QTableWidgetItem(QString("%1").arg(product->mashs.at(product->mashs_row).step_sg, 4, 'f', 3, '0')); item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); ui->mashsTable->setItem(product->mashs_row, 10, item); is_changed(); } void EditProduct::editMashRow_clicked() { QSqlQuery query; if (product->locked) return; QToolButton *pb = qobject_cast<QToolButton *>(QObject::sender()); product->mashs_row = pb->objectName().toInt(); qDebug() << "Edit mash row" << product->mashs_row; MashSteps backup = product->mashs.at(product->mashs_row); QDialog* dialog = new QDialog(this); dialog->resize(738, 290); QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); buttonBox->setObjectName(QString::fromUtf8("buttonBox")); buttonBox->setGeometry(QRect(30, 240, 671, 32)); buttonBox->setLayoutDirection(Qt::LeftToRight); buttonBox->setOrientation(Qt::Horizontal); buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); buttonBox->setCenterButtons(true); QLabel *nameLabel = new QLabel(dialog); nameLabel->setObjectName(QString::fromUtf8("nameLabel")); nameLabel->setText(tr("Step name:")); nameLabel->setGeometry(QRect(10, 10, 141, 20)); nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); stepnameEdit = new QLineEdit(dialog); stepnameEdit->setObjectName(QString::fromUtf8("stepnameEdit")); stepnameEdit->setText(product->mashs.at(product->mashs_row).step_name); stepnameEdit->setGeometry(QRect(160, 10, 511, 23)); QLabel *typeLabel = new QLabel(dialog); typeLabel->setObjectName(QString::fromUtf8("typeLabel")); typeLabel->setText(tr("Step type:")); typeLabel->setGeometry(QRect(10, 40, 141, 20)); typeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); QComboBox *typeEdit = new QComboBox(dialog); typeEdit->setObjectName(QString::fromUtf8("typeEdit")); typeEdit->setGeometry(QRect(160, 40, 161, 23)); typeEdit->addItem(tr("Infusion")); typeEdit->addItem(tr("Temperature")); typeEdit->addItem(tr("Decoction")); typeEdit->setCurrentIndex(product->mashs.at(product->mashs_row).step_type); QLabel *tempLabel = new QLabel(dialog); tempLabel->setObjectName(QString::fromUtf8("tempLabel")); tempLabel->setText(tr("Step start temp:")); tempLabel->setGeometry(QRect(10, 70, 141, 20)); tempLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); steptempEdit = new QDoubleSpinBox(dialog); steptempEdit->setObjectName(QString::fromUtf8("steptempEdit")); steptempEdit->setGeometry(QRect(160, 70, 121, 24)); steptempEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); steptempEdit->setDecimals(1); steptempEdit->setAccelerated(true); steptempEdit->setSingleStep(0.5); steptempEdit->setMaximum(82); steptempEdit->setValue(product->mashs.at(product->mashs_row).step_temp); QLabel *endLabel = new QLabel(dialog); endLabel->setObjectName(QString::fromUtf8("endLabel")); endLabel->setText(tr("Step end temp:")); endLabel->setGeometry(QRect(360, 70, 141, 20)); endLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); endtempEdit = new QDoubleSpinBox(dialog); endtempEdit->setObjectName(QString::fromUtf8("endtempEdit")); endtempEdit->setGeometry(QRect(510, 70, 121, 24)); endtempEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); endtempEdit->setDecimals(1); endtempEdit->setAccelerated(true); endtempEdit->setSingleStep(0.5); endtempEdit->setMaximum(82); endtempEdit->setValue(product->mashs.at(product->mashs_row).end_temp); QLabel *timeLabel = new QLabel(dialog); timeLabel->setObjectName(QString::fromUtf8("timeLabel")); timeLabel->setText(tr("Step rest time:")); timeLabel->setGeometry(QRect(10, 100, 141, 20)); timeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); steptimeEdit = new QDoubleSpinBox(dialog); steptimeEdit->setObjectName(QString::fromUtf8("steptimeEdit")); steptimeEdit->setGeometry(QRect(160, 100, 121, 24)); steptimeEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); steptimeEdit->setDecimals(0); steptimeEdit->setAccelerated(true); steptimeEdit->setMaximum(240.0); steptimeEdit->setValue(product->mashs.at(product->mashs_row).step_time); QLabel *rampLabel = new QLabel(dialog); rampLabel->setObjectName(QString::fromUtf8("rampLabel")); rampLabel->setText(tr("Step ramp time:")); rampLabel->setGeometry(QRect(360, 100, 141, 20)); rampLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); ramptimeEdit = new QDoubleSpinBox(dialog); ramptimeEdit->setObjectName(QString::fromUtf8("ramptimeEdit")); ramptimeEdit->setGeometry(QRect(510, 100, 121, 24)); ramptimeEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); ramptimeEdit->setDecimals(0); ramptimeEdit->setValue(product->mashs.at(product->mashs_row).ramp_time); QLabel *phLabel = new QLabel(dialog); phLabel->setObjectName(QString::fromUtf8("phLabel")); phLabel->setText(tr("Measured pH:")); phLabel->setGeometry(QRect(10, 130, 141, 20)); phLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); stepphEdit = new QDoubleSpinBox(dialog); stepphEdit->setObjectName(QString::fromUtf8("phEdit")); stepphEdit->setGeometry(QRect(160, 130, 121, 24)); stepphEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); stepphEdit->setDecimals(2); stepphEdit->setMaximum(14.0); stepphEdit->setAccelerated(true); stepphEdit->setSingleStep(0.1); stepphEdit->setValue(product->mashs.at(product->mashs_row).step_ph); /* * SG or Brix, both can be used to enter the SG. */ QLabel *brixLabel = new QLabel(dialog); brixLabel->setObjectName(QString::fromUtf8("brixLabel")); brixLabel->setText(tr("Measured Brix:")); brixLabel->setGeometry(QRect(360, 130, 141, 20)); brixLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); brixEdit = new QDoubleSpinBox(dialog); brixEdit->setObjectName(QString::fromUtf8("brixEdit")); brixEdit->setGeometry(QRect(510, 130, 121, 24)); brixEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); brixEdit->setDecimals(1); brixEdit->setMaximum(32); brixEdit->setAccelerated(true); brixEdit->setSingleStep(0.1); QLabel *sgLabel = new QLabel(dialog); sgLabel->setObjectName(QString::fromUtf8("sgLabel")); sgLabel->setText(tr("Measured SG:")); sgLabel->setGeometry(QRect(360, 160, 141, 20)); sgLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); stepsgEdit = new QDoubleSpinBox(dialog); stepsgEdit->setObjectName(QString::fromUtf8("sgEdit")); stepsgEdit->setGeometry(QRect(510, 160, 121, 24)); stepsgEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); stepsgEdit->setDecimals(3); stepsgEdit->setMaximum(1.5); stepsgEdit->setAccelerated(true); stepsgEdit->setSingleStep(0.001); /* Make it smart, if zero then start with the previous step value. */ stepsgEdit->setValue(product->mashs.at(product->mashs_row).step_sg); brixEdit->setValue(Utils::sg_to_brix(product->mashs.at(product->mashs_row).step_sg)); /* * Only used for Infusion steps. */ ivolLabel = new QLabel(dialog); ivolLabel->setObjectName(QString::fromUtf8("ivolLabel")); ivolLabel->setText(tr("Infusion volume:")); ivolLabel->setGeometry(QRect(10, 190, 141, 20)); ivolLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); ivolLabel->setVisible(product->mashs.at(product->mashs_row).step_type == 0); stepivolEdit = new QDoubleSpinBox(dialog); stepivolEdit->setObjectName(QString::fromUtf8("stepivolEdit")); stepivolEdit->setGeometry(QRect(160, 190, 121, 24)); stepivolEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); stepivolEdit->setVisible(product->mashs.at(product->mashs_row).step_type == 0); stepivolEdit->setDecimals(1); stepivolEdit->setAccelerated(true); stepivolEdit->setMaximum(100000.0); stepivolEdit->setSingleStep(0.5); stepivolEdit->setValue(product->mashs.at(product->mashs_row).step_infuse_amount); itmpLabel = new QLabel(dialog); itmpLabel->setObjectName(QString::fromUtf8("itmpLabel")); itmpLabel->setText(tr("Infusion Temperature:")); itmpLabel->setGeometry(QRect(360, 190, 141, 20)); itmpLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); itmpLabel->setVisible(product->mashs.at(product->mashs_row).step_type == 0); stepitmpEdit = new QDoubleSpinBox(dialog); stepitmpEdit->setObjectName(QString::fromUtf8("stepitmpEdit")); stepitmpEdit->setGeometry(QRect(510, 190, 121, 24)); stepitmpEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); stepitmpEdit->setVisible(product->mashs.at(product->mashs_row).step_type == 0); stepitmpEdit->setDecimals(1); stepitmpEdit->setReadOnly(true); stepitmpEdit->setButtonSymbols(QAbstractSpinBox::NoButtons); stepitmpEdit->setValue(product->mashs.at(product->mashs_row).step_infuse_temp); connect(stepnameEdit, &QLineEdit::textEdited, this, &EditProduct::step_name_changed); connect(typeEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::step_type_changed); connect(steptempEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::step_temp_changed); connect(endtempEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::end_temp_changed); connect(steptimeEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::step_time_changed); connect(ramptimeEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::ramp_time_changed); connect(stepivolEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::infuse_changed); connect(stepphEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::stepph_changed); connect(stepsgEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::stepsg_changed); connect(brixEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::stepbrix_changed); connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); dialog->setModal(true); dialog->exec(); if (dialog->result() == QDialog::Rejected) { qDebug() << "reject and rollback"; product->mashs[product->mashs_row] = backup; /* Rollback water volumes too */ double volume = 0; for (int i = 0; i < product->mashs.size(); i++) { if (product->mashs.at(i).step_type == 0) { volume += product->mashs.at(i).step_infuse_amount; } product->mashs[i].step_volume = volume; } product->w1_amount = volume - product->w2_amount; product->wg_amount = volume; ui->w1_volEdit->setValue(product->w1_amount); } disconnect(stepnameEdit, nullptr, nullptr, nullptr); disconnect(steptempEdit, nullptr, nullptr, nullptr); disconnect(endtempEdit, nullptr, nullptr, nullptr); disconnect(steptimeEdit, nullptr, nullptr, nullptr); disconnect(ramptimeEdit, nullptr, nullptr, nullptr); disconnect(stepivolEdit, nullptr, nullptr, nullptr); disconnect(stepphEdit, nullptr, nullptr, nullptr); disconnect(stepsgEdit, nullptr, nullptr, nullptr); disconnect(brixEdit, nullptr, nullptr, nullptr); disconnect(buttonBox, nullptr, nullptr, nullptr); emit refreshAll(); } void EditProduct::mash_name_changed(QString name) { qDebug() << "mash_name_changed" << name; product->mash_name = name; is_changed(); } void EditProduct::mash_select_changed(int val) { QSqlQuery query; int i; qDebug() << "mash_select_changed" << val; const QSignalBlocker blocker1(ui->mash_nameEdit); query.prepare("SELECT name,steps FROM profile_mash ORDER BY name"); query.exec(); query.first(); for (i = 0; i < (val -1); i++) { query.next(); } product->mash_name = query.value(0).toString(); ui->mash_nameEdit->setText(product->mash_name); QJsonParseError parseError; const auto& json = query.value(1).toString(); if (!json.trimmed().isEmpty()) { const auto& formattedJson = QString("%1").arg(json); QJsonDocument newsteps = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError); if (parseError.error != QJsonParseError::NoError) { qWarning() << "Parse error: " << parseError.errorString() << "at" << parseError.offset; } else { /* * Got the json data in the steps array, replace the product steps. */ double infuse = 0; if (product->mashs.size()) { infuse = product->mashs.at(0).step_infuse_amount; product->mashs.clear(); ui->mashsTable->clear(); } if (newsteps.isArray()) { for (i = 0; i < newsteps.array().size(); i++) { QJsonObject obj = newsteps.array().at(i).toObject(); MashSteps m; m.step_name = obj["step_name"].toString(); if (obj["step_type"].isString()) m.step_type = QString(obj["step_type"].toString()).toInt(); else m.step_type = obj["step_type"].toInt(); m.step_volume = 0; m.step_infuse_amount = 0; m.step_infuse_temp = 0; if (obj["step_temp"].isString()) m.step_temp = QString(obj["step_temp"].toString()).toDouble(); else m.step_temp = obj["step_temp"].toDouble(); if (obj["step_time"].isString()) m.step_time = QString(obj["step_time"].toString()).toDouble(); else m.step_time = obj["step_time"].toDouble(); if (obj["ramp_time"].isString()) m.ramp_time = QString(obj["ramp_time"].toString()).toDouble(); else m.ramp_time = obj["ramp_time"].toDouble(); if (obj["end_temp"].isString()) m.end_temp = QString(obj["end_temp"].toString()).toDouble(); else m.end_temp = obj["end_temp"].toDouble(); m.step_wg_ratio = 0; product->mashs.append(m); } } if (product->mashs.at(0).step_type == 0) product->mashs[0].step_infuse_amount = infuse; // Restore saved initial infusion } } is_changed(); emit refreshAll(); }