From 4021ee9a0bbca04e0d2db98efa55eff30f03985a Mon Sep 17 00:00:00 2001 From: sbh Date: Fri, 12 Sep 2014 16:02:48 +0400 Subject: [PATCH] Issue #26 Salome-like double spin box control implemented: * By default takes 6 numbers after floationg point * Default step, min and max values are removed from the "create point" xml defenition --- src/ConstructionPlugin/point_widget.xml | 8 +- src/ModuleBase/CMakeLists.txt | 2 + src/ModuleBase/ModuleBase_DoubleSpinBox.cpp | 304 ++++++++++++++++++ src/ModuleBase/ModuleBase_DoubleSpinBox.h | 44 +++ .../ModuleBase_WidgetDoubleValue.cpp | 15 +- src/ModuleBase/ModuleBase_WidgetDoubleValue.h | 10 +- src/ModuleBase/ModuleBase_WidgetEditor.cpp | 2 +- src/ModuleBase/ModuleBase_WidgetPoint2D.cpp | 6 +- src/ModuleBase/ModuleBase_WidgetPoint2D.h | 6 +- .../ModuleBase_WidgetPoint2dDistance.cpp | 7 +- 10 files changed, 384 insertions(+), 20 deletions(-) create mode 100644 src/ModuleBase/ModuleBase_DoubleSpinBox.cpp create mode 100644 src/ModuleBase/ModuleBase_DoubleSpinBox.h diff --git a/src/ConstructionPlugin/point_widget.xml b/src/ConstructionPlugin/point_widget.xml index fd4a4e5bc..669df071d 100644 --- a/src/ConstructionPlugin/point_widget.xml +++ b/src/ConstructionPlugin/point_widget.xml @@ -1,7 +1,5 @@ - - - - - + + + diff --git a/src/ModuleBase/CMakeLists.txt b/src/ModuleBase/CMakeLists.txt index 16753a2d8..9dd78b582 100644 --- a/src/ModuleBase/CMakeLists.txt +++ b/src/ModuleBase/CMakeLists.txt @@ -27,6 +27,7 @@ SET(PROJECT_HEADERS ModuleBase_ViewerPrs.h ModuleBase_WidgetChoice.h ModuleBase_WidgetFileSelector.h + ModuleBase_DoubleSpinBox.h ) SET(PROJECT_SOURCES @@ -48,6 +49,7 @@ SET(PROJECT_SOURCES ModuleBase_WidgetValueFeature.cpp ModuleBase_WidgetChoice.cpp ModuleBase_WidgetFileSelector.cpp + ModuleBase_DoubleSpinBox.cpp ) SET(PROJECT_LIBRARIES diff --git a/src/ModuleBase/ModuleBase_DoubleSpinBox.cpp b/src/ModuleBase/ModuleBase_DoubleSpinBox.cpp new file mode 100644 index 000000000..171e1e849 --- /dev/null +++ b/src/ModuleBase/ModuleBase_DoubleSpinBox.cpp @@ -0,0 +1,304 @@ +// File: ModuleBase_DoubleSpinBox.cxx +// Author: Sergey TELKOV +// +#include "ModuleBase_DoubleSpinBox.h" + +#include +#include +#include + +#include + +const double PSEUDO_ZERO = 1.e-20; + +/*! + \class ModuleBase_DoubleSpinBox + \brief Enhanced version of the Qt's double spin box. + + The ModuleBase_DoubleSpinBox class represents the widget for entering the + floating point values. In addition to the functionality provided by + QDoubleSpinBox, this class supports "cleared" state - this is the + state corresponding to "None" (or empty) entered value. + + To set "cleared" state use setCleared() method. To check if the spin + box stores "cleared" state, use isCleared() method. + For example: + \code + if (myDblSpinBox->isCleared()) { + ... // process "None" state + } + else { + double value = myDblSpinBox->value(); + ... // process entered value + } + \endcode + + Another useful feature is possibility to use scientific notation (e.g. 1.234e+18) + for the widegt text. To enable this, negative precision should be specified either + through a constructor or using setPrecision() method. + + Note that "decimals" property of QDoubleSpinBox is almost completely substituted + by "myPrecision" field of ModuleBase_DoubleSpinBox class. "decimals" is still used + for proper size hint calculation and for rounding minimum and maximum bounds of + the spin box range. + */ + +/*! + \brief Constructor. + + Constructs a spin box with 0.0 as minimum value and 99.99 as maximum value, + a step value of 1.0 and a precision of 2 decimal places. + The value is initially set to 0.00. + + \param parent parent object + */ +ModuleBase_DoubleSpinBox::ModuleBase_DoubleSpinBox(QWidget* parent, int thePrecision) + : QDoubleSpinBox(parent), + myCleared(false) +{ + // VSR 01/07/2010: Disable thousands separator for spin box + // (to avoid incosistency of double-2-string and string-2-double conversion) + QLocale loc; + loc.setNumberOptions(loc.numberOptions() | QLocale::OmitGroupSeparator | QLocale::RejectGroupSeparator); + setLocale(loc); + + // Use precision equal to default Qt decimals + // it's necessary to set decimals before the range setting, + // by default Qt rounds boundaries to 2 decimals at setRange + setDecimals(thePrecision); + myPrecision = thePrecision; + + connect(lineEdit(), SIGNAL(textChanged( const QString& )), this, + SLOT(onTextChanged( const QString& ))); +} + +/*! + \brief Destructor. + */ +ModuleBase_DoubleSpinBox::~ModuleBase_DoubleSpinBox() +{ +} + +/*! + \brief Check if spin box is in the "cleared" state. + \return \c true if spin box is cleared + \sa setCleared() + */ +bool ModuleBase_DoubleSpinBox::isCleared() const +{ + return myCleared; +} + +/*! + \brief Change "cleared" status of the spin box. + \param on new "cleared" status + \sa isCleared() + */ +void ModuleBase_DoubleSpinBox::setCleared(const bool on) +{ + if (myCleared == on) + return; + + myCleared = on; + setSpecialValueText(specialValueText()); +} + +/*! + \brief Set precision of the spin box + + If precision value is less than 0, the 'g' format is used for value output, + otherwise 'f' format is used. + + \param prec new precision value. + \sa precision() + */ +void ModuleBase_DoubleSpinBox::setPrecision(const int prec) +{ + int newPrec = qMax(prec, 0); + int oldPrec = qMax(myPrecision, 0); + myPrecision = prec; + if (newPrec != oldPrec) + update(); +} + +/*! + \brief Get precision value of the spin box + \return current prevision value + \sa setPrecision() + */ +int ModuleBase_DoubleSpinBox::getPrecision() const +{ + return myPrecision; +} + +/*! + \brief Interpret text entered by the user as a value. + \param text text entered by the user + \return mapped value + \sa textFromValue() + */ +double ModuleBase_DoubleSpinBox::valueFromText(const QString& text) const +{ + if (myPrecision < 0) + return text.toDouble(); + + return QDoubleSpinBox::valueFromText(text); +} + +/*! + \brief This function is used by the spin box whenever it needs to display + the given value. + + \param val spin box value + \return text representation of the value + \sa valueFromText() + */ +QString ModuleBase_DoubleSpinBox::textFromValue(double val) const +{ + QString s = locale().toString(val, myPrecision >= 0 ? 'f' : 'g', qAbs(myPrecision)); + return removeTrailingZeroes(s); +} + +/*! + \brief Return source string with removed leading and trailing zeros. + \param str source string + \return resulting string + */ +QString ModuleBase_DoubleSpinBox::removeTrailingZeroes(const QString& src) const +{ + QString delim(locale().decimalPoint()); + + int idx = src.lastIndexOf(delim); + if (idx == -1) + return src; + + QString iPart = src.left(idx); + QString fPart = src.mid(idx + 1); + QString ePart = ""; + int idx1 = fPart.lastIndexOf(QRegExp("e[+|-]?[0-9]+")); + if (idx1 >= 0) { + ePart = fPart.mid(idx1); + fPart = fPart.left(idx1); + } + + fPart.remove(QRegExp("0+$")); + + QString res = iPart; + if (!fPart.isEmpty()) + res += delim + fPart; + res += ePart; + + return res; +} + +/*! + \brief Perform \a steps increment/decrement steps. + + The \a steps value can be any integer number. If it is > 0, + the value incrementing is done, otherwise value is decremented + \a steps times. + + \param steps number of increment/decrement steps + */ +void ModuleBase_DoubleSpinBox::stepBy(int steps) +{ + myCleared = false; + + QDoubleSpinBox::stepBy(steps); + double tmpval = value(); + if (qAbs(tmpval) < PSEUDO_ZERO) + tmpval = 0.; + if (tmpval < minimum()) + tmpval = minimum(); + else if (tmpval > maximum()) + tmpval = maximum(); + setValue(tmpval); +} + +/*! + \brief This function is used to determine whether input is valid. + \param str currently entered value + \param pos cursor position in the string + \return validating operation result + */ +QValidator::State ModuleBase_DoubleSpinBox::validate(QString& str, int& pos) const +{ + QString pref = this->prefix(); + QString suff = this->suffix(); + uint overhead = pref.length() + suff.length(); + QValidator::State state = QValidator::Invalid; + + QDoubleValidator v(NULL); + + // If 'g' format is used (myPrecision < 0), then + // myPrecision - 1 digits are allowed after the decimal point. + // Otherwise, expect myPrecision digits after the decimal point. + int decs = myPrecision < 0 ? qAbs(myPrecision) - 1 : myPrecision; + + v.setDecimals(decs); + v.setBottom(minimum()); + v.setTop(maximum()); + v.setNotation( + myPrecision >= 0 ? QDoubleValidator::StandardNotation : QDoubleValidator::ScientificNotation); + + if (overhead == 0) + state = v.validate(str, pos); + else { + if (str.length() >= overhead && str.startsWith(pref) && str.right(suff.length()) == suff) { + QString core = str.mid(pref.length(), str.length() - overhead); + int corePos = pos - pref.length(); + state = v.validate(core, corePos); + pos = corePos + pref.length(); + str.replace(pref.length(), str.length() - overhead, core); + } else { + state = v.validate(str, pos); + if (state == QValidator::Invalid) { + QString special = this->specialValueText().trimmed(); + QString candidate = str.trimmed(); + if (special.startsWith(candidate)) { + if (candidate.length() == special.length()) + state = QValidator::Acceptable; + else + state = QValidator::Intermediate; + } + } + } + } + + // Treat values ouside (min; max) range as Invalid + // This check is enabled by assigning "strict_validity_check" dynamic property + // with value "true" to the spin box instance. + if (state == QValidator::Intermediate) { + bool isOk; + double val = str.toDouble(&isOk); + if (isOk) { + QVariant propVal = property("strict_validity_check"); + if (propVal.isValid() && propVal.canConvert(QVariant::Bool) && propVal.toBool()) { + if (val < minimum() || val > maximum()) + state = QValidator::Invalid; + } + } else if (myPrecision < 0) { + // Consider too large negative exponent as Invalid + QChar e(locale().exponential()); + int epos = str.indexOf(e, 0, Qt::CaseInsensitive); + if (epos != -1) { + epos++; // Skip exponential symbol itself + QString exponent = str.right(str.length() - epos); + int expValue = exponent.toInt(&isOk); + if (isOk && expValue < std::numeric_limits::min_exponent10) + state = QValidator::Invalid; + } + } + } + + return state; +} + +/*! + \brief Called when user enters the text in the spin box. + \param txt current spin box text (not used) + */ +void ModuleBase_DoubleSpinBox::onTextChanged(const QString& /*txt*/) +{ + myCleared = false; +} diff --git a/src/ModuleBase/ModuleBase_DoubleSpinBox.h b/src/ModuleBase/ModuleBase_DoubleSpinBox.h new file mode 100644 index 000000000..a247fb8b7 --- /dev/null +++ b/src/ModuleBase/ModuleBase_DoubleSpinBox.h @@ -0,0 +1,44 @@ +// File: ModuleBase_DoubleSpinBox.h +// Author: Sergey TELKOV +// +#ifndef MODULEBASE_DOUBLESPINBOX_H_ +#define MODULEBASE_DOUBLESPINBOX_H_ + +#include "ModuleBase.h" + +#include +#include + +class MODULEBASE_EXPORT ModuleBase_DoubleSpinBox : public QDoubleSpinBox +{ +Q_OBJECT + + public: + ModuleBase_DoubleSpinBox(QWidget* = 0, int thePrecision = 6); + virtual ~ModuleBase_DoubleSpinBox(); + + bool isCleared() const; + virtual void setCleared(const bool); + + int getPrecision() const; + void setPrecision(const int); + + virtual void stepBy(int); + + virtual double valueFromText(const QString&) const; + virtual QString textFromValue(double) const; + + virtual QValidator::State validate(QString&, int&) const; + + protected slots: + virtual void onTextChanged(const QString&); + + protected: + QString removeTrailingZeroes(const QString&) const; + + private: + bool myCleared; + int myPrecision; +}; + +#endif diff --git a/src/ModuleBase/ModuleBase_WidgetDoubleValue.cpp b/src/ModuleBase/ModuleBase_WidgetDoubleValue.cpp index 692c94904..16837acce 100644 --- a/src/ModuleBase/ModuleBase_WidgetDoubleValue.cpp +++ b/src/ModuleBase/ModuleBase_WidgetDoubleValue.cpp @@ -3,6 +3,7 @@ // Author: Vitaly Smetannikov #include +#include #include #include @@ -16,13 +17,18 @@ #include #include #include -#include #include #include +#include + +#include #ifndef DBL_MAX #define DBL_MAX 1.7976931348623158e+308 #endif +#ifdef _DEBUG +#include +#endif ModuleBase_WidgetDoubleValue::ModuleBase_WidgetDoubleValue(QWidget* theParent, const Config_WidgetAPI* theData, @@ -40,7 +46,7 @@ ModuleBase_WidgetDoubleValue::ModuleBase_WidgetDoubleValue(QWidget* theParent, myLabel->setPixmap(QPixmap(aLabelIcon)); aControlLay->addWidget(myLabel); - mySpinBox = new QDoubleSpinBox(myContainer); + mySpinBox = new ModuleBase_DoubleSpinBox(myContainer); QString anObjName = QString::fromStdString(attributeID()); mySpinBox->setObjectName(anObjName); @@ -64,6 +70,10 @@ ModuleBase_WidgetDoubleValue::ModuleBase_WidgetDoubleValue(QWidget* theParent, aProp = theData->getProperty(DOUBLE_WDG_STEP); double aStepVal = QString::fromStdString(aProp).toDouble(&isOk); if (isOk) { + double aMinStep = pow(10, -1. * (double) mySpinBox->decimals()); + if(aStepVal < aMinStep){ + aStepVal = aMinStep; + } mySpinBox->setSingleStep(aStepVal); } @@ -134,3 +144,4 @@ bool ModuleBase_WidgetDoubleValue::eventFilter(QObject *theObject, QEvent *theEv } return ModuleBase_ModelWidget::eventFilter(theObject, theEvent); } + diff --git a/src/ModuleBase/ModuleBase_WidgetDoubleValue.h b/src/ModuleBase/ModuleBase_WidgetDoubleValue.h index 6c819866e..70c8a272b 100644 --- a/src/ModuleBase/ModuleBase_WidgetDoubleValue.h +++ b/src/ModuleBase/ModuleBase_WidgetDoubleValue.h @@ -8,10 +8,11 @@ #include "ModuleBase.h" #include "ModuleBase_ModelWidget.h" +class ModuleBase_DoubleSpinBox; class Config_WidgetAPI; class QWidget; class QLabel; -class QDoubleSpinBox; +class QTimer; class MODULEBASE_EXPORT ModuleBase_WidgetDoubleValue : public ModuleBase_ModelWidget { @@ -47,10 +48,15 @@ Q_OBJECT /// \param theEvent the processed event virtual bool eventFilter(QObject *theObject, QEvent *theEvent); + public slots: + /// Delayed value chnged: when user starts typing something, + // it gives him a 0,5 second to finish typing, when sends valueChnaged() signal +// void onValueChanged(); + protected: QWidget* myContainer; QLabel* myLabel; - QDoubleSpinBox* mySpinBox; + ModuleBase_DoubleSpinBox* mySpinBox; }; #endif diff --git a/src/ModuleBase/ModuleBase_WidgetEditor.cpp b/src/ModuleBase/ModuleBase_WidgetEditor.cpp index e010f40d0..6f32c7606 100644 --- a/src/ModuleBase/ModuleBase_WidgetEditor.cpp +++ b/src/ModuleBase/ModuleBase_WidgetEditor.cpp @@ -3,6 +3,7 @@ // Author: Natalia ERMOLAEVA #include +#include #include #include @@ -22,7 +23,6 @@ #include #include #include -#include ModuleBase_WidgetEditor::ModuleBase_WidgetEditor(QWidget* theParent, const Config_WidgetAPI* theData, diff --git a/src/ModuleBase/ModuleBase_WidgetPoint2D.cpp b/src/ModuleBase/ModuleBase_WidgetPoint2D.cpp index 881a6486d..b20f2e8d7 100644 --- a/src/ModuleBase/ModuleBase_WidgetPoint2D.cpp +++ b/src/ModuleBase/ModuleBase_WidgetPoint2D.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include @@ -19,7 +20,6 @@ #include #include -#include #include #include #include @@ -44,7 +44,7 @@ ModuleBase_WidgetPoint2D::ModuleBase_WidgetPoint2D(QWidget* theParent, aLabel->setPixmap(QPixmap(":pictures/x_point.png")); aGroupLay->addWidget(aLabel, 0, 0); - myXSpin = new QDoubleSpinBox(myGroupBox); + myXSpin = new ModuleBase_DoubleSpinBox(myGroupBox); myXSpin->setMinimum(-DBL_MAX); myXSpin->setMaximum(DBL_MAX); myXSpin->setToolTip("X"); @@ -58,7 +58,7 @@ ModuleBase_WidgetPoint2D::ModuleBase_WidgetPoint2D(QWidget* theParent, aLabel->setPixmap(QPixmap(":pictures/y_point.png")); aGroupLay->addWidget(aLabel, 1, 0); - myYSpin = new QDoubleSpinBox(myGroupBox); + myYSpin = new ModuleBase_DoubleSpinBox(myGroupBox); myYSpin->setMinimum(-DBL_MAX); myYSpin->setMaximum(DBL_MAX); myYSpin->setToolTip("X"); diff --git a/src/ModuleBase/ModuleBase_WidgetPoint2D.h b/src/ModuleBase/ModuleBase_WidgetPoint2D.h index e5d324a83..0151d1455 100644 --- a/src/ModuleBase/ModuleBase_WidgetPoint2D.h +++ b/src/ModuleBase/ModuleBase_WidgetPoint2D.h @@ -15,7 +15,7 @@ class ModuleBase_WidgetValue; class GeomAPI_Pnt2d; class QGroupBox; -class QDoubleSpinBox; +class ModuleBase_DoubleSpinBox; /**\class ModuleBase_WidgetPoint2D * \ingroup GUI @@ -73,8 +73,8 @@ signals: private: QGroupBox* myGroupBox; ///< the parent group box for all intenal widgets - QDoubleSpinBox* myXSpin; ///< the spin box for the X coordinate - QDoubleSpinBox* myYSpin; ///< the spin box for the Y coordinate + ModuleBase_DoubleSpinBox* myXSpin; ///< the spin box for the X coordinate + ModuleBase_DoubleSpinBox* myYSpin; ///< the spin box for the Y coordinate std::string myOptionParam; /// Parameter name which has to be taken from previous feature }; diff --git a/src/ModuleBase/ModuleBase_WidgetPoint2dDistance.cpp b/src/ModuleBase/ModuleBase_WidgetPoint2dDistance.cpp index 1a100c57b..56496ebd1 100644 --- a/src/ModuleBase/ModuleBase_WidgetPoint2dDistance.cpp +++ b/src/ModuleBase/ModuleBase_WidgetPoint2dDistance.cpp @@ -2,8 +2,9 @@ // Created: 23 June 2014 // Author: Vitaly Smetannikov -#include "ModuleBase_WidgetPoint2dDistance.h" -#include "ModuleBase_WidgetValueFeature.h" +#include +#include +#include #include #include @@ -12,8 +13,6 @@ #include #include -#include - ModuleBase_WidgetPoint2dDistance::ModuleBase_WidgetPoint2dDistance(QWidget* theParent, const Config_WidgetAPI* theData, const std::string& theParentId) -- 2.39.2