Bladeren bron

Merge branch 'ctkdicommodel-custom-behavior'

* ctkdicommodel-custom-behavior:
  Rename ctkDICOMModel::displayLevel
  Add ctkFlatProxyModel to squash tree levels
Julien Finet 13 jaren geleden
bovenliggende
commit
fa454a12ae

+ 6 - 1
Libs/Core/ctkModelTester.cpp

@@ -293,7 +293,10 @@ void ctkModelTester::testParent(const QModelIndex& vparent)const
       {
       this->test(d->Model->hasIndex(i, j, vparent), "hasIndex should return true for int range {0->rowCount(), 0->columnCount()}");
       QModelIndex child = vparent.child(i, j);
-      this->test(child.parent() == vparent, "A child's parent can't be different from its parent");
+      this->test(child.row() == i, "A child's row must be the same as given");
+      this->test(child.column() == j, "A child's column must be the same as given");
+      QModelIndex childParent = child.parent();
+      this->test(childParent == vparent, "A child's parent can't be different from its parent");
       this->testModelIndex(child);
       }
     }
@@ -325,6 +328,8 @@ void ctkModelTester::testModel()const
       {
       this->test(d->Model->hasIndex(i, j), "hasIndex should return true for int range {0->rowCount(), 0->columnCount()}");
       QModelIndex child = d->Model->index(i, j);
+      this->test(child.row() == i, "Row should be consistent");
+      this->test(child.column() == j, "Column should be consistent");
       this->test(!child.parent().isValid(), "A child's parent can't be different from its parent");
       this->testModelIndex(child);
       }

+ 17 - 9
Libs/DICOM/Core/ctkDICOMModel.cpp

@@ -51,8 +51,6 @@ public:
   virtual ~ctkDICOMModelPrivate();
   void init();
 
-
- 
   void fetch(const QModelIndex& indexValue, int limit);
   Node* createNode(int row, const QModelIndex& parentValue)const;
   Node* nodeFromIndex(const QModelIndex& indexValue)const;
@@ -71,7 +69,8 @@ public:
   QString      Sort;
   QMap<QString, QVariant> SearchParameters;
 
-  ctkDICOMModel::IndexType displayLevel;
+  ctkDICOMModel::IndexType StartLevel;
+  ctkDICOMModel::IndexType EndLevel;
 };
 
 //------------------------------------------------------------------------------
@@ -103,7 +102,8 @@ struct Node
 ctkDICOMModelPrivate::ctkDICOMModelPrivate(ctkDICOMModel& o):q_ptr(&o)
 {
   this->RootNode     = 0;
-  this->displayLevel = ctkDICOMModel::ImageType;
+  this->StartLevel = ctkDICOMModel::RootType;
+  this->EndLevel = ctkDICOMModel::ImageType;
 }
 
 //------------------------------------------------------------------------------
@@ -544,8 +544,8 @@ bool ctkDICOMModel::hasChildren ( const QModelIndex & parentIndex ) const
     return false;
     }
 
-  // We want to show only until displayLevel
-  if(node->Type >= d->displayLevel)return false;
+  // We want to show only until EndLevel
+  if(node->Type >= d->EndLevel)return false;
 
   // It's not because we don't have row that we don't have children, maybe it
   // just means that the children haven't been fetched yet
@@ -853,10 +853,18 @@ void ctkDICOMModel::setDatabase(const QSqlDatabase &db,const QMap<QString, QVari
   d->fetch(QModelIndex(), 256);
 }
 
-void ctkDICOMModel::setDisplayLevel(ctkDICOMModel::IndexType level){
-    Q_D(ctkDICOMModel);
+//------------------------------------------------------------------------------
+ctkDICOMModel::IndexType  ctkDICOMModel::endLevel()const
+{
+  Q_D(const ctkDICOMModel);
+  return d->EndLevel;
+}
 
-    d->displayLevel = level;
+//------------------------------------------------------------------------------
+void ctkDICOMModel::setEndLevel(ctkDICOMModel::IndexType level)
+{
+  Q_D(ctkDICOMModel);
+  d->EndLevel = level;
 }
 
 //------------------------------------------------------------------------------

+ 10 - 1
Libs/DICOM/Core/ctkDICOMModel.h

@@ -23,6 +23,7 @@
 
 // Qt includes 
 #include <QAbstractItemModel>
+#include <QMetaType>
 #include <QSqlDatabase>
 #include <QStringList>
 
@@ -35,6 +36,9 @@ class CTK_DICOM_CORE_EXPORT ctkDICOMModel
 {
   Q_OBJECT
   typedef QAbstractItemModel Superclass;
+  Q_ENUMS(IndexType)
+  /// startLevel contains the hierarchy depth the model contains
+  Q_PROPERTY(IndexType endLevel READ endLevel WRITE setEndLevel);
 public:
 
   enum {
@@ -55,7 +59,10 @@ public:
 
   void setDatabase(const QSqlDatabase& dataBase);
   void setDatabase(const QSqlDatabase& dataBase, const QMap<QString,QVariant>& parameters);
-  void setDisplayLevel(ctkDICOMModel::IndexType level);
+
+  /// Set it before populating the model
+  ctkDICOMModel::IndexType endLevel()const;
+  void setEndLevel(ctkDICOMModel::IndexType level);
 
   virtual bool canFetchMore ( const QModelIndex & parent ) const;
   virtual int columnCount ( const QModelIndex & parent = QModelIndex() ) const;
@@ -86,4 +93,6 @@ private:
   Q_DISABLE_COPY(ctkDICOMModel);
 };
 
+Q_DECLARE_METATYPE(ctkDICOMModel::IndexType)
+
 #endif

+ 1 - 1
Libs/DICOM/Widgets/ctkDICOMAppWidget.cpp

@@ -217,7 +217,7 @@ void ctkDICOMAppWidget::setDatabaseDirectory(const QString& directory)
     }
   
   d->DICOMModel.setDatabase(d->DICOMDatabase->database());
-  d->DICOMModel.setDisplayLevel(ctkDICOMModel::SeriesType);
+  d->DICOMModel.setEndLevel(ctkDICOMModel::SeriesType);
   d->TreeView->resizeColumnToContents(0);
 
   //pass DICOM database instance to Import widget

+ 3 - 0
Libs/Widgets/CMakeLists.txt

@@ -71,6 +71,8 @@ set(KIT_SRCS
   ctkFileDialog.h
   ctkFittedTextBrowser.cpp
   ctkFittedTextBrowser.h
+  ctkFlatProxyModel.cpp
+  ctkFlatProxyModel.h
   ctkFlowLayout.cpp
   ctkFlowLayout.h
   ctkFontButton.cpp
@@ -198,6 +200,7 @@ set(KIT_MOC_SRCS
   ctkExpandButton.h
   ctkFileDialog.h
   ctkFittedTextBrowser.h
+  ctkFlatProxyModel.h
   ctkFlowLayout.h
   ctkFontButton.h
   ctkIconEnginePlugin.h

+ 6 - 1
Libs/Widgets/Testing/Cpp/CMakeLists.txt

@@ -34,6 +34,7 @@ set(TEST_SOURCES
   ctkErrorLogWidgetTest1.cpp
   ctkExpandButtonTest1.cpp
   ctkFileDialogTest1.cpp
+  ctkFlatProxyModelTest.cpp
   ctkFittedTextBrowserTest1.cpp
   ctkFlowLayoutTest1.cpp
   ctkFontButtonTest1.cpp
@@ -116,7 +117,10 @@ set(Tests_MOC_SRCS
 
 set(Tests_MOC_CPP)
 QT4_WRAP_CPP(Tests_MOC_CPP ${Tests_MOC_SRCS})
-QT4_GENERATE_MOCS(ctkRangeSliderTest.cpp)
+QT4_GENERATE_MOCS(
+  ctkRangeSliderTest.cpp
+  ctkFlatProxyModelTest.cpp
+  )
 
 add_executable(${KIT}CppTests ${Tests} ${Tests_SRCS} ${Tests_MOC_CPP})
 target_link_libraries(${KIT}CppTests ${LIBRARY_NAME})
@@ -160,6 +164,7 @@ SIMPLE_TEST( ctkErrorLogWidgetTest1 )
 SIMPLE_TEST( ctkExampleUseOfWorkflowWidgetUsingDerivedSteps )
 SIMPLE_TEST( ctkExampleUseOfWorkflowWidgetUsingSignalsAndSlots )
 SIMPLE_TEST( ctkExpandButtonTest1 )
+SIMPLE_TEST( ctkFlatProxyModelTest )
 SIMPLE_TEST( ctkFileDialogTest1 )
 SIMPLE_TEST( ctkFittedTextBrowserTest1 )
 SIMPLE_TEST( ctkFlowLayoutTest1 )

+ 271 - 0
Libs/Widgets/Testing/Cpp/ctkFlatProxyModelTest.cpp

@@ -0,0 +1,271 @@
+/*=========================================================================
+
+  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.apache.org/licenses/LICENSE-2.0.txt
+
+  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.
+
+=========================================================================*/
+
+// Qt includes
+#include <QDebug>
+#include <QApplication>
+#include <QEventLoop>
+#include <QTreeView>
+#include <QStandardItem>
+#include <QStandardItemModel>
+#include <QTimer>
+
+// CTK includes
+#include "ctkFlatProxyModel.h"
+#include "ctkModelTester.h"
+#include "ctkTest.h"
+
+// ----------------------------------------------------------------------------
+class ctkFlatProxyModelTester: public QObject
+{
+  Q_OBJECT
+private slots:
+  void testModel();
+  void testModel_data();
+private:
+  QStandardItem* createItem(const QString& name, QVariant& children)const;
+};
+
+// ----------------------------------------------------------------------------
+void ctkFlatProxyModelTester::testModel_data()
+{
+  QTest::addColumn<QVariant >("hashModel");
+  QTest::addColumn<int>("startHideLevel");
+  QTest::addColumn<int>("endFlattenLevel");
+  QTest::addColumn<int>("level0ExpectedRowCount");
+  QTest::addColumn<int>("level1ExpectedRowCount");
+
+  // -\ top
+  //  +--\ item1
+  //  |  +--\ subItem1
+  //  |  |  +--\ subSubItem1
+  //  |  |  |  +-- leaf1
+  //  |  |  +--\ subSubItem2
+  //  |  |  |  +-- leaf2
+  //  |  |  +--\ subSubItem3
+  //  |  |     +-- leaf3
+  //  |  +--\ subItem2
+  //  |  |  +--\ subSubItem1
+  //  |  |  |  +-- leaf1
+  //  |  |  +--\ subSubItem2
+  //  |  |  |  +-- leaf2
+  //  |  |  +--\ subSubItem3
+  //  |  |     +-- leaf3
+  //  |  +--\ subItem3
+  //  |     +--\ subSubItem1
+  //  |     |  +-- leaf1
+  //  |     +--\ subSubItem2
+  //  |     |  +-- leaf2
+  //  |     +--\ subSubItem3
+  //  |        +-- leaf3
+  //  +--\ item2
+  //  |  +--\ subItem1
+  //  |  |  +--\ subSubItem1
+  //  |  |  |  +-- leaf1
+  //  |  |  +--\ subSubItem2
+  //  |  |  |  +-- leaf2
+  //  |  |  +--\ subSubItem3
+  //  |  |     +-- leaf3
+  //  |  +--\ subItem2
+  //  |  |  +--\ subSubItem1
+  //  |  |  |  +-- leaf1
+  //  |  |  +--\ subSubItem2
+  //  |  |  |  +-- leaf2
+  //  |  |  +--\ subSubItem3
+  //  |  |     +-- leaf3
+  //  |  +--\ subItem3
+  //  |     +--\ subSubItem1
+  //  |     |  +-- leaf1
+  //  |     +--\ subSubItem2
+  //  |     |  +-- leaf2
+  //  |     +--\ subSubItem3
+  //  |        +-- leaf3
+  //  +--\ item3
+  //     +--\ subItem1
+  //     |  +--\ subSubItem1
+  //     |  |  +-- leaf1
+  //     |  +--\ subSubItem2
+  //     |  |  +-- leaf2
+  //     |  +--\ subSubItem3
+  //     |     +-- leaf3
+  //     +--\ subItem2
+  //     |  +--\ subSubItem1
+  //     |  |  +-- leaf1
+  //     |  +--\ subSubItem2
+  //     |  |  +-- leaf2
+  //     |  +--\ subSubItem3
+  //     |     +-- leaf3
+  //     +--\ subItem3
+  //        +--\ subSubItem1
+  //        |  +-- leaf1
+  //        +--\ subSubItem2
+  //        |  +-- leaf2
+  //        +--\ subSubItem3
+  //           +-- leaf3
+  QMap<QString, QVariant> subSubModel;
+  subSubModel["subSubItem1"] = QString("leaf1");
+  subSubModel["subSubItem2"] = QString("leaf2");
+  QMap<QString, QVariant> subModel;
+  subModel["subItem1"] = subSubModel;
+  subModel["subItem2"] = subSubModel;
+  subModel["subItem3"] = subSubModel;
+  QMap<QString, QVariant> model;
+  model["item1"] = subModel;
+  model["item2"] = subModel;
+  model["item3"] = subModel;
+  model["item4"] = subModel;
+
+  // -\ top
+  //  +--\ subItem1 (item1)
+  //  |  +--\ subSubItem1
+  //  |  |  +-- leaf1
+  //  |  +--\ subSubItem2
+  //  |  |  +-- leaf2
+  //  |  +--\ subSubItem3
+  //  |     +-- leaf3
+  //  +--\ subItem2 (item1)
+  //  |  +--\ subSubItem1
+  //  |  |  +-- leaf1
+  //  |  +--\ subSubItem2
+  //  |  |  +-- leaf2
+  //  |  +--\ subSubItem3
+  //  |     +-- leaf3
+  //  +--\ subItem3 (item1)
+  //  |  +--\ subSubItem1
+  //  |  |  +-- leaf1
+  //  |  +--\ subSubItem2
+  //  |  |  +-- leaf2
+  //  |  +--\ subSubItem3
+  //  |     +-- leaf3
+  //  +--\ subItem1 (item2)
+  //  |  +--\ subSubItem1
+  //  |  |  +-- leaf1
+  //  |  +--\ subSubItem2
+  //  |  |  +-- leaf2
+  //  |  +--\ subSubItem3
+  //  |     +-- leaf3
+  //  +--\ subItem2 (item2)
+  //  |  +--\ subSubItem1
+  //  |  |  +-- leaf1
+  //  |  +--\ subSubItem2
+  //  |  |  +-- leaf2
+  //  |  +--\ subSubItem3
+  //  |     +-- leaf3
+  //  +--\ subItem3 (item2)
+  //  |  +--\ subSubItem1
+  //  |  |  +-- leaf1
+  //  |  +--\ subSubItem2
+  //  |  |  +-- leaf2
+  //  |  +--\ subSubItem3
+  //  |     +-- leaf3
+  //  +--\ subItem1 (item3)
+  //  |  +--\ subSubItem1
+  //  |  |  +-- leaf1
+  //  |  +--\ subSubItem2
+  //  |  |  +-- leaf2
+  //  |  +--\ subSubItem3
+  //  |     +-- leaf3
+  //  +--\ subItem2 (item3)
+  //  |  +--\ subSubItem1
+  //  |  |  +-- leaf1
+  //  |  +--\ subSubItem2
+  //  |  |  +-- leaf2
+  //  |  +--\ subSubItem3
+  //  |     +-- leaf3
+  //  +--\ subItem3 (item3)
+  //     +--\ subSubItem1
+  //     |  +-- leaf1
+  //     +--\ subSubItem2
+  //     |  +-- leaf2
+  //     +--\ subSubItem3
+  //        +-- leaf3
+  QTest::newRow("flatten level 0") << QVariant(model) << -1 << 0 << 12 << 2;
+  // Don't work yet.
+  //QTest::newRow("flatten level 1") << QVariant(model) << -1<< 1 <<  24 << 1;
+  //QTest::newRow("flatten level 0, hide 2") << QVariant(model) << 0 << 0 << 12 << 2;
+  //QTest::newRow("flatten level 1, hide 2") << QVariant(model) << 0 << 1 << 12 << 2;
+}
+
+// ----------------------------------------------------------------------------
+QStandardItem* ctkFlatProxyModelTester
+::createItem(const QString& name, QVariant& children)const
+{
+  QStandardItem* item = new QStandardItem(name);
+  if (children.canConvert<QString>())
+    {
+    QStandardItem* leaf = new QStandardItem(children.toString());
+    item->appendRow(leaf);
+    return item;
+    }
+  QMap<QString, QVariant> hash = children.toMap();
+  QMap<QString, QVariant>::iterator i = hash.begin();
+  for ( ; i != hash.end(); ++i)
+    {
+    QStandardItem* childItem = this->createItem(i.key(), i.value());
+    item->appendRow(childItem);
+    }
+  return item;
+}
+
+// ----------------------------------------------------------------------------
+void ctkFlatProxyModelTester::testModel()
+{
+  QStandardItemModel model;
+
+  QFETCH(QVariant, hashModel);
+  QFETCH(int, endFlattenLevel);
+  QFETCH(int, startHideLevel);
+  QFETCH(int, level0ExpectedRowCount);
+  QFETCH(int, level1ExpectedRowCount);
+
+  QMap<QString, QVariant> hash = hashModel.toMap();
+  QMap<QString, QVariant>::iterator i = hash.begin();
+  for ( ; i != hash.end(); ++i)
+    {
+    QStandardItem* childItem = this->createItem(i.key(), i.value());
+    model.appendRow(childItem);
+    }
+
+  ctkFlatProxyModel flattenModel;
+  flattenModel.setEndFlattenLevel(endFlattenLevel);
+  flattenModel.setHideLevel(startHideLevel);
+  flattenModel.setSourceModel(&model);
+
+  QCOMPARE( flattenModel.rowCount(QModelIndex()), level0ExpectedRowCount);
+  QModelIndex subIndex1 = flattenModel.index(0,0, QModelIndex());
+  QCOMPARE( flattenModel.rowCount(subIndex1), level1ExpectedRowCount);
+
+  ctkModelTester tester;
+  tester.setTestDataEnabled(false);
+  tester.setModel(&flattenModel);
+
+  /*
+  QTreeView view(0);
+  view.setModel(&flattenModel);
+  view.show();
+
+  QEventLoop eventLoop;
+  eventLoop.exec();
+  */
+}
+
+// ----------------------------------------------------------------------------
+CTK_TEST_MAIN(ctkFlatProxyModelTest)
+#include "moc_ctkFlatProxyModelTest.cpp"

+ 370 - 0
Libs/Widgets/ctkFlatProxyModel.cpp

@@ -0,0 +1,370 @@
+/*=========================================================================
+
+  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.apache.org/licenses/LICENSE-2.0.txt
+
+  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.
+
+=========================================================================*/
+// QT includes
+#include <QDebug>
+
+// CTK includes
+#include "ctkFlatProxyModel.h"
+
+// ----------------------------------------------------------------------------
+class ctkFlatProxyModelPrivate
+{
+  Q_DECLARE_PUBLIC(ctkFlatProxyModel);
+protected:
+  ctkFlatProxyModel* const q_ptr;
+public:
+  ctkFlatProxyModelPrivate(ctkFlatProxyModel& object);
+  void init();
+  int indexLevel(const QModelIndex& index)const;
+  int levelRowCount(const QModelIndex& index)const;
+  int nextLevelRowCount(const QModelIndex& index)const;
+  int rowCount(const QModelIndex& sourceIndex, int depth = 0)const;
+  QModelIndex sourceParent(const QModelIndex& index)const;
+  QModelIndex grandChild(const QModelIndex& parent, int& row, int depth)const;
+
+  int StartFlattenLevel;
+  int EndFlattenLevel;
+  int HideLevel;
+};
+
+// ----------------------------------------------------------------------------
+//  Methods ctkFlatProxyModelPrivate
+
+// ----------------------------------------------------------------------------
+ctkFlatProxyModelPrivate::ctkFlatProxyModelPrivate(ctkFlatProxyModel &object)
+  : q_ptr(&object)
+{
+  this->StartFlattenLevel = -1;
+  this->EndFlattenLevel = -1;
+  this->HideLevel = -1;
+}
+
+// ----------------------------------------------------------------------------
+void ctkFlatProxyModelPrivate::init()
+{
+  Q_Q(ctkFlatProxyModel);
+}
+
+// ----------------------------------------------------------------------------
+int ctkFlatProxyModelPrivate::indexLevel(const QModelIndex& index)const
+{
+  int level = -1;
+  QModelIndex parent = index;
+  while (parent.isValid())
+    {
+    parent = parent.parent();
+    ++level;
+    }
+  return level;
+}
+
+// ----------------------------------------------------------------------------
+int ctkFlatProxyModelPrivate::levelRowCount(const QModelIndex& sourceIndex)const
+{
+  Q_Q(const ctkFlatProxyModel);
+  if (!sourceIndex.isValid()
+      || this->StartFlattenLevel > this->indexLevel(sourceIndex)
+      || this->EndFlattenLevel < this->indexLevel(sourceIndex))
+    {
+    return 0;
+    }
+  int previousRows = 0;
+  for (int row = 0; row != sourceIndex.row() ; ++row)
+    {
+    previousRows += q->sourceModel()->rowCount(sourceIndex.sibling(row, sourceIndex.column()));
+    }
+  return previousRows + this->levelRowCount(sourceIndex.parent());
+}
+
+// ----------------------------------------------------------------------------
+int ctkFlatProxyModelPrivate::nextLevelRowCount(const QModelIndex& sourceIndex)const
+{
+  Q_Q(const ctkFlatProxyModel);
+  if (!sourceIndex.isValid()
+      || this->StartFlattenLevel > this->indexLevel(sourceIndex)
+      || this->EndFlattenLevel < this->indexLevel(sourceIndex))
+    {
+    return q->sourceModel()->rowCount(sourceIndex);
+    }
+  int rowCount = 0;
+  QModelIndex sibling = sourceIndex.sibling(0, sourceIndex.column());
+  for (int row = 0; sibling.isValid() ; ++row)
+    {
+    sibling = sourceIndex.sibling(row, sourceIndex.column());
+    rowCount += q->sourceModel()->rowCount(sibling);
+    }
+  return rowCount;
+}
+
+// ----------------------------------------------------------------------------
+int ctkFlatProxyModelPrivate::rowCount(const QModelIndex& sourceIndex, int depth)const
+{
+  Q_Q(const ctkFlatProxyModel);
+  int rows = 0;
+  if (depth < 0)
+    {
+    rows = 1;
+    }
+  else
+    {
+    --depth;
+    for (int row = 0; row < q->sourceModel()->rowCount(sourceIndex); ++row)
+      {
+      QModelIndex child = q->sourceModel()->index(row, 0, sourceIndex);
+      rows += this->rowCount(child, depth);
+      }
+    }
+  return rows;
+}
+
+// ----------------------------------------------------------------------------
+QModelIndex ctkFlatProxyModelPrivate
+::sourceParent(const QModelIndex& index)const
+{
+  Q_Q(const ctkFlatProxyModel);
+  QModelIndexList sourceIndexes;
+  sourceIndexes << QModelIndex();
+  QMap<int, int> rowCountsPerLevel;
+  while (!sourceIndexes.isEmpty())
+    {
+    QModelIndex sourceIndex = sourceIndexes.takeFirst();
+    const int rowCount = q->sourceModel()->rowCount(sourceIndex);
+    for (int row = 0; row < rowCount; ++row)
+      {
+      QModelIndex child = q->sourceModel()->index(row, 0, sourceIndex);
+      if (child.internalPointer() == index.internalPointer())
+        {
+        return sourceIndex;
+        }
+      else
+        {
+        sourceIndexes << child;
+        }
+      }
+    }
+  Q_ASSERT(false);
+  return QModelIndex();
+}
+
+// ----------------------------------------------------------------------------
+QModelIndex ctkFlatProxyModelPrivate
+::grandChild(const QModelIndex& parent, int& row, int depth)const
+{
+  Q_Q(const ctkFlatProxyModel);
+  const int rowCount = q->sourceModel()->rowCount(parent);
+
+  if (depth > 0)
+    {
+    for (int i = 0; i < rowCount; ++i)
+      {
+      QModelIndex child = q->sourceModel()->index(i, 0, parent);
+      QModelIndex found = this->grandChild(child, row, depth - 1);
+      if (found.isValid())
+        {
+        return found;
+        }
+      }
+    }
+  else
+    {
+    if (row < rowCount)
+      {
+      QModelIndex sourceIndex = q->sourceModel()->index(
+        row, 0, parent);
+      return sourceIndex;
+      }
+    else
+      {
+      row -= rowCount;
+      }
+    }
+  return QModelIndex();
+}
+
+// ----------------------------------------------------------------------------
+//  Methods ctkFlatProxyModel
+
+// ----------------------------------------------------------------------------
+ctkFlatProxyModel::ctkFlatProxyModel(QObject *parentObject)
+  : Superclass(parentObject)
+  , d_ptr(new ctkFlatProxyModelPrivate(*this))
+{
+  Q_D(ctkFlatProxyModel);
+  d->init();
+}
+
+// ----------------------------------------------------------------------------
+ctkFlatProxyModel::~ctkFlatProxyModel()
+{
+}
+
+// ----------------------------------------------------------------------------
+int ctkFlatProxyModel::startFlattenLevel() const
+{
+  Q_D(const ctkFlatProxyModel);
+  return d->StartFlattenLevel;
+}
+
+// ----------------------------------------------------------------------------
+void ctkFlatProxyModel::setStartFlattenLevel(int level)
+{
+  Q_D(ctkFlatProxyModel);
+  d->StartFlattenLevel = level;
+  Q_ASSERT( d->StartFlattenLevel <= d->EndFlattenLevel);
+}
+
+// ----------------------------------------------------------------------------
+int ctkFlatProxyModel::endFlattenLevel() const
+{
+  Q_D(const ctkFlatProxyModel);
+  return d->EndFlattenLevel;
+}
+
+// ----------------------------------------------------------------------------
+void ctkFlatProxyModel::setEndFlattenLevel(int level)
+{
+  Q_D(ctkFlatProxyModel);
+  d->EndFlattenLevel = level;
+  Q_ASSERT( d->EndFlattenLevel >= d->EndFlattenLevel);
+}
+
+// ----------------------------------------------------------------------------
+int ctkFlatProxyModel::hideLevel()const
+{
+  Q_D(const ctkFlatProxyModel);
+  return d->HideLevel;
+}
+
+// ----------------------------------------------------------------------------
+void ctkFlatProxyModel::setHideLevel(int level)
+{
+  Q_D(ctkFlatProxyModel);
+  d->HideLevel = level;
+}
+
+// ----------------------------------------------------------------------------
+QModelIndex ctkFlatProxyModel::mapFromSource( const QModelIndex& sourceIndex ) const
+{
+  Q_D(const ctkFlatProxyModel);
+  if (!sourceIndex.isValid())
+    {
+    return QModelIndex();
+    }
+  int level = d->indexLevel(sourceIndex);
+  if (d->HideLevel != -1
+      && level >= d->HideLevel)
+    {
+    return QModelIndex();
+    }
+  if (d->EndFlattenLevel != -1
+      && level <= d->EndFlattenLevel)
+    {
+    return QModelIndex();
+    }
+  int row = sourceIndex.row();
+  if (d->EndFlattenLevel != -1)
+    {
+    row += d->levelRowCount(sourceIndex.parent());
+    }
+  return this->createIndex(row, sourceIndex.column(),
+                           sourceIndex.internalPointer());
+}
+
+// ----------------------------------------------------------------------------
+QModelIndex ctkFlatProxyModel::mapToSource( const QModelIndex& proxyIndex ) const
+{
+  Q_D(const ctkFlatProxyModel);
+  if (!proxyIndex.isValid())
+    {
+    return QModelIndex();
+    }
+  QModelIndex sourceParent = d->sourceParent(proxyIndex);
+  int level = d->indexLevel(sourceParent);
+  int levelRowCount = 0;
+  if ((d->StartFlattenLevel != -1 || d->EndFlattenLevel != -1) &&
+      (d->StartFlattenLevel != -1 || level >= d->StartFlattenLevel) &&
+      (d->EndFlattenLevel != -1 || level <= d->EndFlattenLevel))
+    {
+    levelRowCount = d->levelRowCount(sourceParent);
+    }
+  QModelIndex sourceIndex = this->sourceModel()->index(
+    proxyIndex.row() - levelRowCount, proxyIndex.column(), sourceParent);
+  Q_ASSERT(sourceIndex.isValid());
+  return sourceIndex;
+}
+
+// ----------------------------------------------------------------------------
+QModelIndex ctkFlatProxyModel::index(int row, int column, const QModelIndex &parent) const
+{
+  Q_D(const ctkFlatProxyModel);
+  if (row < 0 || column < 0)
+    {
+    return QModelIndex();
+    }
+
+  QModelIndex sourceParent = this->mapToSource(parent); // parent is already mapped at this point
+  int sourceRow = row;
+  QModelIndex sourceGrandChild = d->grandChild(
+    sourceParent, sourceRow, qMax(0, d->EndFlattenLevel - d->indexLevel(sourceParent)));
+  return this->createIndex(row, column, sourceGrandChild.internalPointer());
+}
+
+// ----------------------------------------------------------------------------
+QModelIndex ctkFlatProxyModel::parent(const QModelIndex &child) const
+{
+  Q_D(const ctkFlatProxyModel);
+  if (!child.isValid())
+    {
+    return QModelIndex();
+    }
+  QModelIndex sourceChild = this->mapToSource(child);
+  QModelIndex sourceParent = sourceChild.parent();
+  QModelIndex proxyParent = this->mapFromSource(sourceParent);
+  return proxyParent;
+}
+
+// ----------------------------------------------------------------------------
+int ctkFlatProxyModel::rowCount(const QModelIndex &parent) const
+{
+  Q_D(const ctkFlatProxyModel);
+  QModelIndex sourceParent = this->mapToSource(parent);
+  int sourceParentLevel = d->indexLevel(sourceParent);
+  int depth = 0;
+  if (sourceParentLevel >= d->StartFlattenLevel &&
+      sourceParentLevel <= d->EndFlattenLevel)
+    {
+    depth = d->EndFlattenLevel - d->StartFlattenLevel;
+    }
+  return d->rowCount(sourceParent, depth);
+}
+
+// ----------------------------------------------------------------------------
+int ctkFlatProxyModel::columnCount(const QModelIndex &parent) const
+{
+  Q_D(const ctkFlatProxyModel);
+  QModelIndex proxyChild = this->index(0, 0, parent);
+  if (parent.isValid() && !proxyChild.internalPointer())
+    {
+    Q_ASSERT(!parent.isValid() || proxyChild.internalPointer());
+    }
+  QModelIndex sourceChild = this->mapToSource(proxyChild);
+  QModelIndex sourceParent = sourceChild.parent();
+  return this->sourceModel()->columnCount(sourceParent);
+}

+ 81 - 0
Libs/Widgets/ctkFlatProxyModel.h

@@ -0,0 +1,81 @@
+/*=========================================================================
+
+  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.apache.org/licenses/LICENSE-2.0.txt
+
+  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 __ctkFlatProxyModel_h
+#define __ctkFlatProxyModel_h
+
+// QT includes
+#include <QAbstractProxyModel>
+
+// CTK includes
+#include "ctkWidgetsExport.h"
+
+class ctkFlatProxyModelPrivate;
+
+///
+/// ctkFlatProxyModel intends to flatten contiguous hierarchies within a model.
+/// For now, it only supports the toplevel hierarchy flatten with the 1 degree
+/// children.
+/// The items in the levels being flatten don't appear in the model anymore, 
+/// however their children will be visible.
+class CTK_WIDGETS_EXPORT ctkFlatProxyModel : public QAbstractProxyModel
+{
+  Q_OBJECT
+  /// level for which to start flattening the rows. -1 by default
+  /// Not supported yet.
+  Q_PROPERTY(int startFlattenLevel READ startFlattenLevel WRITE setStartFlattenLevel)
+  /// level for which to stop flattening the rows. -1 by default
+  Q_PROPERTY(int endFlattenLevel READ endFlattenLevel WRITE setEndFlattenLevel)
+  /// level for which to stop flattening the rows. -1 by default
+  /// Not supported yet.
+  Q_PROPERTY(int hideLevel READ hideLevel WRITE setHideLevel)
+
+public:
+  typedef QAbstractProxyModel Superclass;
+
+  ctkFlatProxyModel(QObject *parentObject = 0);
+  virtual ~ctkFlatProxyModel();
+
+  void setStartFlattenLevel(int level);
+  int startFlattenLevel() const;
+
+  void setEndFlattenLevel(int level);
+  int endFlattenLevel() const;
+
+  void setHideLevel(int level);
+  int hideLevel() const;
+
+  virtual QModelIndex mapFromSource( const QModelIndex& sourceIndex ) const;
+  virtual QModelIndex mapToSource( const QModelIndex& sourceIndex ) const;
+
+  virtual QModelIndex index(int row, int column, const QModelIndex &parent) const;
+  virtual QModelIndex parent(const QModelIndex &child) const;
+  virtual int rowCount(const QModelIndex &parent) const;
+  virtual int columnCount(const QModelIndex &parent) const;
+
+protected:
+  QScopedPointer<ctkFlatProxyModelPrivate> d_ptr;
+
+private:
+  Q_DECLARE_PRIVATE(ctkFlatProxyModel);
+  Q_DISABLE_COPY(ctkFlatProxyModel);
+};
+
+#endif