Web Analytics Made Easy - Statcounter
Skip to content

Handling units#

In this tutorial, we explore the role of units in SAMSON by developing an app that performs energy conversions. Please, refer to the Units section for an extensive description of the units mechanism in SAMSON.

Note

For the sake of the tutorial, we demonstrate here a simplified implementation of the units converter.

The source code for an Extension created in this tutorial can be found at SAMSON-Developer-Tutorials.

Generating an Extension#

We will start by creating a new SAMSON Extension called EnergyConverter thanks to the SAMSON Extension Generator (please, refer to the SAMSON Extension Generator tutorial for a reminder on how to use it):

  • launch SAMSON and run the SAMSON Extension Generator;
  • specify the path to a folder where you want to develop your SAMSON Extensions;
  • name the SAMSON Extension as EnergyConverter and add some description;
  • add an App class (called SEEnergyConverterApp);
  • generate the SAMSON Extension.

The SAMSON Extension Generator generates an app class that implements the functionality of the app and is derived from the SBDApp class, and a GUI class that implements the user interface of the app and is derived from the SBGApp class.

UnitsConverterDescription Setting the description of the Extension#

Open the header file of the app descriptor (file: SEEnergyConverterAppDescriptor.hpp) and modify the class description:

SB_CLASS_DESCRIPTION("DevTutorial: Energy converter");

Open the source file of the app GUI (file: SEEnergyConverterAppGUI.cpp) and modify the getName function to have a user-friendly description of the app inside SAMSON:

QString SEEnergyConverterAppGUI::getName() const { 

    return "DevTutorial: Energy converter"; 

}

Now, try building the Extension and when you launch SAMSON you should be able to find it in Home > Apps. For now, it has no interface and does nothing, but we will now add functionality to it.

Setting up the interface#

Let's start by adding the interface for the energy conversion.

Modify the user interface SEEnergyConverterAppGUI.ui to include two line edits (QLineEdit), two combo boxes (QComboBox), and a label.

GUI in QtCreator

Note how two vertical layouts and a horizontal one are used to align widgets in the interface. One good strategy is to create the first vertical layout and its contents and then copy it and rename widgets. You might also need to set the minimal width for the label to e.g. 10.

Please, name the added widgets as on an image above, so that you can copy and paste code later in this tutorial.

Now, enter the following entries (eV, Eh, kcal/mol, zJ) in each combo box (right clicl on a combo box and click on Edit items...):

Edit combobox entries

These are the different units supported by our app to perform conversions.

Add two slots called onLeftChanged() and onRightChanged() to the interface:

Add slots

We want to implement the following behavior for the interface:

  • when the user modifies the value in the left line edit or changes the current index in any of the combo boxes, the value in the right line edit should be updated;
  • when the user modifies the value in the right line edit, the value in the left line edit should be updated.

For that, we connect the textEdited signal from the left line edit and the currentIndexChanged signal from both combo boxes to the onLeftChanged() slot, and the textEdited signal from the right line editto the onRightChanged() slot as shown on an image below:

Connect signals to slots

Note

We use the textEdited signal for the line edits because it is emitted when the text was edited by the user; the textChanged signal, on the other hand, is also emitted when the text was changed programatically.

UnitsConverterImplementation Converting energies#

Let's add these slots to the GUI class (class: SEEnergyConverterAppGUI, file: SEEnergyConverterAppGUI.hpp):

class SEEnergyConverterAppGUI : public SBGApp {

    // ...

public slots:

    void    onLeftChanged();
    void    onRightChanged();

    // ...

};

For a clean organization, we separate the functionality of the app from its interface. As a result, the SEEnergyConverterAppGUI class is going to delegate the actual energy conversion work to the app (SEEnergyConverterApp class).

Let's now implement a function for energy conversion in the app. Add in the app class (class: SEEnergyConverterApp, file: SEEnergyConverterApp.hpp) the following function declaration:

class SEEnergyConverterApp : public SBDApp {

    // ...

    /// \name Conversion
    //@{

    double    convert(double sourceEnergy,
                      const unsigned int sourceUnit,
                      const unsigned int destinationUnit);

    //@}

};

In the source file of the app (SEEnergyConverterApp.cpp) add the header SBQuantity.hpp:

#include "SBQuantity.hpp"

And implement the convert function as follows:

double SEEnergyConverterApp::convert(
    double sourceEnergy,
    const unsigned int sourceUnit,
    const unsigned int destinationUnit) {

    SBQuantity::energy source;

    if (sourceUnit == 0) source = SBQuantity::eV(sourceEnergy);
    if (sourceUnit == 1) source = SBQuantity::Eh(sourceEnergy);
    if (sourceUnit == 2) source = SBQuantity::kcalPerMol(sourceEnergy);
    if (sourceUnit == 3) source = SBQuantity::zJ(sourceEnergy);

    if (destinationUnit == 0) return (SBQuantity::eV(source)).getValue();
    if (destinationUnit == 1) return (SBQuantity::Eh(source)).getValue();
    if (destinationUnit == 2) return (SBQuantity::kcalPerMol(source)).getValue();
    if (destinationUnit == 3) return (SBQuantity::zJ(source)).getValue();

    return 0.0;

}

Here, we first declare a physical quantity with energy units (zeptoJoules by default in SAMSON). Then we convert from the source energy to zeptoJoules and then we convert from zeptoJoules to the destination unit, and extract the value as a double from the result. Note, that all the conversions are done internaly by SAMSON. You can check the units page for more information.

Now, we program the slots. Open the source file for the GUI of the app (SEEnergyConverterAppGUI.cpp) and implement the onLeftChanged() slot as follows:

void SEEnergyConverterAppGUI::onLeftChanged() {

    // convert the string from the line edit to a double
    // with checking for valid numerical characters
    // (including the scientific notation)
    bool ok;
    double source = ui.lineEditLeft->text().toDouble(&ok);

    if (ok) {

        // convert units

        unsigned int sourceUnit = ui.comboBoxLeft->currentIndex();
        unsigned int destinationUnit = ui.comboBoxRight->currentIndex();

        double destination = getApp()->convert(source,
            sourceUnit, destinationUnit);

        // set the destination value
        // QString::number will use float or scientific notation format,
        // whichever is the most concise
        ui.lineEditRight->setText(QString::number(destination));

    }
    else {

        // in the case when the line edit contained invalid characters
        // clear another line edit
        ui.lineEditRight->setText("");

    }

}

Here, we first try to convert the string from the line edit into a number: the toDouble function from the QString class takes care of checking the validity of the numeric string, including the support for scientific notation (i.e. numbers in the format 1.5e+5 are supported). Then, if the string has a valid number we do a conversion, else we empty the other line edit.

Implement the onRightChanged() slot in the same way by exchanging the source and destination widgets:

void SEEnergyConverterAppGUI::onRightChanged() {

    // convert the string from the line edit to a double
    // with checking for valid numerical characters
    // (including the scientific notation)
    bool ok;
    double source = ui.lineEditRight->text().toDouble(&ok);

    if (ok) {

        // convert units

        unsigned int sourceUnit = ui.comboBoxRight->currentIndex();
        unsigned int destinationUnit = ui.comboBoxLeft->currentIndex();

        double destination = getApp()->convert(source,
            sourceUnit, destinationUnit);

        // set the destination value
        // QString::number will use float or scientific notation format,
        // whichever is the most concise
        ui.lineEditLeft->setText(QString::number(destination));

    }
    else {

        // in the case when the line edit contained invalid characters
        // clear another line edit
        ui.lineEditLeft->setText("");

    }

}

Now, you have a simple energy converter implementation. Build the Extension, launch SAMSON, open the Extension, and try converting values.

Energy converter app