Selaa lähdekoodia

Merge branch 'DICOMDatasetViewer'

Conflicts:
	Libs/DICOM/Widgets/CMakeLists.txt
Stephen R. Aylward 14 vuotta sitten
vanhempi
commit
fcaac08879

+ 42 - 0
Applications/ctkDICOMObjectViewer/CMakeLists.txt

@@ -0,0 +1,42 @@
+PROJECT(ctkDICOMObjectViewer)
+
+#
+# See CTK/CMake/ctkMacroBuildApp.cmake for details
+#
+
+SET(KIT_SRCS
+  ctkDICOMObjectViewerMain.cpp
+  ctkDICOMObjectViewer.cpp
+)
+
+# Headers that should run through moc
+SET(KIT_MOC_SRCS
+  ctkDICOMObjectViewer.h
+)
+
+# UI files
+SET(KIT_UI_FORMS
+  ctkDICOMObjectViewerMainWindow.ui
+)
+
+# Resources
+SET(KIT_resources
+)
+
+# Target libraries - See CMake/ctkFunctionGetTargetLibraries.cmake
+# The following macro will read the target libraries from the file 'target_libraries.cmake'
+ctkFunctionGetTargetLibraries(KIT_target_libraries)
+
+ctkMacroBuildApp(
+  NAME ${PROJECT_NAME}
+  SRCS ${KIT_SRCS}
+  MOC_SRCS ${KIT_MOC_SRCS}
+  UI_FORMS ${KIT_UI_FORMS}
+  TARGET_LIBRARIES ${KIT_target_libraries}
+  RESOURCES ${KIT_resources}
+  )
+
+# Testing
+IF(BUILD_TESTING)
+#   ADD_SUBDIRECTORY(Testing)
+ENDIF(BUILD_TESTING)

+ 33 - 0
Applications/ctkDICOMObjectViewer/ctkDICOMObjectViewer.cpp

@@ -0,0 +1,33 @@
+#include <iostream>
+
+#include "ctkDICOMObjectViewer.h"
+
+#include "ctkDICOMDatasetViewerWidget.h"
+
+
+/**
+ *
+ */
+ctkDICOMObjectViewer::ctkDICOMObjectViewer( QWidget* _parent,
+  const char* _name, bool _modal,
+  Qt::WFlags _fl )
+:QDialog(_parent)
+{
+  setupUi(this);
+}
+
+/**
+ *  Destroys the object and frees any allocated resources
+ */
+ctkDICOMObjectViewer::~ctkDICOMObjectViewer()
+{
+}
+
+void ctkDICOMObjectViewer::SetInputImage( const QImage * image )
+{
+  this->OpenGlWindow->show();
+  this->OpenGlWindow->addImage( image );
+  std::cout << "Adding image height = " << image->height() << std::endl;
+  std::cout << "Adding image width = " << image->width() << std::endl;
+  this->OpenGlWindow->update();
+}

+ 21 - 0
Applications/ctkDICOMObjectViewer/ctkDICOMObjectViewer.h

@@ -0,0 +1,21 @@
+#ifndef __ctkDICOMObjectViewer_h
+#define __ctkDICOMObjectViewer_h
+
+#include "ui_ctkDICOMObjectViewerMainWindow.h"
+
+#include <QDialog>
+
+class ctkDICOMObjectViewer : public QDialog, Ui::MainWindow
+{
+public:
+
+  ctkDICOMObjectViewer( QWidget* parent = 0, const char* name = 0,
+    bool modal = FALSE, Qt::WFlags fl = 0 );
+  ~ctkDICOMObjectViewer();
+
+  void DisplayPosition(int x,int y ,int z,float value);
+
+  void SetInputImage( const QImage * image );
+};
+
+#endif

+ 65 - 0
Applications/ctkDICOMObjectViewer/ctkDICOMObjectViewerMain.cpp

@@ -0,0 +1,65 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) German Cancer Research Center,
+    Division of Medical and Biological Informatics
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+#include "iostream"
+
+#include "ctkDICOMObjectViewer.h"
+#include "ctkDICOMDatasetViewerWidget.h"
+
+#include <ui_ctkDICOMObjectViewerMainWindow.h>
+
+#include <QApplication>
+#include <QMainWindow>
+#include <QString>
+#include <QFileDialog>
+
+int main(int argv, char** argc)
+{
+  QApplication app(argv, argc);
+
+  qApp->setOrganizationName("CTK");
+  qApp->setOrganizationDomain("commontk.org");
+  qApp->setApplicationName("ctkDICOMObjectViewer");
+
+  ctkDICOMObjectViewer mainWindow;
+
+  mainWindow.show();
+
+  QString s;
+  if( QApplication::argc() > 1 )
+    {
+    s = QApplication::argv()[1];
+    }
+  else
+    {
+    s = QFileDialog::getOpenFileName( 0,
+     "Choose an image file", ".",
+     "JPG (*.jpg *.jpep);; PNG (*.png);; BMP (*.bmp);; TIFF (*.tif *.tiff)" 
+     );
+    }
+
+  QImage image( s );
+  std::cout << "Loading image _" << s.toStdString() << "_" << std::endl;
+
+  mainWindow.SetInputImage( & image );
+
+  return app.exec();
+}

+ 369 - 0
Applications/ctkDICOMObjectViewer/ctkDICOMObjectViewerMainWindow.ui

@@ -0,0 +1,369 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QDialog" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>626</width>
+    <height>667</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="font">
+   <font>
+    <weight>75</weight>
+    <bold>true</bold>
+   </font>
+  </property>
+  <property name="windowTitle">
+   <string>CTK Simple Image Viewer</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>false</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0,0,0,0,0,0">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_5" stretch="0,0">
+     <item>
+      <widget class="ctkQImageViewerWidget" name="OpenGlWindow" native="true">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+         <horstretch>20</horstretch>
+         <verstretch>20</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="minimumSize">
+        <size>
+         <width>100</width>
+         <height>100</height>
+        </size>
+       </property>
+       <property name="autoFillBackground">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout_2">
+       <item row="0" column="0" colspan="3">
+        <widget class="QSpinBox" name="SliceNum">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+           <horstretch>5</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QSlider" name="Slice">
+         <property name="sizePolicy">
+          <sizepolicy hsizetype="Fixed" vsizetype="Expanding">
+           <horstretch>10</horstretch>
+           <verstretch>0</verstretch>
+          </sizepolicy>
+         </property>
+         <property name="layoutDirection">
+          <enum>Qt::RightToLeft</enum>
+         </property>
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>2</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="PositionX">
+       <property name="text">
+        <string>0</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="PositionY">
+       <property name="text">
+        <string>0</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="PositionZ">
+       <property name="text">
+        <string>0</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="TextLabel1">
+       <property name="text">
+        <string>=</string>
+       </property>
+       <property name="wordWrap">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLabel" name="PixelValue">
+       <property name="text">
+        <string>0</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>5</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QToolButton" name="ZoomIn">
+       <property name="font">
+        <font>
+         <family>Arial</family>
+        </font>
+       </property>
+       <property name="text">
+        <string>Zoom In</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QToolButton" name="ZoomOut">
+       <property name="font">
+        <font>
+         <family>Arial</family>
+        </font>
+       </property>
+       <property name="text">
+        <string>Zoom Out</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer_2">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>2</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
+     <item>
+      <widget class="QSpinBox" name="IntensityMinDisplay"/>
+     </item>
+     <item>
+      <widget class="QSlider" name="IntensityMin">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3"/>
+   </item>
+   <item>
+    <spacer name="verticalSpacer_3">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>2</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_6">
+     <property name="spacing">
+      <number>6</number>
+     </property>
+     <property name="margin">
+      <number>0</number>
+     </property>
+     <item>
+      <spacer name="horizontalSpacer_2">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>28</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="buttonOk">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="font">
+        <font>
+         <family>Arial</family>
+        </font>
+       </property>
+       <property name="windowTitle">
+        <string/>
+       </property>
+       <property name="text">
+        <string>&amp;Quit</string>
+       </property>
+       <property name="autoDefault">
+        <bool>true</bool>
+       </property>
+       <property name="default">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>ctkQImageViewerWidget</class>
+   <extends>QWidget</extends>
+   <header location="global">ctkQImageViewerWidget.h</header>
+   <container>1</container>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>buttonOk</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>Slice</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>SliceNum</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>1018</x>
+     <y>134</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>993</x>
+     <y>27</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>SliceNum</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>Slice</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>993</x>
+     <y>29</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>1018</x>
+     <y>118</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonOk</sender>
+   <signal>clicked()</signal>
+   <receiver>MainWindow</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>960</x>
+     <y>701</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>734</x>
+     <y>667</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>IntensityMinDisplay</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>IntensityMin</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>96</x>
+     <y>643</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>118</x>
+     <y>643</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>IntensityMin</sender>
+   <signal>valueChanged(int)</signal>
+   <receiver>IntensityMinDisplay</receiver>
+   <slot>setValue(int)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>116</x>
+     <y>643</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>98</x>
+     <y>643</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

+ 9 - 0
Applications/ctkDICOMObjectViewer/target_libraries.cmake

@@ -0,0 +1,9 @@
+#
+# See CMake/ctkFunctionGetTargetLibraries.cmake
+# 
+# This file should list the libraries required to build the current CTK application.
+# 
+
+SET(target_libraries
+  CTKDICOMWidgets
+  )

+ 1 - 0
CMakeLists.txt

@@ -338,6 +338,7 @@ SET(CTK_APPLICATIONS
   ctkExampleHostedApp:OFF
   ctkPluginBrowser:OFF
   ctkPluginGenerator:OFF
+  ctkDICOMObjectViewer:OFF
   ctkSimplePythonShell:OFF
   )
   

+ 4 - 1
Libs/DICOM/Widgets/CMakeLists.txt

@@ -9,6 +9,8 @@ SET(KIT_export_directive "CTK_DICOM_WIDGETS_EXPORT")
   
 # Source files
 SET(KIT_SRCS
+  ctkDICOMDatasetViewerWidget.cpp
+  ctkDICOMDatasetViewerWidget.h
   ctkDICOMDirectoryListWidget.cpp
   ctkDICOMDirectoryListWidget.h
   ctkDICOMListenerWidget.cpp
@@ -33,13 +35,14 @@ SET(KIT_SRCS
 
 # Headers that should run through moc
 SET(KIT_MOC_SRCS
-  ctkDICOMQueryRetrieveWidget.h
+  ctkDICOMDatasetViewerWidget.h
   ctkDICOMDirectoryListWidget.h
   ctkDICOMServerNodeWidget.h
   ctkDICOMAppWidget.h
   ctkDICOMThumbnailWidget.h
   ctkDICOMThumbnailListWidget.h
   ctkDICOMImportWidget.h
+  ctkDICOMQueryRetrieveWidget.h
   )
 
 # UI files - includes new widgets

+ 129 - 0
Libs/DICOM/Widgets/ctkDICOMDatasetViewerWidget.cpp

@@ -0,0 +1,129 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#include <iostream>
+
+// CTK includes
+#include "ctkQImageViewerWidget.h"
+#include "ctkDICOMDatasetViewerWidget.h"
+
+// Qt includes
+#include <QLabel>
+#include <QHBoxLayout>
+#include <QDebug>
+#include <QResizeEvent>
+#include <QMouseEvent>
+#include <QKeyEvent>
+#include <QPainter>
+
+//--------------------------------------------------------------------------
+class ctkDICOMDatasetViewerWidgetPrivate 
+{
+
+  Q_DECLARE_PUBLIC( ctkDICOMDatasetViewerWidget );
+
+protected:
+
+  ctkDICOMDatasetViewerWidget* const q_ptr;
+
+public:
+
+  ctkDICOMDatasetViewerWidgetPrivate( ctkDICOMDatasetViewerWidget& object );
+
+  void init();
+};
+
+//--------------------------------------------------------------------------
+ctkDICOMDatasetViewerWidgetPrivate::ctkDICOMDatasetViewerWidgetPrivate(
+  ctkDICOMDatasetViewerWidget& object )
+  : q_ptr( & object )
+{
+}
+
+//--------------------------------------------------------------------------
+void ctkDICOMDatasetViewerWidgetPrivate::init()
+{
+  /*
+  Q_Q( ctkDICOMDatasetViewerWidget );
+  this->Window->setParent(q);
+  QHBoxLayout* layout = new QHBoxLayout(q);
+  layout->addWidget(this->Window);
+  layout->setContentsMargins(0,0,0,0);
+  q->setLayout(layout);
+  */
+}
+
+// -------------------------------------------------------------------------
+ctkDICOMDatasetViewerWidget::ctkDICOMDatasetViewerWidget( QWidget* _parent )
+  : Superclass( _parent ),
+    d_ptr( new ctkDICOMDatasetViewerWidgetPrivate( *this ) )
+{
+  Q_D( ctkDICOMDatasetViewerWidget );
+  d->init();
+}
+
+// -------------------------------------------------------------------------
+ctkDICOMDatasetViewerWidget::ctkDICOMDatasetViewerWidget(
+  ctkDICOMDatasetViewerWidgetPrivate& pvt,
+  QWidget* _parent)
+  : Superclass(_parent), d_ptr(&pvt)
+{
+  Q_D(ctkDICOMDatasetViewerWidget);
+  d->init();
+}
+
+// -------------------------------------------------------------------------
+ctkDICOMDatasetViewerWidget::~ctkDICOMDatasetViewerWidget()
+{
+}
+
+// -------------------------------------------------------------------------
+//void ctkDICOMDatasetViewerWidget::addImage( const QImage * image )
+//{
+  //Q_D( ctkQImageViewerWidget );
+  //d->ImageList.push_back( image );
+  //d->TmpXMin = 0;
+  //d->TmpXMax = image->width();
+  //d->TmpYMin = 0;
+  //d->TmpYMax = image->height();
+  //this->update( true, false );
+  //this->setCenter( image->width()/2.0, image->height()/2.0 );
+//}
+
+// -------------------------------------------------------------------------
+void ctkDICOMDatasetViewerWidget::mousePressEvent( QMouseEvent * event )
+{
+  event->ignore();
+}
+
+// -------------------------------------------------------------------------
+void ctkDICOMDatasetViewerWidget::mouseMoveEvent( QMouseEvent * event )
+{
+  event->ignore();
+}
+
+// -------------------------------------------------------------------------
+void ctkDICOMDatasetViewerWidget::update( bool zoomChanged,
+  bool sizeChanged )
+{
+  std::cout << "DICOM Updating.." << std::endl;
+
+  Superclass::update( zoomChanged, sizeChanged );
+}

+ 79 - 0
Libs/DICOM/Widgets/ctkDICOMDatasetViewerWidget.h

@@ -0,0 +1,79 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkDICOMDatasetViewerWidget_h
+#define __ctkDICOMDatasetViewerWidget_h
+
+/// Qt includes
+#include <QWidget>
+#include <QImage>
+
+/// CTK includes
+#include "ctkQImageViewerWidget.h"
+#include "ctkPimpl.h"
+#include "ctkWidgetsExport.h"
+
+class ctkDICOMDatasetViewerWidgetPrivate;
+
+///
+/// ctkDICOMDatasetViewerWidget is the base class of image viewer widgets.
+class CTK_WIDGETS_EXPORT ctkDICOMDatasetViewerWidget
+: public ctkQImageViewerWidget
+{
+
+  Q_OBJECT
+
+public:
+
+  /// Superclass typedef
+  typedef ctkQImageViewerWidget Superclass;
+
+  /// Constructor
+  ctkDICOMDatasetViewerWidget( QWidget * parent = 0 );
+  
+  /// Destructor
+  virtual ~ctkDICOMDatasetViewerWidget( void );
+
+public slots:
+
+  //void add( const QImage * image );
+
+  void mousePressEvent( QMouseEvent * event );
+  void mouseMoveEvent( QMouseEvent * event );
+
+  virtual void update( bool zoomChanged=false, bool sizeChanged=false );
+
+protected:
+
+  /// protected constructor to derive private implementations
+  ctkDICOMDatasetViewerWidget( ctkDICOMDatasetViewerWidgetPrivate & pvt,
+    QWidget* parent=0 );
+
+private:
+
+  QScopedPointer< ctkDICOMDatasetViewerWidgetPrivate > d_ptr;
+
+  Q_DECLARE_PRIVATE( ctkDICOMDatasetViewerWidget );
+
+  Q_DISABLE_COPY( ctkDICOMDatasetViewerWidget );
+
+};
+
+#endif

+ 3 - 0
Libs/Widgets/CMakeLists.txt

@@ -85,6 +85,8 @@ SET(KIT_SRCS
   ctkSettingsDialog.h
   ctkSettingsPanel.cpp
   ctkSettingsPanel.h
+  ctkQImageViewerWidget.cpp
+  ctkQImageViewerWidget.h
   ctkSliderWidget.cpp
   ctkSliderWidget.h
   ctkTestApplication.cpp
@@ -176,6 +178,7 @@ SET(KIT_MOC_SRCS
   ctkSettings.h
   ctkSettingsDialog.h
   ctkSettingsPanel.h
+  ctkQImageViewerWidget.h
   ctkSliderWidget.h
   ctkTestApplication.h
   ctkToolTipTrapper.h

+ 807 - 0
Libs/Widgets/ctkQImageViewerWidget.cpp

@@ -0,0 +1,807 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#include <iostream>
+
+// CTK includes
+#include "ctkQImageViewerWidget.h"
+
+// Qt includes
+#include <QLabel>
+#include <QHBoxLayout>
+#include <QDebug>
+#include <QResizeEvent>
+#include <QMouseEvent>
+#include <QKeyEvent>
+#include <QPainter>
+
+//--------------------------------------------------------------------------
+class ctkQImageViewerWidgetPrivate
+{
+  Q_DECLARE_PUBLIC( ctkQImageViewerWidget );
+protected:
+  ctkQImageViewerWidget* const q_ptr;
+public:
+  ctkQImageViewerWidgetPrivate( ctkQImageViewerWidget& object );
+
+  void init();
+
+  QLabel * Window;
+
+  double Zoom;
+  double PositionX;
+  double PositionY;
+  double CenterX;
+  double CenterY;
+  int    SliceNumber;
+
+  double IntensityWindowMin;
+  double IntensityWindowMax;
+
+  bool FlipXAxis;
+  bool FlipYAxis;
+  bool TransposeXY;
+
+  QList< const QImage * > ImageList;
+
+  QPixmap TmpImage;
+  int     TmpXMin;
+  int     TmpXMax;
+  int     TmpYMin;
+  int     TmpYMax;
+
+  int    MouseLastX;
+  int    MouseLastY;
+  double MouseLastZoom;
+  double MouseLastIntensityWindowMin;
+  double MouseLastIntensityWindowMax;
+  bool   MouseLeftDragging;
+  bool   MouseMiddleDragging;
+  bool   MouseRightDragging;
+
+  double clamp( double x, double xMin, double xMax );
+
+  void fitImageRectangle( double x0, double y0, double x1, double y1 );
+  
+};
+
+//--------------------------------------------------------------------------
+ctkQImageViewerWidgetPrivate::ctkQImageViewerWidgetPrivate(
+  ctkQImageViewerWidget& object )
+  : q_ptr( &object )
+{
+  this->Window = new QLabel();
+}
+
+//--------------------------------------------------------------------------
+void ctkQImageViewerWidgetPrivate::init()
+{
+  Q_Q( ctkQImageViewerWidget );
+
+  this->Window->setParent(q);
+  QHBoxLayout* layout = new QHBoxLayout(q);
+  layout->addWidget(this->Window);
+  layout->setContentsMargins(0,0,0,0);
+  q->setLayout(layout);
+
+
+  // Set parameters for the view
+  this->Zoom = 1;
+  this->PositionX = 0;
+  this->PositionY = 0;
+  this->SliceNumber = 0;
+
+  this->CenterX = 0;
+  this->CenterY = 0;
+
+  this->IntensityWindowMin = 0;
+  this->IntensityWindowMax = 0;
+
+  this->FlipXAxis = false;
+  this->FlipYAxis = true;
+  this->TransposeXY = false;
+
+  this->ImageList.clear();
+
+  this->TmpXMin = 0;
+  this->TmpXMax = 0;
+  this->TmpYMin = 0;
+  this->TmpYMax = 0;
+
+  this->MouseLastX = 0;
+  this->MouseLastY = 0;
+  this->MouseLastZoom = 0;
+  this->MouseLeftDragging = false;
+  this->MouseMiddleDragging = false;
+  this->MouseRightDragging = false;
+
+  // Don't expand for no reason
+  q->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
+}
+
+//--------------------------------------------------------------------------
+double ctkQImageViewerWidgetPrivate::clamp( double x, double xMin,
+  double xMax )
+{
+  if( x < xMin )
+    {
+    return xMin;
+    }
+  if( x > xMax )
+    {
+    return xMax;
+    }
+  return x;
+}
+
+//--------------------------------------------------------------------------
+void ctkQImageViewerWidgetPrivate::fitImageRectangle( double x0,
+  double x1, double y0, double y1 )
+{
+  if( this->SliceNumber >= 0 && this->SliceNumber < this->ImageList.size() )
+    {
+    this->TmpXMin = this->clamp( x0, 0,
+      this->ImageList[ this->SliceNumber ]->width() );
+    this->TmpXMax = this->clamp( x1, this->TmpXMin,
+      this->ImageList[ this->SliceNumber ]->width() );
+    this->TmpYMin = this->clamp( y0, 0,
+      this->ImageList[ this->SliceNumber ]->height() );
+    this->TmpYMax = this->clamp( y1, this->TmpYMin,
+      this->ImageList[ this->SliceNumber ]->height() );
+    this->CenterX = ( this->TmpXMax + this->TmpXMin ) / 2.0;
+    this->CenterY = ( this->TmpYMax + this->TmpYMin ) / 2.0;
+    }
+}
+
+
+// -------------------------------------------------------------------------
+ctkQImageViewerWidget::ctkQImageViewerWidget( QWidget* _parent )
+  : Superclass( _parent ),
+    d_ptr( new ctkQImageViewerWidgetPrivate( *this ) )
+{
+  Q_D( ctkQImageViewerWidget );
+  d->init();
+  d->TmpXMax = this->width();
+  d->TmpYMax = this->height();
+}
+
+// -------------------------------------------------------------------------
+ctkQImageViewerWidget::ctkQImageViewerWidget(
+  ctkQImageViewerWidgetPrivate& pvt,
+  QWidget* _parent)
+  : Superclass(_parent), d_ptr(&pvt)
+{
+  Q_D(ctkQImageViewerWidget);
+  d->init();
+}
+
+// -------------------------------------------------------------------------
+ctkQImageViewerWidget::~ctkQImageViewerWidget()
+{
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::addImage( const QImage * image )
+{
+  Q_D( ctkQImageViewerWidget );
+  d->ImageList.push_back( image );
+  d->TmpXMin = 0;
+  d->TmpXMax = image->width();
+  d->TmpYMin = 0;
+  d->TmpYMax = image->height();
+  this->update( true, false );
+  this->setCenter( image->width()/2.0, image->height()/2.0 );
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::clearImages( void )
+{
+  Q_D( ctkQImageViewerWidget );
+  d->ImageList.clear();
+  this->update( true, true );
+}
+
+// -------------------------------------------------------------------------
+double ctkQImageViewerWidget::xSpacing( void )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( d->SliceNumber >= 0 && d->SliceNumber < d->ImageList.size() )
+    {
+    return( 1000.0 / d->ImageList[ d->SliceNumber ]->dotsPerMeterX() );
+    }
+  else
+    {
+    return 1;
+    }
+}
+
+// -------------------------------------------------------------------------
+double ctkQImageViewerWidget::ySpacing( void )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( d->SliceNumber >= 0 && d->SliceNumber < d->ImageList.size() )
+    {
+    return( 1000.0 / d->ImageList[ d->SliceNumber ]->dotsPerMeterY() );
+    }
+  else
+    {
+    return 1;
+    }
+}
+
+// -------------------------------------------------------------------------
+double ctkQImageViewerWidget::sliceThickness( void )
+{
+  return 1;
+}
+
+// -------------------------------------------------------------------------
+double ctkQImageViewerWidget::xPosition( void )
+{
+  Q_D( ctkQImageViewerWidget );
+  return d->PositionX;
+}
+
+// -------------------------------------------------------------------------
+double ctkQImageViewerWidget::yPosition( void )
+{
+  Q_D( ctkQImageViewerWidget );
+  return d->PositionY;
+}
+
+// -------------------------------------------------------------------------
+double ctkQImageViewerWidget::slicePosition( void )
+{
+  Q_D( ctkQImageViewerWidget );
+  return d->SliceNumber;
+}
+
+// -------------------------------------------------------------------------
+double ctkQImageViewerWidget::positionValue( void )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( d->SliceNumber >= 0 && d->SliceNumber < d->ImageList.size() )
+    {
+    QColor vc( d->ImageList[ d->SliceNumber ]->pixel( d->PositionX,
+      d->PositionY ) );
+    return vc.value();
+    }
+  return 0;
+}
+
+// -------------------------------------------------------------------------
+double ctkQImageViewerWidget::xCenter( void )
+{
+  Q_D( ctkQImageViewerWidget );
+  return d->CenterX;
+}
+
+// -------------------------------------------------------------------------
+double ctkQImageViewerWidget::yCenter( void )
+{
+  Q_D( ctkQImageViewerWidget );
+  return d->CenterY;
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::setSliceNumber( int slicenum )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( slicenum < d->ImageList.size() && slicenum != d->SliceNumber )
+    {
+    d->SliceNumber = slicenum;
+    emit this->sliceNumberChanged( slicenum );
+    emit this->xSpacingChanged( this->xSpacing() );
+    emit this->ySpacingChanged( this->ySpacing() );
+    emit this->sliceThicknessChanged( this->sliceThickness() );
+    emit this->slicePositionChanged( this->slicePosition() );
+    this->update( false, false );
+    }
+}
+//
+// -------------------------------------------------------------------------
+int ctkQImageViewerWidget::sliceNumber( void ) const
+{
+  Q_D( const ctkQImageViewerWidget );
+  if( d->SliceNumber >= 0 && d->SliceNumber < d->ImageList.size() )
+    {
+    return d->SliceNumber;
+    }
+  else
+    {
+    return -1;
+    }
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::setIntensityWindow( double iwMin, double iwMax )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( iwMin != d->IntensityWindowMin )
+    {
+    d->IntensityWindowMin = iwMin;
+    d->IntensityWindowMax = iwMax;
+    emit this->intensityWindowMinChanged( iwMin );
+    emit this->intensityWindowMaxChanged( iwMax );
+    this->update( false, false );
+    }
+}
+
+// -------------------------------------------------------------------------
+double ctkQImageViewerWidget::intensityWindowMin( void ) const
+{
+  Q_D( const ctkQImageViewerWidget );
+  return d->IntensityWindowMin;
+}
+
+// -------------------------------------------------------------------------
+double ctkQImageViewerWidget::intensityWindowMax( void ) const
+{
+  Q_D( const ctkQImageViewerWidget );
+  return d->IntensityWindowMax;
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::setFlipXAxis( bool flip )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( flip != d->FlipXAxis )
+    {
+    d->FlipXAxis = flip;
+    emit this->flipXAxisChanged( flip );
+    this->update( false, false );
+    }
+}
+
+// -------------------------------------------------------------------------
+bool ctkQImageViewerWidget::flipXAxis( void ) const
+{
+  Q_D( const ctkQImageViewerWidget );
+  return d->FlipXAxis;
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::setFlipYAxis( bool flip )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( flip != d->FlipYAxis )
+    {
+    d->FlipYAxis = flip;
+    emit this->flipYAxisChanged( flip );
+    this->update( false, false );
+    }
+}
+
+// -------------------------------------------------------------------------
+bool ctkQImageViewerWidget::flipYAxis( void ) const
+{
+  Q_D( const ctkQImageViewerWidget );
+  return d->FlipYAxis;
+}
+
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::setTransposeXY( bool transpose )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( transpose != d->TransposeXY )
+    {
+    d->TransposeXY = transpose;
+    emit this->transposeXYChanged( transpose );
+    this->update( false, false );
+    }
+}
+
+// -------------------------------------------------------------------------
+bool ctkQImageViewerWidget::transposeXY( void ) const
+{
+  Q_D( const ctkQImageViewerWidget );
+  return d->TransposeXY;
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::setCenter( double x, double y )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( d->SliceNumber >= 0 && d->SliceNumber < d->ImageList.size() )
+    {
+	  int tmpXRange = d->TmpXMax - d->TmpXMin;
+    if( tmpXRange > d->ImageList[ d->SliceNumber ]->width() )
+      {
+      tmpXRange = d->ImageList[ d->SliceNumber ]->width();
+      }
+    int tmpYRange = d->TmpYMax - d->TmpYMin;
+    if( tmpYRange > d->ImageList[ d->SliceNumber ]->height() )
+      {
+      tmpYRange = d->ImageList[ d->SliceNumber ]->height();
+      }
+  
+    int xMin2 = static_cast<int>(x) - tmpXRange/2.0;
+    if( xMin2 < 0 )
+      {
+      xMin2 = 0;
+      }
+    int xMax2 = xMin2 + tmpXRange;
+    if( xMax2 > d->ImageList[ d->SliceNumber ]->width() )
+      {
+      xMax2 = d->ImageList[ d->SliceNumber ]->width();
+      xMin2 = xMax2 - tmpXRange;
+      }
+    int yMin2 = static_cast<int>(y) - tmpYRange/2.0;
+    if( yMin2 < 0 )
+      {
+      yMin2 = 0;
+      }
+    int yMax2 = yMin2 + tmpYRange;
+    if( yMax2 > d->ImageList[ d->SliceNumber ]->height() )
+      {
+      yMax2 = d->ImageList[ d->SliceNumber ]->height();
+      yMin2 = yMax2 - tmpYRange;
+      }
+    d->fitImageRectangle( xMin2, xMax2, yMin2, yMax2 );
+
+    emit this->xCenterChanged( x );
+    emit this->yCenterChanged( y );
+  
+	  this->update( false, false );
+    }
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::setPosition( double x, double y )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( d->SliceNumber >= 0 && d->SliceNumber < d->ImageList.size() )
+    {
+    d->PositionX = x;
+    d->PositionY = y;
+
+    emit this->xPositionChanged( x );
+    emit this->yPositionChanged( y );
+    emit this->positionValueChanged( this->positionValue() );
+
+	  this->update( false, false );
+    }
+}
+
+// -------------------------------------------------------------------------
+double ctkQImageViewerWidget::zoom( void )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( d->SliceNumber >= 0 && d->SliceNumber < d->ImageList.size() )
+    {
+    return d->Zoom;
+    }
+  return 1;
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::setZoom( double factor )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( d->SliceNumber >= 0 && d->SliceNumber < d->ImageList.size() )
+    {
+    const QImage * img = d->ImageList[ d->SliceNumber ];
+    if( factor < 2.0 / img->width() )
+      {
+      factor = 2.0 / img->width();
+      }
+    if( factor > img->width()/2.0 )
+      {
+      factor = img->width()/2.0;
+      }
+    d->Zoom = factor;
+
+    double cx = ( d->TmpXMin + d->TmpXMax ) / 2.0;
+    double cy = ( d->TmpYMin + d->TmpYMax ) / 2.0;
+    double x2 = img->width() / factor;
+    double y2 = img->height() / factor;
+    //double x2 = ( d->TmpXMax - d->TmpXMin ) / 2.0;
+    //double y2 = ( d->TmpYMax - d->TmpYMin ) / 2.0; 
+	  
+    int xMin2 = static_cast<int>(cx) - x2 / 2.0;
+    if( xMin2 < 0 )
+      {
+      xMin2 = 0;
+      }
+    int xMax2 = xMin2 + x2;
+    if( xMax2 > d->ImageList[ d->SliceNumber ]->width() )
+      {
+      xMax2 = d->ImageList[ d->SliceNumber ]->width();
+      xMin2 = xMax2 - x2;
+      }
+    int yMin2 = static_cast<int>(cy) - y2 / 2.0;
+    if( yMin2 < 0 )
+      {
+      yMin2 = 0;
+      }
+    int yMax2 = yMin2 + y2;
+    if( yMax2 > d->ImageList[ d->SliceNumber ]->height() )
+      {
+      yMax2 = d->ImageList[ d->SliceNumber ]->height();
+      yMin2 = yMax2 - y2;
+      }
+    d->fitImageRectangle( xMin2, xMax2, yMin2, yMax2 );
+  
+	  this->update( true, true );
+    }
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::reset( )
+{
+  Q_D( ctkQImageViewerWidget );
+
+  if( d->SliceNumber >= 0 && d->SliceNumber < d->ImageList.size() )
+    {
+    d->fitImageRectangle( 0, 0, d->ImageList[ d->SliceNumber ]->width(),
+      d->ImageList[ d->SliceNumber ]->height() );
+    }
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::keyPressEvent( QKeyEvent * event )
+{
+  event->ignore();
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::mousePressEvent( QMouseEvent * event )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( d->SliceNumber >= 0 && d->SliceNumber < d->ImageList.size() )
+    {
+    switch( event->button() )
+      {
+      case Qt::LeftButton:
+        {
+        d->MouseLeftDragging = true;
+        d->MouseLastX = event->x();
+        d->MouseLastY = event->y();
+        d->MouseLastIntensityWindowMin = this->intensityWindowMin();
+        d->MouseLastIntensityWindowMax = this->intensityWindowMax();
+        break;
+        }
+      case Qt::MiddleButton:
+        {
+        d->MouseMiddleDragging = true;
+        d->MouseLastX = event->x();
+        d->MouseLastY = event->y();
+        d->MouseLastZoom = this->zoom();
+        break;
+        }
+      case Qt::RightButton:
+        {
+        d->MouseRightDragging = true;
+        double relativeX = static_cast<double>( event->x() ) 
+          / this->width();
+        double relativeY = static_cast<double>( event->y() ) 
+          / this->height();
+        double x = (d->TmpXMax - d->TmpXMin) * relativeX + d->TmpXMin;
+        double y = (d->TmpYMax - d->TmpYMin) * relativeY + d->TmpYMin;
+        this->setCenter( x, y );
+        break;
+        }
+      default:
+        {
+        event->ignore();
+        }
+      };
+    }
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::mouseReleaseEvent( QMouseEvent * event )
+{
+  Q_D( ctkQImageViewerWidget );
+  d->MouseLeftDragging = false;
+  d->MouseMiddleDragging = false;
+  d->MouseRightDragging = false;
+  event->ignore();
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::mouseMoveEvent( QMouseEvent * event )
+{
+  Q_D( ctkQImageViewerWidget );
+  if( d->SliceNumber >= 0 && d->SliceNumber < d->ImageList.size() )
+    {
+    if( d->MouseLeftDragging )
+      {
+      double distX = d->MouseLastX - event->x();
+      double distY = d->MouseLastY - event->y();
+      double deltaMin = ( distX / this->height() );
+      if( deltaMin < 0 )  
+        {
+        // Heuristic to make shrinking propotional to enlarging
+        deltaMin *= -deltaMin;
+        }
+      double deltaMax = ( distY / this->width() );
+      if( deltaMax < 0 )  
+        {
+        // Heuristic to make shrinking propotional to enlarging
+        deltaMax *= -deltaMax;
+        }
+      double iRange = d->MouseLastIntensityWindowMax 
+        - d->MouseLastIntensityWindowMin;
+      deltaMin *= iRange;
+      deltaMax *= iRange;
+      double newMin = d->MouseLastIntensityWindowMin + deltaMin;
+      double newMax = d->MouseLastIntensityWindowMax + deltaMax;
+      this->setIntensityWindow( newMin, newMax );
+      }
+    else if( d->MouseMiddleDragging )
+      {
+      double distY = d->MouseLastY - event->y();
+      double deltaZ = (distY / this->height());
+      if( deltaZ < 0 )  
+        {
+        // Heuristic to make shrinking propotional to enlarging
+        deltaZ *= -deltaZ;
+        }
+      double newZoom = d->MouseLastZoom + deltaZ;
+      this->setZoom( newZoom );
+      }
+    else
+      {
+      double relativeX = static_cast<double>( event->x() ) 
+        / this->width();
+      double relativeY = static_cast<double>( event->y() ) 
+        / this->height();
+      double x = (d->TmpXMax - d->TmpXMin) * relativeX + d->TmpXMin;
+      double y = (d->TmpYMax - d->TmpYMin) * relativeY + d->TmpYMin;
+      this->setPosition( x, y );
+      }
+    }
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::resizeEvent( QResizeEvent* event )
+{
+  this->Superclass::resizeEvent( event );
+  this->update( false, true );
+}
+
+// -------------------------------------------------------------------------
+void ctkQImageViewerWidget::update( bool zoomChanged,
+  bool sizeChanged )
+{
+  std::cout << "Updating.." << std::endl;
+
+  Q_D( ctkQImageViewerWidget );
+  if( d->SliceNumber >= 0 && d->SliceNumber < d->ImageList.size() )
+    {
+    const QImage * img = d->ImageList[ d->SliceNumber ];
+    if( zoomChanged || sizeChanged )
+      {
+      std::cout << "Update: Changed" << std::endl;
+      if( this->width() > 0 &&  this->height() > 0 
+        && d->TmpXMax > d->TmpXMin && d->TmpYMax > d->TmpYMin)
+        {
+        int tmpXRange = d->TmpXMax - d->TmpXMin;
+        int tmpYRange = d->TmpYMax - d->TmpYMin;
+        double tmpAspectRatio = static_cast<double>(tmpYRange) / tmpXRange;
+        double screenAspectRatio = static_cast<double>(this->height())
+          / this->width();
+        if( screenAspectRatio > tmpAspectRatio )
+          {
+          int extraTmpYAbove = d->TmpYMin;
+          int extraTmpYBelow = img->height() - d->TmpYMax;
+          int extraTmpYNeeded = tmpXRange * screenAspectRatio 
+            - tmpYRange;
+          int minExtra = extraTmpYAbove;
+          if( extraTmpYBelow < minExtra )
+            {
+            minExtra = extraTmpYBelow;
+            }
+          if(2 * minExtra >= extraTmpYNeeded)
+            {
+            int minNeeded = extraTmpYNeeded / 2.0;
+            int maxNeeded = extraTmpYNeeded - minNeeded;
+            d->TmpYMin -= minNeeded;
+            d->TmpYMax += maxNeeded;
+            }
+          else if(extraTmpYAbove + extraTmpYBelow >= extraTmpYNeeded)
+            {
+            if(extraTmpYAbove < extraTmpYBelow)
+              {
+              d->TmpYMin = 0;
+              d->TmpYMax += extraTmpYNeeded - extraTmpYAbove;
+              }
+            else
+              {
+              d->TmpYMax = img->height();
+              d->TmpYMin -= extraTmpYNeeded - extraTmpYBelow;
+              }
+            }
+          else
+            {
+            d->TmpYMin = 0;
+            d->TmpYMax = img->height();
+            }
+          d->TmpImage = QPixmap( this->width(),
+            static_cast<unsigned int>( 
+              static_cast<double>(d->TmpYMax - d->TmpYMin) 
+              / (d->TmpXMax - d->TmpXMin)
+              * this->width() + 0.5 ) );
+          }
+        else if(screenAspectRatio < tmpAspectRatio)
+          {
+          int extraTmpXLeft = d->TmpXMin;
+          int extraTmpXRight = img->width() - d->TmpXMax;
+          int extraTmpXNeeded = static_cast<double>(tmpYRange) 
+            / screenAspectRatio - tmpXRange;
+          int minExtra = extraTmpXLeft;
+          if( extraTmpXRight < minExtra )
+            {
+            minExtra = extraTmpXRight;
+            }
+          if(2 * minExtra >= extraTmpXNeeded)
+            {
+            int minNeeded = extraTmpXNeeded / 2.0;
+            int maxNeeded = extraTmpXNeeded - minNeeded;
+            d->TmpXMin -= minNeeded;
+            d->TmpXMax += maxNeeded;
+            }
+          else if(extraTmpXLeft + extraTmpXRight >= extraTmpXNeeded)
+            {
+            if(extraTmpXLeft < extraTmpXRight)
+              {
+              d->TmpXMin = 0;
+              d->TmpXMax += extraTmpXNeeded - extraTmpXLeft;
+              }
+            else
+              {
+              d->TmpXMax = img->width();
+              d->TmpXMin -= extraTmpXNeeded - extraTmpXRight;
+              }
+            }
+           else
+            {
+            d->TmpXMin = 0;
+            d->TmpXMax = img->width();
+            }
+          d->TmpImage = QPixmap( static_cast<unsigned int>( this->height()
+            / ( static_cast<double>(d->TmpYMax - d->TmpYMin) 
+            / (d->TmpXMax - d->TmpXMin) ) 
+            + 0.5 ), this->height() );
+          }
+        else
+          {
+          d->TmpImage = QPixmap( this->width(),  this->height() );
+          }
+        }
+      }
+
+    if( d->TmpImage.width() > 0 &&  d->TmpImage.height() > 0)
+      {
+      QPainter painter( &(d->TmpImage) );
+      painter.drawPixmap( 0, 0, d->TmpImage.width(), d->TmpImage.height(),
+        QPixmap::fromImage(*img), d->TmpXMin, d->TmpYMin,
+        d->TmpXMax - d->TmpXMin, d->TmpYMax - d->TmpYMin);
+      }
+
+    d->Window->setPixmap( d->TmpImage );
+    }
+  else
+    {
+    d->Window->setText( "No Image Loaded." );
+    }
+}

+ 147 - 0
Libs/Widgets/ctkQImageViewerWidget.h

@@ -0,0 +1,147 @@
+/*=========================================================================
+
+  Library:   CTK
+
+  Copyright (c) Kitware Inc.
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.commontk.org/LICENSE
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=========================================================================*/
+
+#ifndef __ctkQImageViewerWidget_h
+#define __ctkQImageViewerWidget_h
+
+/// Qt includes
+#include <QWidget>
+#include <QImage>
+
+/// CTK includes
+#include "ctkPimpl.h"
+#include "ctkWidgetsExport.h"
+
+class ctkQImageViewerWidgetPrivate;
+
+///
+/// ctkQImageViewerWidget is the base class of image viewer widgets.
+class CTK_WIDGETS_EXPORT ctkQImageViewerWidget: public QWidget
+{
+
+  Q_OBJECT
+
+public:
+
+  /// Superclass typedef
+  typedef QWidget Superclass;
+
+  /// Constructor
+  ctkQImageViewerWidget( QWidget* parent = 0 );
+  
+  /// Destructor
+  virtual ~ctkQImageViewerWidget( void );
+
+  double xSpacing( void );
+  double ySpacing( void );
+  double sliceThickness( void );
+
+  double xPosition( void );
+  double yPosition( void );
+  double slicePosition( void );
+  double positionValue( void );
+
+  double xCenter( void );
+  double yCenter( void );
+
+  int sliceNumber( void ) const;
+  int numberOfSlices( void ) const;
+
+  double intensityWindowMin( void ) const;
+  double intensityWindowMax( void ) const;
+
+  bool flipXAxis( void ) const;
+  bool flipYAxis( void ) const;
+  bool transposeXY( void ) const;
+
+  double zoom( void );
+
+public slots:
+
+  void addImage( const QImage * image );
+  void clearImages( void );
+
+  void setSliceNumber( int slicenum );
+
+  void setIntensityWindow( double iwMin, double iwMax );
+
+  void setFlipXAxis( bool flip );
+  void setFlipYAxis( bool flip );
+  void setTransposeXY( bool transpose );
+
+  virtual void keyPressEvent( QKeyEvent * event );
+  virtual void mousePressEvent( QMouseEvent * event );
+  virtual void mouseReleaseEvent( QMouseEvent * event );
+  virtual void mouseMoveEvent( QMouseEvent * event );
+
+  void setCenter( double x, double y );
+  void setPosition( double x, double y );
+
+  void setZoom( double factor );
+
+  void reset();
+
+  virtual void update( bool zoomChanged=false, bool sizeChanged=false );
+
+signals:
+
+  void xSpacingChanged( double xSpacing );
+  void ySpacingChanged( double ySpacing );
+  void sliceThicknessChanged( double sliceThickness );
+
+  void xPositionChanged( double xPosition );
+  void yPositionChanged( double yPosition );
+  void slicePositionChanged( double slicePosition );
+  void positionValueChanged( double positionValue );
+
+  void sliceNumberChanged( int sliceNum );
+
+  void zoomChanged( double factor );
+  void xCenterChanged( double x );
+  void yCenterChanged( double y );
+
+  void numberOfSlicesChanged( int numberOfSlices );
+
+  void flipXAxisChanged( bool flipXAxis );
+  void flipYAxisChanged( bool flipYAxis );
+  void transposeXYChanged( bool transposeXY );
+
+  void intensityWindowMinChanged( double intensityWindowMin );
+  void intensityWindowMaxChanged( double intensityWindowMax );
+
+protected:
+
+  virtual void resizeEvent( QResizeEvent* event );
+
+  /// protected constructor to derive private implementations
+  ctkQImageViewerWidget( ctkQImageViewerWidgetPrivate & pvt,
+    QWidget* parent=0 );
+
+private:
+
+  QScopedPointer< ctkQImageViewerWidgetPrivate > d_ptr;
+
+  Q_DECLARE_PRIVATE( ctkQImageViewerWidget );
+
+  Q_DISABLE_COPY( ctkQImageViewerWidget );
+
+};
+
+#endif