Salome HOME
Issue #26 Salome-like double spin box control implemented:
authorsbh <sergey.belash@opencascade.com>
Fri, 12 Sep 2014 12:02:48 +0000 (16:02 +0400)
committersbh <sergey.belash@opencascade.com>
Fri, 12 Sep 2014 12:02:48 +0000 (16:02 +0400)
* 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
src/ModuleBase/CMakeLists.txt
src/ModuleBase/ModuleBase_DoubleSpinBox.cpp [new file with mode: 0644]
src/ModuleBase/ModuleBase_DoubleSpinBox.h [new file with mode: 0644]
src/ModuleBase/ModuleBase_WidgetDoubleValue.cpp
src/ModuleBase/ModuleBase_WidgetDoubleValue.h
src/ModuleBase/ModuleBase_WidgetEditor.cpp
src/ModuleBase/ModuleBase_WidgetPoint2D.cpp
src/ModuleBase/ModuleBase_WidgetPoint2D.h
src/ModuleBase/ModuleBase_WidgetPoint2dDistance.cpp

index fd4a4e5bc174c45e7635479847464b2387e9d05f..669df071de5fed7bdec4ec930391f607ad8bb662 100644 (file)
@@ -1,7 +1,5 @@
 <source>
-  <doublevalue id="x" label="X:" max="50" step="1.0" default="0" icon=":pictures/x_point.png" tooltip="Set X">
-    <!-- validator id="between" parameters="a,b"/ --> 
-  </doublevalue>
-  <doublevalue id="y" label="Y:" min="x" default="0" icon=":pictures/y_point.png" tooltip="Set Y"/>
-  <doublevalue id="z" label="Z:" min="-20" step="0.1" default="0" icon=":pictures/z_point.png" tooltip="Set Z"/>
+  <doublevalue id="x" label="X:" icon=":pictures/x_point.png" tooltip="Set X"/>
+  <doublevalue id="y" label="Y:" icon=":pictures/y_point.png" tooltip="Set Y"/>
+  <doublevalue id="z" label="Z:" icon=":pictures/z_point.png" tooltip="Set Z"/>
 </source>
index 16753a2d8106a04dedfcbed595cac256cb51da74..9dd78b5823b29cce47290457c346ef335561cb2a 100644 (file)
@@ -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 (file)
index 0000000..171e1e8
--- /dev/null
@@ -0,0 +1,304 @@
+// File:      ModuleBase_DoubleSpinBox.cxx
+// Author:    Sergey TELKOV
+//
+#include "ModuleBase_DoubleSpinBox.h"
+
+#include <QLineEdit>
+#include <QDoubleValidator>
+#include <QVariant>
+
+#include <limits>
+
+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<double>::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 (file)
index 0000000..a247fb8
--- /dev/null
@@ -0,0 +1,44 @@
+// File:      ModuleBase_DoubleSpinBox.h
+// Author:    Sergey TELKOV
+//
+#ifndef MODULEBASE_DOUBLESPINBOX_H_
+#define MODULEBASE_DOUBLESPINBOX_H_
+
+#include "ModuleBase.h"
+
+#include <QDoubleSpinBox>
+#include <QValidator>
+
+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
index 692c949042868d2433ff502cc6d84177c21f347e..16837acce7f66f27bb19032263bb96be839ebccf 100644 (file)
@@ -3,6 +3,7 @@
 // Author:      Vitaly Smetannikov
 
 #include <ModuleBase_WidgetDoubleValue.h>
+#include <ModuleBase_DoubleSpinBox.h>
 
 #include <ModelAPI_AttributeDouble.h>
 #include <ModelAPI_Data.h>
 #include <QWidget>
 #include <QLayout>
 #include <QLabel>
-#include <QDoubleSpinBox>
 #include <QEvent>
 #include <QKeyEvent>
+#include <QTimer>
+
+#include <math.h>
 
 #ifndef DBL_MAX
 #define DBL_MAX 1.7976931348623158e+308 
 #endif
+#ifdef _DEBUG
+#include <iostream>
+#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);
 }
+
index 6c819866e8575bd6f2bbc6164c4293deca7f53d2..70c8a272b15d309684668836b7a6447d07295a95 100644 (file)
@@ -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
index e010f40d062656997bc27c63a33357cf501b9fdf..6f32c7606bba64efe3d4c8b9685bd751c90e83d3 100644 (file)
@@ -3,6 +3,7 @@
 // Author:      Natalia ERMOLAEVA
 
 #include <ModuleBase_WidgetEditor.h>
+#include <ModuleBase_DoubleSpinBox.h>
 
 #include <Config_Keywords.h>
 #include <Config_WidgetAPI.h>
@@ -22,7 +23,6 @@
 #include <QTimer>
 #include <QDialog>
 #include <QLayout>
-#include <QDoubleSpinBox>
 
 ModuleBase_WidgetEditor::ModuleBase_WidgetEditor(QWidget* theParent,
                                                  const Config_WidgetAPI* theData,
index 881a6486d6437e3696ce2e271e86fc1170210fb2..b20f2e8d7065077c142cdde128f593c3f631d74f 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <ModuleBase_WidgetPoint2D.h>
 #include <ModuleBase_WidgetValueFeature.h>
+#include <ModuleBase_DoubleSpinBox.h>
 
 #include <Config_Keywords.h>
 #include <Config_WidgetAPI.h>
@@ -19,7 +20,6 @@
 
 #include <QGroupBox>
 #include <QGridLayout>
-#include <QDoubleSpinBox>
 #include <QLabel>
 #include <QEvent>
 #include <QKeyEvent>
@@ -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");
index e5d324a83e4519719c68fa53817a5e79ef4891d2..0151d14552be491a033517f70f01eb71fe7ee779 100644 (file)
@@ -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
 };
index 1a100c57bdf806eba251facd01c351b07a82e917..56496ebd15cdf0a6eab3b59e8fde136562a33f1c 100644 (file)
@@ -2,8 +2,9 @@
 // Created:     23 June 2014
 // Author:      Vitaly Smetannikov
 
-#include "ModuleBase_WidgetPoint2dDistance.h"
-#include "ModuleBase_WidgetValueFeature.h"
+#include <ModuleBase_WidgetPoint2dDistance.h>
+#include <ModuleBase_WidgetValueFeature.h>
+#include <ModuleBase_DoubleSpinBox.h>
 
 #include <GeomAPI_Pnt2d.h>
 #include <Config_WidgetAPI.h>
@@ -12,8 +13,6 @@
 #include <ModelAPI_Data.h>
 #include <ModelAPI_AttributeDouble.h>
 
-#include <QDoubleSpinBox>
-
 ModuleBase_WidgetPoint2dDistance::ModuleBase_WidgetPoint2dDistance(QWidget* theParent,
                                                                    const Config_WidgetAPI* theData,
                                                                    const std::string& theParentId)