ソースを参照

Merge pull request #778 from tcoulange/color-widget-undo

Add undo feature to color widget
Julien Finet 7 年 前
コミット
f904f960b0

BIN
Libs/Visualization/VTK/Widgets/Resources/Icons/expand.png


BIN
Libs/Visualization/VTK/Widgets/Resources/Icons/expand2.png


BIN
Libs/Visualization/VTK/Widgets/Resources/Icons/expand3.png


BIN
Libs/Visualization/VTK/Widgets/Resources/Icons/minus.png


BIN
Libs/Visualization/VTK/Widgets/Resources/Icons/plus.png


BIN
Libs/Visualization/VTK/Widgets/Resources/Icons/shrink.png


BIN
Libs/Visualization/VTK/Widgets/Resources/Icons/shrink2.png


BIN
Libs/Visualization/VTK/Widgets/Resources/Icons/shrink3.png


BIN
Libs/Visualization/VTK/Widgets/Resources/Icons/undo.png


+ 28 - 67
Libs/Visualization/VTK/Widgets/Resources/UI/ctkVTKDiscretizableColorTransferWidget.ui

@@ -17,87 +17,51 @@
    <property name="margin">
     <number>0</number>
    </property>
-   <item row="9" column="1">
-    <widget class="QLabel" name="minOpacityLabel">
-     <property name="text">
-      <string>0</string>
-     </property>
-    </widget>
-   </item>
-   <item row="10" column="1" colspan="3">
-    <widget class="ctkRangeWidget" name="rangeSlider" native="true"/>
-   </item>
    <item row="1" column="3">
-    <widget class="QToolButton" name="resetRangeButton">
+    <widget class="QToolButton" name="undoButton">
      <property name="toolTip">
-      <string>Reset current range to visible range</string>
+      <string>Undo last change of range</string>
      </property>
      <property name="icon">
       <iconset resource="../ctkVTKWidgets.qrc">
-       <normaloff>:/Icons/resetRange.png</normaloff>:/Icons/resetRange.png</iconset>
+       <normaloff>:/Icons/undo.png</normaloff>:/Icons/undo.png</iconset>
      </property>
     </widget>
    </item>
    <item row="2" column="3">
-    <widget class="QToolButton" name="centerRangeButton">
+    <widget class="QToolButton" name="resetRangeButton">
      <property name="toolTip">
-      <string>Center current range on median</string>
+      <string>Reset ranges to data range</string>
      </property>
      <property name="icon">
       <iconset resource="../ctkVTKWidgets.qrc">
-       <normaloff>:/Icons/resetRangeCustom.png</normaloff>:/Icons/resetRangeCustom.png</iconset>
+       <normaloff>:/Icons/resetRange.png</normaloff>:/Icons/resetRange.png</iconset>
      </property>
     </widget>
    </item>
    <item row="3" column="3">
-    <spacer name="verticalSpacer">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>0</width>
-       <height>0</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-   <item row="4" column="3">
-    <widget class="QToolButton" name="zoomInButton">
+    <widget class="QToolButton" name="shrinkRangeButton">
      <property name="toolTip">
-      <string>Zoom in</string>
+      <string>Shrink display range to current range</string>
      </property>
      <property name="icon">
       <iconset resource="../ctkVTKWidgets.qrc">
-       <normaloff>:/Icons/plus.png</normaloff>:/Icons/plus.png</iconset>
+       <normaloff>:/Icons/shrink.png</normaloff>:/Icons/shrink.png</iconset>
      </property>
     </widget>
    </item>
-   <item row="5" column="3">
-    <widget class="QToolButton" name="zoomOutButton">
+   <item row="4" column="3">
+    <widget class="QToolButton" name="expandRangeButton">
      <property name="toolTip">
-      <string>Zoom out</string>
+      <string>Expand current range to display range</string>
      </property>
      <property name="icon">
       <iconset resource="../ctkVTKWidgets.qrc">
-       <normaloff>:/Icons/minus.png</normaloff>:/Icons/minus.png</iconset>
+       <normaloff>:/Icons/expand.png</normaloff>:/Icons/expand.png</iconset>
      </property>
     </widget>
    </item>
-   <item row="6" column="3">
-    <spacer name="verticalSpacer_2">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>0</width>
-       <height>0</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-   <item row="7" column="3">
+   <item row="5" column="3">
     <widget class="QToolButton" name="invertColorTransferFunctionButton">
      <property name="toolTip">
       <string>Invert color map</string>
@@ -111,20 +75,7 @@
      </property>
     </widget>
    </item>
-   <item row="8" column="3">
-    <spacer name="verticalSpacer_3">
-     <property name="orientation">
-      <enum>Qt::Vertical</enum>
-     </property>
-     <property name="sizeHint" stdset="0">
-      <size>
-       <width>0</width>
-       <height>0</height>
-      </size>
-     </property>
-    </spacer>
-   </item>
-   <item row="9" column="3">
+   <item row="6" column="3">
     <widget class="QToolButton" name="optionButton">
      <property name="contextMenuPolicy">
       <enum>Qt::DefaultContextMenu</enum>
@@ -134,8 +85,8 @@
      </property>
     </widget>
    </item>
-   <item row="1" column="1" colspan="2">
-    <widget class="ctkVTKScalarsToColorsComboBox" name="scalarsToColorsSelector"/>
+   <item row="7" column="1" colspan="3">
+    <widget class="ctkRangeWidget" name="rangeSlider" native="true"/>
    </item>
    <item row="2" column="1">
     <widget class="QLabel" name="maxOpacityLabel">
@@ -144,7 +95,7 @@
      </property>
     </widget>
    </item>
-   <item row="3" column="1" rowspan="6">
+   <item row="3" column="1" rowspan="3">
     <widget class="ctkDoubleSlider" name="opacitySlider" native="true">
      <property name="value" stdset="0">
       <double>1.000000000000000</double>
@@ -163,6 +114,16 @@
      </property>
     </widget>
    </item>
+   <item row="1" column="1" colspan="2">
+    <widget class="ctkVTKScalarsToColorsComboBox" name="scalarsToColorsSelector"/>
+   </item>
+   <item row="6" column="1">
+    <widget class="QLabel" name="minOpacityLabel">
+     <property name="text">
+      <string>0</string>
+     </property>
+    </widget>
+   </item>
   </layout>
  </widget>
  <customwidgets>

+ 3 - 0
Libs/Visualization/VTK/Widgets/Resources/ctkVTKWidgets.qrc

@@ -1,8 +1,11 @@
 <!DOCTYPE RCC><RCC version="1.0">
   <qresource>
+    <file>Icons/expand.png</file>
     <file>Icons/invert.png</file>
     <file>Icons/resetRange.png</file>
     <file>Icons/resetRangeCustom.png</file>
+    <file>Icons/shrink.png</file>
     <file>Icons/threshold.png</file>
+    <file>Icons/undo.png</file>
   </qresource>
 </RCC>

+ 150 - 27
Libs/Visualization/VTK/Widgets/ctkVTKDiscretizableColorTransferWidget.cpp

@@ -40,7 +40,7 @@
 #include <QMenu>
 #include <QPushButton>
 #include <QSpinBox>
-#include <QTimer>
+#include <QTime>
 #include <QToolButton>
 #include <QVBoxLayout>
 #include <QWidgetAction>
@@ -81,7 +81,16 @@ public:
   ctkVTKDiscretizableColorTransferWidgetPrivate(
     ctkVTKDiscretizableColorTransferWidget& object);
 
+  struct Ranges
+  {
+    double CurrentRange[2];
+    double VisibleRange[2];
+  };
+
   void setupUi(QWidget* widget);
+  void addRangesInHistory(double* currentRange, double* visibleRange);
+  bool popRangesFromHistory(double* currentRange, double* visibleRange);
+  void clearUndoHistory();
 
 #if CTK_USE_QVTKOPENGLWIDGET
   QVTKOpenGLWidget* ScalarsToColorsView;
@@ -106,6 +115,10 @@ public:
 
   double previousOpacityValue;
 
+  /// History of ranges for undo feature
+  QList<Ranges> rangesHistory;
+  QTime historyUpdateTime;
+
   vtkSmartPointer<vtkCallbackCommand> colorTransferFunctionModified;
   static void colorTransferFunctionModifiedCallback(vtkObject *caller,
     unsigned long eid, void *clientdata, void *calldata);
@@ -130,6 +143,8 @@ ctkVTKDiscretizableColorTransferWidgetPrivate
 
   this->previousOpacityValue = 0.;
 
+  this->historyUpdateTime = QTime::currentTime();
+
   this->colorTransferFunctionModified =
     vtkSmartPointer<vtkCallbackCommand>::New();
   this->colorTransferFunctionModified->SetClientData(this);
@@ -149,7 +164,7 @@ void ctkVTKDiscretizableColorTransferWidgetPrivate::setupUi(QWidget* widget)
 #else
   this->ScalarsToColorsView = new QVTKWidget;
 #endif
-  this->gridLayout->addWidget(this->ScalarsToColorsView, 2, 2, 8, 1);
+  this->gridLayout->addWidget(this->ScalarsToColorsView, 2, 2, 5, 1);
 
   this->scalarsToColorsContextItem = vtkSmartPointer<vtkScalarsToColorsContextItem>::New();
   vtkDiscretizableColorTransferFunction* ctf = this->scalarsToColorsContextItem->GetDiscretizableColorTransferFunction();
@@ -192,23 +207,23 @@ void ctkVTKDiscretizableColorTransferWidgetPrivate::setupUi(QWidget* widget)
   QObject::connect(opacitySlider, SIGNAL(valueChanged(double)),
     q, SLOT(setGlobalOpacity(double)));
 
-  QObject::connect(zoomOutButton, SIGNAL(clicked()),
-    q, SLOT(resetVisibleRangeToData()));
-
-  QObject::connect(zoomInButton, SIGNAL(clicked()),
-    q, SLOT(resetVisibleRangeToCTF()));
+  QObject::connect(undoButton, SIGNAL(clicked()),
+    q, SLOT(onUndoButtonClick()));
 
   QObject::connect(resetRangeButton, SIGNAL(clicked()),
-    q, SLOT(resetColorTransferFunctionRange()));
+    q, SLOT(onResetRangesButtonClick()));
 
-  QObject::connect(centerRangeButton, SIGNAL(clicked()),
-    q, SLOT(centerColorTransferFunctionRange()));
+  QObject::connect(shrinkRangeButton, SIGNAL(clicked()),
+    q, SLOT(onShrinkRangeButtonClick()));
+
+  QObject::connect(expandRangeButton, SIGNAL(clicked()),
+    q, SLOT(onExpandRangeButtonClick()));
 
   QObject::connect(invertColorTransferFunctionButton, SIGNAL(clicked()),
     q, SLOT(invertColorTransferFunction()));
 
   QObject::connect(rangeSlider, SIGNAL(valuesChanged(double, double)),
-    q, SLOT(setColorTransferFunctionRange(double, double)));
+    q, SLOT(onRangeSliderValueChange(double, double)));
 
   /// Option panel menu
   QWidget* nanColorWidget = new QWidget(optionButton);
@@ -265,6 +280,55 @@ void ctkVTKDiscretizableColorTransferWidgetPrivate::setupUi(QWidget* widget)
     nbOfDiscreteValuesSpinBox, SLOT(setEnabled(bool)));
 }
 
+//-----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidgetPrivate::addRangesInHistory(
+    double* currentRange, double* visibleRange)
+{
+  Q_Q(ctkVTKDiscretizableColorTransferWidget);
+
+  if (this->historyUpdateTime.msecsTo(QTime::currentTime()) < 500)
+  {
+    return;
+  }
+
+  Ranges ranges;
+  ranges.CurrentRange[0] = currentRange[0];
+  ranges.CurrentRange[1] = currentRange[1];
+  ranges.VisibleRange[0] = visibleRange[0];
+  ranges.VisibleRange[1] = visibleRange[1];
+  this->rangesHistory.push_back(ranges);
+  this->historyUpdateTime = QTime::currentTime();
+}
+
+//-----------------------------------------------------------------------------
+bool ctkVTKDiscretizableColorTransferWidgetPrivate::popRangesFromHistory(
+    double* currentRange, double* visibleRange)
+{
+  Q_Q(ctkVTKDiscretizableColorTransferWidget);
+
+  if (this->rangesHistory.empty())
+  {
+    return false;
+  }
+
+  Ranges ranges = this->rangesHistory.back();
+  currentRange[0] = ranges.CurrentRange[0];
+  currentRange[1] = ranges.CurrentRange[1];
+  visibleRange[0] = ranges.VisibleRange[0];
+  visibleRange[1] = ranges.VisibleRange[1];
+
+  this->rangesHistory.pop_back();
+  this->historyUpdateTime = QTime::currentTime();
+
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidgetPrivate::clearUndoHistory()
+{
+  this->rangesHistory.clear();
+}
+
 // ----------------------------------------------------------------------------
 void
 ctkVTKDiscretizableColorTransferWidgetPrivate::colorTransferFunctionModifiedCallback(
@@ -412,7 +476,6 @@ void ctkVTKDiscretizableColorTransferWidget::setHistogramConnection(
   {
     d->histogramFilter = CTK_NULLPTR;
     d->dataMean = 0.;
-    this->setDataRange(VTK_DOUBLE_MAX, VTK_DOUBLE_MIN);
     return;
   }
 
@@ -428,13 +491,19 @@ void ctkVTKDiscretizableColorTransferWidget::updateHistogram(
 
   this->updateHistogram();
 
-  if (updateDataRange
-   && d->histogramFilter
-   && d->histogramFilter->GetInputConnection(0, 0))
+  if (updateDataRange)
   {
-    // get min max values from histogram
-    this->setDataRange(d->histogramFilter->GetMin()[0],
-                       d->histogramFilter->GetMax()[0]);
+    if (d->histogramFilter
+     && d->histogramFilter->GetInputConnection(0, 0))
+    {
+      // get min max values from histogram
+      this->setDataRange(d->histogramFilter->GetMin()[0],
+                         d->histogramFilter->GetMax()[0]);
+    }
+    else
+    {
+      this->setDataRange(VTK_DOUBLE_MAX, VTK_DOUBLE_MIN);
+    }
   }
 }
 
@@ -541,10 +610,10 @@ void ctkVTKDiscretizableColorTransferWidget::disableCtfWidgets()
   d->opacitySlider->setValue(d->previousOpacityValue);
   d->opacitySlider->setEnabled(false);
   d->optionButton->setEnabled(false);
+  d->undoButton->setEnabled(false);
   d->resetRangeButton->setEnabled(false);
-  d->zoomInButton->setEnabled(false);
-  d->zoomOutButton->setEnabled(false);
-  d->centerRangeButton->setEnabled(false);
+  d->shrinkRangeButton->setEnabled(false);
+  d->expandRangeButton->setEnabled(false);
   d->invertColorTransferFunctionButton->setEnabled(false);
 
 #ifdef DEBUG_RANGE
@@ -563,10 +632,10 @@ void ctkVTKDiscretizableColorTransferWidget::enableCtfWidgets()
   d->rangeSlider->setEnabled(true);
   d->opacitySlider->setEnabled(true);
   d->optionButton->setEnabled(true);
+  d->undoButton->setEnabled(true);
   d->resetRangeButton->setEnabled(true);
-  d->zoomInButton->setEnabled(true);
-  d->zoomOutButton->setEnabled(true);
-  d->centerRangeButton->setEnabled(true);
+  d->shrinkRangeButton->setEnabled(true);
+  d->expandRangeButton->setEnabled(true);
   d->invertColorTransferFunctionButton->setEnabled(true);
 
   d->previousOpacityValue = 1.0;
@@ -682,12 +751,58 @@ void ctkVTKDiscretizableColorTransferWidget::onPaletteIndexChanged(
   vtkScalarsToColors* ctf)
 {
   Q_D(ctkVTKDiscretizableColorTransferWidget);
-
+  d->addRangesInHistory(this->getColorTransferFunctionRange(), this->getVisibleRange());
   this->copyColorTransferFunction(ctf);
   d->ScalarsToColorsView->GetInteractor()->Render();
 }
 
 // ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::onResetRangesButtonClick()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+  d->addRangesInHistory(this->getColorTransferFunctionRange(), this->getVisibleRange());
+  this->resetRangesToData();
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::onShrinkRangeButtonClick()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+  d->addRangesInHistory(this->getColorTransferFunctionRange(), this->getVisibleRange());
+  this->resetVisibleRangeToCTF();
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::onExpandRangeButtonClick()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+  d->addRangesInHistory(this->getColorTransferFunctionRange(), this->getVisibleRange());
+  this->resetCTFRangeToVisible();
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::onUndoButtonClick()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+
+  double currentRange[2], visibleRange[2];
+  if (d->popRangesFromHistory(currentRange, visibleRange))
+  {
+    this->setVisibleRange(visibleRange[0], visibleRange[1]);
+    this->setColorTransferFunctionRange(currentRange[0], currentRange[1]);
+  }
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::onRangeSliderValueChange(
+    double min, double max)
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+  d->addRangesInHistory(this->getColorTransferFunctionRange(), this->getVisibleRange());
+  this->setColorTransferFunctionRange(min, max);
+}
+
+// ----------------------------------------------------------------------------
 void ctkVTKDiscretizableColorTransferWidget::setGlobalOpacity(double value)
 {
   Q_D(ctkVTKDiscretizableColorTransferWidget);
@@ -842,10 +957,18 @@ void ctkVTKDiscretizableColorTransferWidget::onCurrentPointEdit()
 }
 
 // ----------------------------------------------------------------------------
-void ctkVTKDiscretizableColorTransferWidget::resetVisibleRangeToData()
+void ctkVTKDiscretizableColorTransferWidget::resetRangesToData()
 {
   Q_D(ctkVTKDiscretizableColorTransferWidget);
   this->resetVisibleRange(ctkVTKDiscretizableColorTransferWidget::ONLY_DATA);
+  this->resetColorTransferFunctionRange(ctkVTKDiscretizableColorTransferWidget::VISIBLE);
+}
+
+// ----------------------------------------------------------------------------
+void ctkVTKDiscretizableColorTransferWidget::clearUndoHistory()
+{
+  Q_D(ctkVTKDiscretizableColorTransferWidget);
+  d->clearUndoHistory();
 }
 
 // ----------------------------------------------------------------------------
@@ -856,7 +979,7 @@ void ctkVTKDiscretizableColorTransferWidget::resetVisibleRangeToCTF()
 }
 
 // ----------------------------------------------------------------------------
-void ctkVTKDiscretizableColorTransferWidget::resetColorTransferFunctionRange()
+void ctkVTKDiscretizableColorTransferWidget::resetCTFRangeToVisible()
 {
   Q_D(ctkVTKDiscretizableColorTransferWidget);
   this->resetColorTransferFunctionRange(ctkVTKDiscretizableColorTransferWidget::VISIBLE);

+ 17 - 10
Libs/Visualization/VTK/Widgets/ctkVTKDiscretizableColorTransferWidget.h

@@ -84,8 +84,19 @@ public:
 
   ctkVTKScalarsToColorsComboBox* scalarsToColorsSelector() const;
 
+  void setVisibleRange(double min, double max);
   void resetVisibleRange(ResetVisibleRange resetMode);
+  void resetVisibleRangeToCTF();
+
+  void setColorTransferFunctionRange(double min, double max);
   void resetColorTransferFunctionRange(ResetCTFRange resetMode);
+  void resetCTFRangeToVisible();
+  void centerColorTransferFunctionRange();
+
+  void setDataRange(double min, double max);
+  void resetRangesToData();
+
+  void clearUndoHistory();
 
 signals:
   void currentScalarsToColorsModified();
@@ -94,21 +105,17 @@ signals:
 public slots:
   void onCurrentPointEdit();
   void onPaletteIndexChanged(vtkScalarsToColors* ctf);
+  void onResetRangesButtonClick();
+  void onShrinkRangeButtonClick();
+  void onExpandRangeButtonClick();
+  void onUndoButtonClick();
+  void onRangeSliderValueChange(double min, double max);
 
-  void setGlobalOpacity(double opacity);
-
-  void resetVisibleRangeToCTF();
-  void resetVisibleRangeToData();
-  void resetColorTransferFunctionRange();
-  void centerColorTransferFunctionRange();
   void invertColorTransferFunction();
-
+  void setGlobalOpacity(double opacity);
   void setNaNColor();
   void setDiscretize(bool checked);
   void setNumberOfDiscreteValues(int value);
-  void setColorTransferFunctionRange(double min, double max);
-  void setVisibleRange(double min, double max);
-  void setDataRange(double min, double max);
 
 protected:
   QScopedPointer<ctkVTKDiscretizableColorTransferWidgetPrivate> d_ptr;