Warning, file /education/kalzium/src/elementdataviewer.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2004, 2005, 2006 Carsten Niehaus <cniehaus@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "elementdataviewer.h" 0007 0008 #include <element.h> 0009 0010 #include "prefs.h" 0011 0012 // Qt-Includes 0013 #include "kalzium_debug.h" 0014 #include <QDialogButtonBox> 0015 #include <QKeyEvent> 0016 #include <QPen> 0017 #include <QPushButton> 0018 #include <QTimer> 0019 #include <QVBoxLayout> 0020 0021 #include <KActionCollection> 0022 #include <KConfig> 0023 #include <KConfigGroup> 0024 #include <KHelpClient> 0025 #include <KPlotAxis> 0026 #include <KPlotObject> 0027 #include <KStandardAction> 0028 #include <KUnitConversion/Converter> 0029 0030 AxisData::AxisData(AXISTYPE type) 0031 : currentDataType(-1) 0032 { 0033 m_type = type; 0034 } 0035 0036 double AxisData::value(int element) const 0037 { 0038 if ((element < 1) || (element > dataList.count())) { 0039 return 0.0; 0040 } 0041 0042 return dataList[element - 1]; 0043 } 0044 0045 ElementDataViewer::ElementDataViewer(QWidget *parent) 0046 : QDialog(parent) 0047 , m_yData(new AxisData(AxisData::Y)) 0048 , m_xData(new AxisData(AxisData::X)) 0049 { 0050 setWindowTitle(i18nc("@title:window", "Plot Data")); 0051 auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Close); 0052 auto mainWidget = new QWidget(this); 0053 auto mainLayout = new QVBoxLayout(this); 0054 mainLayout->addWidget(mainWidget); 0055 connect(buttonBox, &QDialogButtonBox::accepted, this, &ElementDataViewer::accept); 0056 connect(buttonBox, &QDialogButtonBox::rejected, this, &ElementDataViewer::reject); 0057 mainLayout->addWidget(buttonBox); 0058 buttonBox->button(QDialogButtonBox::Close)->setDefault(true); 0059 0060 KalziumDataObject *kdo = KalziumDataObject::instance(); 0061 0062 ui.setupUi(mainWidget); 0063 0064 m_timer = new QTimer(this); 0065 m_timer->setSingleShot(true); 0066 0067 // setup the list of names 0068 foreach (Element *e, kdo->ElementList) { 0069 names << e->dataAsString(ChemicalDataObject::name); 0070 symbols << e->dataAsString(ChemicalDataObject::symbol); 0071 elecConfig << e->dataAsString(ChemicalDataObject::electronicConfiguration); 0072 block << e->dataAsString(ChemicalDataObject::periodTableBlock); 0073 } 0074 0075 m_actionCollection = new KActionCollection(this); 0076 KStandardAction::quit(this, SLOT(close()), m_actionCollection); 0077 0078 connect(m_timer, &QTimer::timeout, this, &ElementDataViewer::drawPlot); 0079 connect(ui.KCB_y, QOverload<int>::of(&KComboBox::activated), this, &ElementDataViewer::rangeChanged); 0080 connect(ui.KCB_x, QOverload<int>::of(&KComboBox::activated), this, &ElementDataViewer::rangeChanged); 0081 connect(ui.comboElementLabels, QOverload<int>::of(&KComboBox::activated), this, &ElementDataViewer::rangeChanged); 0082 connect(ui.comboElementType, QOverload<int>::of(&KComboBox::activated), this, &ElementDataViewer::rangeChanged); 0083 connect(ui.from, QOverload<int>::of(&QSpinBox::valueChanged), this, &ElementDataViewer::rangeChanged); 0084 connect(ui.to, QOverload<int>::of(&QSpinBox::valueChanged), this, &ElementDataViewer::rangeChanged); 0085 connect(buttonBox->button(QDialogButtonBox::Help), &QPushButton::clicked, this, &ElementDataViewer::slotHelp); 0086 connect(ui.full, &QPushButton::clicked, this, &ElementDataViewer::fullRange); 0087 connect(ui.swapXYAxis, &QPushButton::clicked, this, &ElementDataViewer::swapXYAxis); 0088 drawPlot(); 0089 0090 resize(650, 500); 0091 } 0092 0093 ElementDataViewer::~ElementDataViewer() 0094 { 0095 delete m_yData; 0096 delete m_xData; 0097 } 0098 0099 void ElementDataViewer::slotHelp() 0100 { 0101 KHelpClient::invokeHelp(QStringLiteral("tools.html#plot_data"), QStringLiteral("kalzium")); 0102 } 0103 0104 void ElementDataViewer::rangeChanged() 0105 { 0106 if (ui.from->value() > ui.to->value()) { 0107 ui.to->setValue(ui.from->value()); 0108 } 0109 0110 m_timer->stop(); 0111 m_timer->start(500); 0112 } 0113 0114 void ElementDataViewer::fullRange() 0115 { 0116 ui.from->setValue(1); 0117 ui.to->setValue(116); 0118 } 0119 0120 void ElementDataViewer::swapXYAxis() 0121 { 0122 int x = ui.KCB_x->currentIndex(); 0123 int y = ui.KCB_y->currentIndex(); 0124 0125 ui.KCB_x->setCurrentIndex(y); 0126 ui.KCB_y->setCurrentIndex(x); 0127 0128 rangeChanged(); 0129 } 0130 0131 void ElementDataViewer::setLimits() 0132 { 0133 qCDebug(KALZIUM_LOG) << "ElementDataViewer::setLimits()"; 0134 0135 double x1 = 0.0, x2 = 0.0, y1 = 0.0, y2 = 0.0; 0136 0137 getMinMax(x1, x2, m_xData); 0138 getMinMax(y1, y2, m_yData); 0139 0140 qCDebug(KALZIUM_LOG) << x1 << " :: " << x2 << " ----- " << y1 << " :: " << y2; 0141 0142 // JH: add some padding to show all points 0143 double dx = 0.05 * (x2 - x1); 0144 double dy = 0.05 * (y2 - y1); 0145 x1 -= dx; 0146 x2 += dx; 0147 y1 -= dy; 0148 y2 += dy; 0149 0150 // X // try to put a small padding to make the points on the axis visible 0151 // X double dx = (to - from + 1) / 25 + 1.0; 0152 // X double dy = (maxY - minY) / 10.0; 0153 // X // in case that dy is quite small (for example, when plotting a single 0154 // X // point) 0155 // X if (dy < 1e-7) 0156 // X dy = maxY / 10.0; 0157 // X ui.plotwidget->setLimits(from - dx, to + dx, minY - dy, maxY + dy); 0158 0159 ui.plotwidget->setLimits(x1, x2, y1, y2); 0160 } 0161 0162 void ElementDataViewer::getMinMax(double &min, double &max, AxisData *data) 0163 { 0164 int firstElement = ui.from->value(); 0165 int lastElement = ui.to->value(); 0166 0167 double minValue = data->value(firstElement); 0168 double maxValue = data->value(firstElement); 0169 0170 qCDebug(KALZIUM_LOG) << "Taking elements from " << firstElement << " to " << lastElement; 0171 0172 for (int _currentVal = firstElement; _currentVal <= lastElement; ++_currentVal) { // go over all selected elements 0173 double v = data->value(_currentVal); 0174 0175 if (minValue > v) { 0176 minValue = v; 0177 } 0178 if (maxValue < v) { 0179 maxValue = v; 0180 } 0181 } 0182 0183 qCDebug(KALZIUM_LOG) << "The value are ]" << minValue << " , " << maxValue << "[."; 0184 0185 min = minValue; 0186 max = maxValue; 0187 } 0188 0189 void ElementDataViewer::keyPressEvent(QKeyEvent *e) 0190 { 0191 switch (e->key()) { 0192 case Qt::Key_Plus: 0193 case Qt::Key_Equal: 0194 slotZoomIn(); 0195 break; 0196 case Qt::Key_Minus: 0197 case Qt::Key_Underscore: 0198 slotZoomOut(); 0199 break; 0200 case Qt::Key_Escape: 0201 close(); 0202 break; 0203 } 0204 } 0205 0206 void ElementDataViewer::slotZoomIn() 0207 { 0208 } 0209 void ElementDataViewer::slotZoomOut() 0210 { 0211 } 0212 0213 void ElementDataViewer::setupAxisData(AxisData *data) 0214 { 0215 int selectedData = 0; 0216 if (data->type() == AxisData::X) { 0217 selectedData = ui.KCB_x->currentIndex(); 0218 } else { 0219 selectedData = ui.KCB_y->currentIndex(); 0220 } 0221 0222 data->currentDataType = selectedData; 0223 0224 // init to _something_ 0225 ChemicalDataObject::BlueObelisk kind = ChemicalDataObject::mass; 0226 QString caption; 0227 int unit = KUnitConversion::NoUnit; 0228 0229 switch (selectedData) { 0230 case AxisData::NUMBER: { 0231 kind = ChemicalDataObject::atomicNumber; 0232 caption = i18n("Atomic Number"); 0233 break; 0234 } 0235 case AxisData::MASS: { 0236 kind = ChemicalDataObject::mass; 0237 caption = i18n("Atomic Mass"); 0238 break; 0239 } 0240 case AxisData::EN: { 0241 kind = ChemicalDataObject::electronegativityPauling; 0242 caption = i18n("Electronegativity"); 0243 break; 0244 } 0245 case AxisData::MELTINGPOINT: { 0246 kind = ChemicalDataObject::meltingpoint; 0247 caption = i18n("Melting Point"); 0248 unit = Prefs::temperatureUnit(); 0249 break; 0250 } 0251 case AxisData::BOILINGPOINT: { 0252 kind = ChemicalDataObject::boilingpoint; 0253 caption = i18n("Boiling Point"); 0254 unit = Prefs::temperatureUnit(); 0255 break; 0256 } 0257 case AxisData::ATOMICRADIUS: { 0258 kind = ChemicalDataObject::radiusVDW; 0259 caption = i18n("Atomic Radius"); 0260 unit = Prefs::lengthUnit(); 0261 break; 0262 } 0263 case AxisData::COVALENTRADIUS: { 0264 kind = ChemicalDataObject::radiusCovalent; 0265 caption = i18n("Covalent Radius"); 0266 unit = Prefs::lengthUnit(); 0267 break; 0268 } 0269 } 0270 KalziumDataObject *kdo = KalziumDataObject::instance(); 0271 0272 DoubleList dblDataList; 0273 foreach (Element *element, kdo->ElementList) { 0274 dblDataList << element->dataAsVariant(kind, unit).toDouble(); 0275 } 0276 0277 data->dataList.clear(); 0278 data->dataList << dblDataList; 0279 data->kind = kind; 0280 0281 if (unit != KUnitConversion::NoUnit) { 0282 QString stringUnit = KalziumDataObject::instance()->unitAsString(unit); 0283 data->unit = QString(' ' + stringUnit); 0284 0285 caption.append(" ["); 0286 caption.append(stringUnit); 0287 caption.append(']'); 0288 } 0289 0290 if (data->type() == AxisData::X) { 0291 ui.plotwidget->axis(KPlotWidget::BottomAxis)->setLabel(caption); 0292 } else { 0293 ui.plotwidget->axis(KPlotWidget::LeftAxis)->setLabel(caption); 0294 ui.plotwidget->axis(KPlotWidget::RightAxis)->setLabel(caption); 0295 } 0296 } 0297 0298 void ElementDataViewer::drawPlot() 0299 { 0300 /* 0301 * to be 100% safe delete the old list 0302 */ 0303 ui.plotwidget->removeAllPlotObjects(); 0304 0305 /* 0306 * spare the next step in case everything is already set and done 0307 */ 0308 if (m_yData->currentDataType != ui.KCB_y->currentIndex()) { 0309 initData(); 0310 } 0311 0312 if (m_xData->currentDataType != ui.KCB_x->currentIndex()) { 0313 initData(); 0314 } 0315 0316 /* 0317 * if the user selected the elements 20 to 30 the list-values are 19 to 29!!! 0318 */ 0319 const int tmpfrom = ui.from->value(); 0320 const int tmpto = ui.to->value(); 0321 const int from = qMin(tmpfrom, tmpto); 0322 const int to = qMax(tmpfrom, tmpto); 0323 0324 /* 0325 * The number of elements. #20 to 30 are 30-20+1=11 Elements 0326 */ 0327 int num = to - from + 1; 0328 0329 setLimits(); 0330 0331 QSet<int> metals, metalloids, nonMetals; 0332 0333 metals << 3 << 4 << 11 << 12 << 13; 0334 for (int i = 19; i <= 31; ++i) { 0335 metals << i; 0336 } 0337 for (int i = 37; i <= 50; ++i) { 0338 metals << i; 0339 } 0340 for (int i = 55; i <= 83; ++i) { 0341 metals << i; 0342 } 0343 for (int i = 87; i <= 116; ++i) { 0344 metals << i; 0345 } 0346 0347 metalloids << 5 << 14 << 32 << 33 << 51 << 52 << 84 << 85; 0348 0349 nonMetals << 1 << 2 << 6 << 7 << 8 << 9 << 10 << 15 << 16; 0350 nonMetals << 17 << 18 << 34 << 35 << 36 << 53 << 54 << 86; 0351 0352 /* 0353 * check if the users wants to see the elementnames or not 0354 */ 0355 int whatShow = ui.comboElementLabels->currentIndex(); 0356 0357 /* 0358 * Checks what type of element, the user wants to plot. 0359 * example, metal, non-metal. 0360 */ 0361 int whichType = ui.comboElementType->currentIndex(); 0362 0363 KPlotObject *dataPointGreen = nullptr; 0364 KPlotObject *dataPointRed = nullptr; 0365 0366 double av_x = 0.0; 0367 double max_x = m_xData->value(from); 0368 double min_x = m_xData->value(from); 0369 double av_y = 0.0; 0370 double max_y = m_yData->value(from); 0371 double min_y = m_yData->value(from); 0372 0373 /* 0374 * iterate for example from element 20 to 30 and construct 0375 * the KPlotObjects 0376 */ 0377 dataPointGreen = new KPlotObject(Qt::green, KPlotObject::Points, 4, KPlotObject::Star); 0378 dataPointGreen->setLabelPen(QPen(Qt::blue)); 0379 0380 dataPointRed = new KPlotObject(Qt::red, KPlotObject::Points, 4, 0381 KPlotObject::Star); // Star can be replaced with a cross 0382 dataPointRed->setLabelPen(QPen(Qt::blue)); 0383 0384 for (int i = from; i < to + 1; ++i) { 0385 double value_y = m_yData->value(i); 0386 double value_x = m_xData->value(i); 0387 0388 bool known = ((value_y) > 0.0) ? true : false; 0389 // The element is know if its value is not zero 0390 bool belongs = true; 0391 // The value of belongs is one if it belongs to the particular group 0392 0393 // See if the particular element belongs to the selected set or not. 0394 // If a particular group of elements is selected, 0395 if (whichType > 0) { 0396 belongs = false; 0397 switch (whichType) { 0398 case 1: // Plot only metals 0399 belongs = metals.contains(i); 0400 break; 0401 case 2: // plot only nonmetals and metalloids 0402 belongs = (nonMetals.contains(i) || metalloids.contains(i)); 0403 break; 0404 case 3: // Plot s block elements 0405 belongs = (block[i - 1] == QLatin1String("s")); 0406 break; 0407 case 4: // Plot p block elements 0408 belongs = (block[i - 1] == QLatin1String("p")); 0409 break; 0410 case 5: // Plot d block elements 0411 belongs = (block[i - 1] == QLatin1String("d")); 0412 break; 0413 case 6: // plot f block elements 0414 belongs = (block[i - 1] == QLatin1String("f")); 0415 break; 0416 case 7: // Noble gases 0417 belongs = ((elecConfig[i - 1]).endsWith(QLatin1String("p6"))); 0418 belongs |= (i == 2); // Include Helium 0419 break; 0420 case 8: // Alkalie metals 0421 belongs = ((elecConfig[i - 1]).endsWith(QLatin1String("s1"))); 0422 belongs &= (block[i - 1] == QLatin1String("s")); // exclude chromium 0423 belongs &= (i != 1); // exclude Hydrogen 0424 break; 0425 case 9: // Alkaline earth metals 0426 belongs = ((elecConfig[i - 1]).endsWith(QLatin1String("s2"))); 0427 belongs &= (block[i - 1] == QLatin1String("s")); // exclude chromium 0428 belongs &= (i != 2); // exclude Helium 0429 break; 0430 case 10: // Lanthanides 0431 // If element i is an f block element, with 0432 // electronic configuration containing "f4" in it 0433 // or the element is Lanthanum 0434 belongs = ((block[i - 1] == QLatin1String("f")) && ((elecConfig[i - 1]).contains(QLatin1String("4f")))) || (i == 57); // Lanthanum 57 0435 break; 0436 case 11: // Actinides 0437 // If element i is an f block element, with 0438 // electronic configuration containing "f5" in it 0439 // or the element is Actinium 0440 belongs = (((block[i - 1] == QLatin1String("f"))) && ((elecConfig[i - 1]).contains(QLatin1String("5f")))) || (i == 89); // Actinium 89 0441 break; 0442 case 12: // Radio active 0443 belongs = ((i == 43) || (i == 61) || (i > 84)); 0444 // Technitium prothomium and then polonium onwards. 0445 break; 0446 default: 0447 whichType = 0; 0448 belongs = true; 0449 } 0450 } 0451 if (belongs) { 0452 if (known) { 0453 av_x += value_x; 0454 av_y += value_y; 0455 0456 if (value_x > max_x) { 0457 max_x = value_x; 0458 } 0459 if (value_y > max_y) { 0460 max_y = value_y; 0461 } 0462 if (value_x < min_x) { 0463 min_x = value_x; 0464 } 0465 if (value_y < min_y) { 0466 min_y = value_y; 0467 } 0468 0469 QString lbl; 0470 if (whatShow > 0) { // The users wants to see the labels 0471 lbl = whatShow == 1 ? names[i - 1] : symbols[i - 1]; 0472 } 0473 0474 dataPointGreen->addPoint(value_x, value_y, lbl); 0475 } else { // unknown value 0476 // num is required while finding the average, if an element is not 0477 // known it should not contribute to the average. 0478 --num; 0479 0480 QString lbl; 0481 if (whatShow > 0) { // The user wants to see the labels 0482 lbl = whatShow == 1 ? names[i - 1] : symbols[i - 1]; 0483 } 0484 0485 dataPointRed->addPoint(value_x, value_y, lbl); 0486 // For an Unknown value, use a red point to mark the data-point. 0487 } 0488 } else { // The element does not belong to the set 0489 // num is required while finding average, if an element is 0490 // not in the selected set, it should not contribute to the avg. 0491 --num; 0492 } 0493 } 0494 0495 ui.plotwidget->addPlotObject(dataPointGreen); 0496 ui.plotwidget->addPlotObject(dataPointRed); 0497 0498 if (num > 0) { 0499 // now set the values for the min, max and average value 0500 ui.av_x->setText(QString::number(av_x / num).append(m_xData->unit)); 0501 ui.minimum_x->setText(QString::number(min_x).append(m_xData->unit)); 0502 ui.maximum_x->setText(QString::number(max_x).append(m_xData->unit)); 0503 0504 ui.av_y->setText(QString::number(av_y / num).append(m_yData->unit)); 0505 ui.minimum_y->setText(QString::number(min_y).append(m_yData->unit)); 0506 ui.maximum_y->setText(QString::number(max_y).append(m_yData->unit)); 0507 } else { 0508 ui.av_x->setText(QString::number(0.0)); 0509 ui.minimum_x->setText(QString::number(0.0)); 0510 ui.maximum_x->setText(QString::number(0.0)); 0511 ui.av_y->setText(QString::number(0.0)); 0512 ui.minimum_y->setText(QString::number(0.0)); 0513 ui.maximum_y->setText(QString::number(0.0)); 0514 } 0515 } 0516 0517 void ElementDataViewer::initData() 0518 { 0519 setupAxisData(m_xData); 0520 setupAxisData(m_yData); 0521 }