Bläddra i källkod

Apply ValueProxy to widgets min and max

Julien Finet 12 år sedan
förälder
incheckning
b9d0ab9d97

+ 1 - 1
Libs/Core/Testing/Cpp/ctkLinearValueProxyTest.cpp

@@ -126,7 +126,7 @@ void ctkLinearValueProxyTester::testCoefficient_data()
   QTest::addColumn<double>("expectedProxyValue");
 
   QTest::newRow("Null coeff") << 0.0 << 0.0;
-  QTest::newRow("Very very small coeff") << 1e-26 << 0.0;
+  QTest::newRow("Very very small coeff") << 1e-26 << 1.32e-25;
   QTest::newRow("Not so small coeff") << 1e-6 << 1.32e-5;
   QTest::newRow("Normal coeff") << 2.0 << 26.4;
   QTest::newRow("Negative coeff") << -2.0 << -26.4;

+ 4 - 3
Libs/Core/Testing/Cpp/ctkUtilsTest.cpp

@@ -168,6 +168,7 @@ void ctkUtilsTester::testSignificantDecimals()
 void ctkUtilsTester::testSignificantDecimals_data()
 {
   QTest::addColumn<double>("value");
+  QTest::addColumn<int>("defaultDecimals");
   QTest::addColumn<int>("expectedDecimals");
 
   // Default decimals= -1
@@ -241,10 +242,10 @@ void ctkUtilsTester::testSignificantDecimals_data()
   QTest::newRow("inf -> 0") << std::numeric_limits<double>::infinity() << 3 << 0;
   QTest::newRow("-inf -> 0") << -std::numeric_limits<double>::infinity() << 3 << 0;
   QTest::newRow("nan -> -1") << std::numeric_limits<double>::quiet_NaN() << 3 << -1;
-  QTest::newRow("min -> 16") << std::numeric_limits<double>::min() << 3 << 16;
+  QTest::newRow("min -> 3") << std::numeric_limits<double>::min() << 3 << 3;
   QTest::newRow("max -> 0") << std::numeric_limits<double>::max() << 3 << 0;
-  QTest::newRow("denorm -> 16") << std::numeric_limits<double>::denorm_min()
-                                << 3 << 16;
+  QTest::newRow("denorm -> 3") << std::numeric_limits<double>::denorm_min()
+                                << 3 << 3;
 
 }
 

+ 68 - 97
Libs/Widgets/Testing/Cpp/ctkCoordinatesWidgetValueProxyTest.cpp

@@ -41,14 +41,14 @@ public:
 
   void getSpyReport(QString coordinatesString)
     {
-    ctkTest::COMPARE(AcknowledgedSignals, 1);
+    QCOMPARE(AcknowledgedSignals, 1);
     AcknowledgedSignals = 0;
 
     QStringList coordinatesList = coordinatesString.split(',');
-    ctkTest::COMPARE(coordinatesList.count(), this->Coordinates.size());
+    QCOMPARE(coordinatesList.count(), this->Coordinates.size());
     for (int i = 0; i < this->Coordinates.size(); ++i)
       {
-      ctkTest::COMPARE(this->Coordinates[i], coordinatesList[i].toDouble());
+      QCOMPARE(this->Coordinates[i], coordinatesList[i].toDouble());
       }
     this->Coordinates.clear();
     };
@@ -87,12 +87,14 @@ private slots:
 
   void testSetValue();
   void testSetValue_data();
+
+  void testPrecision();
+  void testPrecision_data();
 };
 
 //-----------------------------------------------------------------------------
 void ctkCoordinatesWidgetValueProxyTester::testSetValue()
 {
-  // Setup
   ctkCoordinatesWidget coordinatesWidget;
   coordinatesWidget.setMinimum(-200);
   coordinatesWidget.setMaximum(200);
@@ -129,47 +131,23 @@ void ctkCoordinatesWidgetValueProxyTester::testSetValue_data()
 
   //---------------------------------------------------------------------------
   // Offset
-  QTest::newRow("Offset only") << 1.0 << 42.19 << "0.1,0.2,0.3"
+  QTest::newRow("Offset only") << 1. << 42.19 << "0.1,0.2,0.3"
     << "0.1,0.2,0.3";
 
   QTest::newRow("Offset only: less than min")
-    << 1.0 << 42.19 << "-250.0,-900.0,-3000.0" << "-242.19,-242.19,-242.19";
+    << 1. << 42.19 << "-250.,-900.,-3000." << "-200,-200,-200";
   QTest::newRow("Offset only: less than min but ok with offset")
-    << 1.0 << 42.19 << "-240.3,-232.1,-200.01" << "-240.3,-232.1,-200.01";
+    << 1. << 42.19 << "-240.3,-232.1,-200.01" << "-200,-200,-200";
   QTest::newRow("Offset only: less than min with offset")
-    << 1.0 << -42.19 << "-160.15,-199.99,-159.0" << "-157.81,-157.81,-157.81";
+    << 1. << -42.19 << "-160.15,-199.99,-159." << "-160.15,-199.99,-159";
 
   QTest::newRow("Offset only: more than max with offset")
-    << 1.0 << 42.19 << "160.0,199.9,163.32" << "157.81,157.81,157.81";
+    << 1. << 42.19 << "160.,199.9,163.32" << "160,199.9,163.32";
   QTest::newRow("Offset only: more than max")
-    << 1.0 << -42.19 << "4830.0,250.01,1e6" << "242.19,242.19,242.19";
+    << 1. << -42.19 << "4830.,250.01,1e6" << "200,200,200";
   QTest::newRow("Offset only: less than max but ok with offset")
-    << 1.0 << -42.19 << "210.3,200.01,241.03" << "210.3,200.01,241.03";
-
-  QTest::newRow("Offset only: max")
-    << 1.0 << 42.19
-    << coordinatesFromValue(std::numeric_limits<double>::max())
-    << "157.81,157.81,157.81";
-
-  QTest::newRow("Offset only:  min")
-    << 1.0 << 42.19
-    << coordinatesFromValue(- std::numeric_limits<double>::max())
-    << "-242.19,-242.19,-242.19";
-
-  QTest::newRow("Offset only: infinity")
-    << 1.0 << 42.19
-    << coordinatesFromValue(std::numeric_limits<double>::infinity())
-    << "157.81,157.81,157.81";
+    << 1. << -42.19 << "210.3,200.01,241.03" << "200,200,200";
 
-  QTest::newRow("Offset only:  - infinity")
-    << 1.0 << 42.19
-    << coordinatesFromValue(- std::numeric_limits<double>::infinity())
-    << "-242.19,-242.19,-242.19";
-
-  QTest::newRow("Offset only: Nan")
-    << 1.0 << 42.19
-    << coordinatesFromValue(std::numeric_limits<double>::quiet_NaN())
-    << "157.81,157.81,157.81";
 
   //---------------------------------------------------------------------------
   // Coefficient
@@ -177,43 +155,19 @@ void ctkCoordinatesWidgetValueProxyTester::testSetValue_data()
     << "0.1,0.2,0.3";
 
   QTest::newRow("Coeff only: less than min")
-    << 5.0 << 0.0 << "-510.08,-2000,-1000000." << "-40,-40,-40";
+    << 5. << 0. << "-510.08,-2000,-1000000." << "-200,-200,-200";
   QTest::newRow("Coeff only: less than min but ok with coeff")
-    << 0.5 << 0.0 << "-250.08,-399.99,-120" << "-250.08,-399.99,-120";
+    << 0.5 << 0. << "-250.08,-399.99,-120" << "-200,-200,-120";
   QTest::newRow("Coeff only: less than min with coeff")
-    << 5.0 << 0.0<< "-42.08,-199.99,-40.01" << "-40,-40,-40";
+    << 5. << 0.<< "-42.08,-199.99,-40.01" << "-42.08,-199.99,-40.01";
 
   QTest::newRow("Coeff only: more than max with coeff")
-    << 5.0 << 0.0 << "160.08,40.01,199.99" << "40,40,40";
+    << 5. << 0. << "160.08,40.01,199.99" << "160.08,40.01,199.99";
   QTest::newRow("Coeff only: more than max")
-    << 5.0 << 0.0 << "510.08,2000,1000000." << "40,40,40";
+    << 5. << 0. << "510.08,2000,1000000." << "200,200,200";
   QTest::newRow("Offset only: more than max but ok with coeff")
-    << 0.5 << 0.0 << "380.08,399.99,200.01" << "380.08,399.99,200.01";
-
-  QTest::newRow("Offset only: max")
-    << 5.0 << 0.0
-    << coordinatesFromValue(std::numeric_limits<double>::max())
-    << "40,40,40";
-
-  QTest::newRow("Offset only:  min")
-    << 5.0 << 0.0
-    << coordinatesFromValue(- std::numeric_limits<double>::max())
-    << "-40,-40,-40";
-
-  QTest::newRow("Offset only: infinity")
-    << 5.0 << 0.0
-    << coordinatesFromValue(std::numeric_limits<double>::infinity())
-    << "40,40,40";
+    << 0.5 << 0. << "380.08,399.99,200.01" << "200,200,200";
 
-  QTest::newRow("Offset only:  - infinity")
-    << 5.0 << 0.0
-    << coordinatesFromValue(- std::numeric_limits<double>::infinity())
-    << "-40,-40,-40";
-
-  QTest::newRow("Offset only: Nan")
-    << 5.0 << 0.0
-    << coordinatesFromValue(std::numeric_limits<double>::quiet_NaN())
-    << "40,40,40";
 
   //---------------------------------------------------------------------------
   // Linear
@@ -221,43 +175,60 @@ void ctkCoordinatesWidgetValueProxyTester::testSetValue_data()
     << "0.1,0.2,0.3";
 
   QTest::newRow("Linear: less than min")
-    << 5.0 << 12.0 << "-510.08,-2000,-1000000." << "-42.4,-42.4,-42.4";
+    << 5.0 << 12.0 << "-510.08,-2000,-1000000." << "-200,-200,-200";
   QTest::newRow("Linear: less than min but ok with function")
-    << 0.5 << 12.0 << "-250.08,-411.99,-120" << "-250.08,-411.99,-120";
+    << 0.5 << 12.0 << "-250.08,-411.99,-120" << "-200,-200,-120";
   QTest::newRow("Linear: less than min with function")
-    << 5.0 << 12.0 << "-64.08,-199.99,-52.01" << "-42.4,-42.4,-42.4";
+    << 5.0 << 12.0 << "-64.08,-199.99,-52.01" << "-64.08,-199.99,-52.01";
 
   QTest::newRow("Linear: more than max with function")
-    << 5.0 << 12.0 << "64.08,189.99,37.61" << "37.6,37.6,37.6";
+    << 5.0 << 12.0 << "64.08,189.99,37.61" << "64.08,189.99,37.61";
   QTest::newRow("Linear: more than max")
-    << 5.0 << 12.0 << "200.01,900000.0,411.99" << "37.6,37.6,37.6";
-  QTest::newRow("Offset only: more than max but ok with function")
-    << 0.5 << 12.0 << "209.01,356.9,350.9" << "209.01,356.9,350.9";
-
-  QTest::newRow("Linear: max")
-    << 5.0 << 12.0
-    << coordinatesFromValue(std::numeric_limits<double>::max())
-    << "37.6,37.6,37.6";
-
-  QTest::newRow("Offset only:  min")
-    << 5.0 << 12.0
-    << coordinatesFromValue(- std::numeric_limits<double>::max())
-    << "-42.4,-42.4,-42.4";
-
-  QTest::newRow("Offset only: infinity")
-    << 5.0 << 12.0
-    << coordinatesFromValue(std::numeric_limits<double>::infinity())
-    << "37.6,37.6,37.6";
-
-  QTest::newRow("Offset only:  - infinity")
-    << 5.0 << 12.0
-    << coordinatesFromValue(- std::numeric_limits<double>::infinity())
-    << "-42.4,-42.4,-42.4";
-
-  QTest::newRow("Offset only: Nan")
-    << 5.0 << 12.0
-    << coordinatesFromValue(std::numeric_limits<double>::quiet_NaN())
-    << "37.6,37.6,37.6";
+    << 5.0 << 12.0 << "200.01,900000.0,411.99" << "200,200,200";
+  QTest::newRow("Linear: more than max but ok with function")
+    << 0.5 << 12.0 << "209.01,356.9,350.9" << "200,200,200";
+}
+
+//-----------------------------------------------------------------------------
+void ctkCoordinatesWidgetValueProxyTester::testPrecision()
+{
+  ctkCoordinatesWidget coordinatesWidget;
+  coordinatesWidget.setDecimalsOption(ctkDoubleSpinBox::DecimalsByValue);
+  double coordinates[3] = {0., 0., 0.};
+  coordinatesWidget.setCoordinates(coordinates);
+  coordinatesWidget.setDecimals(3);
+  coordinatesWidget.setSingleStep(0.001);
+  coordinatesWidget.setRange(-10000., 10000.);
+  coordinatesWidget.setDecimals(3);
+  coordinatesWidget.setSingleStep(0.001);
+
+
+  coordinates[0] = 1.;
+  coordinates[1] = 1.;
+  coordinates[1] = 1.;
+//  coordinatesWidget.setCoordinates(coordinates);
+
+  QFETCH(double, coefficient);
+
+  ctkLinearValueProxy proxy;
+  proxy.setCoefficient(coefficient);
+  coordinatesWidget.setValueProxy(&proxy);
+
+  coordinatesWidget.setCoordinates(coordinates);
+  coordinates[2] = 1.3;
+  coordinatesWidget.setCoordinates(coordinates);
+  const double* res = coordinatesWidget.coordinates();
+
+  QCOMPARE(coordinates[0], res[0]);
+  QCOMPARE(coordinates[1], res[1]);
+  QCOMPARE(coordinates[2], res[2]);
+}
+
+//-----------------------------------------------------------------------------
+void ctkCoordinatesWidgetValueProxyTester::testPrecision_data()
+{
+  QTest::addColumn<double>("coefficient");
+  QTest::newRow("1000000.") << 1000000.;
 }
 
 // ----------------------------------------------------------------------------

+ 22 - 1
Libs/Widgets/Testing/Cpp/ctkDoubleSliderValueProxyTest.cpp

@@ -51,17 +51,38 @@ class ctkDoubleSliderValueProxyTester: public QObject
   Q_OBJECT
 private slots:
 
+  void testSetValueProxy();
+
   void testSetValue();
   void testSetValue_data();
 
   void testSetSliderPosition();
   void testSetSliderPosition_data();
+
 };
 
 //-----------------------------------------------------------------------------
+void ctkDoubleSliderValueProxyTester::testSetValueProxy()
+{
+  ctkDoubleSlider slider;
+  slider.setRange(-200., 200.);
+  slider.setValue(-32.6);
+
+  ctkLinearValueProxy proxy;
+  proxy.setCoefficient(-1.);
+  proxy.setOffset(20.);
+
+  QSignalSpy valueSpy(&slider, SIGNAL(valueChanged(double)));
+  QSignalSpy rangeSpy(&slider, SIGNAL(rangeChanged(double,double)));
+  slider.setValueProxy(&proxy);
+
+  QCOMPARE(valueSpy.count(), 0);
+  QCOMPARE(rangeSpy.count(), 0);
+}
+
+//-----------------------------------------------------------------------------
 void ctkDoubleSliderValueProxyTester::testSetValue()
 {
-  // Setup
   ctkDoubleSlider slider;
   slider.setRange(-200., 200.);
   slider.setSingleStep(0.01);

+ 39 - 32
Libs/Widgets/Testing/Cpp/ctkDoubleSpinBoxValueProxyTest.cpp

@@ -41,7 +41,7 @@ void getSpyReport(QSignalSpy& spy, double expectedValue)
   QCOMPARE(spy.count(), 1);
 
   QList<QVariant> arguments = spy.takeFirst(); // take the first signal
-  ctkTest::COMPARE(arguments.at(0).toDouble(), expectedValue);
+  QCOMPARE(arguments.at(0).toDouble(), expectedValue);
 }
 
 //-----------------------------------------------------------------------------
@@ -112,47 +112,52 @@ void ctkDoubleSpinBoxValueProxyTester::testSetValue_data()
 
   //---------------------------------------------------------------------------
   // Offset
-  QTest::newRow("Offset only") << 1.0 << 42.19176 << 0.1 << 0.1 << "0.10";
+  QTest::newRow("Offset only") << 1. << 42.19 << 0.1 << 0.1 << "0.10";
+  // \tbd could be improved ?
+  QTest::newRow("Precision descrepancy 3")
+    << 1. << 42.197 << 0.1 << 0.103 << "0.10";
+  QTest::newRow("Precision descrepancy 4")
+    << 1. << 42.1971 << 0.1 << 0.1029 << "0.10";
 
   QTest::newRow("Offset only: less than min")
-    << 1.0 << -42.19176 << -220.0 << -200. << "-200.00";
+    << 1. << -42.19 << -220.0 << -200. << "-200.00";
   QTest::newRow("Offset only: less than min with offset")
-    << 1.0 << -42.1976 << -190.0 << -190. << "-190.00";
+    << 1. << -42.19 << -190.0 << -190. << "-190.00";
   QTest::newRow("Offset only: more than min")
-    << 1.0 << 42.1976 << -190.0 << -190. << "-190.00";
+    << 1. << 42.19 << -190.0 << -190. << "-190.00";
   QTest::newRow("Offset only: more than min with offset")
-    << 1.0 << 42.19176 << -220.0 << -200. << "-200.00";
+    << 1. << 42.19 << -220.0 << -200. << "-200.00";
 
   QTest::newRow("Offset only: more than max")
-    << 1.0 << 42.1976 << 220.0 << 200. << "200.00";
+    << 1. << 42.19 << 220.0 << 200. << "200.00";
   QTest::newRow("Offset only: more than max with offset")
-    << 1.0 << 42.19176 << 190.0 << 190. << "190.00";
+    << 1. << 42.19 << 190.0 << 190. << "190.00";
   QTest::newRow("Offset only: less than max")
-    << 1.0 << -42.1976 << 190.0 << 190. << "190.00";
+    << 1. << -42.19 << 190.0 << 190. << "190.00";
   QTest::newRow("Offset only: less than max with offset")
-    << 1.0 << -42.19176 << 220.0 << 200. << "200.00";
+    << 1. << -42.19 << 220.0 << 200. << "200.00";
 
   //---------------------------------------------------------------------------
   // Coefficient
   QTest::newRow("Coeff only") << 5.0 << 0.0 << 0.1 << 0.1 << "0.10";
 
   QTest::newRow("Coeff only: less than min")
-    << 5.0 << 0. << -220.0 << -200. << "-200.00";
+    << 5. << 0. << -220. << -200. << "-200.00";
   QTest::newRow("Coeff only: less than min with offset")
-    << 5.0 << 0. << -190.0 << -190. << "-190.00";
+    << 5. << 0. << -190. << -190. << "-190.00";
   QTest::newRow("Coeff only: more than min")
-    << 0.5 << 0. << -190.0 << -190. << "-190.00";
+    << 0.5 << 0. << -190. << -190. << "-190.00";
   QTest::newRow("Coeff only: more than min with offset")
-    << 0.5 << 0. << -220.0 << -200. << "-200.00";
+    << 0.5 << 0. << -220. << -200. << "-200.00";
 
   QTest::newRow("Coeff only: more than max")
-    << 5. << 0. << 220.0 << 200. << "200.00";
+    << 5. << 0. << 220. << 200. << "200.00";
   QTest::newRow("Coeff only: more than max with offset")
-    << 5. << 0. << 190.0 << 190. << "190.00";
+    << 5. << 0. << 190. << 190. << "190.00";
   QTest::newRow("Coeff only: less than max")
-    << 0.5 << 0. << 190.0 << 190. << "190.00";
+    << 0.5 << 0. << 190. << 190. << "190.00";
   QTest::newRow("Coeff only: less than max with offset")
-    << 0.5 << 0. << 220.0 << 200. << "200.00";
+    << 0.5 << 0. << 220. << 200. << "200.00";
 }
 
 //-----------------------------------------------------------------------------
@@ -184,7 +189,7 @@ void ctkDoubleSpinBoxValueProxyTester::testSetDisplayedValue()
   QFETCH(double, expectedDisplayValue);
   getSpyReport(valueSpy, expectedValue);
   getSpyReport(valueStringSpy, expectedStringValue);
-  ctkTest::COMPARE(spinBox.value(), expectedValue);
+  QCOMPARE(spinBox.value(), expectedValue);
   QCOMPARE(spinBox.displayedValue(), expectedDisplayValue);
 }
 
@@ -201,12 +206,12 @@ void ctkDoubleSpinBoxValueProxyTester::testSetDisplayedValue_data()
   //---------------------------------------------------------------------------
   // Offset
   QTest::newRow("Offset only")
-    << 1.0 << 42.19176 << 0.1 << -42.09 << "-42.09" << 0.1;
+    << 1.0 << 42.19 << 0.1 << -42.09 << "-42.09" << 0.1;
 
   QTest::newRow("Offset only: less than min")
-    << 1.0 << 42.19176 << -510.0 << -200. << "-200.00" << -157.81;
+    << 1.0 << 42.19 << -510.0 << -200. << "-200.00" << -157.81;
   QTest::newRow("Offset only: more than max")
-    << 1.0 << -42.1976 << 65010.0 << 200. << "200.00" << 157.8;
+    << 1.0 << -42.19 << 65010.0 << 200. << "200.00" << 157.81;
 
   //---------------------------------------------------------------------------
   // Coefficient
@@ -233,34 +238,36 @@ void ctkDoubleSpinBoxValueProxyTester::testSetCoefficient()
 {
   ctkDoubleSpinBox spinBox;
   spinBox.setRange(-10000., 10000.);
-  spinBox.setValue(10.);
+  spinBox.setValue(10.12);
 
   ctkLinearValueProxy proxy;
   proxy.setCoefficient(10.);
   spinBox.setValueProxy(&proxy);
 
-  ctkTest::COMPARE(spinBox.value(), 10.);
-  ctkTest::COMPARE(spinBox.displayedValue(), 100.);
+  QCOMPARE(spinBox.value(), 10.12);
+  QCOMPARE(spinBox.displayedValue(), 101.2);
 
   QFETCH(double, newCoefficient);
   proxy.setCoefficient(newCoefficient);
 
+  QFETCH(double, expectedValue);
   QFETCH(double, expectedDisplayedValue);
-  ctkTest::COMPARE(spinBox.value(), 10.);
-  ctkTest::COMPARE(spinBox.displayedValue(), expectedDisplayedValue);
+  QCOMPARE(spinBox.value(), expectedValue);
+  QCOMPARE(spinBox.displayedValue(), expectedDisplayedValue);
 }
 
 //-----------------------------------------------------------------------------
 void ctkDoubleSpinBoxValueProxyTester::testSetCoefficient_data()
 {
   QTest::addColumn<double>("newCoefficient");
+  QTest::addColumn<double>("expectedValue");
   QTest::addColumn<double>("expectedDisplayedValue");
 
-  QTest::newRow("100") << 100.0 << 1000.;
-  QTest::newRow("10") << 10.0 << 100.;
-  QTest::newRow("1") << 1.0 << 10.;
-  QTest::newRow("0.10") << 0.1 << 1.;
-  QTest::newRow("-10") << -10.0 << -100.;
+  QTest::newRow("100") << 100.0 << 10.12 << 1012.;
+  QTest::newRow("10") << 10.0 << 10.12 << 101.2;
+  QTest::newRow("1") << 1.0 << 10.12 << 10.12;
+  QTest::newRow("0.10") << 0.1 << 10.1 << 1.01;
+  QTest::newRow("-10") << -10.0 << 10.12 << -101.2;
 }
 
 // ----------------------------------------------------------------------------

+ 75 - 206
Libs/Widgets/Testing/Cpp/ctkRangeWidgetValueProxyTest.cpp

@@ -130,8 +130,8 @@ void ctkRangeWidgetValueProxyTester::testSetValues()
   QFETCH(double, expectedMin);
   QFETCH(double, expectedMax);
   //valuesSpy.getSpyReport(expectedMin, expectedMax);
-  ctkTest::COMPARE(ranger.minimumValue(), expectedMin);
-  ctkTest::COMPARE(ranger.maximumValue(), expectedMax);
+  QCOMPARE(ranger.minimumValue(), expectedMin);
+  QCOMPARE(ranger.maximumValue(), expectedMax);
 }
 
 //-----------------------------------------------------------------------------
@@ -144,126 +144,60 @@ void ctkRangeWidgetValueProxyTester::testSetValues_data()
   QTest::addColumn<double>("max");
   QTest::addColumn<double>("expectedMax");
 
-  const double max = std::numeric_limits<double>::max();
-  const double inf = std::numeric_limits<double>::infinity();
-  const double NaN = std::numeric_limits<double>::quiet_NaN();
-
   //---------------------------------------------------------------------------
   // Offset
-  QTest::newRow("Offset only") << 1.0 << 49.19 << 0.1 << 0.1 << 0.2 << 0.2;
+  QTest::newRow("Offset only") << 1. << 49.19 << 0.1 << 0.1 << 0.2 << 0.2;
 
   QTest::newRow("Offset only: max+offset < min+offset < -200")
-    << 1.0 << -42.19 << -160.0 << -157.81 << -190.9 << -157.81;
+    << 1. << -42.19 << -160. << -190.9 << -190.9 << -160.;
   QTest::newRow("Offset only: max+offset < -200 < min+offset")
-    << 1.0 << -42.19 << -0.1 << -157.81 << -160.9 << -0.1;
+    << 1. << -42.19 << -0.1 << -160.9 << -160.9 << -0.1;
   QTest::newRow("Offset only: -200 < max+offset < min+offset")
-    << 1.0 << 42.19<< -0.1 << -130.9 << -130.9 << -0.1;
+    << 1. << 42.19<< -0.1 << -130.9 << -130.9 << -0.1;
 
   QTest::newRow("Offset only: 200 < max+offset < min+offset")
-    << 1.0 << 42.19 << 160.0 << 157.81 << 190.9 << 157.81;
+    << 1. << 42.19 << 160. << 160. << 190.9 << 190.9;
   QTest::newRow("Offset only: max+offset < 200 < min+offset")
-    << 1.0 << 42.19 << 160.9 << -0.9 << -0.9 << 157.81;
+    << 1. << 42.19 << 160.9 << -0.9 << -0.9 << 160.9;
   QTest::newRow("Offset only: max+offset < min+offset < 200")
-    << 1.0 << 42.19 << 130.6 << -13.9 << -13.9 << 130.6;
-
-  QTest::newRow("Offset only: 200 < max = max_double = min = max_double")
-    << 1.0 << 42.19 << max << 157.81 << max << 157.81;
-  QTest::newRow("Offset only: max = -max_double < -200 < 200 < min = max_double")
-    << 1.0 << 42.19 << max << -242.19 << -max << 157.81;
-  QTest::newRow("Offset only: max = -max_double = min = -max_double < -200")
-    << 1.0 << 42.19 << -max << -242.19 << -max << -242.19;
-
-  QTest::newRow("Offset only: 200 < max = infinity = min = infinity")
-    << 1.0 << 42.19 << inf << 157.81 << inf << 157.81;
-  QTest::newRow("Offset only: max = -infinity < -200 < 200 < min = infinity")
-    << 1.0 << 42.19 << inf << -242.19 << -inf << 157.81;
-  QTest::newRow("Offset only: max = -infinity = min = -infinity < -200")
-    << 1.0 << 42.19 << - inf << -242.19 << - inf << -242.19;
-
-  QTest::newRow("Offset only: max = min = NaN")
-    << 1.0 << 42.19 << NaN << 157.81 << NaN << 157.81;
-  QTest::newRow("Offset only: max = NaN && min > 200")
-    << 1.0 << 42.19 << 630.0 << 157.81 << NaN << 157.81;
-  QTest::newRow("Offset only: min = NaN && max < -200")
-    << 1.0 << 42.19 << NaN << -32.6 << -794348.12 << -32.6;
+    << 1. << 42.19 << 130.6 << -13.9 << -13.9 << 130.6;
 
   //---------------------------------------------------------------------------
   // Coefficient
-  QTest::newRow("Coeff only") << 5.0 << 0.0 << 0.1 << 0.1 << 0.2 << 0.2;
+  QTest::newRow("Coeff only") << 5. << 0. << 0.1 << 0.1 << 0.2 << 0.2;
 
   QTest::newRow("Coeff only: max*coeff < min*coeff < -200")
-    << 5.0 << 0.0 << -160.0 << -40.0 << -190.9 << -40.0;
+    << 5. << 0. << -160. << -190.9 << -190.9 << -160.;
   QTest::newRow("Coeff only: max*coeff < -200 < min*coeff")
-    << 5.0 << 0.0 << -0.1 << -40.0 << -160.9 << -0.1;
+    << 5. << 0. << -0.1 << -160.9 << -160.9 << -0.1;
   QTest::newRow("Coeff only: -200 < max*coeff < min*coeff")
-    << 5.0 << 0.0 << -0.1 << -20.9 << -20.9 << -0.1;
+    << 5. << 0. << -0.1 << -20.9 << -20.9 << -0.1;
 
   QTest::newRow("Coeff only: 200 < max*coeff < min*coeff")
-    << 5.0 << 0.0 << 160.0 << 40.0 << 190.9 << 40.0;
+    << 5. << 0. << 160. << 160. << 190.9 << 190.9;
   QTest::newRow("Coeff only: max*coeff < 200 < min*coeff")
-    << 5.0 << 0.0 << 160.9 << -0.9 << -0.9 << 40.00;
+    << 5. << 0. << 160.9 << -0.9 << -0.9 << 160.9;
   QTest::newRow("Coeff only: max*coeff < min*coeff < 200")
-    << 5.0 << 0.0 << 13.6 << -13.9 << -13.9 << 13.6;
-
-  QTest::newRow("Coeff only: 200 < max = max_double = min = max_double")
-    << 5.0 << 0.0 << max << 40.0 << max << 40.0;
-  QTest::newRow("Coeff only: max = -max_double < -200 < 200 < min = max_double")
-    << 5.0 << 0.0 << max << -40.0 << - max << 40.0;
-  QTest::newRow("Coeff only: max = -max_double = min = -max_double < -200")
-    << 5.0 << 0.0 << -max << -40.0 << -max << -40.0;
-
-  QTest::newRow("Coeff only: 200 < max = infinity = min = infinity")
-    << 5.0 << 0.0 << inf << 40.0 << inf << 40.0;
-  QTest::newRow("Coeff only: max = -infinity < -200 < 200 < min = infinity")
-    << 5.0 << 0.0 << inf << -40.0 << -inf << 40.0;
-  QTest::newRow("Coeff only: max = -infinity = min = -infinity < -200")
-    << 5.0 << 0.0 << - inf << -40.0 << - inf << -40.0;
-
-  QTest::newRow("Coeff only: max = min = NaN")
-    << 5.0 << 0.0 << NaN << 40.0 << NaN << 40.0;
-  QTest::newRow("Coeff only: max = NaN && min > 200")
-    << 5.0 << 0.0 << 630.0 << 40.0 << NaN << 40.0;
-  QTest::newRow("Coeff only: min = NaN && max < -200")
-    << 5.0 << 0.0 << NaN << -32.6 << -794348.12 << -32.6;
+    << 5. << 0. << 13.6 << -13.9 << -13.9 << 13.6;
 
   //---------------------------------------------------------------------------
   // Linear
-  QTest::newRow("Linear") << 5.0 << 12.0 << 0.1 << 0.1 << 0.2 << 0.2;
+  QTest::newRow("Linear") << 5.0 << 12. << 0.1 << 0.1 << 0.2 << 0.2;
 
   QTest::newRow("Linear:f(max) < f(min) < -200")
-    << 5.0 << 12.0 << -160.0 << -42.4 << -190.9 << -42.4;
+    << 5. << 12. << -160. << -190.9 << -190.9 << -160.;
   QTest::newRow("Linear: f(max) < -200 < f(min)")
-    << 5.0 << 12.0 << -0.1 << -42.4 << -160.9 << -0.1;
+    << 5. << 12. << -0.1 << -160.9 << -160.9 << -0.1;
   QTest::newRow("Linear: -200 < f(max) < f(min)")
-    << 5.0 << 12.0 << -0.1 << -20.9 << -20.9 << -0.1;
+    << 5. << 12. << -0.1 << -20.9 << -20.9 << -0.1;
 
   QTest::newRow("Linear: 200 < f(max) < f(min)")
-    << 5.0 << 12.0 << 160.0 << 37.6 << 190.9 << 37.6;
+    << 5. << 12. << 160.0 << 160.0 << 190.9 << 190.9;
   QTest::newRow("Linear: f(max) < 200 < f(min)")
-    << 5.0 << 12.0 << 160.9 << -0.9 << -0.9 << 37.6;
+    << 5. << 12. << 160.9 << -0.9 << -0.9 << 160.9;
   QTest::newRow("Linear: f(max) < f(min) < 200")
-    << 5.0 << 12.0 << 13.6 << -13.9 << -13.9 << 13.6;
-
-  QTest::newRow("Linear: 200 < max = max_double = min = max_double")
-    << 5.0 << 12.0 << max << 37.6 << max << 37.6;
-  QTest::newRow("Linear: max = -max_double < -200 < 200 < min = max_double")
-    << 5.0 << 12.0 << max << -42.4 << - max << 37.6;
-  QTest::newRow("Linear: max = -max_double = min = -max_double < -200")
-    << 5.0 << 12.0 << -max << -42.4 << -max << -42.4;
-
-  QTest::newRow("Linear: 200 < max = infinity = min = infinity")
-    << 5.0 << 12.0 << inf << 37.6 << inf << 37.6;
-  QTest::newRow("Linear: max = -infinity < -200 < 200 < min = infinity")
-    << 5.0 << 12.0 << inf << -42.4 << -inf << 37.6;
-  QTest::newRow("Linear: max = -infinity = min = -infinity < -200")
-    << 5.0 << 12.0 << - inf << -42.4 << - inf << -42.4;
-
-  QTest::newRow("Linear: max = min = NaN")
-    << 5.0 << 12.0 << NaN << 37.6 << NaN << 37.6;
-  QTest::newRow("Linear: max = NaN && f(min) > 200")
-    << 5.0 << 12.0 << 630.0 << 37.6 << NaN << 37.6;
-  QTest::newRow("Linear: min = NaN && f(max) < -200")
-    << 5.0 << 12.0 << NaN << -32.6 << -794348.12 << -32.6;
+    << 5. << 12. << 13.6 << -13.9 << -13.9 << 13.6;
+
 }
 
 //-----------------------------------------------------------------------------
@@ -292,7 +226,8 @@ void ctkRangeWidgetValueProxyTester::testSetMinValue()
 
   QFETCH(double, expectedValue);
   //getSpyReport(valueSpy, expectedValue);
-  ctkTest::COMPARE(ranger.minimumValue(), expectedValue);
+  QCOMPARE(ranger.minimumValue(), expectedValue);
+  QVERIFY(ranger.minimumValue() <= ranger.maximumValue());
 }
 
 //-----------------------------------------------------------------------------
@@ -305,88 +240,55 @@ void ctkRangeWidgetValueProxyTester::testSetMinValue_data()
 
   //---------------------------------------------------------------------------
   // Offset
-  QTest::newRow("Offset only") << 1.0 << 42.19 << 0.1 << 0.1;
+  QTest::newRow("Offset only") << 1. << 42.19 << 0.1 << 0.1;
 
   QTest::newRow("Offset only: less than min")
-    << 1.0 << 42.19 << -510.0 << -242.19;
+    << 1. << 42.19 << -510. << -200.;
   QTest::newRow("Offset only: less than min but ok with offset")
-    << 1.0 << 42.19 << -230.0 << -230.0;
+    << 1. << 42.19 << -230. << -200.;
   QTest::newRow("Offset only: less than min with offset")
-    << 1.0 << -42.19 << -190.0 << -157.81;
+    << 1. << -42.19 << -190. << -190.;
 
   QTest::newRow("Offset only: more than max with offset")
-    << 1.0 << 42.19 << 160.0 << 99.;
+    << 1. << 42.19 << 160. << 99.;
   QTest::newRow("Offset only: more than max")
-    << 1.0 << -42.19 << 65010.0 << 99.;
-
-  QTest::newRow("Offset only: max")
-    << 1.0 << 42.19 << std::numeric_limits<double>::max() << 99.;
-  QTest::newRow("Offset only:  min")
-    << 1.0 << 42.19 << -std::numeric_limits<double>::max() << -242.19;
-  QTest::newRow("Offset only: infinity")
-    << 1.0 << 42.19 << std::numeric_limits<double>::infinity() << 99.;
-  QTest::newRow("Offset only:  - infinity")
-    << 1.0 << 42.19 << -std::numeric_limits<double>::infinity() << -242.19;
-  QTest::newRow("Offset only: Nan")
-    << 1.0 << 42.19 << std::numeric_limits<double>::quiet_NaN() << 99.;
+    << 1. << -42.19 << 65010.0 << 99.;
 
   //---------------------------------------------------------------------------
   // Coefficient
-  QTest::newRow("Coeff only") << 5.0 << 0.0 << 0.1 << 0.1;
+  QTest::newRow("Coeff only") << 5. << 0. << 0.1 << 0.1;
 
   QTest::newRow("Coeff only: less than min")
-    << 5.0 << 0.0 << -510.0 << -40.0;
+    << 5. << 0. << -510. << -200.;
   QTest::newRow("Coeff only: less than min but ok with coeff")
-    << 0.5 << 0.0 << -230.0 << -230.0;
+    << 0.5 << 0. << -230.0 << -200.0;
   QTest::newRow("Coeff only: less than min with coeff")
-    << 5.0 << 0.0 << -190.0 << -40.0;
+    << 5. << 0. << -190.0 << -190.0;
 
   QTest::newRow("Coeff only: more than max with coeff")
-    << 5.0 << 0.0 << 160.0 << 40.0;
+    << 5. << 0. << 160. << 99.;
   QTest::newRow("Coeff only: more than max")
-    << 5.0 << 0.0 << 65010.0 << 40.0;
+    << 5. << 0. << 65010. << 99.;
   QTest::newRow("Coeff only: less than max but ok with coeff")
-    << 0.5 << 0.0 << 229.2 << 99.;
-
-  QTest::newRow("Coeff only: max")
-    << 5.0 << 0.0 << std::numeric_limits<double>::max() << 40.0;
-  QTest::newRow("Coeff only:  min")
-    << 5.0 << 0.0 << -std::numeric_limits<double>::max() << -40.0;
-  QTest::newRow("Coeff only: infinity")
-    << 5.0 << 0.0 << std::numeric_limits<double>::infinity() << 40.0;
-  QTest::newRow("Coeff only:  - infinity")
-    << 5.0 << 0.0 << -std::numeric_limits<double>::infinity() << -40.0;
-  QTest::newRow("Coeff only: Nan")
-    << 5.0 << 0.0 << std::numeric_limits<double>::quiet_NaN() << 40.0;
+    << 0.5 << 0. << 229.2 << 99.;
 
   //---------------------------------------------------------------------------
   // Linear
-  QTest::newRow("Linear") << 5.0 << 0.0 << 0.1 << 0.1;
+  QTest::newRow("Linear") << 5. << 0. << 0.1 << 0.1;
 
   QTest::newRow("Linear: less than min")
-    << 5.0 << 12.0 << -510.0 << -42.4;
+    << 5. << 12. << -510. << -200.;
   QTest::newRow("Linear: less than min but ok with function")
-    << 0.5 << 12.0 << -230.0 << -230.0;
+    << 0.5 << 12. << -230. << -200.;
   QTest::newRow("Linear: less than min with function")
-    << 5.0 << 12.0 << -61.5 << -42.4;
+    << 5. << 12. << -61.5 << -61.5;
 
   QTest::newRow("Linear: more than max with function")
-    << 5.0 << 12.0 << 160.0 << 37.6;
+    << 5. << 12. << 160.0 << 99.;
   QTest::newRow("Linear: more than max")
-    << 5.0 << 12.0 << 65010.0 << 37.6;
+    << 5. << 12. << 65010. << 99.;
   QTest::newRow("Linear: less than max but ok with function")
-    << 0.5 << 12.0 << 229.2 << 99.;
-
-  QTest::newRow("Linear: max")
-    << 5.0 << 12.0 << std::numeric_limits<double>::max() << 37.6;
-  QTest::newRow("Linear:  min")
-    << 5.0 << 12.0 << -std::numeric_limits<double>::max() << -42.4;
-  QTest::newRow("Linear: infinity")
-    << 5.0 << 12.0 << std::numeric_limits<double>::infinity() << 37.6;
-  QTest::newRow("Linear:  - infinity")
-    << 5.0 << 12.0 << -std::numeric_limits<double>::infinity() << -42.4;
-  QTest::newRow("Linear: Nan")
-    << 5.0 << 12.0 << std::numeric_limits<double>::quiet_NaN() << 37.6;
+    << 0.5 << 12. << 229.2 << 99.;
 }
 
 //-----------------------------------------------------------------------------
@@ -432,87 +334,54 @@ void ctkRangeWidgetValueProxyTester::testSetMaxValue_data()
   QTest::newRow("Offset only") << 1.0 << 42.19 << 0.1 << 0.1;
 
   QTest::newRow("Offset only: less than min")
-    << 1.0 << 42.19 << -510.0 << -32.6;
+    << 1. << 42.19 << -510. << -32.6;
   QTest::newRow("Offset only: less than min but ok with offset")
-    << 1.0 << 42.19 << -230.0 << -32.6;
+    << 1. << 42.19 << -230. << -32.6;
   QTest::newRow("Offset only: less than min with offset")
-    << 1.0 << -42.19 << -190.0 << -32.6;
+    << 1. << -42.19 << -190. << -32.6;
 
   QTest::newRow("Offset only: more than max with offset")
-    << 1.0 << 42.19 << 160.0 << 157.81;
+    << 1. << 42.19 << 160. << 160.;
   QTest::newRow("Offset only: more than max")
-    << 1.0 << -42.19 << 65010.0 << 242.19;
+    << 1. << -42.19 << 65010.0 << 200.;
   QTest::newRow("Offset only: less than max but ok with offset")
-    << 1.0 << -42.19 << 229.1 << 229.1;
-
-  QTest::newRow("Offset only: max")
-    << 1.0 << 42.19 << std::numeric_limits<double>::max() << 157.81;
-  QTest::newRow("Offset only: min")
-    << 1.0 << 42.19 << -std::numeric_limits<double>::max() << -32.6;
-  QTest::newRow("Offset only: infinity")
-    << 1.0 << 42.19 << std::numeric_limits<double>::infinity() << 157.81;
-  QTest::newRow("Offset only: -infinity")
-    << 1.0 << 42.19 << -std::numeric_limits<double>::infinity() << -32.6;
-  QTest::newRow("Offset only: Nan")
-    << 1.0 << 42.19 << std::numeric_limits<double>::quiet_NaN() << 157.81;
+    << 1. << -42.19 << 229.1 << 200.;
 
   //---------------------------------------------------------------------------
   // Coefficient
-  QTest::newRow("Coeff only") << 5.0 << 0.0 << 0.1 << 0.1;
+  QTest::newRow("Coeff only") << 5.0 << 0. << 0.1 << 0.1;
 
   QTest::newRow("Coeff only: less than min")
-    << 5.0 << 0.0 << -510.0 << -32.6;
+    << 5. << 0. << -510. << -32.6;
   QTest::newRow("Coeff only: less than min but ok with coeff")
-    << 0.5 << 0.0 << -230.0 << -32.6;;
+    << 0.5 << 0. << -230. << -32.6;
   QTest::newRow("Coeff only: less than min with coeff")
-    << 5.0 << 0.0 << -190.0 << -32.6;
+    << 5. << 0. << -190. << -32.6;
 
   QTest::newRow("Coeff only: more than max with coeff")
-    << 5.0 << 0.0 << 160.0 << 40.0;
+    << 5. << 0. << 160. << 160.;
   QTest::newRow("Coeff only: more than max")
-    << 5.0 << 0.0 << 65010.0 << 40.0;
+    << 5. << 0. << 65010. << 200.;
   QTest::newRow("Offset only: less than max but ok with coeff")
-    << 0.5 << 0.0 << 229.2 << 229.2;
-
-  QTest::newRow("Coeff only: max")
-    << 5.0 << 0.0 << std::numeric_limits<double>::max() << 40.0;
-  QTest::newRow("Coeff only: min")
-    << 5.0 << 0.0 << -std::numeric_limits<double>::max() << -32.6;
-  QTest::newRow("Coeff only: infinity")
-    << 5.0 << 0.0 << std::numeric_limits<double>::infinity() << 40.0;
-  QTest::newRow("Coeff only: -infinity")
-    << 5.0 << 0.0 << -std::numeric_limits<double>::infinity() << -32.6;
-  QTest::newRow("Coeff only: Nan")
-    << 5.0 << 0.0 << std::numeric_limits<double>::quiet_NaN() << 40.0;
+    << 0.5 << 0. << 229.2 << 200.;
 
   //---------------------------------------------------------------------------
   // Linear
-  QTest::newRow("Linear") << 5.0 << 0.0 << 0.1 << 0.1;
+  QTest::newRow("Linear") << 5. << 0. << 0.1 << 0.1;
 
   QTest::newRow("Linear: less than min")
-    << 5.0 << 12.0 << -510.0 << -32.6;
+    << 5. << 12. << -510. << -32.6;
   QTest::newRow("Linear: less than min but ok with function")
-    << 0.5 << 12.0 << -230.0 << -32.6;
+    << 0.5 << 12. << -230. << -32.6;
   QTest::newRow("Linear: less than min with function")
-    << 5.0 << 12.0 << -61.5 << -32.6;
+    << 5. << 12. << -61.5 << -32.6;
 
   QTest::newRow("Linear: more than max with function")
-    << 5.0 << 12.0 << 160.0 << 37.6;
+    << 5. << 12. << 160. << 160.;
   QTest::newRow("Linear: more than max")
-    << 5.0 << 12.0 << 65010.0 << 37.6;
+    << 5. << 12. << 65010.0 << 200.;
   QTest::newRow("Linear: less than max but ok with function")
-    << 0.5 << 12.0 << 229.2 << 229.2;
-
-  QTest::newRow("Linear: max")
-    << 5.0 << 12.0 << std::numeric_limits<double>::max() << 37.6;
-  QTest::newRow("Linear: min")
-    << 5.0 << 12.0 << -std::numeric_limits<double>::max() << -32.6;
-  QTest::newRow("Linear: infinity")
-    << 5.0 << 12.0 << std::numeric_limits<double>::infinity() << 37.6;
-  QTest::newRow("Linear: -infinity")
-    << 5.0 << 12.0 << -std::numeric_limits<double>::infinity() << -32.6;
-  QTest::newRow("Linear: Nan")
-    << 5.0 << 12.0 << std::numeric_limits<double>::quiet_NaN() << 37.6;
+    << 0.5 << 12. << 229.2 << 200.;
 }
 
 //-----------------------------------------------------------------------------
@@ -526,21 +395,21 @@ void ctkRangeWidgetValueProxyTester::testSetCoefficient()
   proxy.setCoefficient(10.);
   rangeWidget.setValueProxy(&proxy);
 
-  ctkTest::COMPARE(rangeWidget.minimumValue(), 10.);
-  ctkTest::COMPARE(rangeWidget.maximumValue(), 50.);
-  ctkTest::COMPARE(rangeWidget.minimumSpinBox()->displayedValue(), 100.);
-  ctkTest::COMPARE(rangeWidget.maximumSpinBox()->displayedValue(), 500.);
+  QCOMPARE(rangeWidget.minimumValue(), 10.);
+  QCOMPARE(rangeWidget.maximumValue(), 50.);
+  QCOMPARE(rangeWidget.minimumSpinBox()->displayedValue(), 100.);
+  QCOMPARE(rangeWidget.maximumSpinBox()->displayedValue(), 500.);
 
   QFETCH(double, newCoefficient);
   proxy.setCoefficient(newCoefficient);
 
   QFETCH(double, expectedMinimumDisplayedValue);
   QFETCH(double, expectedMaximumDisplayedValue);
-  ctkTest::COMPARE(rangeWidget.minimumValue(), 10.);
-  ctkTest::COMPARE(rangeWidget.maximumValue(), 50.);
-  ctkTest::COMPARE(rangeWidget.minimumSpinBox()->displayedValue(),
+  QCOMPARE(rangeWidget.minimumValue(), 10.);
+  QCOMPARE(rangeWidget.maximumValue(), 50.);
+  QCOMPARE(rangeWidget.minimumSpinBox()->displayedValue(),
                    expectedMinimumDisplayedValue);
-  ctkTest::COMPARE(rangeWidget.maximumSpinBox()->displayedValue(),
+  QCOMPARE(rangeWidget.maximumSpinBox()->displayedValue(),
                    expectedMaximumDisplayedValue);
 }
 
@@ -555,7 +424,7 @@ void ctkRangeWidgetValueProxyTester::testSetCoefficient_data()
   QTest::newRow("10") << 10.0 << 100. << 500.;
   QTest::newRow("1") << 1.0 << 10. << 50.;
   QTest::newRow("0.10") << 0.1 << 1. << 5.;
-  QTest::newRow("-10") << -10.0 << -500. << -100.;
+  QTest::newRow("-10") << -10.0 << -100. << -500.;
 }
 
 // ----------------------------------------------------------------------------

+ 73 - 59
Libs/Widgets/Testing/Cpp/ctkSliderWidgetValueProxyTest.cpp

@@ -43,7 +43,7 @@ void getSpyReport(QSignalSpy& spy, double expectedValue)
   QCOMPARE(spy.count(), 1);
 
   QList<QVariant> arguments = spy.takeFirst(); // take the first signal
-  ctkTest::COMPARE(arguments.at(0).toDouble(), expectedValue);
+  QCOMPARE(arguments.at(0).toDouble(), expectedValue);
 }
 } // end namespace
 
@@ -51,13 +51,16 @@ void getSpyReport(QSignalSpy& spy, double expectedValue)
 class ctkSliderWidgetValueProxyTester: public QObject
 {
   Q_OBJECT
-private slots:
 
+private slots:
   void testSetValue();
   void testSetValue_data();
 
   void testSetCoefficient();
   void testSetCoefficient_data();
+
+  void testDecimalsOption();
+  void testDecimalsOption_data();
 };
 
 //-----------------------------------------------------------------------------
@@ -85,7 +88,7 @@ void ctkSliderWidgetValueProxyTester::testSetValue()
 
   QFETCH(double, expectedValue);
   getSpyReport(valueSpy, expectedValue);
-  ctkTest::COMPARE(slider.value(), expectedValue);
+  QCOMPARE(slider.value(), expectedValue);
 }
 
 //-----------------------------------------------------------------------------
@@ -98,95 +101,62 @@ void ctkSliderWidgetValueProxyTester::testSetValue_data()
 
   //---------------------------------------------------------------------------
   // Offset
-  QTest::newRow("Offset only") << 1.0 << 42.19176 << 0.1 << 0.1;
+  QTest::newRow("Offset only") << 1.0 << 42.19 << 0.1 << 0.1;
+  QTest::newRow("Offset only: lost precision") << 1.0 << 42.19176 << 0.1 << 0.09824;
 
   QTest::newRow("Offset only: less than min")
-    << 1.0 << 42.19 << -510.0 << -242.19;
+    << 1.0 << 42.19 << -510.0 << -200.;
   QTest::newRow("Offset only: less than min but ok with offset")
-    << 1.0 << 42.19 << -230.0 << -230.0;
+    << 1.0 << 42.19 << -230.0 << -200.;
   QTest::newRow("Offset only: less than min with offset")
-    << 1.0 << -42.19 << -190.0 << -157.81;
+    << 1.0 << -42.19 << -190.0 << -190.0;
 
   QTest::newRow("Offset only: more than max with offset")
-    << 1.0 << 42.19 << 160.0 << 157.81;
+    << 1.0 << 42.19 << 160.0 << 160.;
   QTest::newRow("Offset only: more than max")
-    << 1.0 << -42.19 << 65010.0 << 242.19;
+    << 1.0 << -42.19 << 65010.0 << 200.;
   QTest::newRow("Offset only: less than max but ok with offset")
-    << 1.0 << -42.19 << 229.1 << 229.1;
-
-  QTest::newRow("Offset only: max")
-    << 1.0 << 42.19 << std::numeric_limits<double>::max() << 157.81;
-  QTest::newRow("Offset only:  min")
-    << 1.0 << 42.19 << -std::numeric_limits<double>::max() << -242.19;
-  QTest::newRow("Offset only: infinity")
-    << 1.0 << 42.19 << std::numeric_limits<double>::infinity() << 157.81;
-  QTest::newRow("Offset only:  - infinity")
-    << 1.0 << 42.19 << -std::numeric_limits<double>::infinity() << -242.19;
-  QTest::newRow("Offset only: Nan")
-    << 1.0 << 42.19 << std::numeric_limits<double>::quiet_NaN() << 157.81;
+    << 1.0 << -42.19 << 229.1 << 200.;
 
   // coeff // offset // value // expectedValue
   //---------------------------------------------------------------------------
   // Coefficient
-  QTest::newRow("Coeff only") << 5.0 << 0.0 << 0.1 << 0.1;
+  QTest::newRow("Coeff only") << 5. << 0. << 0.1 << 0.1;
 
   QTest::newRow("Coeff only: less than min")
-    << 5.0 << 0.0 << -510.0 << -40.0;
+    << 5. << 0. << -510. << -200.;
   QTest::newRow("Coeff only: less than min but ok with coeff")
-    << 0.5 << 0.0 << -230.0 << -230.0;
+    << 0.5 << 0. << -230. << -200.;
   QTest::newRow("Coeff only: less than min with coeff")
-    << 5.0 << 0.0 << -190.0 << -40.0;
+    << 5. << 0. << -190. << -190.;
 
   QTest::newRow("Coeff only: more than max with coeff")
-    << 5.0 << 0.0 << 160.0 << 40.0;
+    << 5. << 0. << 160. << 160.;
   QTest::newRow("Coeff only: more than max")
-    << 5.0 << 0.0 << 65010.0 << 40.0;
+    << 5. << 0. << 65010.0 << 200.;
   QTest::newRow("Offset only: less than max but ok with coeff")
-    << 0.5 << 0.0 << 229.2 << 229.2;
-
-  QTest::newRow("Coeff only: max")
-    << 5.0 << 0.0 << std::numeric_limits<double>::max() << 40.0;
-  QTest::newRow("Coeff only:  min")
-    << 5.0 << 0.0 << -std::numeric_limits<double>::max() << -40.0;
-  QTest::newRow("Coeff only: infinity")
-    << 5.0 << 0.0 << std::numeric_limits<double>::infinity() << 40.0;
-  QTest::newRow("Coeff only:  - infinity")
-    << 5.0 << 0.0 << -std::numeric_limits<double>::infinity() << -40.0;
-  QTest::newRow("Coeff only: Nan")
-    << 5.0 << 0.0 << std::numeric_limits<double>::quiet_NaN() << 40.0;
+    << 0.5 << 0. << 229.2 << 200.;
 
   // coeff // offset // value // expectedValue
   //---------------------------------------------------------------------------
   // Linear
-  QTest::newRow("Linear") << 5.0 << 0.0 << 0.1 << 0.1;
+  QTest::newRow("Linear") << 5. << 0. << 0.1 << 0.1;
 
   QTest::newRow("Linear: less than min")
-    << 5.0 << 12.0 << -510.0 << -42.4;
+    << 5. << 12. << -510.0 << -200.;
   QTest::newRow("Linear: less than min but ok with function")
-    << 0.5 << 12.0 << -230.0 << -230.0;
+    << 0.5 << 12. << -230. << -200.;
   QTest::newRow("Linear: less than min with function")
-    << 5.0 << 12.0 << -61.5 << -42.4;
+    << 5. << 12. << -61.5 << -61.5;
 
   QTest::newRow("Linear: more than max with function")
-    << 5.0 << 12.0 << 160.0 << 37.6;
+    << 5. << 12. << 160. << 160.;
   QTest::newRow("Linear: more than max")
-    << 5.0 << 12.0 << 65010.0 << 37.6;
-  QTest::newRow("Offset only: less than max but ok with function")
-    << 0.5 << 12.0 << 229.2 << 229.2;
-
-  QTest::newRow("Linear: max")
-    << 5.0 << 12.0 << std::numeric_limits<double>::max() << 37.6;
-  QTest::newRow("Linear:  min")
-    << 5.0 << 12.0 << -std::numeric_limits<double>::max() << -42.4;
-  QTest::newRow("Linear: infinity")
-    << 5.0 << 12.0 << std::numeric_limits<double>::infinity() << 37.6;
-  QTest::newRow("Linear:  - infinity")
-    << 5.0 << 12.0 << -std::numeric_limits<double>::infinity() << -42.4;
-  QTest::newRow("Linear: Nan")
-    << 5.0 << 12.0 << std::numeric_limits<double>::quiet_NaN() << 37.6;
+    << 5. << 12. << 65010. << 200.;
+  QTest::newRow("Linear: less than max but ok with function")
+    << 0.5 << 12. << 229.2 << 200.;
 }
 
-
 //-----------------------------------------------------------------------------
 void ctkSliderWidgetValueProxyTester::testSetCoefficient()
 {
@@ -223,6 +193,50 @@ void ctkSliderWidgetValueProxyTester::testSetCoefficient_data()
   QTest::newRow("-10") << -10.0 << -100.;
 }
 
+
+//-----------------------------------------------------------------------------
+void ctkSliderWidgetValueProxyTester::testDecimalsOption()
+{
+  ctkSliderWidget sliderWidget;
+  sliderWidget.setRange(-10000., 10000.);
+  sliderWidget.setDecimals(3);
+  sliderWidget.setValue(6.924);
+
+  QFETCH(int, decimalsOption);
+  sliderWidget.spinBox()->setDecimalsOption(
+    static_cast<ctkDoubleSpinBox::DecimalsOptions>(decimalsOption));
+
+  ctkLinearValueProxy proxy;
+  sliderWidget.setValueProxy(&proxy);
+
+  QFETCH(double, coefficient);
+  proxy.setCoefficient(coefficient);
+
+  // test if it does not trigger asserts
+  sliderWidget.setRange(-10000., 10000.);
+  if (decimalsOption & ctkDoubleSpinBox::DecimalsByValue)
+    {
+    QCOMPARE(sliderWidget.spinBox()->value(), 6.924);
+    }
+
+  proxy.setCoefficient(1.);
+}
+
+//-----------------------------------------------------------------------------
+void ctkSliderWidgetValueProxyTester::testDecimalsOption_data()
+{
+  QTest::addColumn<int>("decimalsOption");
+  QTest::addColumn<double>("coefficient");
+
+  for (double coef = 1.; coef < 10000000.; coef *= 10.)
+    {
+    QTest::newRow("coef by value") << static_cast<int>(ctkDoubleSpinBox::DecimalsByValue) << coef;
+    QTest::newRow("coef by value") << static_cast<int>(ctkDoubleSpinBox::FixedDecimals) << coef;
+    QTest::newRow("1./coef by value") << static_cast<int>(ctkDoubleSpinBox::DecimalsByValue) << 1. / coef;
+    QTest::newRow("1./coef") << static_cast<int>(ctkDoubleSpinBox::FixedDecimals) << 1./ coef;
+    }
+}
+
 // ----------------------------------------------------------------------------
 CTK_TEST_MAIN(ctkSliderWidgetValueProxyTest)
 #include "moc_ctkSliderWidgetValueProxyTest.cpp"

+ 92 - 71
Libs/Widgets/ctkCoordinatesWidget.cpp

@@ -125,13 +125,7 @@ void ctkCoordinatesWidget::setMinimum(double min)
 {
   for (int i = 0; this->layout()->itemAt(i); ++i)
     {
-    QLayoutItem* item = this->layout()->itemAt(i);
-    ctkDoubleSpinBox* spinBox = item ? qobject_cast<ctkDoubleSpinBox*>(
-      item->widget()) : 0;
-    if (spinBox)
-      {
-      spinBox->setMinimum(min);
-      }
+    this->spinBox(i)->setMinimum(min);
     }
   this->Minimum = min;
 }
@@ -147,13 +141,7 @@ void ctkCoordinatesWidget::setMaximum(double max)
 {
   for (int i = 0; this->layout()->itemAt(i); ++i)
     {
-    QLayoutItem* item = this->layout()->itemAt(i);
-    ctkDoubleSpinBox* spinBox = item ? qobject_cast<ctkDoubleSpinBox*>(
-      item->widget()) : 0;
-    if (spinBox)
-      {
-      spinBox->setMaximum(max);
-      }
+    this->spinBox(i)->setMaximum(max);
     }
   this->Maximum = max;
 }
@@ -165,6 +153,17 @@ double ctkCoordinatesWidget::maximum() const
 }
 
 //------------------------------------------------------------------------------
+void ctkCoordinatesWidget::setRange(double min, double max)
+{
+  for (int i = 0; this->layout()->itemAt(i); ++i)
+    {
+    this->spinBox(i)->setRange(min, max);
+    }
+  this->Minimum = min;
+  this->Maximum = max;
+}
+
+//------------------------------------------------------------------------------
 void ctkCoordinatesWidget::setNormalized(bool normalized)
 {
   this->Normalized = normalized;
@@ -194,28 +193,27 @@ void ctkCoordinatesWidget::setDecimals(int newDecimals)
   this->Decimals = newDecimals;
   for (int i = 0; this->layout()->itemAt(i); ++i)
     {
-    QLayoutItem* item = this->layout()->itemAt(i);
-    ctkDoubleSpinBox* spinBox = item ? qobject_cast<ctkDoubleSpinBox*>(
-      item->widget()) : 0;
-    if (spinBox)
-      {
-      spinBox->setDecimals(newDecimals);
-      }
+    this->spinBox(i)->setDecimals(newDecimals);
     }
 }
 
 //------------------------------------------------------------------------------
+void ctkCoordinatesWidget::updateDecimals()
+{
+  int maxDecimals = 0;
+  for (int i = 0; this->layout()->itemAt(i); ++i)
+    {
+    maxDecimals = qMax(maxDecimals, this->spinBox(i)->decimals());
+    }
+  this->setTemporaryDecimals(maxDecimals);
+}
+
+//------------------------------------------------------------------------------
 void ctkCoordinatesWidget::setTemporaryDecimals(int newDecimals)
 {
   for (int i = 0; this->layout()->itemAt(i); ++i)
     {
-    QLayoutItem* item = this->layout()->itemAt(i);
-    ctkDoubleSpinBox* spinBox = item ? qobject_cast<ctkDoubleSpinBox*>(
-      item->widget()) : 0;
-    if (spinBox)
-      {
-      spinBox->spinBox()->setDecimals(newDecimals);
-      }
+    this->spinBox(i)->spinBox()->setDecimals(newDecimals);
     }
 }
 
@@ -237,13 +235,7 @@ void ctkCoordinatesWidget
 {
   for (int i = 0; this->layout()->itemAt(i); ++i)
     {
-    QLayoutItem* item = this->layout()->itemAt(i);
-    ctkDoubleSpinBox* spinBox = item ? qobject_cast<ctkDoubleSpinBox*>(
-      item->widget()) : 0;
-    if (spinBox)
-      {
-      spinBox->setDecimalsOption(newDecimalsOption);
-      }
+    this->spinBox(i)->setDecimalsOption(newDecimalsOption);
     }
   this->DecimalsOption = newDecimalsOption;
 }
@@ -253,13 +245,7 @@ void ctkCoordinatesWidget::setSingleStep(double step)
 {
   for (int i = 0; this->layout()->itemAt(i); ++i)
     {
-    QLayoutItem* item = this->layout()->itemAt(i);
-    ctkDoubleSpinBox* spinBox = item ? qobject_cast<ctkDoubleSpinBox*>(
-      item->widget()) : 0;
-    if (spinBox)
-      {
-      spinBox->setSingleStep(step);
-      }
+    this->spinBox(i)->setSingleStep(step);
     }
   this->SingleStep = step;
 }
@@ -314,31 +300,27 @@ void ctkCoordinatesWidget::setCoordinates(double* coordinates)
     this->normalize(this->Coordinates, this->Dimension);
     }
   bool valuesModified = false;
-  int maxDecimals = 0;
   bool blocked = this->blockSignals(true);
   for (int i = 0; i < this->Dimension; ++i)
     {
-    QLayoutItem* item = this->layout()->itemAt(i);
-    ctkDoubleSpinBox* spinBox =
-      item ? qobject_cast<ctkDoubleSpinBox*>(item->widget()) : 0;
-    if (spinBox)
+    ctkDoubleSpinBox* spinbox = this->spinBox(i);
+    if (spinbox)
       {
       // we don't want updateCoordinate() to be called.
       // it could mess with the LastUserEditedCoordinates list.
-      bool spinBoxSignalWasBlocked = spinBox->blockSignals(true);
-      if (spinBox->value() != this->Coordinates[i])
+      bool spinBoxSignalWasBlocked = spinbox->blockSignals(true);
+      if (spinbox->value() != this->Coordinates[i])
         {
         valuesModified = true;
         }
       // Still setValue needs to be called to recompute the number of decimals
       // if DecimalsByValue is set.
-      spinBox->setValue(this->Coordinates[i]);
-      spinBox->blockSignals(spinBoxSignalWasBlocked);
-      maxDecimals = qMax(maxDecimals, spinBox->decimals());
+      spinbox->setValue(this->Coordinates[i]);
+      spinbox->blockSignals(spinBoxSignalWasBlocked);
       }
     }
   this->blockSignals(blocked);
-  this->setTemporaryDecimals(maxDecimals);
+  this->updateDecimals();
   if (valuesModified)
     {
     this->updateCoordinates();
@@ -385,10 +367,7 @@ void ctkCoordinatesWidget::updateCoordinate(double coordinate)
   int element = -1;
   for (int i = 0; i < this->Dimension; ++i)
     {
-    QLayoutItem* item = this->layout()->itemAt(i);
-    ctkDoubleSpinBox* spinBox =
-      item ? qobject_cast<ctkDoubleSpinBox*>(item->widget()) : 0;
-    if ( spinBox && spinBox == this->sender())
+    if ( this->spinBox(i) && this->spinBox(i) == this->sender())
       {
       this->Coordinates[i] = coordinate;
       element = i;
@@ -491,13 +470,7 @@ void ctkCoordinatesWidget::updateCoordinates()
 {
   for (int i = 0; i < this->Dimension; ++i)
     {
-    QLayoutItem* item = this->layout()->itemAt(i);
-    ctkDoubleSpinBox* spinBox =
-      item ? qobject_cast<ctkDoubleSpinBox*>(item->widget()) : 0;
-    if ( spinBox)
-      {
-      this->Coordinates[i] = spinBox->value();
-      }
+    this->Coordinates[i] = this->spinBox(i)->value();
     }
   emit coordinatesChanged(this->Coordinates);
 }
@@ -557,6 +530,15 @@ double ctkCoordinatesWidget::squaredNorm(double* coordinates, int dimension)
 }
 
 //----------------------------------------------------------------------------
+ctkDoubleSpinBox* ctkCoordinatesWidget::spinBox(int i)
+{
+  QLayoutItem* item = this->layout()->itemAt(i);
+  ctkDoubleSpinBox* spinBox =
+    item ? qobject_cast<ctkDoubleSpinBox*>(item->widget()) : 0;
+  return spinBox;
+}
+
+//----------------------------------------------------------------------------
 void ctkCoordinatesWidget::setValueProxy(ctkValueProxy* proxy)
 {
   if (this->Proxy.data() == proxy)
@@ -564,17 +546,35 @@ void ctkCoordinatesWidget::setValueProxy(ctkValueProxy* proxy)
     return;
     }
 
+  this->onValueProxyAboutToBeModified();
+
+  if (this->Proxy)
+    {
+    disconnect(this->Proxy.data(), SIGNAL(proxyAboutToBeModified()),
+               this, SLOT(onValueProxyAboutToBeModified()));
+    disconnect(this->Proxy.data(), SIGNAL(proxyModified()),
+               this, SLOT(onValueProxyModified()));
+    }
+
   this->Proxy = proxy;
+
+  if (this->Proxy)
+    {
+    connect(this->Proxy.data(), SIGNAL(proxyAboutToBeModified()),
+            this, SLOT(onValueProxyAboutToBeModified()));
+    }
+
   for (int i = 0; i < this->Dimension; ++i)
     {
-    QLayoutItem* item = this->layout()->itemAt(i);
-    ctkDoubleSpinBox* spinBox =
-      item ? qobject_cast<ctkDoubleSpinBox*>(item->widget()) : 0;
-    if ( spinBox)
-      {
-      spinBox->setValueProxy(this->Proxy.data());
-      }
+    this->spinBox(i)->setValueProxy(this->Proxy.data());
+    }
+
+  if (this->Proxy)
+    {
+    connect(this->Proxy.data(), SIGNAL(proxyModified()),
+            this, SLOT(onValueProxyModified()));
     }
+  this->onValueProxyModified();
 }
 
 //----------------------------------------------------------------------------
@@ -582,3 +582,24 @@ ctkValueProxy* ctkCoordinatesWidget::valueProxy() const
 {
   return this->Proxy.data();
 }
+
+//----------------------------------------------------------------------------
+void ctkCoordinatesWidget::onValueProxyAboutToBeModified()
+{
+  for (int i = 0; i < this->Dimension; ++i)
+    {
+    this->spinBox(i)->blockSignals(true);
+    }
+}
+
+//----------------------------------------------------------------------------
+void ctkCoordinatesWidget::onValueProxyModified()
+{
+  for (int i = 0; i < this->Dimension; ++i)
+    {
+    this->spinBox(i)->blockSignals(false);
+    }
+  // Only decimals (not range/nor value) may have change during a proxy
+  // modification.
+  this->updateDecimals();
+}

+ 9 - 0
Libs/Widgets/ctkCoordinatesWidget.h

@@ -91,6 +91,10 @@ public:
   void setMaximum(double minimum);
   double maximum() const;
 
+  /// Set the minimum and maximum of each coordinate spinbox at once.
+  /// \sa minimum, maximum
+  void setRange(double minimum, double maximum);
+
   /// Change the normalized property. If \a normalize is true, it normalizes
   /// the current coordinates, the range of possible values is reset to [-1, 1].
   /// \sa isNormalized
@@ -120,6 +124,8 @@ public:
   void setValueProxy(ctkValueProxy* proxy);
   ctkValueProxy* valueProxy() const;
 
+  /// Return the spinbox identitfied by id
+  ctkDoubleSpinBox* spinBox(int id);
 public Q_SLOTS:
   void normalize();
 
@@ -136,7 +142,10 @@ Q_SIGNALS:
 protected Q_SLOTS:
   void updateCoordinate(double);
   void updateCoordinates();
+  void updateDecimals();
   void setTemporaryDecimals(int);
+  void onValueProxyAboutToBeModified();
+  void onValueProxyModified();
 
 protected:
   void addSpinBox();

+ 175 - 60
Libs/Widgets/ctkDoubleRangeSlider.cpp

@@ -21,6 +21,7 @@
 // Qt includes
 #include <QDebug>
 #include <QHBoxLayout>
+#include <QVariant>
 #include <QWeakPointer>
 
 // CTK includes
@@ -146,7 +147,6 @@ int ctkDoubleRangeSliderPrivate::toInt(double doubleValue)const
 #endif
   tmp = qBound(minInt, tmp, maxInt);
   int intValue = qRound(tmp);
-  //qDebug() << __FUNCTION__ << doubleValue << tmp << intValue;
   return intValue;
 }
 
@@ -212,11 +212,15 @@ ctkDoubleRangeSlider::~ctkDoubleRangeSlider()
 }
 
 // --------------------------------------------------------------------------
-void ctkDoubleRangeSlider::setMinimum(double min)
+void ctkDoubleRangeSlider::setMinimum(double newMin)
 {
   Q_D(ctkDoubleRangeSlider);
+  if (d->Proxy)
+    {
+    newMin = d->Proxy.data()->proxyValueFromValue(newMin);
+    }
   double oldMin = d->Minimum;
-  d->Minimum = min;
+  d->Minimum = newMin;
   if (d->Minimum >= d->MinValue)
     {// TBD: use same offset
     d->updateMinOffset(d->Minimum);
@@ -225,12 +229,13 @@ void ctkDoubleRangeSlider::setMinimum(double min)
     {// TBD: use same offset
     d->updateMaxOffset(d->Minimum);
     }
+  bool wasSettingRange = d->SettingRange;
   d->SettingRange = true;
-  d->Slider->setMinimum(d->toInt(min));
-  d->SettingRange = false;
-  if (d->Minimum != oldMin)
+  d->Slider->setMinimum(d->toInt(newMin));
+  d->SettingRange = wasSettingRange;
+  if (!wasSettingRange && d->Minimum != oldMin)
     {
-    emit this->rangeChanged(d->Minimum, d->Maximum);
+    emit this->rangeChanged(this->minimum(), this->maximum());
     }
 }
 
@@ -238,15 +243,26 @@ void ctkDoubleRangeSlider::setMinimum(double min)
 double ctkDoubleRangeSlider::minimum()const
 {
   Q_D(const ctkDoubleRangeSlider);
-  return d->Minimum;
+  double min = d->Minimum;
+  double max = d->Maximum;
+  if (d->Proxy)
+    {
+    min = d->Proxy.data()->valueFromProxyValue(min);
+    max = d->Proxy.data()->valueFromProxyValue(max);
+    }
+  return qMin(min, max);
 }
 
 // --------------------------------------------------------------------------
-void ctkDoubleRangeSlider::setMaximum(double max)
+void ctkDoubleRangeSlider::setMaximum(double newMax)
 {
   Q_D(ctkDoubleRangeSlider);
+  if (d->Proxy)
+    {
+    newMax = d->Proxy.data()->proxyValueFromValue(newMax);
+    }
   double oldMax = d->Maximum;
-  d->Maximum = max;
+  d->Maximum = newMax;
   if (d->Maximum <= d->MinValue)
     {// TBD: use same offset
     d->updateMinOffset(d->Maximum);
@@ -255,12 +271,13 @@ void ctkDoubleRangeSlider::setMaximum(double max)
     {// TBD: use same offset ?
     d->updateMaxOffset(d->Maximum);
     }
+  bool wasSettingRange = d->SettingRange;
   d->SettingRange = true;
-  d->Slider->setMaximum(d->toInt(max));
-  d->SettingRange = false;
-  if (d->Maximum != oldMax)
+  d->Slider->setMaximum(d->toInt(newMax));
+  d->SettingRange = wasSettingRange;
+  if (!wasSettingRange && d->Maximum != oldMax)
     {
-    emit this->rangeChanged(d->Minimum, d->Maximum);
+    emit this->rangeChanged(this->minimum(), this->maximum());
     }
 }
 
@@ -268,17 +285,35 @@ void ctkDoubleRangeSlider::setMaximum(double max)
 double ctkDoubleRangeSlider::maximum()const
 {
   Q_D(const ctkDoubleRangeSlider);
-  return d->Maximum;
+  double min = d->Minimum;
+  double max = d->Maximum;
+  if (d->Proxy)
+    {
+    min = d->Proxy.data()->valueFromProxyValue(min);
+    max = d->Proxy.data()->valueFromProxyValue(max);
+    }
+  return qMax(min, max);
 }
 
 // --------------------------------------------------------------------------
-void ctkDoubleRangeSlider::setRange(double min, double max)
+void ctkDoubleRangeSlider::setRange(double newMin, double newMax)
 {
   Q_D(ctkDoubleRangeSlider);
+  if (d->Proxy)
+    {
+    newMin = d->Proxy.data()->proxyValueFromValue(newMin);
+    newMax = d->Proxy.data()->proxyValueFromValue(newMax);
+    }
+
+  if (newMin > newMax)
+    {
+    qSwap(newMin, newMax);
+    }
+
   double oldMin = d->Minimum;
   double oldMax = d->Maximum;
-  d->Minimum = min;
-  d->Maximum = max;
+  d->Minimum = newMin;
+  d->Maximum = newMax;
   if (d->Minimum >= d->MinValue)
     {// TBD: use same offset
     d->updateMinOffset(d->Minimum);
@@ -295,12 +330,13 @@ void ctkDoubleRangeSlider::setRange(double min, double max)
     {// TBD: use same offset ?
     d->updateMaxOffset(d->Maximum);
     }
+  bool wasSettingRange = d->SettingRange;
   d->SettingRange = true;
-  d->Slider->setRange(d->toInt(min), d->toInt(max));
-  d->SettingRange = false;
-  if (d->Minimum != oldMin || d->Maximum != oldMax)
+  d->Slider->setRange(d->toInt(newMin), d->toInt(newMax));
+  d->SettingRange = wasSettingRange;
+  if (!wasSettingRange && (d->Minimum != oldMin || d->Maximum != oldMax))
     {
-    emit this->rangeChanged(d->Minimum, d->Maximum);
+    emit this->rangeChanged(this->minimum(), this->maximum());
     }
 }
 
@@ -308,47 +344,78 @@ void ctkDoubleRangeSlider::setRange(double min, double max)
 double ctkDoubleRangeSlider::minimumPosition()const
 {
   Q_D(const ctkDoubleRangeSlider);
-  return d->safeMinFromInt(d->Slider->minimumPosition());
+  int intMinPos = d->Slider->minimumPosition();
+  double minPos = d->safeMinFromInt(intMinPos);
+  if (d->Proxy)
+    {
+    minPos = d->Proxy.data()->valueFromProxyValue(minPos);
+    }
+  return minPos;
 }
 
 // --------------------------------------------------------------------------
-void ctkDoubleRangeSlider::setMinimumPosition(double minPos)
+void ctkDoubleRangeSlider::setMinimumPosition(double newMinPos)
 {
   Q_D(ctkDoubleRangeSlider);
-  d->Slider->setMinimumPosition(d->toInt(minPos));
+  if (d->Proxy)
+    {
+    newMinPos = d->Proxy.data()->proxyValueFromValue(newMinPos);
+    }
+  int newIntMinPos = d->toInt(newMinPos);
+  d->Slider->setMinimumPosition(newIntMinPos);
 }
 
 // --------------------------------------------------------------------------
 double ctkDoubleRangeSlider::maximumPosition()const
 {
   Q_D(const ctkDoubleRangeSlider);
-  return d->safeMaxFromInt(d->Slider->maximumPosition());
+  int intMaxPos = d->Slider->maximumPosition();
+  double maxPos = d->safeMaxFromInt(intMaxPos);
+  if (d->Proxy)
+    {
+    maxPos = d->Proxy.data()->valueFromProxyValue(maxPos);
+    }
+  return maxPos;
 }
 
 // --------------------------------------------------------------------------
-void ctkDoubleRangeSlider::setMaximumPosition(double maxPos)
+void ctkDoubleRangeSlider::setMaximumPosition(double newMaxPos)
 {
   Q_D(ctkDoubleRangeSlider);
-  d->Slider->setMaximumPosition(d->toInt(maxPos));
+  if (d->Proxy)
+    {
+    newMaxPos = d->Proxy.data()->proxyValueFromValue(newMaxPos);
+    }
+  int newIntMaxPos = d->toInt(newMaxPos);
+  d->Slider->setMaximumPosition(newIntMaxPos);
 }
 
 // --------------------------------------------------------------------------
-void ctkDoubleRangeSlider::setPositions(double minPos, double maxPos)
+void ctkDoubleRangeSlider::setPositions(double newMinPos, double newMaxPos)
 {
   Q_D(ctkDoubleRangeSlider);
-  d->Slider->setPositions(d->toInt(minPos), d->toInt(maxPos));
+  if (d->Proxy)
+    {
+    newMinPos = d->Proxy.data()->proxyValueFromValue(newMinPos);
+    newMaxPos = d->Proxy.data()->proxyValueFromValue(newMaxPos);
+    }
+  int newIntMinPos = d->toInt(newMinPos);
+  int newIntMaxPos = d->toInt(newMaxPos);
+  d->Slider->setPositions(newIntMinPos, newIntMaxPos);
 }
 
 // --------------------------------------------------------------------------
 double ctkDoubleRangeSlider::minimumValue()const
 {
   Q_D(const ctkDoubleRangeSlider);
-  double val = d->MinValue;
+  double minValue = d->MinValue;
+  double maxValue = d->MaxValue;
   if (d->Proxy)
     {
-    val = d->Proxy.data()->valueFromProxyValue(val);
+    minValue = d->Proxy.data()->valueFromProxyValue(minValue);
+    maxValue = d->Proxy.data()->valueFromProxyValue(maxValue);
     }
-  return val;
+  return qMin(minValue, maxValue);
 }
 
 // --------------------------------------------------------------------------
@@ -360,7 +427,7 @@ void ctkDoubleRangeSlider::setMinimumValue(double newMinValue)
     newMinValue = d->Proxy.data()->proxyValueFromValue(newMinValue);
     }
   newMinValue = qBound(d->Minimum, newMinValue, d->Maximum);
-  d->updateMinOffset(newMinValue);  
+  d->updateMinOffset(newMinValue);
   if (newMinValue >= d->MaxValue)
     {
     d->updateMaxOffset(newMinValue);
@@ -390,12 +457,14 @@ void ctkDoubleRangeSlider::setMinimumValue(double newMinValue)
 double ctkDoubleRangeSlider::maximumValue()const
 {
   Q_D(const ctkDoubleRangeSlider);
-  double val = d->MaxValue;
+  double minValue = d->MinValue;
+  double maxValue = d->MaxValue;
   if (d->Proxy)
     {
-    val = d->Proxy.data()->valueFromProxyValue(val);
+    minValue = d->Proxy.data()->valueFromProxyValue(minValue);
+    maxValue = d->Proxy.data()->valueFromProxyValue(maxValue);
     }
-  return val;
+  return qMax(minValue, maxValue);
 }
 
 // --------------------------------------------------------------------------
@@ -468,6 +537,7 @@ void ctkDoubleRangeSlider::setValues(double newMinVal, double newMaxVal)
     // similar to the old value.
     bool minChanged = qAbs(newMinValue - oldMinValue) > (d->SingleStep * 0.000000001);
     bool maxChanged = qAbs(newMaxValue - oldMaxValue) > (d->SingleStep * 0.000000001);
+
     if (minChanged || maxChanged)
       {
       emit this->valuesChanged(this->minimumValue(), this->maximumValue());
@@ -506,17 +576,23 @@ void ctkDoubleRangeSlider::setSingleStep(double newStep)
   bool oldBlockSignals = this->blockSignals(true);
   d->updateMinOffset(d->MinValue);
   d->updateMaxOffset(d->MaxValue);
-  // update the new values of the ctkRangeSlider
-  double _minvalue = d->MinValue;
-  double _maxvalue = d->MaxValue;
-  // calling setMinimum or setMaximum can change the values MinimumValue
-  // and MaximumValue, this is why we re-set them later.
-  this->setMinimum(d->Minimum);
-  this->setMaximum(d->Maximum);
-  this->setMinimumValue(_minvalue);
-  this->setMinimumPosition(_minvalue);
-  this->setMaximumValue(_maxvalue);
-  this->setMaximumPosition(_maxvalue);
+  double minimum = d->Minimum;
+  double maximum = d->Maximum;
+  // update the new int values
+  double minValue = d->MinValue;
+  double maxValue = d->MaxValue;
+  if (d->Proxy)
+    {
+    minimum = d->Proxy.data()->valueFromProxyValue(minimum);
+    maximum = d->Proxy.data()->valueFromProxyValue(maximum);
+    minValue = d->Proxy.data()->valueFromProxyValue(minValue);
+    maxValue = d->Proxy.data()->valueFromProxyValue(maxValue);
+   }
+  // calling setRange can change the MinimumValue and MaximumValue values,
+  // this is why we re-set them after.
+  this->setRange(minimum, maximum);
+  this->setValues(minValue, maxValue);
+  this->setPositions(minValue, maxValue);
   this->blockSignals(oldBlockSignals);
 }
 
@@ -524,6 +600,10 @@ void ctkDoubleRangeSlider::setSingleStep(double newStep)
 bool ctkDoubleRangeSlider::isValidStep(double step)const
 {
   Q_D(const ctkDoubleRangeSlider);
+  if (d->Minimum == d->Maximum)
+    {
+    return true;
+    }
   const double minStep = qMax(d->Maximum / std::numeric_limits<double>::max(),
                               std::numeric_limits<double>::epsilon());
   const double maxStep = qMin(d->Maximum - d->Minimum,
@@ -658,34 +738,59 @@ void ctkDoubleRangeSlider::onValuesChanged(int newMinValue, int newMaxValue)
 }
 
 // --------------------------------------------------------------------------
-void ctkDoubleRangeSlider::onMinPosChanged(int newPosition)
+void ctkDoubleRangeSlider::onMinPosChanged(int newIntMinPos)
 {
   Q_D(const ctkDoubleRangeSlider);
-  emit this->minimumPositionChanged(d->safeMinFromInt(newPosition));
+  double newMinPos = d->safeMinFromInt(newIntMinPos);
+  if (d->Proxy)
+    {
+    newMinPos = d->Proxy.data()->valueFromProxyValue(newMinPos);
+    }
+  emit this->minimumPositionChanged(newMinPos);
 }
 
 // --------------------------------------------------------------------------
-void ctkDoubleRangeSlider::onMaxPosChanged(int newPosition)
+void ctkDoubleRangeSlider::onMaxPosChanged(int newIntMaxPos)
 {
   Q_D(const ctkDoubleRangeSlider);
-  emit this->maximumPositionChanged(d->safeMaxFromInt(newPosition));
+  double newMaxPos = d->safeMaxFromInt(newIntMaxPos);
+  if (d->Proxy)
+    {
+    //newMaxPos = d->Proxy.data()->valueFromProxyValue(newMaxPos);
+    }
+  emit this->maximumPositionChanged(newMaxPos);
 }
 
 // --------------------------------------------------------------------------
-void ctkDoubleRangeSlider::onPositionsChanged(int min, int max)
+void ctkDoubleRangeSlider::onPositionsChanged(int newIntMinPos, int newIntMaxPos)
 {
   Q_D(const ctkDoubleRangeSlider);
-  emit this->positionsChanged(d->safeMinFromInt(min), d->safeMaxFromInt(max));
+  double newMinPos = d->safeMinFromInt(newIntMinPos);
+  double newMaxPos = d->safeMaxFromInt(newIntMaxPos);
+  if (d->Proxy)
+    {
+    newMinPos = d->Proxy.data()->valueFromProxyValue(newMinPos);
+    newMaxPos = d->Proxy.data()->valueFromProxyValue(newMaxPos);
+    }
+  emit this->positionsChanged(newMinPos, newMaxPos);
 }
 
 // --------------------------------------------------------------------------
-void ctkDoubleRangeSlider::onRangeChanged(int min, int max)
+void ctkDoubleRangeSlider::onRangeChanged(int newIntMin, int newIntMax)
 {
   Q_D(const ctkDoubleRangeSlider);
-  if (!d->SettingRange)
+  if (d->SettingRange)
+    {
+    return;
+    }
+  double newMin = d->minFromInt(newIntMin);
+  double newMax = d->maxFromInt(newIntMax);
+  if (d->Proxy)
     {
-    this->setRange(d->minFromInt(min), d->maxFromInt(max));
+    newMin = d->Proxy.data()->valueFromProxyValue(newMin);
+    newMax = d->Proxy.data()->valueFromProxyValue(newMax);
     }
+  this->setRange(newMin, newMax);
 }
 
 // --------------------------------------------------------------------------
@@ -753,13 +858,23 @@ ctkValueProxy* ctkDoubleRangeSlider::valueProxy() const
 void ctkDoubleRangeSlider::onValueProxyAboutToBeModified()
 {
   Q_D(ctkDoubleRangeSlider);
-  d->InputMinValue = this->minimumValue();
-  d->InputMaxValue = this->maximumValue();
+  d->Slider->setProperty("inputMinimumValue", this->minimumValue());
+  d->Slider->setProperty("inputMaximumValue", this->maximumValue());
+  d->Slider->setProperty("inputMinimum", this->minimum());
+  d->Slider->setProperty("inputMaximum", this->maximum());
 }
 
 //-----------------------------------------------------------------------------
 void ctkDoubleRangeSlider::onValueProxyModified()
 {
   Q_D(ctkDoubleRangeSlider);
-  this->setValues(d->InputMinValue, d->InputMaxValue);
+  bool wasBlockingSignals = this->blockSignals(true);
+  bool wasSettingRange = d->SettingRange;
+  d->SettingRange = true;
+  this->setRange(d->Slider->property("inputMinimum").toDouble(),
+                 d->Slider->property("inputMaximum").toDouble());
+  d->SettingRange = wasSettingRange;
+  this->setValues(d->Slider->property("inputMinimumValue").toDouble(),
+                  d->Slider->property("inputMaximumValue").toDouble());
+  this->blockSignals(wasBlockingSignals);
 }

+ 42 - 12
Libs/Widgets/ctkDoubleSlider.cpp

@@ -191,13 +191,13 @@ ctkDoubleSlider::~ctkDoubleSlider()
 // --------------------------------------------------------------------------
 void ctkDoubleSlider::setMinimum(double min)
 {
-  this->setRange(min, this->maximum());
+  this->setRange(min, qMax(min, this->maximum()));
 }
 
 // --------------------------------------------------------------------------
 void ctkDoubleSlider::setMaximum(double max)
 {
-  this->setRange(this->minimum(), max);
+  this->setRange(qMin(this->minimum(), max), max);
 }
 
 // --------------------------------------------------------------------------
@@ -215,6 +215,9 @@ void ctkDoubleSlider::setRange(double newMin, double newMax)
     qSwap(newMin, newMax);
     }
 
+  double oldMin = d->Minimum;
+  double oldMax = d->Maximum;
+
   d->Minimum = newMin;
   d->Maximum = newMax;
 
@@ -226,10 +229,14 @@ void ctkDoubleSlider::setRange(double newMin, double newMax)
     {
     d->updateOffset(d->Maximum);
     }
+  bool wasSettingRange = d->SettingRange;
   d->SettingRange = true;
   d->Slider->setRange(d->toInt(newMin), d->toInt(newMax));
-  d->SettingRange = false;
-  emit this->rangeChanged(d->Minimum, d->Maximum);
+  d->SettingRange = wasSettingRange;
+  if (!wasSettingRange && (d->Minimum != oldMin || d->Maximum != oldMax))
+    {
+    emit this->rangeChanged(this->minimum(), this->maximum());
+    }
   /// In case QSlider::setRange(...) didn't notify the value
   /// has changed.
   this->setValue(this->value());
@@ -240,23 +247,27 @@ double ctkDoubleSlider::minimum()const
 {
   Q_D(const ctkDoubleSlider);
   double min = d->Minimum;
+  double max = d->Maximum;
   if (d->Proxy)
     {
     min = d->Proxy.data()->valueFromProxyValue(min);
+    max = d->Proxy.data()->valueFromProxyValue(max);
     }
-  return min;
+  return qMin(min, max);
 }
 
 // --------------------------------------------------------------------------
 double ctkDoubleSlider::maximum()const
 {
   Q_D(const ctkDoubleSlider);
+  double min = d->Minimum;
   double max = d->Maximum;
   if (d->Proxy)
     {
+    min = d->Proxy.data()->valueFromProxyValue(min);
     max = d->Proxy.data()->valueFromProxyValue(max);
     }
-  return max;
+  return qMax(min, max);
 }
 
 // --------------------------------------------------------------------------
@@ -360,6 +371,10 @@ void ctkDoubleSlider::setSingleStep(double newStep)
 bool ctkDoubleSlider::isValidStep(double step)const
 {
   Q_D(const ctkDoubleSlider);
+  if (d->Minimum == d->Maximum)
+    {
+    return true;
+    }
   const double minStep = qMax(d->Maximum / std::numeric_limits<double>::max(),
                               std::numeric_limits<double>::epsilon());
   const double maxStep = qMin(d->Maximum - d->Minimum,
@@ -388,14 +403,16 @@ double ctkDoubleSlider::tickInterval()const
 {
   Q_D(const ctkDoubleSlider);
   // No need to apply Offset
-  return d->SingleStep * d->Slider->tickInterval();
+  double interval = d->SingleStep * d->Slider->tickInterval();
+  return interval;
 }
 
 // --------------------------------------------------------------------------
-void ctkDoubleSlider::setTickInterval(double newTickInterval)
+void ctkDoubleSlider::setTickInterval(double newInterval)
 {
   Q_D(ctkDoubleSlider);
-  d->Slider->setTickInterval(d->toInt(newTickInterval));
+  int newIntInterval = d->toInt(newInterval);
+  d->Slider->setTickInterval(newIntInterval);
 }
 
 // --------------------------------------------------------------------------
@@ -527,13 +544,21 @@ void ctkDoubleSlider::onSliderMoved(int newPosition)
 }
 
 // --------------------------------------------------------------------------
-void ctkDoubleSlider::onRangeChanged(int min, int max)
+void ctkDoubleSlider::onRangeChanged(int newIntMin, int newIntMax)
 {
   Q_D(const ctkDoubleSlider);
-  if (!d->SettingRange)
+  if (d->SettingRange)
     {
-    this->setRange(d->fromInt(min), d->fromInt(max));
+    return;
     }
+  double newMin = d->fromInt(newIntMin);
+  double newMax = d->fromInt(newIntMax);
+  if (d->Proxy)
+    {
+    newMin = d->Proxy.data()->valueFromProxyValue(newMin);
+    newMax = d->Proxy.data()->valueFromProxyValue(newMax);
+    }
+  this->setRange(newMin, newMax);
 }
 
 // --------------------------------------------------------------------------
@@ -616,8 +641,13 @@ void ctkDoubleSlider::onValueProxyAboutToBeModified()
 void ctkDoubleSlider::onValueProxyModified()
 {
   Q_D(ctkDoubleSlider);
+  bool wasBlockingSignals = this->blockSignals(true);
+  bool wasSettingRange = d->SettingRange;
+  d->SettingRange = true;
   this->setRange(d->Slider->property("inputMinimum").toDouble(),
                  d->Slider->property("inputMaximum").toDouble());
+  d->SettingRange = wasSettingRange;
   this->setValue(d->Slider->property("inputValue").toDouble());
+  this->blockSignals(wasBlockingSignals);
 }
 

+ 45 - 226
Libs/Widgets/ctkDoubleSpinBox.cpp

@@ -255,6 +255,17 @@ int ctkDoubleSpinBoxPrivate::boundDecimals(int dec)const
 }
 
 //-----------------------------------------------------------------------------
+int ctkDoubleSpinBoxPrivate::decimalsForValue(double value) const
+{
+  int decimals = this->DefaultDecimals;
+  if (this->DOption & ctkDoubleSpinBox::DecimalsByValue)
+    {
+    decimals = ctk::significantDecimals(value, decimals);
+    }
+  return this->boundDecimals(decimals);
+}
+
+//-----------------------------------------------------------------------------
 void ctkDoubleSpinBoxPrivate::setValue(double value, int dec)
 {
   Q_Q(ctkDoubleSpinBox);
@@ -313,23 +324,19 @@ double ctkDoubleSpinBoxPrivate
                        QValidator::State &state, int &decimals) const
 {
   Q_Q(const ctkDoubleSpinBox);
-  //qDebug() << "input: " << input << "pos:" << pos;
   if (this->CachedText == input)
     {
     state = this->CachedState;
     decimals = this->CachedDecimals;
-    //qDebug() << "cachedText was '" << this->CachedText << "' state was "
-    //         << int(state) << " and value was " << this->CachedValue;
     return this->CachedValue;
     }
-  const double max = q->maximum();
-  const double min = q->minimum();
+  const double max = this->SpinBox->maximum();
+  const double min = this->SpinBox->minimum();
 
   int posInValue = pos;
   QString text = this->stripped(input, &posInValue);
   // posInValue can change, track the offset.
   const int oldPosInValue = posInValue;
-  //qDebug() << "text: " << text << pos;
   state = QValidator::Acceptable;
   decimals = 0;
 
@@ -424,7 +431,6 @@ double ctkDoubleSpinBoxPrivate
     }
   if (state == QValidator::Acceptable)
     {
-    //qDebug() << "Acceptable: " << text << value << ok;
     if (!ok)
       {
       state = QValidator::Invalid;
@@ -458,202 +464,9 @@ double ctkDoubleSpinBoxPrivate
   this->CachedState = state;
   this->CachedValue = value;
   this->CachedDecimals = decimals;
-  //qDebug() << "end: text is '" << this->CachedText << "' state is "
-  //         << int(state) << ", value is " << this->CachedValue
-  //         << " decimals is " << decimals
-  //         << " and pos is " << pos;
   return value;
 }
 
-/*
-//-----------------------------------------------------------------------------
-double ctkDoubleSpinBoxPrivate
-::validateAndInterpret(QString &input, int &pos,
-                       QValidator::State &state, int &decimals) const
-{
-  Q_Q(const ctkDoubleSpinBox);
-  qDebug() << "input: " << input << "pos:" << pos;
-  if (this->CachedText == input && !input.isEmpty())
-    {
-    state = this->CachedState;
-    decimals = this->CachedDecimals;
-    qDebug() << "cachedText was '" << this->CachedText << "' state was "
-             << int(state) << " and value was " << this->CachedValue;
-    return this->CachedValue;
-    }
-  const double max = q->maximum();
-  const double min = q->minimum();
-
-  QString copy = this->stripped(input, &pos);
-  qDebug() << "copy: " << copy << pos;
-  int len = copy.size();
-  double num = min;
-  const bool plus = max >= 0;
-  const bool minus = min <= 0;
-  decimals = 0;
-
-
-  switch (len)
-    {
-    case 0:
-      state = max != min ? QValidator::Intermediate : QValidator::Invalid;
-      goto end;
-      break;
-    case 1:
-      if (copy.at(0) == q->locale().decimalPoint()
-          || (plus && copy.at(0) == QLatin1Char('+'))
-          || (minus && copy.at(0) == QLatin1Char('-')))
-        {
-        state = QValidator::Intermediate;
-        goto end;
-        }
-      break;
-    case 2:
-      if (copy.at(1) == q->locale().decimalPoint()
-          && ((plus && copy.at(0) == QLatin1Char('+')) || (minus && copy.at(0) == QLatin1Char('-'))))
-        {
-        state = QValidator::Intermediate;
-        goto end;
-        }
-      break;
-    default:
-      break;
-    }
-
-  if (copy.at(0) == q->locale().groupSeparator())
-    {
-    state = QValidator::Invalid;
-    goto end;
-    }
-  else if (len > 1)
-    {
-    const int dec = copy.indexOf(q->locale().decimalPoint());
-    if (dec != -1)
-      {
-      if (dec + 1 < copy.size() && copy.at(dec + 1) == q->locale().decimalPoint() && pos == dec + 1)
-        {
-        copy.remove(dec + 1, 1); // typing a delimiter when you are on the delimiter
-        } // should be treated as typing right arrow
-      // When DecimalsByKey is set, it is possible to extend the number of decimals
-      if (!(this->DOption & ctkDoubleSpinBox::DecimalsByKey) &&
-          (copy.size() - dec > q->decimals() + 1))
-        {
-        state = QValidator::Invalid;
-        goto end;
-        }
-      for (int i=dec + 1; i<copy.size(); ++i)
-        {
-        if (copy.at(i).isSpace() || copy.at(i) == q->locale().groupSeparator())
-          {
-          state = QValidator::Invalid;
-          goto end;
-          }
-        }
-      decimals = copy.size() - dec - 1;
-      }
-    else
-      {
-      /// Don't accept lack of decimal point.
-      /// It could change 1.00 into 100 in 1 key stroke (delete or backspace).
-      if (this->DOption & ctkDoubleSpinBox::DecimalsByKey)
-        {
-        state = QValidator::Invalid;
-        goto end;
-        }
-      const QChar &last = copy.at(len - 1);
-      const QChar &secondLast = copy.at(len - 2);
-      if ((last == q->locale().groupSeparator() || last.isSpace())
-          && (secondLast == q->locale().groupSeparator() || secondLast.isSpace()))
-        {
-        state = QValidator::Invalid;
-        goto end;
-        }
-      else if (last.isSpace() && (!q->locale().groupSeparator().isSpace() || secondLast.isSpace()))
-        {
-        state = QValidator::Invalid;
-        goto end;
-        }
-      }
-    }
-
-    {
-    bool ok = false;
-    num = q->locale().toDouble(copy, &ok);
-
-    if (!ok) {
-    if (q->locale().groupSeparator().isPrint())
-      {
-      if (max < 1000 && min > -1000 && copy.contains(q->locale().groupSeparator()))
-        {
-        state = QValidator::Invalid;
-        goto end;
-        }
-      const int len = copy.size();
-      for (int i=0; i<len- 1; ++i)
-        {
-        if (copy.at(i) == q->locale().groupSeparator() && copy.at(i + 1) == q->locale().groupSeparator())
-          {
-          state = QValidator::Invalid;
-          goto end;
-          }
-        }
-
-      QString copy2 = copy;
-      copy2.remove(q->locale().groupSeparator());
-      num = q->locale().toDouble(copy2, &ok);
-
-      if (!ok)
-        {
-        state = QValidator::Invalid;
-        goto end;
-        }
-      }
-    }
-
-    if (!ok)
-      {
-      state = QValidator::Invalid;
-      }
-    else if (num >= min && num <= max)
-      {
-      state = QValidator::Acceptable;
-      }
-    else if (max == min)
-      { // when max and min is the same the only non-Invalid input is max (or min)
-      state = QValidator::Invalid;
-      }
-    else
-      {
-      if ((num >= 0 && num > max) || (num < 0 && num < min))
-        {
-        state = QValidator::Invalid;
-        }
-      else
-        {
-        state = QValidator::Intermediate;
-        }
-      }
-    }
-end:
-    if (state != QValidator::Acceptable)
-      {
-      num = max > 0 ? min : max;
-      }
-
-    input = q->prefix() + copy + q->suffix();
-    this->CachedText = input;
-    this->CachedState = state;
-    this->CachedValue = num;
-    this->CachedDecimals = decimals;
-    qDebug() << "end: text is '" << this->CachedText << "' state is "
-             << int(state) << ", value is " << this->CachedValue
-             << " decimals is " << decimals
-             << " and pos is " << pos;
-
-    return num;
-}
-*/
-
 //-----------------------------------------------------------------------------
 void ctkDoubleSpinBoxPrivate::onValueChanged()
 {
@@ -676,9 +489,18 @@ void ctkDoubleSpinBoxPrivate::onValueProxyAboutToBeModified()
 void ctkDoubleSpinBoxPrivate::onValueProxyModified()
 {
   Q_Q(ctkDoubleSpinBox);
+  int decimals = q->decimals();
+  bool wasBlocking = q->blockSignals(true);
   q->setRange(this->SpinBox->property("inputMinimum").toDouble(),
               this->SpinBox->property("inputMaximum").toDouble());
   q->setValue(this->SpinBox->property("inputValue").toDouble());
+  q->value();
+  q->blockSignals(wasBlocking);
+  // only decimals can change when value proxy is modified.
+  if (decimals != q->decimals())
+    {
+    emit q->decimalsChanged(q->decimals());
+    }
 }
 
 //-----------------------------------------------------------------------------
@@ -709,7 +531,7 @@ double ctkDoubleSpinBox::value() const
   double val = d->SpinBox->value();
   if (d->Proxy)
     {
-    val = this->round(d->Proxy.data()->valueFromProxyValue(val));
+    val = d->Proxy.data()->valueFromProxyValue(val);
     }
   return val;
 }
@@ -856,11 +678,13 @@ double ctkDoubleSpinBox::minimum() const
 {
   Q_D(const ctkDoubleSpinBox);
   double min = d->SpinBox->minimum();
+  double max = d->SpinBox->maximum();
   if (d->Proxy)
     {
     min = d->Proxy.data()->valueFromProxyValue(min);
+    max = d->Proxy.data()->valueFromProxyValue(max);
     }
-  return min;
+  return qMin(min, max);
 }
 
 //-----------------------------------------------------------------------------
@@ -884,12 +708,14 @@ void ctkDoubleSpinBox::setMinimum(double newMin)
 double ctkDoubleSpinBox::maximum() const
 {
   Q_D(const ctkDoubleSpinBox);
+  double min = d->SpinBox->minimum();
   double max = d->SpinBox->maximum();
   if (d->Proxy)
     {
+    min = d->Proxy.data()->valueFromProxyValue(min);
     max = d->Proxy.data()->valueFromProxyValue(max);
     }
-  return max;
+  return qMax(min, max);
 }
 
 //-----------------------------------------------------------------------------
@@ -951,7 +777,15 @@ void ctkDoubleSpinBox::setDecimals(int dec)
     }
 
   d->DefaultDecimals = dec;
-  d->setDecimals(d->DefaultDecimals);
+  // The number of decimals may or may not depend on the value. Recompute the
+  // new number of decimals.
+  double value = this->value();
+  if (d->Proxy)
+    {
+    value = d->Proxy.data()->proxyValueFromValue(value);
+    }
+  int newDecimals = d->decimalsForValue(value);
+  d->setDecimals(newDecimals);
 }
 
 //-----------------------------------------------------------------------------
@@ -993,27 +827,17 @@ void ctkDoubleSpinBox::setValue(double value)
 void ctkDoubleSpinBox::setValueIfDifferent(double newValue)
 {
   Q_D(ctkDoubleSpinBox);
-  bool set = false;
-  double proxyValue = newValue;
+  double newValueToDisplay = newValue;
   if (d->Proxy)
     {
-    proxyValue = d->Proxy.data()->proxyValueFromValue(proxyValue);
+    newValueToDisplay = d->Proxy.data()->proxyValueFromValue(newValueToDisplay);
     }
 
-  if (d->DOption & ctkDoubleSpinBox::DecimalsByValue)
+  int newValueDecimals = d->decimalsForValue(newValueToDisplay);
+  if (d->SpinBox->value() != d->round(newValueToDisplay, newValueDecimals)
+      || d->SpinBox->decimals() != newValueDecimals)
     {
-    int newValueDecimals =
-      ctk::significantDecimals(proxyValue, d->DefaultDecimals);
-    set = this->value() != d->round(proxyValue, newValueDecimals)
-      || d->SpinBox->decimals() != newValueDecimals;
-    }
-  else
-    {
-    set = !d->compare(this->value(), proxyValue);
-    }
-  if (set)
-    {
-    // Pass newValue and not proxyValue as setValueAlways also computes the
+    // Pass newValue and not newValueToDisplay as setValueAlways also computes the
     // proxyValue from the given value
     this->setValueAlways(newValue);
     }
@@ -1028,12 +852,7 @@ void ctkDoubleSpinBox::setValueAlways(double value)
     value = d->Proxy.data()->proxyValueFromValue(value);
     }
 
-  int decimals = -1;
-  if (d->DOption & ctkDoubleSpinBox::DecimalsByValue)
-    {
-    decimals = ctk::significantDecimals(value, d->DefaultDecimals);
-    }
-
+  int decimals = d->decimalsForValue(value);
   d->setValue(value, decimals);
 }
 

+ 4 - 1
Libs/Widgets/ctkDoubleSpinBox_p.h

@@ -84,7 +84,6 @@ public:
   ctkQDoubleSpinBox* SpinBox;
   ctkDoubleSpinBox::SetMode Mode;
   int DefaultDecimals;
-  int MinimumDecimals;
   ctkDoubleSpinBox::DecimalsOptions DOption;
   bool InvertedControls;
 
@@ -108,6 +107,10 @@ public:
   /// decimals.
   /// If -1, returns the current number of decimals.
   int boundDecimals(int decimals)const;
+  /// Return the number of decimals to use to display the value.
+  /// Note that if DecimalsByValue is not set, the number of decimals to use
+  /// is DefaultDecimals.
+  int decimalsForValue(double value)const;
   /// Set the number of decimals of the spinbox and emit the signal
   /// No check if they are the same.
   void setDecimals(int dec);

+ 58 - 96
Libs/Widgets/ctkRangeWidget.cpp

@@ -51,6 +51,7 @@ public:
   bool          Tracking;
   bool          Changing;
   bool          SettingSliderRange;
+  bool          BlockSliderUpdate;
   double        MinimumValueBeforeChange;
   double        MaximumValueBeforeChange;
   bool          AutoSpinBoxWidth;
@@ -79,6 +80,7 @@ ctkRangeWidgetPrivate::ctkRangeWidgetPrivate(ctkRangeWidget& object)
   this->Tracking = true;
   this->Changing = false;
   this->SettingSliderRange = false;
+  this->BlockSliderUpdate = false;
   this->MinimumValueBeforeChange = 0.;
   this->MaximumValueBeforeChange = 0.;
   this->AutoSpinBoxWidth = true;
@@ -97,9 +99,9 @@ void ctkRangeWidgetPrivate::connectSlider()
                    q, SLOT(changeMaximumValue(double)));
 
   QObject::connect(this->MinimumSpinBox, SIGNAL(valueChanged(double)),
-                   this->Slider, SLOT(setMinimumValue(double)));
+                   q, SLOT(setSliderValues()));
   QObject::connect(this->MaximumSpinBox, SIGNAL(valueChanged(double)),
-                   this->Slider, SLOT(setMaximumValue(double)));
+                   q, SLOT(setSliderValues()));
   QObject::connect(this->MinimumSpinBox, SIGNAL(valueChanged(double)),
                    q, SLOT(setMinimumToMaximumSpinBox(double)));
   QObject::connect(this->MaximumSpinBox, SIGNAL(valueChanged(double)),
@@ -217,10 +219,8 @@ ctkRangeWidget::ctkRangeWidget(QWidget* _parent) : Superclass(_parent)
   
   d->setupUi(this);
 
-  d->MinimumSpinBox->setMinimum(d->Slider->minimum());
-  d->MinimumSpinBox->setMaximum(d->Slider->maximum());
-  d->MaximumSpinBox->setMinimum(d->Slider->minimum());
-  d->MaximumSpinBox->setMaximum(d->Slider->maximum());
+  d->MinimumSpinBox->setRange(d->Slider->minimum(), d->Slider->maximum());
+  d->MaximumSpinBox->setRange(d->Slider->minimum(), d->Slider->maximum());
   d->MinimumSpinBox->setValue(d->Slider->minimumValue());
   d->MaximumSpinBox->setValue(d->Slider->maximumValue());
   
@@ -238,36 +238,17 @@ ctkRangeWidget::~ctkRangeWidget()
 // --------------------------------------------------------------------------
 double ctkRangeWidget::minimum()const
 {
-  double minimumMaximum[2];
-  this->range(minimumMaximum);
-  return minimumMaximum[0];
+  Q_D(const ctkRangeWidget);
+  Q_ASSERT(d->equal(d->MinimumSpinBox->minimum(),d->Slider->minimum()));
+  return d->Slider->minimum();
 }
 
 // --------------------------------------------------------------------------
 double ctkRangeWidget::maximum()const
 {
-  double minimumMaximum[2];
-  this->range(minimumMaximum);
-  return minimumMaximum[1];
-}
-
-// --------------------------------------------------------------------------
-void ctkRangeWidget::range(double* range)const
-{
   Q_D(const ctkRangeWidget);
-  Q_ASSERT(d->equal(d->MinimumSpinBox->minimum(),d->Slider->minimum()));
   Q_ASSERT(d->equal(d->MaximumSpinBox->maximum(), d->Slider->maximum()));
-  range[0] = d->Slider->minimum();
-  range[1] = d->Slider->maximum();
-  if (d->Proxy)
-    {
-    range[0] = d->Proxy.data()->valueFromProxyValue(range[0]);
-    range[1] = d->Proxy.data()->valueFromProxyValue(range[1]);
-    if (range[0] > range[1])
-      {
-      qSwap(range[0], range[1]);
-      }
-    }
+  return d->Slider->maximum();
 }
 
 // --------------------------------------------------------------------------
@@ -312,7 +293,7 @@ void ctkRangeWidget::setMaximum(double max)
 void ctkRangeWidget::setRange(double min, double max)
 {
   Q_D(ctkRangeWidget);
-  
+
   double oldMin = d->MinimumSpinBox->minimum();
   double oldMax = d->MaximumSpinBox->maximum();
   bool blocked = d->MinimumSpinBox->blockSignals(true);
@@ -339,6 +320,16 @@ void ctkRangeWidget::setRange(double min, double max)
 }
 
 // --------------------------------------------------------------------------
+void ctkRangeWidget::range(double range[2])const
+{
+  Q_D(const ctkRangeWidget);
+  Q_ASSERT(d->equal(d->MinimumSpinBox->maximum(), d->Slider->minimum()));
+  Q_ASSERT(d->equal(d->MaximumSpinBox->maximum(), d->Slider->maximum()));
+  range[0] = d->Slider->minimum();
+  range[1] = d->Slider->maximum();
+}
+
+// --------------------------------------------------------------------------
 void ctkRangeWidget::onSliderRangeChanged(double min, double max)
 {
   Q_D(ctkRangeWidget);
@@ -377,12 +368,6 @@ void ctkRangeWidget::values(double &minValue, double &maxValue)const
   Q_ASSERT(d->equal(d->Slider->maximumValue(), d->MaximumSpinBox->value()));
   minValue = d->Changing ? d->MinimumValueBeforeChange : d->Slider->minimumValue();
   maxValue = d->Changing ? d->MaximumValueBeforeChange : d->Slider->maximumValue();
-
-  if (d->Proxy)
-    {
-    //minValue = d->Proxy.data()->valueFromProxyValue(minValue);
-    //maxValue = d->Proxy.data()->valueFromProxyValue(maxValue);
-    }
 }
 
 // --------------------------------------------------------------------------
@@ -390,18 +375,9 @@ double ctkRangeWidget::minimumValue()const
 {
   Q_D(const ctkRangeWidget);
   Q_ASSERT(d->equal(d->Slider->minimumValue(), d->MinimumSpinBox->value()));
-
-  double minValue =
+  const double minValue =
     d->Changing ? d->MinimumValueBeforeChange : d->Slider->minimumValue();
-  double maxValue =
-    d->Changing ? d->MaximumValueBeforeChange : d->Slider->maximumValue();
-  if (d->Proxy)
-    {
-    /// The proxy can invert the signs (e.g. linear coef < 0.)
-    //minValue = d->Proxy.data()->valueFromProxyValue(minValue);
-    //maxValue = d->Proxy.data()->valueFromProxyValue(maxValue);
-    }
-  return qMin(minValue, maxValue);
+  return minValue;
 }
 
 // --------------------------------------------------------------------------
@@ -409,34 +385,20 @@ double ctkRangeWidget::maximumValue()const
 {
   Q_D(const ctkRangeWidget);
   Q_ASSERT(d->equal(d->Slider->maximumValue(), d->MaximumSpinBox->value()));
-
-  double minValue =
-    d->Changing ? d->MinimumValueBeforeChange : d->Slider->minimumValue();
-  double maxValue =
+  const double maxValue =
     d->Changing ? d->MaximumValueBeforeChange : d->Slider->maximumValue();
-  if (d->Proxy)
-    {
-    //minValue = d->Proxy.data()->valueFromProxyValue(minValue);
-    //maxValue = d->Proxy.data()->valueFromProxyValue(maxValue);
-    }
-  return qMax(minValue, maxValue);
+  return maxValue;
 }
 
 // --------------------------------------------------------------------------
 void ctkRangeWidget::setMinimumValue(double _value)
 {
   Q_D(ctkRangeWidget);
-  if (d->Proxy)
-    {
-    //_value = d->Proxy.data()->proxyValueFromValue(_value);
-    }
-
   // disable the tracking temporally to emit the
   // signal valueChanged if changeValue() is called
   bool isChanging = d->Changing;
   d->Changing = false;
   d->MinimumSpinBox->setValue(_value);
-
   Q_ASSERT(d->equal(d->Slider->minimumValue(), d->MinimumSpinBox->value()));
   // restore the prop
   d->Changing = isChanging;
@@ -446,17 +408,11 @@ void ctkRangeWidget::setMinimumValue(double _value)
 void ctkRangeWidget::setMaximumValue(double _value)
 {
   Q_D(ctkRangeWidget);
-  if (d->Proxy)
-    {
-    //_value = d->Proxy.data()->proxyValueFromValue(_value);
-    }
-
   // disable the tracking temporally to emit the
   // signal valueChanged if changeValue() is called
   bool isChanging = d->Changing;
   d->Changing = false;
   d->MaximumSpinBox->setValue(_value);
-
   Q_ASSERT(d->equal(d->Slider->maximumValue(), d->MaximumSpinBox->value()));
   // restore the prop
   d->Changing = isChanging;
@@ -466,12 +422,6 @@ void ctkRangeWidget::setMaximumValue(double _value)
 void ctkRangeWidget::setValues(double newMinimumValue, double newMaximumValue)
 {
   Q_D(ctkRangeWidget);
-  if (d->Proxy)
-    {
-    newMinimumValue = d->Proxy.data()->proxyValueFromValue(newMinimumValue);
-    newMaximumValue = d->Proxy.data()->proxyValueFromValue(newMaximumValue);
-    }
-
   if (newMinimumValue > newMaximumValue)
     {
     qSwap(newMinimumValue, newMaximumValue);
@@ -484,6 +434,8 @@ void ctkRangeWidget::setValues(double newMinimumValue, double newMaximumValue)
   d->Changing = false;
   // \todo: setting the spinbox separately is currently firing 2 signals and
   // between the signals, the state of the widget is inconsistent.
+  bool wasBlocking = d->BlockSliderUpdate;
+  d->BlockSliderUpdate = true;
   if (minimumFirst)
     {
     d->MinimumSpinBox->setValue(newMinimumValue);
@@ -494,6 +446,8 @@ void ctkRangeWidget::setValues(double newMinimumValue, double newMaximumValue)
     d->MaximumSpinBox->setValue(newMaximumValue);
     d->MinimumSpinBox->setValue(newMinimumValue);
     }
+  d->BlockSliderUpdate = wasBlocking;
+  this->setSliderValues();
 
   Q_ASSERT(d->equal(d->Slider->minimumValue(), d->MinimumSpinBox->value()));
   Q_ASSERT(d->equal(d->Slider->maximumValue(), d->MaximumSpinBox->value()));
@@ -502,6 +456,17 @@ void ctkRangeWidget::setValues(double newMinimumValue, double newMaximumValue)
 }
 
 // --------------------------------------------------------------------------
+void ctkRangeWidget::setSliderValues()
+{
+  Q_D(ctkRangeWidget);
+  if (d->BlockSliderUpdate)
+    {
+    return;
+    }
+  d->Slider->setValues(d->MinimumSpinBox->value(), d->MaximumSpinBox->value());
+}
+
+// --------------------------------------------------------------------------
 void ctkRangeWidget::setMinimumToMaximumSpinBox(double minimum)
 {
   Q_D(ctkRangeWidget);
@@ -509,7 +474,7 @@ void ctkRangeWidget::setMinimumToMaximumSpinBox(double minimum)
     {
     return;
     }
-  d->MaximumSpinBox->setMinimum(minimum);
+  d->MaximumSpinBox->setRange(minimum, d->Slider->maximum());
 }
 
 // --------------------------------------------------------------------------
@@ -520,8 +485,7 @@ void ctkRangeWidget::setMaximumToMinimumSpinBox(double maximum)
     {
     return;
     }
-
-  d->MinimumSpinBox->setMaximum(maximum);
+  d->MinimumSpinBox->setRange(d->Slider->minimum(), maximum);
 }
 
 // --------------------------------------------------------------------------
@@ -550,7 +514,7 @@ void ctkRangeWidget::stopChanging()
   bool emitMaxValChanged = qAbs(this->maximumValue() - d->MaximumValueBeforeChange) > (this->singleStep() * 0.000000001);
   if (emitMinValChanged || emitMaxValChanged)
     {
-	// emit the valuesChanged signal first
+    // emit the valuesChanged signal first
     emit this->valuesChanged(this->minimumValue(), this->maximumValue());
     }
   if (emitMinValChanged)
@@ -574,7 +538,7 @@ void ctkRangeWidget::changeMinimumValue(double newValue)
   if (!d->Changing)
     {
     emit this->minimumValueChanged(newValue);
-	}
+    }
 }
 
 // --------------------------------------------------------------------------
@@ -595,8 +559,11 @@ void ctkRangeWidget::changeMaximumValue(double newValue)
 void ctkRangeWidget::changeValues(double newMinValue, double newMaxValue)
 {
   Q_D(ctkRangeWidget);
+  bool wasBlocking = d->BlockSliderUpdate;
+  d->BlockSliderUpdate = true;
   d->MinimumSpinBox->setValue(newMinValue);
   d->MaximumSpinBox->setValue(newMaxValue);
+  d->BlockSliderUpdate = wasBlocking;
   if (!d->Changing)
     {
     emit this->valuesChanged(newMinValue, newMaxValue);
@@ -675,8 +642,7 @@ void ctkRangeWidget::setDecimals(int newDecimals)
   // i.e. 50.55 with 2 decimals -> 51 with 0 decimals
   // As the SpinBox range change doesn't fire signals, 
   // we have to do the synchronization manually here
-  d->Slider->setMinimum(d->MinimumSpinBox->minimum());
-  d->Slider->setMaximum(d->MaximumSpinBox->maximum());
+  d->Slider->setRange(d->MinimumSpinBox->minimum(), d->MaximumSpinBox->maximum());
 }
 
 // --------------------------------------------------------------------------
@@ -728,8 +694,7 @@ void ctkRangeWidget::setTickInterval(double ti)
 // -------------------------------------------------------------------------
 void ctkRangeWidget::reset()
 {
-  this->setMinimumValue(this->minimum());
-  this->setMaximumValue(this->maximum());
+  this->setValues(this->minimum(), this->maximum());
 }
 
 // -------------------------------------------------------------------------
@@ -823,8 +788,7 @@ void ctkRangeWidget::setSlider(ctkDoubleRangeSlider* slider)
   Q_D(ctkRangeWidget);
 
   slider->setOrientation(d->Slider->orientation());
-  slider->setMinimum(d->Slider->minimum());
-  slider->setMaximum(d->Slider->maximum());
+  slider->setRange(d->Slider->minimum(), d->Slider->maximum());
   slider->setValues(d->Slider->minimumValue(), d->Slider->maximumValue());
   slider->setSingleStep(d->Slider->singleStep());
   slider->setTracking(d->Slider->hasTracking());
@@ -877,6 +841,14 @@ void ctkRangeWidget::setValueProxy(ctkValueProxy* proxy)
     {
     connect(d->Proxy.data(), SIGNAL(proxyAboutToBeModified()),
             this, SLOT(onValueProxyAboutToBeModified()));
+    }
+
+  this->slider()->setValueProxy(proxy);
+  this->minimumSpinBox()->setValueProxy(proxy);
+  this->maximumSpinBox()->setValueProxy(proxy);
+
+  if (d->Proxy)
+    {
     connect(d->Proxy.data(), SIGNAL(proxyModified()),
             this, SLOT(onValueProxyModified()));
     }
@@ -894,19 +866,9 @@ ctkValueProxy* ctkRangeWidget::valueProxy() const
 //-----------------------------------------------------------------------------
 void ctkRangeWidget::onValueProxyAboutToBeModified()
 {
-  Q_D(ctkRangeWidget);
-  d->Slider->setProperty("inputMinimumValue", this->minimumValue());
-  d->Slider->setProperty("inputMaximumValue", this->maximumValue());
-  d->Slider->setProperty("inputMinimum", this->minimum());
-  d->Slider->setProperty("inputMaximum", this->maximum());
 }
 
 //-----------------------------------------------------------------------------
 void ctkRangeWidget::onValueProxyModified()
 {
-  Q_D(ctkRangeWidget);
-  this->setRange(d->Slider->property("inputMinimum").toDouble(),
-                 d->Slider->property("inputMaximum").toDouble());
-  this->setValues(d->Slider->property("inputMinimumValue").toDouble(),
-                  d->Slider->property("inputMaximumValue").toDouble());
 }

+ 3 - 3
Libs/Widgets/ctkRangeWidget.h

@@ -89,9 +89,7 @@ public:
   /// Description
   /// Utility function that set the min/max in once
   void setRange(double min, double max);
-  /// Description
-  /// Return the range of the slider
-  void range(double* range)const;
+  void range(double minimumAndMaximum[2])const;
 
   ///
   /// This property holds the slider and spinbox minimum value.
@@ -227,6 +225,8 @@ protected Q_SLOTS:
   void changeValues(double newMinValue, double newMaxValue);
   void changeMinimumValue(double value);
   void changeMaximumValue(double value);
+  /// A spinbox value has been modified, update the slider.
+  void setSliderValues();
   void setMinimumToMaximumSpinBox(double minimum);
   void setMaximumToMinimumSpinBox(double maximum);
   void onSliderRangeChanged(double min, double max);

+ 47 - 1
Libs/Widgets/ctkSliderWidget.cpp

@@ -52,6 +52,11 @@ public:
   void synchronizeSiblingDecimals(int decimals);
   bool equal(double spinBoxValue, double sliderValue)const
   {
+    if (this->Proxy)
+      {
+      spinBoxValue = this->Proxy.data()->proxyValueFromValue(spinBoxValue);
+      sliderValue = this->Proxy.data()->proxyValueFromValue(sliderValue);
+      }
     return qAbs(sliderValue - spinBoxValue) < std::pow(10., -this->SpinBox->decimals());
   }
 
@@ -407,7 +412,8 @@ void ctkSliderWidget::setSingleStep(double newStep)
   Q_D(ctkSliderWidget);
   if (!d->Slider->isValidStep(newStep))
     {
-    qWarning() << "Single step " << newStep << "is out of bounds.";
+    qWarning() << "ctkSliderWidget::setSingleStep() " << newStep << "is out of bounds." <<
+      this->minimum() << this->maximum() <<this->value();
     return;
     }
   d->SpinBox->setSingleStep(newStep);
@@ -682,9 +688,32 @@ void ctkSliderWidget::setValueProxy(ctkValueProxy* proxy)
     return;
     }
 
+  this->onValueProxyAboutToBeModified();
+
+  if (d->Proxy)
+    {
+    disconnect(d->Proxy.data(), SIGNAL(proxyAboutToBeModified()),
+               this, SLOT(onValueProxyAboutToBeModified()));
+    disconnect(d->Proxy.data(), SIGNAL(proxyModified()),
+               this, SLOT(onValueProxyModified()));
+    }
+
   d->Proxy = proxy;
+
+  if (d->Proxy)
+    {
+    connect(d->Proxy.data(), SIGNAL(proxyAboutToBeModified()),
+            this, SLOT(onValueProxyAboutToBeModified()));
+    }
   this->slider()->setValueProxy(proxy);
   this->spinBox()->setValueProxy(proxy);
+
+  if (d->Proxy)
+    {
+    connect(d->Proxy.data(), SIGNAL(proxyModified()),
+            this, SLOT(onValueProxyModified()));
+    }
+  this->onValueProxyModified();
 }
 
 // --------------------------------------------------------------------------
@@ -693,3 +722,20 @@ ctkValueProxy* ctkSliderWidget::valueProxy() const
   Q_D(const ctkSliderWidget);
   return d->Proxy.data();
 }
+
+// --------------------------------------------------------------------------
+void ctkSliderWidget::onValueProxyAboutToBeModified()
+{
+}
+
+// --------------------------------------------------------------------------
+void ctkSliderWidget::onValueProxyModified()
+{
+  Q_D(ctkSliderWidget);
+  Q_ASSERT(d->equal(d->SpinBox->minimum(),d->Slider->minimum()));
+  Q_ASSERT(d->equal(d->SpinBox->maximum(),d->Slider->maximum()));
+  // resync as the modification of proxy could have discarded decimals
+  // in the process. The slider always keeps the exact value (no rounding).
+  d->SpinBox->setValue(d->Slider->value());
+  Q_ASSERT(d->equal(d->SpinBox->value(),d->Slider->value()));
+}

+ 5 - 2
Libs/Widgets/ctkSliderWidget.h

@@ -43,6 +43,8 @@ class ctkValueProxy;
 class CTK_WIDGETS_EXPORT ctkSliderWidget : public QWidget
 {
   Q_OBJECT
+  Q_FLAGS(SynchronizeSiblings)
+
   Q_PROPERTY(int decimals READ decimals WRITE setDecimals NOTIFY decimalsChanged)
   Q_PROPERTY(double singleStep READ singleStep WRITE setSingleStep)
   Q_PROPERTY(double pageStep READ pageStep WRITE setPageStep)
@@ -53,8 +55,7 @@ class CTK_WIDGETS_EXPORT ctkSliderWidget : public QWidget
   Q_PROPERTY(QString suffix READ suffix WRITE setSuffix)
   Q_PROPERTY(double tickInterval READ tickInterval WRITE setTickInterval)
   Q_PROPERTY(QSlider::TickPosition tickPosition READ tickPosition WRITE setTickPosition)
-  Q_FLAGS(SynchronizeSiblings)
-  Q_PROPERTY(SynchronizeSiblings SynchronizeSibling READ synchronizeSiblings WRITE setSynchronizeSiblings)
+  Q_PROPERTY(SynchronizeSiblings synchronizeSiblings READ synchronizeSiblings WRITE setSynchronizeSiblings)
   Q_PROPERTY(Qt::Alignment spinBoxAlignment READ spinBoxAlignment WRITE setSpinBoxAlignment)
   Q_PROPERTY(bool tracking READ hasTracking WRITE setTracking)
   Q_PROPERTY(bool spinBoxVisible READ isSpinBoxVisible WRITE setSpinBoxVisible);
@@ -295,6 +296,8 @@ protected Q_SLOTS:
   void stopChanging();
   void setSpinBoxValue(double sliderValue);
   void setSliderValue(double spinBoxValue);
+  void onValueProxyAboutToBeModified();
+  void onValueProxyModified();
 
 protected:
   virtual bool eventFilter(QObject *obj, QEvent *event);