File indexing completed on 2024-04-21 03:41:38
0001 /* 0002 SPDX-FileCopyrightText: 2010 Luca Tringali <TRINGALINVENT@libero.it> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "titrationCalculator.h" 0008 0009 #include <cctype> 0010 #include <cfloat> 0011 #include <cmath> 0012 #include <cstdio> 0013 #include <cstdlib> 0014 #include <cstring> 0015 #include <fstream> 0016 #include <iostream> 0017 0018 #include <QFileDialog> 0019 #include <QMessageBox> 0020 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0021 #include <QScriptClass> 0022 #include <QScriptEngine> 0023 #include <QScriptValue> 0024 #endif 0025 #include <QVarLengthArray> 0026 0027 #include <KLocalizedString> 0028 0029 #include "prefs.h" 0030 0031 using namespace std; 0032 0033 titrationCalculator::titrationCalculator(QWidget *parent) 0034 : QWidget(parent) 0035 { 0036 xmin = 0; 0037 xmax = 50; 0038 ymin = 0; 0039 ymax = 50; 0040 width = int(xmax - xmin); 0041 0042 uid.setupUi(this); 0043 uid.tabWidget->setTabText(0, i18n("Experimental values")); 0044 uid.tabWidget->setTabText(1, i18n("Theoretical equations")); 0045 uid.tab->setFocus(); 0046 plot(); 0047 0048 connect(uid.pushButton, &QAbstractButton::clicked, this, &titrationCalculator::on_pushButton_clicked); 0049 connect(uid.xmin, SIGNAL(valueChanged(double)), this, SLOT(on_xmin_valueChanged(double))); 0050 connect(uid.xmax, SIGNAL(valueChanged(double)), this, SLOT(on_xmax_valueChanged(double))); 0051 connect(uid.ymin, SIGNAL(valueChanged(double)), this, SLOT(on_ymin_valueChanged(double))); 0052 connect(uid.ymax, SIGNAL(valueChanged(double)), this, SLOT(on_ymax_valueChanged(double))); 0053 0054 connect(uid.saveimage, &QAbstractButton::clicked, this, &titrationCalculator::on_actionSave_image_triggered); 0055 connect(uid.open, &QAbstractButton::clicked, this, &titrationCalculator::on_actionOpen_triggered); 0056 connect(uid.save, &QAbstractButton::clicked, this, &titrationCalculator::on_actionSave_triggered); 0057 connect(uid.newfile, &QAbstractButton::clicked, this, &titrationCalculator::on_actionNew_triggered); 0058 connect(uid.rapidhelp, &QAbstractButton::clicked, this, &titrationCalculator::on_actionRapid_Help_triggered); 0059 } 0060 0061 titrationCalculator::~titrationCalculator() = default; 0062 0063 void titrationCalculator::plot() 0064 { 0065 width = int(xmax - xmin); 0066 // now I'm preparing the kplot widget 0067 uid.kplotwidget->removeAllPlotObjects(); 0068 uid.kplotwidget->setLimits(xmin, xmax, ymin, ymax); // now I need to set the limits of the plot 0069 0070 auto kpor = new KPlotObject(Qt::red, KPlotObject::Lines); 0071 auto kpog = new KPlotObject(Qt::green, KPlotObject::Lines); 0072 auto kpob = new KPlotObject(Qt::blue, KPlotObject::Lines); 0073 redplot = QStringLiteral("<polyline points=\""); 0074 greenplot = QStringLiteral("<polyline points=\""); 0075 blueplot = QStringLiteral("<polyline points=\""); 0076 0077 if (!uid.tableWidget->item(0, 0) || uid.tableWidget->item(0, 0)->text().isEmpty()) { 0078 // go on 0079 } else { 0080 char yvalue[80]; 0081 int tmpy = 0; 0082 for (int i = 0; i < uid.tableWidget->rowCount(); ++i) { 0083 if (!uid.tableWidget->item(i, 0) || uid.tableWidget->item(i, 0)->text().isEmpty()) { 0084 break; 0085 } else { 0086 if (uid.tableWidget->item(i, 0)->data(Qt::DisplayRole).toString() == uid.yaxis->text()) { 0087 QString yvalueq = uid.tableWidget->item(i, 1)->data(Qt::DisplayRole).toString(); 0088 QByteArray ba = yvalueq.toLatin1(); 0089 char *yvaluen = ba.data(); 0090 strcpy(yvalue, yvaluen); 0091 tmpy = 1; 0092 } 0093 } 0094 } 0095 QString mreporto; 0096 int iter = 0; 0097 if (uid.xaxis->text().isEmpty() || uid.xaxis->text() == QLatin1String(" ")) { 0098 uid.xaxis->setText(i18n("nothing")); 0099 } 0100 if (tmpy == 0) { 0101 QMessageBox::critical(this, i18n("Error"), i18n("Unable to find an equation for Y-axis variable.")); 0102 } else { 0103 // now we have to solve the system of equations NOTE:yvalue contains the equation of Y-axis variable 0104 // we iterates the process until you have an equation in one only unknown variable or a numeric expression 0105 mreporto = solve(yvalue); 0106 while (end == 0 || lettere == 1) { 0107 QByteArray ba = mreporto.toLatin1(); 0108 char *tmreport = ba.data(); 0109 ++iter; 0110 if (end == 1 || lettere == 0) { 0111 break; 0112 } 0113 if (iter > 100) { 0114 break; // preventing from an endless iteration 0115 } 0116 mreporto = solve(tmreport); 0117 } 0118 } 0119 // if (mreporto!="") uid.note->setText("Theoretical Curve: "+mreporto); 0120 if (!mreporto.isEmpty()) { 0121 uid.note->setText(i18n("Theoretical curve") + ": " + mreporto); 0122 for (int i = int(xmin); i < int(xmax); ++i) { 0123 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0124 double id = i; 0125 QScriptEngine myEngine; 0126 QByteArray ban = mreporto.toLatin1(); 0127 char *tmreporto = ban.data(); 0128 0129 QString istr; 0130 istr.append(QStringLiteral("%1").arg((id))); 0131 // now i'm using QScript language to solve the expression 0132 // in a future we can consider to change it supporting some backends, but it's really complex 0133 QString myscript = solvex(tmreporto, istr); 0134 QScriptValue three = myEngine.evaluate(myscript); 0135 0136 double tvalue = three.toNumber(); 0137 kpor->addPoint(id, tvalue); 0138 redplot = redplot + ' ' + QString::number((id * 10) + 5).replace(QChar(','), QChar('.')) + ',' 0139 + QString::number((ymax - tvalue) * 10).replace(QChar(','), QChar('.')); 0140 #else 0141 #pragma "NEED TO PORT QTSCRIPT"; 0142 #endif 0143 } 0144 } 0145 temponu = 0; 0146 } // here ends the equations mode 0147 0148 // uid.tableWidget_2->sortItems(1, Qt::AscendingOrder); //seems that the sorting doesn't work correctly 0149 if (!uid.tableWidget_2->item(0, 0) || uid.tableWidget_2->item(0, 0)->text().isEmpty()) { 0150 // go on 0151 } else { 0152 // now we can plot the values 0153 double a, b, c, d, xval; 0154 QVarLengthArray<double, 64> px(uid.tableWidget_2->rowCount()); 0155 QVarLengthArray<double, 64> py(uid.tableWidget_2->rowCount()); 0156 int totaldata = 0; 0157 for (int i = 0; i < uid.tableWidget_2->rowCount(); ++i) { 0158 if (!uid.tableWidget_2->item(i, 0) || uid.tableWidget_2->item(i, 0)->text().isEmpty()) { 0159 break; 0160 } else { 0161 ++totaldata; 0162 kpob->addPoint(uid.tableWidget_2->item(i, 1)->data(Qt::DisplayRole).toDouble(), 0163 uid.tableWidget_2->item(i, 0)->data(Qt::DisplayRole).toDouble()); 0164 py[i] = uid.tableWidget_2->item(i, 0)->data(Qt::DisplayRole).toDouble(); 0165 px[i] = uid.tableWidget_2->item(i, 1)->data(Qt::DisplayRole).toDouble(); 0166 blueplot = blueplot + ' ' 0167 + QString::number((uid.tableWidget_2->item(i, 1)->data(Qt::DisplayRole).toDouble() * 10) + 5).replace(QChar(','), QChar('.')) + ',' 0168 + QString::number((ymax - uid.tableWidget_2->item(i, 0)->data(Qt::DisplayRole).toDouble()) * 10).replace(QChar(','), QChar('.')); 0169 } 0170 } 0171 a = py[totaldata - 1] - py[0]; 0172 b = 4 / (px[totaldata - 1] - px[0]); 0173 d = 0; 0174 if (a > 0) { 0175 d = py[0] + (a / 2); 0176 } else if (a < 0) { 0177 d = py[totaldata - 1] - (a / 2); 0178 } 0179 double cn = 0.0; 0180 int th = 0; 0181 for (int i = 1; i < (totaldata - 1); ++i) { 0182 // now i'm using the value of the points to fit the curve 0183 double ci = ((setttanh((py[i] - d) / a)) / b) - px[i]; 0184 if ((ci * 0) == 0) { 0185 cn = cn + ci; 0186 ++th; 0187 } 0188 } 0189 // c = cn/(th); it doesn't wok, but i found out this little hack. The strange thing is that in the standalone application it works fine. 0190 c = cn / (th * 2); 0191 // THIS IS THE PLOT OF APPROXIMATED CURVE 0192 for (int i = int(xmin); i < (int(xmax)); ++i) { 0193 double id = i; 0194 xval = (a * tanh(b * (id + c))) + d; 0195 kpog->addPoint(id, xval); 0196 greenplot = greenplot + ' ' + QString::number((id * 10) + 5).replace(QChar(','), QChar('.')) + ',' 0197 + QString::number((ymax - xval) * 10).replace(QChar(','), QChar('.')); 0198 } 0199 // THIS IS THE EQUIVALENCE POINT (THE INFLECTION OF THE CURVE) 0200 QString es = QString::number(-c); 0201 QString as = QString::number(a); 0202 QString bs = QString::number(b); 0203 QString cs = QString::number(c); 0204 QString ds = QString::number(d); 0205 QString tempon = uid.note->toPlainText() + QChar('\n'); 0206 if (temponu != 0) { 0207 tempon = QLatin1String(""); 0208 } 0209 uid.note->setText(tempon + '\n' + i18n("Approximated curve") + ": " + as + "*tanh(" + bs + "*(x+" + cs + "))+" + ds + '\n' + i18n("Equivalence point") 0210 + ": " + es); 0211 } // here ends the experimental values mode 0212 0213 uid.kplotwidget->addPlotObject(kpor); 0214 uid.kplotwidget->addPlotObject(kpog); 0215 uid.kplotwidget->addPlotObject(kpob); 0216 0217 redplot = redplot + R"(" style="stroke:red;fill:none"/> )"; 0218 blueplot = blueplot + R"(" style="stroke:blue;fill:none"/> )"; 0219 greenplot = greenplot + R"(" style="stroke:green;fill:none"/> )"; 0220 } 0221 0222 double titrationCalculator::setttanh(double x) 0223 { 0224 double temp; 0225 temp = log((1 + x) / (1 - x)) / 2; 0226 return temp; 0227 } 0228 0229 QString titrationCalculator::solve(char *yvalue) 0230 { 0231 QString mreport; 0232 lettere = 0; 0233 // now we have to solve the system of equations 0234 // yvalue contains the equation of Y-axis variable 0235 QString tempy = QLatin1String(""); 0236 end = 1; 0237 mreport = QLatin1String(""); 0238 QString tempyval; 0239 QString ptem; 0240 for (int i = 0; strlen(yvalue) + 1; ++i) { 0241 if (!(yvalue[i] == 'q' || yvalue[i] == 'w' || yvalue[i] == 'e' || yvalue[i] == 'r' || yvalue[i] == 't' || yvalue[i] == 'y' || yvalue[i] == 'u' 0242 || yvalue[i] == 'i' || yvalue[i] == 'o' || yvalue[i] == 'p' || yvalue[i] == 'a' || yvalue[i] == 's' || yvalue[i] == 'd' || yvalue[i] == 'f' 0243 || yvalue[i] == 'g' || yvalue[i] == 'h' || yvalue[i] == 'j' || yvalue[i] == 'k' || yvalue[i] == 'l' || yvalue[i] == 'z' || yvalue[i] == 'x' 0244 || yvalue[i] == 'c' || yvalue[i] == 'v' || yvalue[i] == 'b' || yvalue[i] == 'n' || yvalue[i] == 'm' || yvalue[i] == '+' || yvalue[i] == '-' 0245 || yvalue[i] == '^' || yvalue[i] == '*' || yvalue[i] == '/' || yvalue[i] == '(' || yvalue[i] == ')' || yvalue[i] == 'Q' || yvalue[i] == 'W' 0246 || yvalue[i] == 'E' || yvalue[i] == 'R' || yvalue[i] == 'T' || yvalue[i] == 'Y' || yvalue[i] == 'U' || yvalue[i] == 'I' || yvalue[i] == 'O' 0247 || yvalue[i] == 'P' || yvalue[i] == 'A' || yvalue[i] == 'S' || yvalue[i] == 'D' || yvalue[i] == 'F' || yvalue[i] == 'G' || yvalue[i] == 'H' 0248 || yvalue[i] == 'J' || yvalue[i] == 'K' || yvalue[i] == 'L' || yvalue[i] == 'Z' || yvalue[i] == 'X' || yvalue[i] == 'C' || yvalue[i] == 'V' 0249 || yvalue[i] == 'B' || yvalue[i] == 'N' || yvalue[i] == 'M' || yvalue[i] == '1' || yvalue[i] == '2' || yvalue[i] == '3' || yvalue[i] == '4' 0250 || yvalue[i] == '5' || yvalue[i] == '6' || yvalue[i] == '7' || yvalue[i] == '8' || yvalue[i] == '9' || yvalue[i] == '0' || yvalue[i] == '.' 0251 || yvalue[i] == ',')) { 0252 break; // if current value is not a permitted value, this means that something is wrong 0253 } 0254 if (yvalue[i] == 'q' || yvalue[i] == 'w' || yvalue[i] == 'e' || yvalue[i] == 'r' || yvalue[i] == 't' || yvalue[i] == 'y' || yvalue[i] == 'u' 0255 || yvalue[i] == 'i' || yvalue[i] == 'o' || yvalue[i] == 'p' || yvalue[i] == 'a' || yvalue[i] == 's' || yvalue[i] == 'd' || yvalue[i] == 'f' 0256 || yvalue[i] == 'g' || yvalue[i] == 'h' || yvalue[i] == 'j' || yvalue[i] == 'k' || yvalue[i] == 'l' || yvalue[i] == 'z' || yvalue[i] == 'x' 0257 || yvalue[i] == 'c' || yvalue[i] == 'v' || yvalue[i] == 'b' || yvalue[i] == 'n' || yvalue[i] == 'm' || yvalue[i] == 'Q' || yvalue[i] == 'W' 0258 || yvalue[i] == 'E' || yvalue[i] == 'R' || yvalue[i] == 'T' || yvalue[i] == 'Y' || yvalue[i] == 'U' || yvalue[i] == 'I' || yvalue[i] == 'O' 0259 || yvalue[i] == 'P' || yvalue[i] == 'A' || yvalue[i] == 'S' || yvalue[i] == 'D' || yvalue[i] == 'F' || yvalue[i] == 'G' || yvalue[i] == 'H' 0260 || yvalue[i] == 'J' || yvalue[i] == 'K' || yvalue[i] == 'L' || yvalue[i] == 'Z' || yvalue[i] == 'X' || yvalue[i] == 'C' || yvalue[i] == 'V' 0261 || yvalue[i] == 'B' || yvalue[i] == 'N' || yvalue[i] == 'M' || yvalue[i] == '.' || yvalue[i] == ',') { 0262 lettere = 1; // if lettere == 0 then the equation contains only mnumbers 0263 } 0264 if (yvalue[i] == '+' || yvalue[i] == '-' || yvalue[i] == '^' || yvalue[i] == '*' || yvalue[i] == '/' || yvalue[i] == '(' || yvalue[i] == ')' 0265 || yvalue[i] == '1' || yvalue[i] == '2' || yvalue[i] == '3' || yvalue[i] == '4' || yvalue[i] == '5' || yvalue[i] == '6' || yvalue[i] == '7' 0266 || yvalue[i] == '8' || yvalue[i] == '9' || yvalue[i] == '0' || yvalue[i] == '.' || yvalue[i] == ',') { 0267 tempyval = tempyval + QString(yvalue[i]); 0268 } else { 0269 tempy = tempy + QString(yvalue[i]); 0270 for (int i = 0; i < uid.tableWidget->rowCount(); ++i) { 0271 QTableWidgetItem *titem = uid.tableWidget->item(i, 0); 0272 QTableWidgetItem *titemo = uid.tableWidget->item(i, 1); 0273 if (!titem || titem->text().isEmpty()) { 0274 break; 0275 } else { 0276 if (tempy == uid.xaxis->text()) { 0277 tempyval = uid.xaxis->text(); 0278 tempy = QLatin1String(""); 0279 } 0280 if (titem->data(Qt::DisplayRole).toString() == tempy) { 0281 QString yvaluerq = titemo->data(Qt::DisplayRole).toString(); 0282 QByteArray ba = yvaluerq.toLatin1(); 0283 char *yvalure = ba.data(); 0284 tempyval = QChar('(') + QString(yvalure) + QChar(')'); 0285 tempy = QLatin1String(""); 0286 end = 1; 0287 } 0288 if (tempy != uid.xaxis->text()) { 0289 if (yvalue[i] == '+' || yvalue[i] == '-' || yvalue[i] == '^' || yvalue[i] == '*' || yvalue[i] == '/' || yvalue[i] == '(' 0290 || yvalue[i] == ')' || yvalue[i] == '1' || yvalue[i] == '2' || yvalue[i] == '3' || yvalue[i] == '4' || yvalue[i] == '5' 0291 || yvalue[i] == '6' || yvalue[i] == '7' || yvalue[i] == '8' || yvalue[i] == '9' || yvalue[i] == '0' || yvalue[i] == '.' 0292 || yvalue[i] == ',') { 0293 // actually nothing 0294 } else { 0295 end = 0; 0296 } 0297 } 0298 } 0299 } 0300 } // simbol end 0301 if (!tempyval.isEmpty()) { 0302 mreport = mreport + tempyval; 0303 } 0304 tempyval = QLatin1String(""); 0305 } 0306 return mreport; 0307 } 0308 0309 QString titrationCalculator::solvex(char *yvalue, const QString &dnum) 0310 { 0311 QString mreport = QLatin1String(""); 0312 lettere = 0; 0313 // now we have to solve the system of equations 0314 // yvalue contains the equation of Y-axis variable 0315 // Remember that the function to elevate to power is Math.pow(b,e) 0316 QString tempy; 0317 QString tempyold; 0318 QString tempyolda = QLatin1String(""); 0319 int olda = 0; 0320 end = 1; 0321 QString tempyval; 0322 tempy = QLatin1String(""); 0323 for (int i = 0; strlen(yvalue) + 1; ++i) { 0324 if (!(yvalue[i] == 'q' || yvalue[i] == 'w' || yvalue[i] == 'e' || yvalue[i] == 'r' || yvalue[i] == 't' || yvalue[i] == 'y' || yvalue[i] == 'u' 0325 || yvalue[i] == 'i' || yvalue[i] == 'o' || yvalue[i] == 'p' || yvalue[i] == 'a' || yvalue[i] == 's' || yvalue[i] == 'd' || yvalue[i] == 'f' 0326 || yvalue[i] == 'g' || yvalue[i] == 'h' || yvalue[i] == 'j' || yvalue[i] == 'k' || yvalue[i] == 'l' || yvalue[i] == 'z' || yvalue[i] == 'x' 0327 || yvalue[i] == 'c' || yvalue[i] == 'v' || yvalue[i] == 'b' || yvalue[i] == 'n' || yvalue[i] == 'm' || yvalue[i] == '+' || yvalue[i] == '-' 0328 || yvalue[i] == '^' || yvalue[i] == '*' || yvalue[i] == '/' || yvalue[i] == '(' || yvalue[i] == ')' || yvalue[i] == 'Q' || yvalue[i] == 'W' 0329 || yvalue[i] == 'E' || yvalue[i] == 'R' || yvalue[i] == 'T' || yvalue[i] == 'Y' || yvalue[i] == 'U' || yvalue[i] == 'I' || yvalue[i] == 'O' 0330 || yvalue[i] == 'P' || yvalue[i] == 'A' || yvalue[i] == 'S' || yvalue[i] == 'D' || yvalue[i] == 'F' || yvalue[i] == 'G' || yvalue[i] == 'H' 0331 || yvalue[i] == 'J' || yvalue[i] == 'K' || yvalue[i] == 'L' || yvalue[i] == 'Z' || yvalue[i] == 'X' || yvalue[i] == 'C' || yvalue[i] == 'V' 0332 || yvalue[i] == 'B' || yvalue[i] == 'N' || yvalue[i] == 'M' || yvalue[i] == '1' || yvalue[i] == '2' || yvalue[i] == '3' || yvalue[i] == '4' 0333 || yvalue[i] == '5' || yvalue[i] == '6' || yvalue[i] == '7' || yvalue[i] == '8' || yvalue[i] == '9' || yvalue[i] == '0' || yvalue[i] == '.' 0334 || yvalue[i] == ',')) { 0335 break; // if current value is not a permitted value, this means that something is wrong 0336 } 0337 if (yvalue[i] == 'q' || yvalue[i] == 'w' || yvalue[i] == 'e' || yvalue[i] == 'r' || yvalue[i] == 't' || yvalue[i] == 'y' || yvalue[i] == 'u' 0338 || yvalue[i] == 'i' || yvalue[i] == 'o' || yvalue[i] == 'p' || yvalue[i] == 'a' || yvalue[i] == 's' || yvalue[i] == 'd' || yvalue[i] == 'f' 0339 || yvalue[i] == 'g' || yvalue[i] == 'h' || yvalue[i] == 'j' || yvalue[i] == 'k' || yvalue[i] == 'l' || yvalue[i] == 'z' || yvalue[i] == 'x' 0340 || yvalue[i] == 'c' || yvalue[i] == 'v' || yvalue[i] == 'b' || yvalue[i] == 'n' || yvalue[i] == 'm' || yvalue[i] == 'Q' || yvalue[i] == 'W' 0341 || yvalue[i] == 'E' || yvalue[i] == 'R' || yvalue[i] == 'T' || yvalue[i] == 'Y' || yvalue[i] == 'U' || yvalue[i] == 'I' || yvalue[i] == 'O' 0342 || yvalue[i] == 'P' || yvalue[i] == 'A' || yvalue[i] == 'S' || yvalue[i] == 'D' || yvalue[i] == 'F' || yvalue[i] == 'G' || yvalue[i] == 'H' 0343 || yvalue[i] == 'J' || yvalue[i] == 'K' || yvalue[i] == 'L' || yvalue[i] == 'Z' || yvalue[i] == 'X' || yvalue[i] == 'C' || yvalue[i] == 'V' 0344 || yvalue[i] == 'B' || yvalue[i] == 'N' || yvalue[i] == 'M' || yvalue[i] == '.' || yvalue[i] == ',') { 0345 tempy = tempy + yvalue[i]; // if lettere == 0 then the equation contains only mnumbers 0346 } 0347 if (yvalue[i] == '+' || yvalue[i] == '-' || yvalue[i] == '^' || yvalue[i] == '*' || yvalue[i] == '/' || yvalue[i] == '(' || yvalue[i] == ')' 0348 || yvalue[i] == '1' || yvalue[i] == '2' || yvalue[i] == '3' || yvalue[i] == '4' || yvalue[i] == '5' || yvalue[i] == '6' || yvalue[i] == '7' 0349 || yvalue[i] == '8' || yvalue[i] == '9' || yvalue[i] == '0' || yvalue[i] == '.' || yvalue[i] == ',') { 0350 if (!tempyolda.isEmpty()) { 0351 tempy = tempy + yvalue[i]; 0352 if (tempyolda == uid.xaxis->text()) { 0353 tempyolda = dnum; 0354 } 0355 tempyval = tempyval + QStringLiteral("Math.pow(") + tempyolda + QChar(',') + tempy + QChar(')'); 0356 tempyolda = QLatin1String(""); 0357 tempyold = QLatin1String(""); 0358 olda = 1; 0359 } 0360 if (yvalue[i] == '^') { 0361 tempyolda = tempyold; 0362 } else { 0363 tempyold = QLatin1String(""); 0364 if (((olda != 1) && (yvalue[i + 1] != '^')) 0365 || (yvalue[i] == '+' || yvalue[i] == '-' || yvalue[i] == '^' || yvalue[i] == '*' || yvalue[i] == '/' || yvalue[i] == '(' 0366 || yvalue[i] == ')')) { 0367 tempyval = tempyval + QString(yvalue[i]); 0368 } 0369 } 0370 0371 } else { 0372 if (!tempyolda.isEmpty()) { 0373 tempyval = tempyval + QStringLiteral("Math.pow(") + tempyolda + QChar(',') + tempy + QChar(')'); 0374 tempyolda = QLatin1String(""); 0375 tempyold = QLatin1String(""); 0376 olda = 1; 0377 } 0378 if ((tempy == uid.xaxis->text()) && (!tempyolda.isEmpty())) { 0379 if (yvalue[i + 1] != '^') { 0380 tempyval = tempyval + dnum; 0381 } 0382 tempyold = tempy; 0383 tempy = QLatin1String(""); 0384 } 0385 } // simbol end 0386 if (!tempyval.isEmpty()) { 0387 mreport = mreport + tempyval; 0388 } 0389 tempyval = QLatin1String(""); 0390 } 0391 // QMessageBox::information(this, "report", mreport); 0392 return mreport; 0393 } 0394 0395 void titrationCalculator::on_xmin_valueChanged(double val) 0396 { 0397 xmin = val; 0398 on_pushButton_clicked(); // please take note that calling directly the plot() function will give a wrong value for equivalence point 0399 } 0400 0401 void titrationCalculator::on_xmax_valueChanged(double val) 0402 { 0403 xmax = val; 0404 on_pushButton_clicked(); // please take note that calling directly the plot() function will give a wrong value for equivalence point 0405 } 0406 0407 void titrationCalculator::on_ymin_valueChanged(double val) 0408 { 0409 ymin = val; 0410 on_pushButton_clicked(); // please take note that calling directly the plot() function will give a wrong value for equivalence point 0411 } 0412 0413 void titrationCalculator::on_ymax_valueChanged(double val) 0414 { 0415 ymax = val; 0416 on_pushButton_clicked(); // please take note that calling directly the plot() function will give a wrong value for equivalence point 0417 } 0418 0419 void titrationCalculator::on_pushButton_clicked() 0420 { 0421 plot(); 0422 } 0423 0424 void titrationCalculator::on_actionRapid_Help_triggered() 0425 { 0426 on_actionNew_triggered(); 0427 // now I'm going to fill the tables with the example values 0428 0429 // table1 0430 QTableWidgetItem *titemo = uid.tableWidget->item(0, 0); 0431 titemo->setText(QStringLiteral("A")); 0432 titemo = uid.tableWidget->item(0, 1); 0433 titemo->setText(QStringLiteral("(C*D)/(B*K)")); 0434 titemo = uid.tableWidget->item(1, 0); 0435 titemo->setText(QStringLiteral("K")); 0436 titemo = uid.tableWidget->item(1, 1); 0437 titemo->setText(QStringLiteral("10^-3")); 0438 titemo = uid.tableWidget->item(2, 0); 0439 titemo->setText(QStringLiteral("C")); 0440 titemo = uid.tableWidget->item(2, 1); 0441 titemo->setText(QStringLiteral("OH")); 0442 titemo = uid.tableWidget->item(3, 0); 0443 titemo->setText(QStringLiteral("OH")); 0444 titemo = uid.tableWidget->item(3, 1); 0445 titemo->setText(QStringLiteral("(10^-14)/H")); 0446 titemo = uid.tableWidget->item(4, 0); 0447 titemo->setText(QStringLiteral("H")); 0448 titemo = uid.tableWidget->item(4, 1); 0449 titemo->setText(QStringLiteral("10^-4")); 0450 titemo = uid.tableWidget->item(5, 0); 0451 titemo->setText(QStringLiteral("B")); 0452 titemo = uid.tableWidget->item(5, 1); 0453 titemo->setText(QStringLiteral("6*(10^-2)")); 0454 // xaxis 0455 uid.xaxis->setText(QStringLiteral("D")); 0456 // yaxis 0457 uid.yaxis->setText(QStringLiteral("A")); 0458 // table2 0459 titemo = uid.tableWidget_2->item(0, 0); 0460 titemo->setText(QStringLiteral("7,19")); 0461 titemo = uid.tableWidget_2->item(0, 1); 0462 titemo->setText(QStringLiteral("30")); 0463 titemo = uid.tableWidget_2->item(1, 0); 0464 titemo->setText(QStringLiteral("7,64")); 0465 titemo = uid.tableWidget_2->item(1, 1); 0466 titemo->setText(QStringLiteral("30,5")); 0467 titemo = uid.tableWidget_2->item(2, 0); 0468 titemo->setText(QStringLiteral("10,02")); 0469 titemo = uid.tableWidget_2->item(2, 1); 0470 titemo->setText(QStringLiteral("31")); 0471 titemo = uid.tableWidget_2->item(3, 0); 0472 titemo->setText(QStringLiteral("10,45")); 0473 titemo = uid.tableWidget_2->item(3, 1); 0474 titemo->setText(QStringLiteral("31,5")); 0475 0476 // I think it's better if I don't give so much information here. 0477 // This information could be included into kalzium help, but I don't know how to do 0478 // QMessageBox::information(this, "IceeQt Rapid Help", "There are two ways to use IceeQt:\n\nTheoretical Equations\n Here you can fill the table with the 0479 // equations you have previously obtained for the chemical equilibria. FOR EXAMPLE if you have this reaction A + B -> C + D then you will have the equation 0480 // K=(C*D)/(A*B) so you must write 'K' in the Parameter column and '(C*D)/(A*B)' in the Value column. If you want to assign a known value to a parameter you 0481 // can simply write the numeric value in the Value field. FOR EXAMPLE you can use the system \nA=(C*D)/(B*K) \nK=10^-3 \nC=OH \nOH=(10^-14)/H \nH=10^-4 0482 // \nB=6*(10^-2) \nThen you have to write D as X axis and A as Y axis: so you will find out how the concentration of A change in function of D 0483 // concentration.\nPlease don't use parenthesis for exponents: 10^-3 is correct, while 10^(-3) is wrong. \n\nExperimental Values\n You can use this program 0484 // to draw the plot of your experimental data obtained during a titration and find out the volume of equivalence. It's strongly recommended to insert a even 0485 // number of points, because of the best fit algorithm, sorted by volume (the X axis value).\n\nPlot\n The plot shows in red the curve that comes from 0486 // theoretical equations, in blue the experimental points, and in green the approximated curve for experimental points."); 0487 } 0488 0489 /* 0490 void titrationCalculator::on_actionAbout_triggered() 0491 { 0492 QMessageBox::information(this, "IceeQt About", "I\nCompute\nEquilibria\nExactly\n\nIceeQt is a program for computing chemical equilibria in a easy way. The 0493 first version of Icee was written by Gabriele Balducci(University of Trieste, Italy) using matheval, gnuplot, and tk. This version, called IceeQt, was written 0494 by Luca Tringali using Qtopia. \n IceeQt is installable on every system supported by Qt: Windows, MacOS, GNU/Linux, FreeBSD, Solaris, Symbian, etc..\n This 0495 program is released under GPL3 licence.\n\nThe website is http://web.archive.org/web/20041207065103/http://www.dsch.units.it/~balducci/lca1/"); 0496 } 0497 */ 0498 void titrationCalculator::on_actionNew_triggered() 0499 { 0500 // set all the table cells as empty ("") 0501 for (int i = 0; i < uid.tableWidget->rowCount(); ++i) { 0502 auto titem = new QTableWidgetItem; 0503 titem->setText(QLatin1String("")); 0504 uid.tableWidget->setItem(i, 0, titem); 0505 auto titemo = new QTableWidgetItem; 0506 titemo->setText(QLatin1String("")); 0507 uid.tableWidget->setItem(i, 1, titemo); 0508 } 0509 uid.xaxis->setText(QLatin1String("")); 0510 uid.yaxis->setText(QLatin1String("")); 0511 for (int i = 0; i < uid.tableWidget_2->rowCount(); ++i) { 0512 auto titem = new QTableWidgetItem; 0513 titem->setText(QLatin1String("")); 0514 uid.tableWidget_2->setItem(i, 0, titem); 0515 auto titemo = new QTableWidgetItem; 0516 titemo->setText(QLatin1String("")); 0517 uid.tableWidget_2->setItem(i, 1, titemo); 0518 } 0519 uid.note->setText(QLatin1String("")); 0520 } 0521 0522 void titrationCalculator::on_actionSave_triggered() 0523 { 0524 // save all the cells values 0525 // if we have for example: 0526 // table1: 0527 // |a|f| 0528 // |d|h| 0529 // table2: 0530 // |w|q| 0531 // |h|l| 0532 // then the file would be: 0533 // table1| 0534 // a| 0535 // f| 0536 // d| 0537 // h| 0538 // table2| 0539 // w| 0540 // q| 0541 // h| 0542 // l| 0543 // note| 0544 // ewewewww| 0545 // as you can see we don't save also the empty cells, this is obvious. 0546 0547 QString tempyval; 0548 tempyval = QStringLiteral("table1|"); 0549 for (int i = 0; i < uid.tableWidget->rowCount(); ++i) { 0550 QTableWidgetItem *titem = uid.tableWidget->item(i, 0); 0551 QTableWidgetItem *titemo = uid.tableWidget->item(i, 1); 0552 if (!titem || titem->text().isEmpty()) { 0553 break; 0554 } else { 0555 QString yvaluerq = titemo->data(Qt::DisplayRole).toString(); 0556 QString valuerq = titem->data(Qt::DisplayRole).toString(); 0557 tempyval = tempyval + QChar('\n') + valuerq + QStringLiteral("|\n") + yvaluerq + QChar('|'); 0558 } 0559 } 0560 tempyval = tempyval + QStringLiteral("\nxaxis|"); 0561 tempyval = tempyval + QStringLiteral("\n") + uid.xaxis->text() + QChar('|'); 0562 tempyval = tempyval + QStringLiteral("\nyaxis|"); 0563 tempyval = tempyval + QStringLiteral("\n") + uid.yaxis->text() + QChar('|'); 0564 tempyval = tempyval + QStringLiteral("\ntable2|"); 0565 for (int i = 0; i < uid.tableWidget_2->rowCount(); ++i) { 0566 QTableWidgetItem *titem = uid.tableWidget_2->item(i, 0); 0567 QTableWidgetItem *titemo = uid.tableWidget_2->item(i, 1); 0568 if (!titem || titem->text().isEmpty()) { 0569 break; 0570 } else { 0571 QString yvaluerq = titemo->data(Qt::DisplayRole).toString(); 0572 QString valuerq = titem->data(Qt::DisplayRole).toString(); 0573 tempyval = tempyval + QChar('\n') + valuerq + QStringLiteral("|\n") + yvaluerq + QChar('|'); 0574 } 0575 } 0576 tempyval = tempyval + QStringLiteral("\nnote|\n") + uid.note->toPlainText() + QChar('|'); 0577 0578 QString file = QFileDialog::getSaveFileName(this, i18n("Save work"), QLatin1String(""), i18n("Icee File (*.icee)")); 0579 if (!file.isEmpty()) { 0580 QByteArray ba = tempyval.toLatin1(); 0581 char *strsave = ba.data(); 0582 QByteArray bac = file.toLatin1(); 0583 char *filec = bac.data(); 0584 0585 ofstream out(filec); 0586 cout << "|"; 0587 cout << filec; 0588 cout << "|"; 0589 if (!out) { 0590 QMessageBox::critical(this, i18n("Error"), i18n("Unable to create %1", file)); 0591 } 0592 out << strsave; 0593 out.close(); 0594 // if (out) QMessageBox::information(this, "Information", "File " + file + " successfully saved."); 0595 } 0596 } 0597 0598 void titrationCalculator::on_actionOpen_triggered() 0599 { 0600 // loads all the cells text from a file previously saved 0601 QString file = QFileDialog::getOpenFileName(this, i18n("Open work"), QLatin1String(""), i18n("Icee File (*.icee)")); 0602 if (!file.isEmpty()) { 0603 QByteArray bac = file.toLatin1(); 0604 char *filec = bac.data(); 0605 ifstream texto(filec); 0606 if (!texto) { 0607 QMessageBox::critical(this, i18n("Error"), i18n("Unable to open %1", file)); 0608 } 0609 if (texto) { 0610 on_actionNew_triggered(); 0611 QString tempyval; 0612 char tmpchr; 0613 int i = 0; 0614 int tablea = 0; 0615 int tableb = 0; 0616 int xax = 0; 0617 int yax = 0; 0618 int notea = 0; 0619 do { 0620 texto >> tmpchr; 0621 if (tmpchr != '|') { 0622 tempyval = tempyval + tmpchr; 0623 } else { 0624 if ((tablea == 1) && (tempyval != QStringLiteral("table1")) && (tempyval != QStringLiteral("table2")) 0625 && (tempyval != QStringLiteral("xaxis")) && (tempyval != QStringLiteral("yaxis")) && (tempyval != QStringLiteral("note"))) { 0626 if ((i % 2) != 0) { 0627 QTableWidgetItem *titemo = uid.tableWidget->item((i - 1) / 2, 1); 0628 if (titemo) { 0629 titemo->setText(tempyval); 0630 } 0631 } else { 0632 QTableWidgetItem *titem = uid.tableWidget->item((i / 2), 0); 0633 if (titem) { 0634 titem->setText(tempyval); 0635 } 0636 } 0637 ++i; 0638 } 0639 0640 if ((tableb == 1) && (tempyval != QStringLiteral("table1")) && (tempyval != QStringLiteral("table2")) 0641 && (tempyval != QStringLiteral("xaxis")) && (tempyval != QStringLiteral("yaxis")) && (tempyval != QStringLiteral("note"))) { 0642 if ((i % 2) != 0) { 0643 QTableWidgetItem *titemo = uid.tableWidget_2->item((i - 1) / 2, 1); 0644 if (titemo) { 0645 titemo->setText(tempyval); 0646 } 0647 } else { 0648 QTableWidgetItem *titem = uid.tableWidget_2->item((i / 2), 0); 0649 if (titem) { 0650 titem->setText(tempyval); 0651 } 0652 // cout << i; 0653 } 0654 ++i; 0655 } 0656 if ((xax == 1) && (tempyval != QStringLiteral("table1")) && (tempyval != QStringLiteral("table2")) && (tempyval != QStringLiteral("xaxis")) 0657 && (tempyval != QStringLiteral("yaxis")) && (tempyval != QStringLiteral("note"))) { 0658 uid.xaxis->setText(tempyval); 0659 } 0660 if ((yax == 1) && (tempyval != QStringLiteral("table1")) && (tempyval != QStringLiteral("table2")) && (tempyval != QStringLiteral("xaxis")) 0661 && (tempyval != QStringLiteral("yaxis")) && (tempyval != QStringLiteral("note"))) { 0662 uid.yaxis->setText(tempyval); 0663 } 0664 if ((notea == 1) && (tempyval != QStringLiteral("table1")) && (tempyval != QStringLiteral("table2")) 0665 && (tempyval != QStringLiteral("xaxis")) && (tempyval != QStringLiteral("yaxis")) && (tempyval != QStringLiteral("note"))) { 0666 uid.note->setText(tempyval); 0667 } 0668 0669 if (tempyval == QStringLiteral("table1")) { 0670 i = 0; 0671 tablea = 1; 0672 tableb = 0; 0673 xax = 0; 0674 yax = 0; 0675 notea = 0; 0676 } 0677 if (tempyval == QStringLiteral("table2")) { 0678 i = 0; 0679 tablea = 0; 0680 tableb = 1; 0681 xax = 0; 0682 yax = 0; 0683 notea = 0; 0684 } 0685 if (tempyval == QStringLiteral("xaxis")) { 0686 tablea = 0; 0687 tableb = 0; 0688 xax = 1; 0689 yax = 0; 0690 notea = 0; 0691 } 0692 if (tempyval == QStringLiteral("yaxis")) { 0693 tablea = 0; 0694 tableb = 0; 0695 xax = 0; 0696 yax = 1; 0697 notea = 0; 0698 } 0699 if (tempyval == QStringLiteral("note")) { 0700 tablea = 0; 0701 tableb = 0; 0702 xax = 0; 0703 yax = 0; 0704 notea = 1; 0705 } 0706 tempyval = QLatin1String(""); 0707 } 0708 } while (!texto.eof()); 0709 texto.close(); 0710 } 0711 } 0712 } 0713 0714 void titrationCalculator::on_actionSave_image_triggered() 0715 { 0716 // This function saves the plot into a SVG file 0717 QString svgheader = 0718 R"(<?xml version="1.0" encoding="iso-8859-1" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//Dtd SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/Dtd/svg11.dtd"> <svg width=")" 0719 + QString::number((xmax * 10) + 5) + "\" height=\"" + QString::number((ymax * 10) + 5) 0720 + R"(" version="1.1" xmlns="http://www.w3.org/2000/svg"><polyline points="5,)" + QString::number(ymax * 10) + " " + QString::number((xmax * 10) - 5) 0721 + "," + QString::number(ymax * 10) + " " + QString::number((xmax * 10) - 5) + "," + QString::number((ymax * 10) - 5) + " " + QString::number(xmax * 10) 0722 + "," + QString::number(ymax * 10) + " " + QString::number((xmax * 10) - 5) + "," + QString::number((ymax * 10) + 5) + " " 0723 + QString::number((xmax * 10) - 5) + "," + QString::number(ymax * 10) + R"(" style="stroke:black;fill:none"/> <polyline points="5,)" 0724 + QString::number(ymax * 10) + R"( 5,5 10,5 5,0 0,5 5,5" style="stroke:black;fill:none"/> )"; 0725 QString svgcomplete = svgheader + redplot + greenplot + blueplot + "</svg> "; 0726 0727 QString file = QFileDialog::getSaveFileName(this, i18n("Save plot"), QLatin1String(""), i18n("Svg image (*.svg)")); 0728 if (!file.isEmpty()) { 0729 QByteArray svgt = svgcomplete.toLatin1(); 0730 char *strsave = svgt.data(); 0731 QByteArray ban = file.toLatin1(); 0732 char *filec = ban.data(); 0733 0734 ofstream out(filec); 0735 cout << "|"; 0736 cout << filec; 0737 cout << "|"; 0738 if (!out) { 0739 QMessageBox::critical(this, i18n("Error"), i18n("Unable to create %1", file)); 0740 } 0741 out << strsave; 0742 out.close(); 0743 } 0744 } 0745 0746 #include "moc_titrationCalculator.cpp"