1
/***************************************************************************
2
copyright : (C) 2009 by Kashyap R Puranik
3
email : kashthealien@gmail.com
4
***************************************************************************/
5
/***************************************************************************
7
* This program is free software; you can redistribute it and/or modify *
8
* it under the terms of the GNU General Public License as published by *
9
* the Free Software Foundation; either version 2 of the License, or *
10
* (at your option) any later version. *
12
***************************************************************************/
13
#include "nuclearCalculator.h"
15
#include <QFontMetrics>
16
#include <QApplication>
17
#include <QGridLayout>
18
#include <QPushButton>
19
#include <QRadioButton>
23
#include <KIntSpinBox>
27
#include <Plasma/ComboBox>
28
#include <Plasma/LineEdit>
29
#include <Plasma/Label>
30
#include <Plasma/Frame>
31
#include <Plasma/RadioButton>
32
#include <Plasma/SpinBox>
33
#include <Plasma/Slider>
34
#include <Plasma/GroupBox>
35
#include <Plasma/PushButton>
36
#include <QGraphicsGridLayout>
37
#include <QGraphicsLinearLayout>
38
#include <plasma/svg.h>
39
#include <plasma/theme.h>
42
#include <KConfigDialog>
44
using namespace KUnitConversion;
46
nuclearCalculator::nuclearCalculator(QObject *parent, const QVariantList &args)
47
: Plasma::PopupApplet(parent, args)
50
KGlobal::locale()->insertCatalog( "libkdeedu" );
51
m_converter = new Converter( this );
52
setAspectRatioMode(Plasma::IgnoreAspectRatio);
53
setPopupIcon("accessories-calculator");
54
setHasConfigurationInterface(true);
55
setAssociatedApplication("kalzium");
59
nuclearCalculator::~nuclearCalculator()
61
if (hasFailedToLaunch()) {
62
// Do some cleanup here
68
void nuclearCalculator::init()
73
void nuclearCalculator::configChanged()
75
KConfigGroup cg = config();
77
m_massOnly = cg.readEntry("massOnly",true);
80
void nuclearCalculator::reset()
82
const int ISOTOPE_NUM = 22;
83
error(RESET_NUKE_MESG);
85
// Add all isotope names of Uranium ( by default )to the isotope comboBox
86
QList<Isotope*> list = KalziumDataObject::instance()->isotopes(92);
90
foreach(Isotope * i , list) {
91
iso.setNum(i->mass());
92
m_isotope->addItem(iso);
95
// initialize the data, initially selected values ( Uranium, 92, 238)
96
m_element->nativeWidget()-> setCurrentIndex(91);
97
m_isotope->nativeWidget()-> setCurrentIndex(ISOTOPE_NUM);
98
m_halfLife->setValue(list.at(ISOTOPE_NUM) -> halflife());
99
m_initAmt->setValue(6.0);
100
m_finalAmt->setValue(3.0);
101
m_time->setValue(list.at(ISOTOPE_NUM)->halflife());
103
m_halfLifeUnit->nativeWidget()->setCurrentIndex(0);
104
m_initType->nativeWidget()->setCurrentIndex(0);
105
m_finalType->nativeWidget()->setCurrentIndex(0);
106
m_initUnit->nativeWidget()->setCurrentIndex(0);
107
m_finalUnit->nativeWidget()->setCurrentIndex(0);
108
m_timeUnit->nativeWidget()->setCurrentIndex(0);
109
m_calculationMode ->nativeWidget()->setCurrentIndex(2);
111
// Setup of the UI done
113
m_InitAmount = Value(6.0, "g") ;
114
m_FinalAmount = Value(3.0, "g");
115
m_Mass = list.at(ISOTOPE_NUM) -> mass();
116
m_Time = Value((list.at(ISOTOPE_NUM) -> halflife()), "y");
117
m_HalfLife = Value(list.at(ISOTOPE_NUM) -> halflife(), "y");
119
m_Element = * KalziumDataObject::instance() -> element(92);
120
m_Isotope = * list.at(ISOTOPE_NUM);
123
// Initialization of values done
126
QGraphicsWidget *nuclearCalculator::graphicsWidget()
129
// Currently the spin boxes are integer, please convert them into double after
130
// doubleSpinBoxes are available
133
// Position all UI elements
134
m_widget = new QGraphicsWidget(this);
135
Plasma::Frame *pHeader = new Plasma::Frame(this);
136
pHeader->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
137
pHeader->setText(i18n("Nuclear Calculator"));
139
Plasma::GroupBox *pGroupBox1 = new Plasma::GroupBox(this);
140
Plasma::GroupBox *pGroupBox2 = new Plasma::GroupBox(this);
142
QGraphicsGridLayout *pGridLayout = new QGraphicsGridLayout(pGroupBox1);
143
QGraphicsGridLayout *pGridLayout2 = new QGraphicsGridLayout(pGroupBox2);
144
QGraphicsLinearLayout *pVLayout = new QGraphicsLinearLayout(Qt::Vertical,m_widget);
145
pVLayout->addItem(pGroupBox1);
146
pVLayout->addItem(pGroupBox2);
147
// pVLayout->addItem(new Plasma::)
149
// here comes the element - isotope and halfLife info part
150
Plasma::Label *eleLabel = new Plasma::Label(this);
151
eleLabel->nativeWidget()->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
152
eleLabel->setText(i18n("Element name:"));
153
Plasma::Label *isoLabel = new Plasma::Label(this);
154
isoLabel->nativeWidget()->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
155
isoLabel->setText(i18n("Isotope mass:"));
156
Plasma::Label *hLifeLabel = new Plasma::Label(this);
157
hLifeLabel->nativeWidget()->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
158
hLifeLabel->setText(i18n("Half-Life"));
160
m_element = new Plasma::ComboBox(this);
161
m_element->setZValue(2);
162
m_isotope = new Plasma::ComboBox(this);
163
m_isotope->setZValue(1);
165
m_halfLife = new Plasma::SpinBox(this);
166
m_halfLife->nativeWidget()->setMinimumWidth(100);
167
m_halfLife->nativeWidget()->setMaximum(1000000000);
168
//m_halfLife->setDecimals(4);
169
m_halfLife->setMaximum(1e+09);
171
m_halfLifeUnit = new Plasma::ComboBox(this);
172
m_halfLifeUnit->nativeWidget()->insertItems(0, QStringList()
179
m_halfLifeUnit->setZValue(6);
181
pGridLayout->addItem(pHeader, 0, 0, 1, 4);
182
pGridLayout->addItem(eleLabel, 1, 0);
183
pGridLayout->addItem(m_element, 1, 1);
184
pGridLayout->addItem(isoLabel, 2, 0);
185
pGridLayout->addItem(m_isotope, 2, 1);
186
pGridLayout->addItem(hLifeLabel, 3, 0);
187
pGridLayout->addItem(m_halfLifeUnit, 3, 2);
188
pGridLayout->addItem(m_halfLife, 3, 1);
190
// Here comes the amount and time part
193
Plasma::Label *calcModeLabel = new Plasma::Label(this);
194
calcModeLabel->nativeWidget()->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
195
calcModeLabel->setText(i18n("Calculation Mode:"));
197
m_calculationMode = new Plasma::ComboBox(this);
198
m_calculationMode->setZValue(3);
199
m_calculationMode->nativeWidget()->insertItems(0, QStringList()
200
<< i18n("Initial amount")
201
<< i18n("Final amount")
205
Plasma::Label *initLabel = new Plasma::Label(this);
206
initLabel->nativeWidget()->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
207
initLabel->setText(i18n("Initial amount:"));
208
Plasma::Label *finalLabel = new Plasma::Label(this);
209
finalLabel->nativeWidget()->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
210
finalLabel->setText(i18n("Final amount:"));
211
Plasma::Label *timeLabel = new Plasma::Label(this);
212
timeLabel->nativeWidget()->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
213
timeLabel->setText(i18n("Time"));
214
/*x Plasma::Label *m_sliderLabel = new Plasma::Label(this);
215
m_sliderLabel->nativeWidget()->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
216
m_sliderLabel->setText(i18n("Time in Half-Lives"));*/
218
m_numHalfLives = new Plasma::Label(this);
219
m_numHalfLives->nativeWidget()->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
220
m_numHalfLives->setText(i18n("0 seconds"));
221
m_initAmt = new Plasma::SpinBox(this);
222
m_initAmt->nativeWidget()->setMinimumWidth(200);
223
m_initAmt->nativeWidget()->setMaximum(1000000000);
224
//m_initAmt->setDecimals(4);
225
m_initAmt->setMaximum(1e+09);
226
m_finalAmt = new Plasma::SpinBox(this);
227
m_finalAmt->nativeWidget()->setMinimumWidth(200);
228
m_finalAmt->nativeWidget()->setMaximum(1000000000);
229
//m_finalAmt->setDecimals(4);
230
m_finalAmt->setMaximum(1e+09);
231
m_time = new Plasma::SpinBox(this);
232
m_time->nativeWidget()->setMinimumWidth(200);
233
m_time->nativeWidget()->setMaximum(1000000000);
234
//m_time->setDecimals(4);
235
m_time->setMaximum(1e+09);
237
m_initUnit = new Plasma::ComboBox(this);
238
m_initUnit->setZValue(2);
239
m_initUnit->nativeWidget()->insertItems(0, QStringList()
245
<< i18n("troy ounces"));
246
m_initUnit->setZValue(3);
248
m_finalUnit = new Plasma::ComboBox(this);
249
m_finalUnit->setZValue(2);
250
m_finalUnit->nativeWidget()->insertItems(0, QStringList()
256
<< i18n("troy ounces"));
257
m_finalUnit->setZValue(2);
259
m_timeUnit = new Plasma::ComboBox(this);
260
m_timeUnit->setZValue(2);
261
m_timeUnit->nativeWidget()->insertItems(0, QStringList()
268
m_timeUnit->setZValue(1);
270
m_initType = new Plasma::ComboBox(this);
271
m_initType->setZValue(2);
272
m_initType->nativeWidget()->insertItems(0, QStringList()
275
m_initType->setZValue(2);
277
m_finalType = new Plasma::ComboBox(this);
278
m_finalType->setZValue(2);
279
m_finalType->nativeWidget()->insertItems(0, QStringList()
282
m_finalType->setZValue(1);
284
/*m_slider = new Plasma::Slider(this);
285
m_slider->setRange(0, 100);
286
m_slider->setOrientation(Qt::Horizontal);
287
m_slider->setMaximum(100); */
289
m_error = new Plasma::Label(this);
291
m_reset = new Plasma::PushButton(this);
292
m_reset->setText(i18n("Reset"));
294
pGridLayout2->addItem(calcModeLabel, 5, 0);
295
pGridLayout2->addItem(initLabel, 6, 0);
296
pGridLayout2->addItem(finalLabel, 7, 0);
297
pGridLayout2->addItem(timeLabel, 8, 0);
298
// pGridLayout2->addItem(m_sliderLabel, 9, 0);
299
pGridLayout2->addItem(m_error, 10, 1, 1, 3);
300
pGridLayout2->addItem(m_reset, 10, 0);
302
pGridLayout2->addItem(m_calculationMode, 5, 1);
303
pGridLayout2->addItem(m_initAmt, 6, 1);
304
pGridLayout2->addItem(m_finalAmt, 7, 1);
305
pGridLayout2->addItem(m_time, 8, 1);
306
// pGridLayout2->addItem(m_slider , 9, 1);
308
pGridLayout2->addItem(m_initType, 6, 3);
309
pGridLayout2->addItem(m_finalType, 7, 3);
310
pGridLayout2->addItem(m_numHalfLives, 9, 2);
312
pGridLayout2->addItem(m_initUnit, 6, 2);
313
pGridLayout2->addItem(m_finalUnit, 7, 2);
314
pGridLayout2->addItem(m_timeUnit, 8, 2);
316
// Positioning of UI elements done
317
// Now add required properties to the UI widgets
319
/**************************************************************************/
320
// Nuclear Calculator set up //
321
/**************************************************************************/
322
KalziumDataObject *kdo = KalziumDataObject::instance();
324
// add all element names to the comboBox in the user interface
325
foreach(Element * e, kdo -> ElementList) {
326
m_element->nativeWidget()->addItem(e -> dataAsString(ChemicalDataObject::name));
329
/* The last three elements will be removed because information is not available
330
and causes the program to crash when selected. */
331
int count = m_element->nativeWidget()->count();
332
m_element->nativeWidget()->removeItem(count - 1);
333
m_element->nativeWidget()->removeItem(count - 2);
334
m_element->nativeWidget()->removeItem(count - 3);
335
// Add all isotope names of Uranium ( by default )to the isotope comboBox
338
// Connect signals with slots
339
connect(m_element->nativeWidget(), SIGNAL(activated(int)),
340
this, SLOT(elementChanged(int)));
341
connect(m_isotope->nativeWidget(), SIGNAL(activated(int)),
342
this, SLOT(isotopeChanged(int)));
343
//FIXME change int to double in the following signals after finding doubleSpinBox
344
connect(m_halfLife, SIGNAL(valueChanged(int)),
345
this, SLOT(halfLifeChanged()));
346
connect(m_halfLifeUnit->nativeWidget(), SIGNAL(activated(int)),
347
this, SLOT(halfLifeChanged()));
348
connect(m_initAmt, SIGNAL(valueChanged(int)),
349
this, SLOT(initAmtChanged()));
350
connect(m_initUnit->nativeWidget(), SIGNAL(activated(int)),
351
this, SLOT(initAmtChanged()));
352
connect(m_initType->nativeWidget(), SIGNAL(activated(int)),
353
this, SLOT(initAmtChanged()));
354
connect(m_finalAmt, SIGNAL(valueChanged(int)),
355
this, SLOT(finalAmtChanged()));
356
connect(m_finalUnit->nativeWidget(), SIGNAL(activated(int)),
357
this, SLOT(finalAmtChanged()));
358
connect(m_finalType->nativeWidget(), SIGNAL(activated(int)),
359
this, SLOT(finalAmtChanged()));
360
connect(m_time, SIGNAL(valueChanged(int)),
361
this, SLOT(timeChanged()));
362
connect(m_timeUnit->nativeWidget(), SIGNAL(activated(int)),
363
this, SLOT(timeChanged()));
364
/* connect(m_slider, SIGNAL(valueChanged(int)),
365
this, SLOT(sliderMoved(int)));*/
366
connect(m_calculationMode->nativeWidget(), SIGNAL(activated(int)),
367
this, SLOT(setMode(int)));
368
connect(m_reset, SIGNAL(clicked()),
369
this, SLOT(reset()));
370
/**************************************************************************/
371
// Nuclear Calculator setup complete
372
/**************************************************************************/
377
void nuclearCalculator::elementChanged (int index)
379
// set the newly chosen element
380
m_Element = * KalziumDataObject::instance()-> element(index + 1);
382
// Add all isotope names of Uranium ( by default ) to the isotope comboBox
383
QList<Isotope*> list = KalziumDataObject::instance()-> isotopes(index + 1);
384
QString isotope; // A temporary string
385
m_isotope -> clear(); // Clear the contents of the combo box
386
// update the combobox with isotopes of the new element
387
foreach(Isotope * i , list) {
388
isotope.setNum(i -> mass());
389
m_isotope-> addItem(isotope);
392
// Set the halfLife to that of the first isotope of the element.
393
m_halfLife-> setValue(list. at(0)-> halflife());
394
// Recalculate and update
398
void nuclearCalculator::isotopeChanged(int index)
400
// update the nuclear Calculator
401
int elementNumber = m_element->nativeWidget()-> currentIndex() + 1;
402
QList<Isotope*> list = KalziumDataObject::instance() -> isotopes(elementNumber);
403
m_Isotope = * list.at(index);
405
// get the halfLife of the new isotope
406
double halfLife = list . at(index) -> halflife();
407
m_Mass = list.at(index)-> mass();
409
// A string used for searching the right Unit
410
QString halfLifeUnit = list . at(index) -> halflifeUnit();
411
halfLifeUnit = (halfLifeUnit == "y") ? "year" : "seconds";
413
// Update the UI with the halfLife value
414
m_halfLife-> setValue(halfLife);
415
int x = m_halfLifeUnit->nativeWidget()-> findText(halfLifeUnit);
417
m_halfLifeUnit->nativeWidget()-> setCurrentIndex(x);
418
m_HalfLife = Value(halfLife, halfLifeUnit);
419
// Recalculate and update
423
void nuclearCalculator::halfLifeChanged()
425
// update the halfLife value
426
m_HalfLife = Value(m_halfLife -> value(), m_halfLifeUnit->nativeWidget()-> currentText());
427
// recalculate the required
431
void nuclearCalculator::initAmtChanged()
434
// If quantity is specified in terms of mass, quantity <- ( mass , Unit)
435
if (m_initType->nativeWidget() -> currentIndex() == 0)
436
m_InitAmount = Value(m_initAmt -> value(), m_initUnit->nativeWidget() -> currentText());
438
// If quantity is specified in terms of moles quantity <- ( moles * atomicMass, Unit )
440
m_InitAmount = Value(((m_initAmt -> value()) * m_Mass), \
441
m_initUnit->nativeWidget()-> currentText());
446
void nuclearCalculator::finalAmtChanged()
448
// If quantity is specified in terms of mass, quantity <- ( mass , Unit)
449
if (m_finalType->nativeWidget() -> currentIndex() == 0) {
450
m_FinalAmount = Value(m_finalAmt -> value(), \
451
m_finalUnit->nativeWidget()-> currentText());
452
// If quantity is specified in terms of moles quantity <- ( moles * atomicMass, Unit )
454
m_FinalAmount = Value(((m_finalAmt -> value()) * m_Mass), \
455
m_finalUnit->nativeWidget() -> currentText());
461
void nuclearCalculator::timeChanged()
463
m_Time = Value(m_time-> value(), m_timeUnit->nativeWidget()-> currentText());
468
/*void nuclearCalculator::sliderMoved(int numHlives)
470
double num = numHlives / 10.0;
471
m_Time = Value(num * m_HalfLife. number() , m_HalfLife. unit());
473
m_time-> setValue(m_Time. number());
474
m_timeUnit->nativeWidget()-> setCurrentIndex(m_halfLifeUnit->nativeWidget()-> currentIndex());
475
m_numHalfLives-> setText(m_Time . toString());
478
void nuclearCalculator::calculate()
480
error(RESET_NUKE_MESG);
481
// Validate the values involved in calculation
482
if (m_HalfLife. number() == 0.0) {
483
error(HALFLIFE_ZERO);
488
case 0: // Calculate initial amount
489
if (m_FinalAmount.number() == 0.0) {
490
error(FINAL_AMT_ZERO);
493
calculateInitAmount();
495
case 1: // Calulate final Amount after time
496
if (m_InitAmount.number() == 0.0) {
497
error(INIT_AMT_ZERO);
500
calculateFinalAmount();
502
case 2: // Calculate Time
503
// If final amount greater than initial, error
504
if (m_FinalAmount.number() > (m_converter->convert(m_InitAmount,
505
m_FinalAmount.unit()->symbol())).number()) {
506
error(FINAL_AMT_GREATER);
508
} else if (m_finalAmt-> value() == 0.0)
509
{ // final amount is 0.0
510
error(FINAL_AMT_ZERO);
518
void nuclearCalculator::setMode(int mode)
522
m_initAmt->nativeWidget()->setReadOnly(false);
523
m_finalAmt->nativeWidget()->setReadOnly(false);
524
m_time->nativeWidget()->setReadOnly(false);
526
// set the quantity that should be calculated to readOnly
530
m_initAmt->nativeWidget()->setReadOnly(true);
534
m_finalAmt->nativeWidget()->setReadOnly(true);
538
m_time->nativeWidget()->setReadOnly(true);
539
// showSlider(false);
546
void nuclearCalculator::showSlider(bool show)
549
m_sliderLabel->hide();
551
m_numHalfLives->hide();
554
m_sliderLabel->show();
556
m_numHalfLives->show();
561
void nuclearCalculator::calculateInitAmount()
563
// If no time has elapsed, initial and final amounts are the same
564
m_InitAmount = m_FinalAmount;
565
if (m_Time. number() == 0.0) {
566
m_initAmt-> setValue(m_InitAmount . number());
569
// Calculate the number of halfLives that have elapsed
570
double ratio = (m_converter->convert(m_Time, m_HalfLife. unit() \
571
->symbol()). number()) /m_HalfLife. number();
572
// find out the initial amount
573
m_InitAmount = Value(m_InitAmount. number() * pow(2.0 , ratio), m_InitAmount. unit());
574
// Convert into the required units
575
m_InitAmount = m_converter->convert(m_InitAmount, m_InitAmount. unit()->symbol());
576
m_initAmt-> setValue(m_InitAmount . number());
579
void nuclearCalculator::calculateFinalAmount()
581
// If no time has elapsed, initial and final amounts are the same
582
m_FinalAmount = m_InitAmount;
583
if (m_Time. number() == 0.0) {
584
m_finalAmt-> setValue(m_FinalAmount. number());
587
// Calculate the number of halfLives that have elapsed
588
double ratio = (m_converter->convert(m_Time , m_HalfLife. unit() \
589
->symbol()). number()) / m_HalfLife. number();
590
// Calculate the final amount
591
m_FinalAmount = Value(m_FinalAmount . number() / pow(2.0, ratio), m_InitAmount. unit());
592
// Convert into the required units
593
m_FinalAmount = m_converter->convert(m_FinalAmount, m_FinalAmount. unit()->symbol());
594
m_finalAmt-> setValue(m_FinalAmount. number());
597
void nuclearCalculator::calculateTime()
599
// If initial and final masses are the same ( both units and value )
600
// the time is also 0
601
if (m_InitAmount.number() == m_FinalAmount.number() && \
602
m_InitAmount. unit() == m_FinalAmount . unit()) {
603
m_Time = Value(0.0, m_Time. unit());
604
m_time-> setValue(m_Time. number());
608
// calculate the ratio of final to initial masses
609
double ratio = (m_converter->convert(m_InitAmount , m_FinalAmount.unit()\
610
->symbol())).number() / m_FinalAmount.number();
611
// The number of halfLives ( log 2 ( x ) = log x / log 2 )
612
double numHalfLives = log(ratio) / log(2.0);
613
double time_value = numHalfLives * m_HalfLife . number();
614
// Calculate the total time taken
615
Value temp = Value(time_value, m_HalfLife. unit());
616
m_Time = m_converter->convert(temp , m_Time.unit()->symbol());
617
m_time-> setValue(m_Time. number());
620
void nuclearCalculator::error( int mode)
622
switch (mode) { // Depending on the mode, set the error messages.
623
case RESET_NUKE_MESG:
624
m_error->setText("");
627
m_error->setText(i18n("Initial amount cannot be zero."));
630
m_error->setText(i18n("Final amount cannot be zero."));
633
m_error->setText(i18n("Time is zero, please enter a valid value."));
635
case FINAL_AMT_GREATER:
636
m_error->setText(i18n("Final amount cannot be greater than initial amount."));
641
void nuclearCalculator::createConfigurationInterface(KConfigDialog *parent)
643
QWidget *widget = new QWidget();
645
parent->addPage(widget, i18n("General"), icon());
647
ui.massOnly->setChecked(m_massOnly);
649
connect ( parent, SIGNAL ( applyClicked() ), this, SLOT ( configAccepted() ) );
650
connect ( parent, SIGNAL ( okClicked() ), this, SLOT ( configAccepted() ) );
651
connect (ui.massOnly, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
654
void nuclearCalculator::configAccepted()
656
KConfigGroup cg = config();
657
QGraphicsItem::update();
659
m_massOnly = ui.massOnly->isChecked();
660
cg.writeEntry("massOnly", m_massOnly);
662
m_configUpdated = true;
665
emit configNeedsSaving();
667
#include "nuclearCalculator.moc"