--- a/src/EditRecipe.cpp Fri Apr 01 17:25:42 2022 +0200 +++ b/src/EditRecipe.cpp Sat Apr 02 23:01:13 2022 +0200 @@ -44,6 +44,15 @@ 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); @@ -56,7 +65,7 @@ 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(query.value(8).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()); @@ -67,18 +76,22 @@ 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); @@ -87,6 +100,8 @@ 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()); @@ -95,6 +110,7 @@ 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()); @@ -156,30 +172,104 @@ // 81 wa_acid_name // 82 wa_acid_perc // 83 wa_base_name - // 84 json_fermentables + + /* + * Progress bars. + * perc_maltShow pmalts = mashkg / (dataRecord.boil_size / 3) * 100; + * perc_sugarsShow if (row.f_type == 1 && row.f_added < 4) // Sugar + * psugar += row.f_percentage; + * perc_caraShow if (row.f_graintype == 2 && row.f_added < 4) // Crystal + * pcara += row.f_percentage; + * lintner if (row.f_added == 0 && (row.f_type == 0 || row.f_type == 4) && row.f_color < 50) + * lintner += row.f_diastatic_power * row.f_amount; + * lintner = parseFloat(lintner / mashkg) + * + * perc_malts range(0, 120) + * stop: 90, color: '#008C00' + * stop: 100, color: '#EB7331' + * stop: 120, color: '#FF0000' + * + * perc_sugars range(0, 50) + * stop: 20, color: '#008C00' + * stop: 50, color: '#FF0000' + * perc_cara range(0, 50) + * stop: 25, color: '#008C00' + * stop: 50, color: '#FF0000' + * lintner range(0, 200) + * stop: 30, color: '#FF0000' red + * stop: 40, color: '#EB7331' orange + * stop: 200, color: '#008C00' green + */ + +// ui->lintnerShow->setValue(52); + if (ui->lintnerShow->value() < 30) + ui->lintnerShow->setStyleSheet(bar_red); + else if (ui->lintnerShow->value() < 40) + ui->lintnerShow->setStyleSheet(bar_orange); + else + ui->lintnerShow->setStyleSheet(bar_green); + + QJsonParseError parseError; + const auto& json = query.value(84).toString(); + if (!json.trimmed().isEmpty()) { + const auto& formattedJson = QString("%1").arg(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"; + } + // 85 json_hops // 86 json_miscs // 87 json_yeasts // 88 json_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::is_changed); + connect(ui->ibu_methodEdit, &QComboBox::currentTextChanged, this, &EditRecipe::is_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->fermentablesTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_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(); } @@ -192,6 +282,230 @@ } +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("Fermentable"), tr("EBC"), tr("Type"), tr("Graintype"), tr("When"), tr("Yield"), tr("Amount"), tr("Procent"), tr("100%"), tr("Button")}); + ui->fermentablesTable->setColumnCount(10); + ui->fermentablesTable->setColumnWidth(0, 350); /* Fermentable */ + ui->fermentablesTable->setColumnWidth(1, 50); /* Color */ + ui->fermentablesTable->setColumnWidth(2, 75); /* Type */ + ui->fermentablesTable->setColumnWidth(3, 75); /* Graintype */ + ui->fermentablesTable->setColumnWidth(4, 75); /* Added */ + ui->fermentablesTable->setColumnWidth(5, 75); /* Yield */ + ui->fermentablesTable->setColumnWidth(6, 75); /* Amount */ + ui->fermentablesTable->setColumnWidth(7, 60); /* Procent */ + ui->fermentablesTable->setColumnWidth(8, 50); /* 100% */ + ui->fermentablesTable->setColumnWidth(9, 80); /* Button */ + 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(); + + w = obj["f_supplier"].toString()+" / "+obj["f_name"].toString(); + ui->fermentablesTable->setItem(i, 0, new QTableWidgetItem(w)); + + if (obj["f_color"].isString()) + d = QString(obj["f_color"].toString()).toDouble(); + else + d = obj["f_color"].toDouble(); + w = QString("%1").arg(d, 1, 'f', 0, '0'); + item = new QTableWidgetItem(w); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 1, item); + + if (obj["f_type"].isString()) + j = QString(obj["f_type"].toString()).toInt(); + else + j = obj["f_type"].toInt(); + item = new QTableWidgetItem(f_types[j]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 2, item); + + if (obj["f_graintype"].isString()) + j = QString(obj["f_graintype"].toString()).toInt(); + else + j = obj["f_graintype"].toInt(); + item = new QTableWidgetItem(f_graintypes[j]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 3, item); + + if (obj["f_added"].isString()) + j = QString(obj["f_added"].toString()).toInt(); + else + j = obj["f_added"].toInt(); + item = new QTableWidgetItem(f_added[j]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 4, item); + + if (obj["f_yield"].isString()) + d = QString(obj["f_yield"].toString()).toDouble(); + else + d = obj["f_yield"].toDouble(); + w = QString("%1%").arg(d, 2, 'f', 1, '0'); + item = new QTableWidgetItem(w); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 5, item); + + if (obj["f_amount"].isString()) + d = QString(obj["f_amount"].toString()).toDouble(); + else + d = obj["f_amount"].toDouble(); + w = QString("%1 Kg").arg(d, 4, 'f', 3, '0'); + item = new QTableWidgetItem(w); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 6, item); + + if (obj["f_percentage"].isString()) + d = QString(obj["f_percentage"].toString()).toDouble(); + else + d = obj["f_percentage"].toDouble(); + w = QString("%1%").arg(d, 2, 'f', 1, '0'); + item = new QTableWidgetItem(w); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 7, item); + + if (obj["f_adjust_to_total_100"].toString().toInt()) { + pWidget = new QWidget(); + label = new QLabel; + label->setPixmap(QPixmap(":icons/silk/tick.png")); + pLayout = new QHBoxLayout(pWidget); + pLayout->addWidget(label); + pLayout->setAlignment(Qt::AlignCenter); + pLayout->setContentsMargins(0, 0, 0, 0); + pWidget->setLayout(pLayout); + ui->fermentablesTable->setCellWidget(i, 8, pWidget); + } else { + ui->fermentablesTable->removeCellWidget(i, 8); + } + + /* Add the Delete row button */ + pWidget = new QWidget(); + QPushButton* btn_edit = new QPushButton(); + btn_edit->setObjectName(QString("%1").arg(i)); /* Send row with the button */ + btn_edit->setText(tr("Delete")); + connect(btn_edit, SIGNAL(clicked()), this, SLOT(on_deleteFermentRow_clicked())); + pLayout = new QHBoxLayout(pWidget); + pLayout->addWidget(btn_edit); + pLayout->setContentsMargins(5, 0, 5, 0); + pWidget->setLayout(pLayout); + ui->fermentablesTable->setCellWidget(i, 9, pWidget); + } + } + + 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; + 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 + QJsonObject obj; + + qDebug() << "calcFermentables()"; + + // Get mashtemp and mashtime from the 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) { + // colors + } + } + + qDebug() << " lintner" << lintner << " mashkg" << mashkg << "final" << round(lintner / mashkg); + ui->lintnerShow->setValue(round(lintner / mashkg)); +} + + /* * Window header, mark any change with '**' */ @@ -288,12 +602,57 @@ } +/* + * 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::time_changed() { is_changed(); } +void EditRecipe::on_deleteFermentRow_clicked() +{ + QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender()); + int row = pb->objectName().toInt(); + qDebug() << "Delete row" << row; + ui->fermentablesTable->removeRow(row); +// make_Json(); +} + + void EditRecipe::on_quitButton_clicked() { if (this->textIsChanged) {