nav-left cat-right
cat-right
Matthias Pospiech > Programming > C++ > Qt > Qt SpinBox Widget with Scientific Notation

Qt SpinBox Widget with Scientific Notation

QScienceSpinBox

Since I started using Qt over a year ago I have been asking and looking for a Qt widget that can handle numbers in a scientific notation. Unfortunately Qt Software does not offer such a solution nor is it trivial to implement. With the hints I got in some public web forums I implementet the solution provided here.

This widget is derived from QDoubleSpinBox. It uses a decimal value of 1000 (that is more decimal points than a double can handle) and implements a new decimal value for the presentation in scientific notation. The Validator is realised by setting the LineEdit to a QDoubleValidator::ScientificNotation. However the most important part is the reimplementation of textFromValue and valueFromText. This unfortunately requires to copy the whole validation code of QDoubleSpinBox, which can not be borrowed and represents the major part of the code.

If someone can show a shrinked but still functional equivalent version that would be great. In the end I think that it would be better if such a solution would be included into a Qt release, especially because in its current form I use so much of their code.

Sample Project

Source Code

QScienceSpinBox.h
#ifndef __QScienceSpinBox_H__
#define __QScienceSpinBox_H__
 
#include <QtGui/QDoubleSpinBox>
#include <QtGui/QDoubleValidator>
#include <QtGui/QLineEdit>
#include <QtCore/QVariant>
#include <QtCore/QDebug>
#include <QtCore/QString>
 
static bool isIntermediateValueHelper(qint64 num, qint64 minimum, qint64 maximum, qint64 *match = 0);
 
class QScienceSpinBox : public QDoubleSpinBox
{
Q_OBJECT
public:
    QScienceSpinBox(QWidget * parent = 0);
 
	int decimals() const;
	void setDecimals(int value);
 
    QString textFromValue ( double value ) const;
    double valueFromText ( const QString & text ) const;
 
private:
	int dispDecimals;
    QChar delimiter, thousand;
	QDoubleValidator * v;
 
 
private:
	void initLocalValues(QWidget *parent);
    bool isIntermediateValue(const QString &str) const;
    QVariant validateAndInterpret(QString &input, int &pos, QValidator::State &state) const;
	QValidator::State validate(QString &text, int &pos) const;
	void fixup(QString &input) const;
	QString stripped(const QString &t, int *pos) const;
	double round(double value) const;
	void stepBy(int steps);
 
public slots:
	void stepDown();
	void stepUp();
 
};
 
#endif
QScienceSpinBox.cpp
#include "QScienceSpinBox.h"
 
#include <limits>
 
//#define QSPINBOX_QSBDEBUG
#ifdef QSPINBOX_QSBDEBUG
#  define QSBDEBUG qDebug
#else
#  define QSBDEBUG if (false) qDebug
#endif
 
 
QScienceSpinBox::QScienceSpinBox(QWidget * parent)
    : QDoubleSpinBox(parent)
{
	initLocalValues(parent);
	setDecimals(8);
	QDoubleSpinBox::setDecimals(1000);
 
	// set Range to maximum possible values
	double doubleMax = std::numeric_limits<double>::max();
	setRange(-doubleMax, doubleMax);
 
	v = new QDoubleValidator(this);
	v->setDecimals(1000); // (standard anyway)
	v->setNotation(QDoubleValidator::ScientificNotation);
	this->lineEdit()->setValidator(v);		
}
 
void QScienceSpinBox::initLocalValues(QWidget *parent)
{
    const QString str = (parent ? parent->locale() : QLocale()).toString(4567.1);
    if (str.size() == 6) {
        delimiter = str.at(4);
        thousand = QChar((ushort)0);
    } else if (str.size() == 7) {
        thousand = str.at(1);
        delimiter = str.at(5);
    }
    Q_ASSERT(!delimiter.isNull());
}
 
int QScienceSpinBox::decimals() const
{
	return dispDecimals;
}
 
void QScienceSpinBox::setDecimals(int value)
{
	dispDecimals = value;
}
 
// overwritten virtual function of QAbstractSpinBox
void QScienceSpinBox::stepBy(int steps)
{
	if (steps < 0)
		stepDown();
	else
		stepUp();
}
 
void QScienceSpinBox::stepDown()
{
	QSBDEBUG() << "stepDown()";
	setValue(value()/10.0);
}
 
void QScienceSpinBox::stepUp()
{
	QSBDEBUG() << "stepUp()";
	setValue(value()*10.0);
}
 
/*!
 *  text to be displayed in spinbox
 */
QString QScienceSpinBox::textFromValue(double value) const
{
 
	// convert to string -> Using exponetial display with internal decimals
	QString str = locale().toString(value, 'e', dispDecimals);
    // remove thousand sign
	if (qAbs(value) >= 1000.0) {
        str.remove(thousand);
    }
    return str;
}
 
double QScienceSpinBox::valueFromText(const QString &text) const
{
    QString copy = text;
    int pos = this->lineEdit()->cursorPosition();
    QValidator::State state = QValidator::Acceptable;
    return validateAndInterpret(copy, pos, state).toDouble();
}
 
// this function is never used...?
double QScienceSpinBox::round(double value) const
{
	const QString strDbl = locale().toString(value, 'g', dispDecimals);
    return locale().toDouble(strDbl);
}
 
// overwritten virtual function of QAbstractSpinBox
QValidator::State QScienceSpinBox::validate(QString &text, int &pos) const
{
    QValidator::State state;
    validateAndInterpret(text, pos, state);
    return state;
}
 
// overwritten virtual function of QAbstractSpinBox
void QScienceSpinBox::fixup(QString &input) const
{
    input.remove(thousand);
}
 
// reimplemented function, copied from QDoubleSpinBoxPrivate::isIntermediateValue
bool QScienceSpinBox::isIntermediateValue(const QString &str) const
{
    QSBDEBUG() << "input is" << str << minimum() << maximum();
    qint64 dec = 1;	
 
    for (int i=0; i < decimals(); ++i)
        dec *= 10;
 
    const QLatin1Char dot('.');
 
    /*!
	 * determine minimum possible values on left and right of Decimal-char
	 */
	// I know QString::number() uses CLocale so I use dot
	const QString minstr = QString::number(minimum(), 'f', QDoubleSpinBox::decimals());
    qint64 min_left = minstr.left(minstr.indexOf(dot)).toLongLong();
    qint64 min_right = minstr.mid(minstr.indexOf(dot) + 1).toLongLong();
 
	const QString maxstr = QString::number(maximum(), 'f', QDoubleSpinBox::decimals());
    qint64 max_left = maxstr.left(maxstr.indexOf(dot)).toLongLong();
    qint64 max_right = maxstr.mid(maxstr.indexOf(dot) + 1).toLongLong();
 
	/*!
	 * determine left and right long values (left and right of delimiter)
	 */
	const int dotindex = str.indexOf(delimiter);
    const bool negative = maximum() < 0;
    qint64 left = 0, right = 0;
    bool doleft = true;
    bool doright = true;
	// no separator -> everthing in left
    if (dotindex == -1) {
        left = str.toLongLong();
        doright = false;
    }
	// separator on left or contains '+'
	else if (dotindex == 0 || (dotindex == 1 && str.at(0) == QLatin1Char('+'))) {
        // '+' at negative max
		if (negative) {
            QSBDEBUG() << __FILE__ << __LINE__ << "returns false";
            return false;
        }
        doleft = false;
        right = str.mid(dotindex + 1).toLongLong();
    }
	// contains '-'	
	else if (dotindex == 1 && str.at(0) == QLatin1Char('-')) {
		// '-' at positiv max
        if (!negative) {
            QSBDEBUG() << __FILE__ << __LINE__ << "returns false";
            return false;
        }
        doleft = false;
        right = str.mid(dotindex + 1).toLongLong();
    } else {
        left = str.left(dotindex).toLongLong();
        if (dotindex == str.size() - 1) { // nothing right of Separator
            doright = false;
        } else {
            right = str.mid(dotindex + 1).toLongLong();
        }
    }
	// left > 0, with max < 0 and no '-'
    if ((left >= 0 && max_left < 0 && !str.startsWith(QLatin1Char('-'))) 
	// left > 0, with min > 0 
		|| (left < 0 && min_left >= 0)) 
	{
        QSBDEBUG("returns false");
        return false;
    }
 
    qint64 match = min_left;
    if (doleft && !isIntermediateValueHelper(left, min_left, max_left, &match)) {
        QSBDEBUG() << __FILE__ << __LINE__ << "returns false";
        return false;
    }
    if (doright) {
        QSBDEBUG("match %lld min_left %lld max_left %lld", match, min_left, max_left);
        if (!doleft) {
            if (min_left == max_left) {
                const bool ret = isIntermediateValueHelper(qAbs(left),
                                                           negative ? max_right : min_right,
                                                           negative ? min_right : max_right);
                QSBDEBUG() << __FILE__ << __LINE__ << "returns" << ret;
                return ret;
            } else if (qAbs(max_left - min_left) == 1) {
                const bool ret = isIntermediateValueHelper(qAbs(left), min_right, negative ? 0 : dec)
                                 || isIntermediateValueHelper(qAbs(left), negative ? dec : 0, max_right);
                QSBDEBUG() << __FILE__ << __LINE__ << "returns" << ret;
                return ret;
            } else {
                const bool ret = isIntermediateValueHelper(qAbs(left), 0, dec);
                QSBDEBUG() << __FILE__ << __LINE__ << "returns" << ret;
                return ret;
            }
        }
        if (match != min_left) {
            min_right = negative ? dec : 0;
        }
        if (match != max_left) {
            max_right = negative ? 0 : dec;
        }
        qint64 tmpl = negative ? max_right : min_right;
        qint64 tmpr = negative ? min_right : max_right;
        const bool ret = isIntermediateValueHelper(right, tmpl, tmpr);
        QSBDEBUG() << __FILE__ << __LINE__ << "returns" << ret;
        return ret;
    }
    QSBDEBUG() << __FILE__ << __LINE__ << "returns true";
    return true;
}
 
 
/*!
    \internal Multi purpose function that parses input, sets state to
    the appropriate state and returns the value it will be interpreted
    as.
*/
// reimplemented function, copied from QDoubleSpinBoxPrivate::validateAndInterpret
QVariant QScienceSpinBox::validateAndInterpret(
	QString &input, 
	int &pos,
    QValidator::State &state) const
{
	/*! return 'cachedText' if
	 *   input = cachedText, or input Empty
     */
 
	static QString cachedText;
	static QValidator::State cachedState;
    static QVariant cachedValue;
 
    if (cachedText == input && !input.isEmpty()) {
        state = cachedState;
        QSBDEBUG() << "cachedText was" << "'" << cachedText << "'" << "state was "
                   << state << " and value was " << cachedValue;
        return cachedValue;
    }
    const double max = maximum();
    const double min = minimum();
 
	// removes prefix & suffix
    QString copy = stripped(input, &pos);
    QSBDEBUG() << "input" << input << "copy" << copy;
 
    int len = copy.size();
    double num = min;
    const bool plus = max >= 0;
    const bool minus = min <= 0;
 
	// Test possible 'Intermediate' reasons
	switch (len) 
	{
		case 0:
			// Length 0 is always 'Intermediate', except for min=max
			if (max != min)	{
				state = QValidator::Intermediate;
			} else {
				state = QValidator::Invalid;
			}
			goto end;
		case 1:
			// if only char is '+' or '-'
			if (copy.at(0) == delimiter
				|| (plus && copy.at(0) == QLatin1Char('+'))
				|| (minus && copy.at(0) == QLatin1Char('-'))) {
				state = QValidator::Intermediate;
				goto end;
			}
			break;
		case 2:
			// if only chars are '+' or '-' followed by Comma seperator (delimiter)
			if (copy.at(1) == delimiter
				&& ((plus && copy.at(0) == QLatin1Char('+')) || (minus && copy.at(0) == QLatin1Char('-')))) {
				state = QValidator::Intermediate;
				goto end;
			}
			break;
		default: break;
    } // end switch
 
 
    // First char must not be thousand-char
	if (copy.at(0) == thousand) 
	{
        QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
        state = QValidator::Invalid;
        goto end;
    }
	// Test possible 'Invalid' reasons
	else if (len > 1) 
	{		
        const int dec = copy.indexOf(delimiter); // position of delimiter
        // if decimal separator (delimiter) exists
		if (dec != -1) {
			// not two delimiters after one other (meaning something like ',,')
            if (dec + 1 < copy.size() && copy.at(dec + 1) == delimiter && pos == dec + 1) {
                copy.remove(dec + 1, 1); // typing a delimiter when you are on the delimiter
            }							 // should be treated as typing right arrow
			// too many decimal points
			if (copy.size() - dec > QDoubleSpinBox::decimals() + 1) {
                QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
                state = QValidator::Invalid;
                goto end;
            }
			// after decimal separator no thousand char
            for (int i=dec + 1; i<copy.size(); ++i) {
                if (copy.at(i).isSpace() || copy.at(i) == thousand) {
                    QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
                    state = QValidator::Invalid;
                    goto end;
                }
            }
		// if no decimal separator exists
        } else {
            const QChar &last = copy.at(len - 1);
            const QChar &secondLast = copy.at(len - 2);
			// group of two thousand or space chars is invalid
            if ((last == thousand || last.isSpace())
                && (secondLast == thousand || secondLast.isSpace())) {
                state = QValidator::Invalid;
                QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
                goto end;
            } 
			// two space chars is invalid
			else if (last.isSpace() && (!thousand.isSpace() || secondLast.isSpace())) {
                state = QValidator::Invalid;
                QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
                goto end;
            }
        }
	} // end if (len > 1)
 
	// block of remaining test before 'end' mark
	{
        bool ok = false;
		bool notAcceptable = false;
 
		// convert 'copy' to double, and check if that was 'ok'
        QLocale loc(locale());
        num = loc.toDouble(copy, &ok);
        QSBDEBUG() << __FILE__ << __LINE__ << loc << copy << num << ok;
 
 
		// conversion to double did fail
        if (!ok) {
			// maybe thousand char was responsable
            if (thousand.isPrint()) 
			{
				// if no thousand sign is possible, then
				// something else is responable -> Invalid
                if (max < 1000 && min > -1000 && copy.contains(thousand)) {
                    state = QValidator::Invalid;
                    QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
                    goto end;
                }
 
				// two thousand-chars after one other are not valid
                const int len = copy.size();
                for (int i=0; i<len- 1; ++i) {
                    if (copy.at(i) == thousand && copy.at(i + 1) == thousand) {
                        QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
                        state = QValidator::Invalid;
                        goto end;
                    }
                }
 
				// remove thousand-chars
                const int s = copy.size();
                copy.remove(thousand);
                pos = qMax(0, pos - (s - copy.size()));
 
                num = loc.toDouble(copy, &ok);
                QSBDEBUG() << thousand << num << copy << ok;
 
				// if conversion still not valid, then reason unknown -> Invalid
                if (!ok) {
                    state = QValidator::Invalid;
                    QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
                    goto end;
                }
                notAcceptable = true; // -> state = Intermediate
            } // endif: (thousand.isPrint())
        }
 
		// no thousand sign, but still invalid for unknown reason
        if (!ok) {
            state = QValidator::Invalid;
            QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
        } 
		// number valid and within valid range
		else if (num >= min && num <= max) {
			if (notAcceptable) {
				state = QValidator::Intermediate; // conversion to num initially failed
			} else {
				state = QValidator::Acceptable;
			}
            QSBDEBUG() << __FILE__ << __LINE__<< "state is set to "
                       << (state == QValidator::Intermediate ? "Intermediate" : "Acceptable");
        } 
		// when max and min is the same the only non-Invalid input is max (or min)
		else if (max == min) { 
            state = QValidator::Invalid;
            QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
        } else {
			// value out of valid range (coves only special cases)
            if ((num >= 0 && num > max) || (num < 0 && num < min)) {
                state = QValidator::Invalid;
                QSBDEBUG() << __FILE__ << __LINE__<< "state is set to Invalid";
            } else {
				// invalid range, further test with 'isIntermediateValue'
				if (isIntermediateValue(copy)) {
					state =  QValidator::Intermediate;
				} else {
					state =  QValidator::Invalid;
				}
                QSBDEBUG() << __FILE__ << __LINE__<< "state is set to "
                           << (state == QValidator::Intermediate ? "Intermediate" : "Acceptable");
            }
        }
    }
 
end:
    // if something went wrong, set num to something valid
	if (state != QValidator::Acceptable) {
        num = max > 0 ? min : max;
    }
 
	// save (private) cache values
	cachedText = prefix() + copy + suffix();
    cachedState = state;
    cachedValue = QVariant(num);
	// return resulting valid num
    return QVariant(num);
}
 
 
/*!
    \internal
    Strips any prefix/suffix from \a text.
*/
// reimplemented function, copied from QAbstractSpinBoxPrivate::stripped
QString QScienceSpinBox::stripped(const QString &t, int *pos) const
{
    QString text = t;
	QString prefixtext = prefix();
	QString suffixtext = suffix();
 
    if (specialValueText().size() == 0 || text != specialValueText()) {
        int from = 0;
        int size = text.size();
        bool changed = false;
        if (prefixtext.size() && text.startsWith(prefixtext)) {
            from += prefixtext.size();
            size -= from;
            changed = true;
        }
        if (suffixtext.size() && text.endsWith(suffixtext)) {
            size -= suffixtext.size();
            changed = true;
        }
        if (changed)
            text = text.mid(from, size);
    }
 
    const int s = text.size();
    text = text.trimmed();
    if (pos)
        (*pos) -= (s - text.size());
    return text;
}
 
// reimplemented function, copied from qspinbox.cpp
static bool isIntermediateValueHelper(qint64 num, qint64 min, qint64 max, qint64 *match)
{
    QSBDEBUG("%lld %lld %lld", num, min, max);
 
    if (num >= min && num <= max) {
        if (match)
            *match = num;
        QSBDEBUG("returns true 0");
        return true;
    }
    qint64 tmp = num;
 
    int numDigits = 0;
    int digits[10];
    if (tmp == 0) {
        numDigits = 1;
        digits[0] = 0;
    } else {
        tmp = qAbs(num);
        for (int i=0; tmp > 0; ++i) {
            digits[numDigits++] = tmp % 10;
            tmp /= 10;
        }
    }
 
    int failures = 0;
    qint64 number;
    for (number=max; number>=min; --number) {
        tmp = qAbs(number);
        for (int i=0; tmp > 0;) {
            if (digits[i] == (tmp % 10)) {
                if (++i == numDigits) {
                    if (match)
                        *match = number;
                    QSBDEBUG("returns true 1");
                    return true;
                }
            }
            tmp /= 10;
        }
        if (failures++ == 500000) { //upper bound
            if (match)
                *match = num;
            QSBDEBUG("returns true 2");
            return true;
        }
    }
    QSBDEBUG("returns false");
    return false;
}

16 Antworten : “Qt SpinBox Widget with Scientific Notation”

  1. I created a simpler implementation of a spin box for scientific notation using PySide on my blog: http://jdreaver.com/posts/2014-07-28-scientific-notation-spin-box-pyside.html

  2. Al_ sagt:

    Hi all

    I know my post that I wrote/write a technical spinbox is ancient. Just in case anyone is still interested: I posted the code on https://www.gitorious.org/techspinbox.

    Cheers

    Al_

  3. pospiech sagt:

    I added all my code to a google code repository.
    You can check it out with

    svn checkout http://qsciencespinbox.googlecode.com/svn/trunk/ qsciencespinbox

    I only updated the pro file, the widget code is them as 3 years ago with all its features and bugs...

  4. Derek sagt:

    Please can you add this to a public code repository e.g. Google Code?

  5. Jonathan McLaughlin sagt:

    I dont have any improved code at the moment but he problem seems to lie with the fact that entering the value and using the up and down arrows follow different validation paths.

    To fully validate I think that the validate() method has to be over-ridden for both the QDoubleSpinBox and the QLineEdit, contained within the spin box.

  6. pospiech sagt:

    Honestly, I have not been using this code for a longer time. So if you find that something is wrong (bug) or can be improved feel free to do so. I would be very glad to get the improved code back though. If more people are interested one could also think of putting this into a public repository…

  7. Jonathan McLaughlin sagt:

    Matthias,

    Really good work, however, I have implemented this and the validation is not applied when the up and down arrow of the spin box is clicked…

    Is this expected behaviour?

    Jonathan

  8. Al, do you have a website or a download of your ‚QTechSpinBox‘? Google only finds my website.

  9. Al_ sagt:

    After trying a few options, I inherited QTechSpinBox from QAbstractSpinBox. I have now an alpha version of QTechSpinBox ready, together with a very small sample project to demonstrate its use. I will mail you source and exe for WinXP separately. I would very much appreciate getting your feedback. And below the description as I entered it as comment into the source code.

    Regarsd, A_

    Description of QTechSpinBox:
    QTechSpinBox allows user to set double values in exponential notation
    If no exponential notation is needed, QDoubleSpoinBox from Qt can be used;
    if scientific notation, but neither units nor technical notation is needed,
    QScienceSpinBox from Matthias Pospiech (http://www.matthiaspospiech.de).

    QTechSpinBox allows the user to choose a value by clicking the up/down
    buttons or pressing up/down on the keyboard to increase/decrease
    the value currently displayed. The user can also type the value in
    manually.

    The displayed value can be appended with arbitrary strings indicating
    the unit. If three digits are allowed in front of the decimal sign, the
    unit will be displayed with unit modifiers (milli, kilo, micro, Mega and so on).
    The value retrieved by value() factors the unit modifier in. If a textual
    representation is needed, the value can be formatted using textFromValue().

    When entering data manually, all entries acceptable as output (i.e., number of
    pre- and postdigits, unit) are allowed. In addition, exponents and unit modifier
    (with unit, unless empty) may be combined. E.g., if unit is m (meter) and
    ‚3e12 nm‘ or ‚3e12 n‘ is entered, it will be formatted to ‚3 km‘ (if
    predigits == 3 and modifierunits == true) or ‚3e3 m‘ (otherwise).
    Ambiguity resolution: units and unit modifier might be ambiguous (e.g., ‚P‘
    could be first letter of Pa (pascal, unit for pressure) or unit modifier for
    Penta (10^15). In such cases it is interpreted as pascal. This only applies if
    the baseunit starts with one of the letters used as unit prefix. In these cases,
    at least the first letter of the base unit needs to be included (e.g., PP for
    pentapascal, full unit would be PPa).

    Every time the value changes QTechSpinBox emits valueChanged()
    signals. If only the textual representation changes, valueChanged(string)
    is emitted. The current value can be fetched with value() and set
    with setValue().

    Clicking the up/down buttons or using the keyboard accelerator’s
    up and down arrows will increase or decrease the current value by
    factors of size singleStep(). If you want to change this behaviour you
    can reimplement the virtual function stepBy(). The minimum and
    maximum value and the step size can be changed with setMinimum(),
    setMaximum() and setSingleStep().

  10. Al_ sagt:

    ok, understood.

    Greek letters are relevant for QTechSpinBox, too (for micro). Seems to be really straight forward as QString is Unicode based. I just tried it: in a QLineEdit I can type ‚micro’W and whatever greek letters I want. Or do I miss a complication downstream? Bear with me, I am new to Qt (the last 12 years I used Borland).

    ^2 and ^3 (i.e., superscript 2 and 3) also happen to be regular characters (appear like superscripts as the ‚2‘ and ‚3‘ are printed tiny and well above the baseline of the letters). ‚micro’W^2 and ‚micro’m/s^2 look nice on my screen (using Win XP Pro). I do not have a suggestion for ^4 and above, nor for negative superscripts.

  11. by fractions I meant that units such as m/s (meters per second) or W/s/m^2 are looking not nice. Also it is not possible (or at least I do not know how) to display greek letters.

  12. Al_ sagt:

    Ok, so this leaves a small bit for me to contribute, on top of the major work done by you. 🙂

    What do you mean by ‚display fractions‘?

    While trying to implement the ‚technical notation‘ feature as QTechSpinBox : public QScienceSpinBox, I also plan to adapt the behaviour of stepDown() and stepUp(), unless you have soemthing like this already in planning for QScienceSpinBox (please let me know). I like to honor QDoubleSpinBox::singleStep by using this value as multiplication factor (instead of hard-coded 10). Also, if value==0, I like stepUp() to set value=1 (as multiplication of zero does nothing, an unexpected behaviour for end-users).

    Using unit modifiers such as kilo or nano (if a base unit is given) is a nice idea. I will have a look into.

  13. I have played with such code, but never got it finished. I was thinking about a widget that would automatically rescale to micro, milli and such. I wanted it to have a unit, such that it would display mJ, µJ, pJ or ps, fs (femto seconds) and so forth. the biggest problem about units is, that there is not nice way to display fractions inside a Spinbox.

  14. Al_ sagt:

    Thanks for providing the scientific spinbox to the community. I wanted to implement something very similar, then read the discussion on the Qt forum and realized that is much more complex than I thought. I will need a small adaptation of your code; prior to inheriting my own class from QScienceSpinBox, I want to confirm that you do not have this already coded: technical notation instead of scientific. I.e., the exponent is only 0, 3, 6 , -3 and so on (matching kilo, Mega, Giga, and for my use more likely milli, micro, nano, pico).

  15. Well, in any case you deal with number such as 147.05e-235, you will be happy, that it is not converted to 0.0 (which it would be with a normal QDoubleSpinBox).

    I will add a note for licencing it under GPL.

  16. Thomas sagt:

    Hiya; I tried the app and I love how you show the customizability of the spinbox. I’m trying to find out what is a good useage (or usecase) for this widget. Can you tell me when its a good widget?

    Also, would be nice if you add that this is all licensed under the GPL in your zip; to make it clear for everyone downloading it. Just a README or LICENCE file or somesuch.

Einen Kommentar schreiben

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*

This blog is kept spam free by WP-SpamFree.