diff --git a/Modules/ImageStatisticsUI/CMakeLists.txt b/Modules/ImageStatisticsUI/CMakeLists.txt index dae8b9505b..8c9069b05f 100644 --- a/Modules/ImageStatisticsUI/CMakeLists.txt +++ b/Modules/ImageStatisticsUI/CMakeLists.txt @@ -1,5 +1,5 @@ MITK_CREATE_MODULE( INCLUDE_DIRS Qmitk - DEPENDS MitkCore MitkChart MitkImageStatistics + DEPENDS MitkCore MitkChart MitkImageStatistics MitkQtWidgets PACKAGE_DEPENDS PRIVATE Qt5|WebEngineWidgets ) \ No newline at end of file diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.cpp index 3317e639fb..a801455f01 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.cpp +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.cpp @@ -1,306 +1,302 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkImageStatisticsTableModel.h" #include "mitkProportionalTimeGeometry.h" #include "mitkStatisticsToImageRelationRule.h" #include "mitkImageStatisticsContainerManager.h" +#include "itkMutexLockHolder.h" +#include "itkSimpleFastMutexLock.h" QmitkImageStatisticsTableModel::QmitkImageStatisticsTableModel(QObject *parent) : - QAbstractTableModel(parent) + QmitkAbstractDataStorageModel(parent) {} -void QmitkImageStatisticsTableModel::SetDataStorage(mitk::DataStorage *newDataStorage) +QmitkImageStatisticsTableModel +::~QmitkImageStatisticsTableModel() { - if (m_DataStorage == newDataStorage) - { - return; - } - - if (!m_DataStorage.IsExpired()) - { - auto dataStorage = m_DataStorage.Lock(); - - // remove Listener for the data storage itself - dataStorage->RemoveObserver(m_DataStorageDeletedTag); - - // remove listener from old data storage - dataStorage->AddNodeEvent.RemoveListener( - mitk::MessageDelegate1(this, &QmitkImageStatisticsTableModel::NodeAdded)); - dataStorage->RemoveNodeEvent.RemoveListener( - mitk::MessageDelegate1(this, &QmitkImageStatisticsTableModel::NodeRemoved)); - dataStorage->ChangedNodeEvent.RemoveListener( - mitk::MessageDelegate1(this, &QmitkImageStatisticsTableModel::NodeChanged)); - } + // set data storage to nullptr so that the event listener gets removed + this->SetDataStorage(nullptr); +}; - m_DataStorage = newDataStorage; - - if (!m_DataStorage.IsExpired()) - { - auto dataStorage = m_DataStorage.Lock(); - - // add Listener for the data storage itself - auto command = itk::SimpleMemberCommand::New(); - command->SetCallbackFunction(this, &QmitkImageStatisticsTableModel::SetDataStorageDeleted); - m_DataStorageDeletedTag = dataStorage->AddObserver(itk::DeleteEvent(), command); - - // add listener for new data storage - dataStorage->AddNodeEvent.AddListener( - mitk::MessageDelegate1(this, &QmitkImageStatisticsTableModel::NodeAdded)); - dataStorage->RemoveNodeEvent.AddListener( - mitk::MessageDelegate1(this, &QmitkImageStatisticsTableModel::NodeRemoved)); - dataStorage->ChangedNodeEvent.AddListener( - mitk::MessageDelegate1(this, &QmitkImageStatisticsTableModel::NodeChanged)); - } +void QmitkImageStatisticsTableModel::DataStorageChanged() +{ + emit beginResetModel(); + UpdateByDataStorage(); + emit endResetModel(); +} +void QmitkImageStatisticsTableModel::NodePredicateChanged() +{ emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); } + int QmitkImageStatisticsTableModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } return m_StatisticNames.size(); } int QmitkImageStatisticsTableModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; if (m_ViewMode == ViewMode::imageXStatistic) { return static_cast(m_TimeStepResolvedImageNodes.size()); } else { mitkThrow() << "View mode ImageXMask is not implemented yet"; } } std::pair QmitkImageStatisticsTableModel::GetRelevantStatsticsByIndex(const QModelIndex &index) const { auto result = std::make_pair(nullptr, 0); if (m_ViewMode == ViewMode::imageXStatistic) { if (index.column() < static_cast(m_TimeStepResolvedImageNodes.size())) { auto selectedImage = m_TimeStepResolvedImageNodes.at(static_cast(index.column())); //get the right statistic auto rule = mitk::StatisticsToImageRelationRule::New(); for (mitk::ImageStatisticsContainer::ConstPointer container : m_Statistics) { if (rule->HasRelation(container, selectedImage.first->GetData()) >= mitk::PropertyRelationRuleBase::RelationType::Connected_Data) { return std::make_pair(container, selectedImage.second); } } } } else { mitkThrow() << "View mode ImageXMask is not implemented yet"; } return result; } QVariant QmitkImageStatisticsTableModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); QVariant result; if (m_ViewMode == ViewMode::imageXStatistic) { auto containerInfo = GetRelevantStatsticsByIndex(index); if (containerInfo.first.IsNotNull() && index.row() < rowCount(QModelIndex())) { if (Qt::DisplayRole == role) { auto statsObj = containerInfo.first->GetStatisticsForTimeStep(containerInfo.second); auto statisticKey = m_StatisticNames.at(index.row()); std::stringstream ss; if (statsObj.HasStatistic(statisticKey)) { ss << statsObj.GetValueNonConverted(statisticKey); } else { ss << "N/A"; } result = QVariant(QString::fromStdString(ss.str())); } else if (Qt::UserRole == role) { result = QVariant(index.row()); } } } return result; } +QModelIndex QmitkImageStatisticsTableModel::index(int row, int column, const QModelIndex &parent) const +{ + bool hasIndex = this->hasIndex(row, column, parent); + if (hasIndex) + { + return this->createIndex(row, column); + } + + return QModelIndex(); +} + +QModelIndex QmitkImageStatisticsTableModel::parent(const QModelIndex &/*child*/) const +{ + return QModelIndex(); +} + + Qt::ItemFlags QmitkImageStatisticsTableModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags = QAbstractItemModel::flags(index); return flags; } QVariant QmitkImageStatisticsTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if ((Qt::DisplayRole == role) && (Qt::Horizontal == orientation)) { if (!m_TimeStepResolvedImageNodes.empty()) { if (m_ViewMode == ViewMode::imageXStatistic) { std::stringstream ss; auto imageInfo = m_TimeStepResolvedImageNodes[section]; ss << imageInfo.first->GetName(); if (imageInfo.first->GetData() && imageInfo.first->GetData()->GetTimeSteps() > 1) { ss << " #" << imageInfo.second; } if (!m_MaskNodes.empty() && m_MaskNodes.size() == m_ImageNodes.size()) { ss << " / " << m_MaskNodes.at(section)->GetName(); } return QVariant(ss.str().c_str()); } } } else if ((Qt::DisplayRole == role) && (Qt::Vertical == orientation)) { if (m_ViewMode == ViewMode::imageXStatistic) { return QVariant(m_StatisticNames.at(section).c_str()); } } return QVariant(); } void QmitkImageStatisticsTableModel::SetImageNodes(const std::vector& nodes) { std::vector > tempNodes; for (const auto& node: nodes) { auto data = node->GetData(); if (data) { auto timeSteps = data->GetTimeSteps(); for (unsigned int i = 0; i < timeSteps; i++) { tempNodes.push_back(std::make_pair(node, i)); } } } emit beginResetModel(); m_TimeStepResolvedImageNodes = std::move(tempNodes); m_ImageNodes = nodes; UpdateByDataStorage(); emit endResetModel(); } void QmitkImageStatisticsTableModel::SetMaskNodes(const std::vector& nodes) { emit beginResetModel(); m_MaskNodes = nodes; UpdateByDataStorage(); emit endResetModel(); } void QmitkImageStatisticsTableModel::SetViewMode(ViewMode m) { emit beginResetModel(); m_ViewMode = m; emit endResetModel(); } void QmitkImageStatisticsTableModel::Clear() { emit beginResetModel(); m_Statistics.clear(); m_ImageNodes.clear(); m_TimeStepResolvedImageNodes.clear(); m_MaskNodes.clear(); m_StatisticNames.clear(); emit endResetModel(); } void QmitkImageStatisticsTableModel::UpdateByDataStorage() { - m_Statistics.clear(); + StatisticsContainerVector newStatistics; auto datamanager = m_DataStorage.Lock(); - for (const auto& image : m_ImageNodes) + if (datamanager.IsNotNull()) { - if (m_MaskNodes.empty()) + for (const auto& image : m_ImageNodes) { - auto stats = mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData()); - if (stats.IsNotNull()) + if (m_MaskNodes.empty()) { - m_Statistics.emplace_back(stats); + auto stats = mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData()); + if (stats.IsNotNull()) + { + newStatistics.emplace_back(stats); + } } - } - else - { - for (const auto& mask : m_MaskNodes) + else { - auto stats = mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData(), mask->GetData()); - if (stats.IsNotNull()) + for (const auto& mask : m_MaskNodes) { - m_Statistics.emplace_back(stats); + auto stats = mitk::ImageStatisticsContainerManager::GetImageStatistics(datamanager, image->GetData(), mask->GetData()); + if (stats.IsNotNull()) + { + newStatistics.emplace_back(stats); + } } } } } + { + itk::MutexLockHolder locked(m_Mutex); + m_Statistics = newStatistics; + } + m_StatisticNames = mitk::GetAllStatisticNames(m_Statistics); } void QmitkImageStatisticsTableModel::NodeRemoved(const mitk::DataNode *node) { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); } void QmitkImageStatisticsTableModel::NodeAdded(const mitk::DataNode *node) { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); } void QmitkImageStatisticsTableModel::NodeChanged(const mitk::DataNode *node) { emit beginResetModel(); UpdateByDataStorage(); emit endResetModel(); } - -void QmitkImageStatisticsTableModel::SetDataStorageDeleted() -{ - m_DataStorage = nullptr; -} diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.h b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.h index 23c8d847b2..b5daed1208 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.h +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsTableModel.h @@ -1,104 +1,111 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkImageStatisticsTableModel_h #define QmitkImageStatisticsTableModel_h -#include +#include "itkSimpleFastMutexLock.h" + +#include "QmitkAbstractDataStorageModel.h" //MITK #include #include "mitkImageStatisticsContainer.h" #include "mitkDataNode.h" #include "mitkDataStorage.h" /*! \class QmitkImageStatisticsTableModel Model that takes a mitk::ImageStatisticsContainer and represents it as model in context of the QT view-model-concept. */ -class MITKIMAGESTATISTICSUI_EXPORT QmitkImageStatisticsTableModel : public QAbstractTableModel +class MITKIMAGESTATISTICSUI_EXPORT QmitkImageStatisticsTableModel : public QmitkAbstractDataStorageModel { Q_OBJECT public: enum class ViewMode { imageXStatistic, imageXMask }; QmitkImageStatisticsTableModel(QObject *parent = nullptr); - virtual ~QmitkImageStatisticsTableModel() {}; + virtual ~QmitkImageStatisticsTableModel(); - /**Documentation - Set the data storage the model should fetch its statistic objects from. - @pre data storage must be valid - */ - void SetDataStorage(mitk::DataStorage* newDataStorage); + void SetImageNodes(const std::vector& nodes); + void SetMaskNodes(const std::vector& nodes); + void SetViewMode(ViewMode m); + void Clear(); - void SetImageNodes(const std::vector& nodes); - void SetMaskNodes(const std::vector& nodes); - void SetViewMode(ViewMode m); - void Clear(); + virtual Qt::ItemFlags flags(const QModelIndex &index) const override; + virtual QVariant data(const QModelIndex &index, int role) const override; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; - virtual Qt::ItemFlags flags(const QModelIndex &index) const override; - virtual QVariant data(const QModelIndex &index, int role) const override; - virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - virtual int rowCount(const QModelIndex &parent = QModelIndex()) const override; - virtual int columnCount(const QModelIndex &parent = QModelIndex()) const override; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex &child) const override; protected: - /** Helper triggered if storage gets a new node.*/ - void NodeAdded(const mitk::DataNode* node); - /** Helper triggered if storage has a node changed.*/ - void NodeChanged(const mitk::DataNode* node); - /** Helper triggered if storage has a node removed.*/ - void NodeRemoved(const mitk::DataNode* node); + /* + * @brief See 'QmitkAbstractDataStorageModel' + */ + void DataStorageChanged() override; + /* + * @brief See 'QmitkAbstractDataStorageModel' + */ + void NodePredicateChanged() override; + /* + * @brief See 'QmitkAbstractDataStorageModel' + */ + void NodeAdded(const mitk::DataNode *node) override; + /* + * @brief See 'QmitkAbstractDataStorageModel' + */ + void NodeChanged(const mitk::DataNode *node) override; + /* + * @brief See 'QmitkAbstractDataStorageModel' + */ + void NodeRemoved(const mitk::DataNode *node) override; private: - - /** Helper triggered on the storage delete event */ - void SetDataStorageDeleted(); - void UpdateByDataStorage(); - using StatisticsContainerVector = std::vector; /* the function returns the statistic container and the time point indicated by the index. If the index is not valid result.first will point to a nullptr.*/ std::pair GetRelevantStatsticsByIndex(const QModelIndex &index) const; StatisticsContainerVector m_Statistics; - mitk::WeakPointer m_DataStorage; /** Relevant images set by the user.*/ std::vector m_ImageNodes; /** Helper that is constructed when m_ImageNodes is set. It has the same order like m_ImageNodes, but each image is represented n times, while n is the number of time steps the respective image has. This structure makes the business logic to select the correct image given a QIndex much simpler and therfore easy to understand/maintaine. */ std::vector > m_TimeStepResolvedImageNodes; /** relevant masks set by the user.*/ std::vector m_MaskNodes; std::vector m_StatisticNames; ViewMode m_ViewMode = ViewMode::imageXStatistic; - unsigned long m_DataStorageDeletedTag; + itk::SimpleFastMutexLock m_Mutex; }; #endif // mitkQmitkImageStatisticsTableModel_h diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.cpp index f276e497bf..9d7a9a7dff 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.cpp +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.cpp @@ -1,127 +1,127 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkTableModelToStringConverter.h" #include QmitkTableModelToStringConverter::QmitkTableModelToStringConverter() { } -void QmitkTableModelToStringConverter::SetTableModel(QAbstractTableModel* model) +void QmitkTableModelToStringConverter::SetTableModel(QAbstractItemModel* model) { m_tableModel = model; } QString QmitkTableModelToStringConverter::GetString() const { if (m_tableModel == nullptr) { mitkThrow() << "Cannot convert TableModel to String: TableModel is nullptr"; } if (m_transposeOutput) { return GetStringTransposed(); } else { return GetStringNonTransposed(); } } void QmitkTableModelToStringConverter::SetTransposeOutput(bool transposeOutput) { m_transposeOutput = transposeOutput; } void QmitkTableModelToStringConverter::SetLineDelimiter(QChar lineDelimiter) { m_lineDelimiter = lineDelimiter; } void QmitkTableModelToStringConverter::SetColumnDelimiter(QChar columnDelimiter) { m_columnDelimiterWithSpace = columnDelimiter + QString(" "); } void QmitkTableModelToStringConverter::SetIncludeHeaderData(bool includeHeaderData) { m_includeHeaderData = includeHeaderData; } QString QmitkTableModelToStringConverter::GetStringTransposed() const { QString textData; int rows = m_tableModel->rowCount(); int columns = m_tableModel->columnCount(); if (m_includeHeaderData) { textData += " "; for (int i = 0; i < rows; i++) { textData += m_columnDelimiterWithSpace; textData += m_tableModel->headerData(i, Qt::Vertical).toString(); } textData += m_lineDelimiter; } for (int i = 0; i < columns; i++) { if (i > 0) { textData += m_lineDelimiter; } if (m_includeHeaderData) { textData += m_tableModel->headerData(i, Qt::Horizontal).toString() + m_columnDelimiterWithSpace; } for (int j = 0; j < rows; j++) { if (j > 0) { textData += m_columnDelimiterWithSpace; } textData += m_tableModel->data(m_tableModel->index(j, i)).toString(); MITK_WARN << i << " " << j; } } return textData; } QString QmitkTableModelToStringConverter::GetStringNonTransposed() const { QString textData; int rows = m_tableModel->rowCount(); int columns = m_tableModel->columnCount(); if (m_includeHeaderData) { textData += " "; for (int i = 0; i < columns; i++) { textData += m_columnDelimiterWithSpace; textData += m_tableModel->headerData(i, Qt::Horizontal).toString(); } textData += m_lineDelimiter; } for (int i = 0; i < rows; i++) { if (i > 0) { textData += m_lineDelimiter; } if (m_includeHeaderData) { textData += m_tableModel->headerData(i, Qt::Vertical).toString() + m_columnDelimiterWithSpace; } for (int j = 0; j < columns; j++) { if (j > 0) { textData += m_columnDelimiterWithSpace; } textData += m_tableModel->data(m_tableModel->index(i, j)).toString(); } } return textData; } diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.h b/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.h index 637bf34f22..69be6b605b 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.h +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkTableModelToStringConverter.h @@ -1,67 +1,67 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkTableModelToQString_H__INCLUDED #define QmitkTableModelToQString_H__INCLUDED #include -#include +#include /** \brief Converts the content of a table model to a string \details The content of a table model is converted (as-is or transposed) to a string. Each cell of the table is converted to a string. Default oder: iteration over rows. The line separation delimiter (default: '\n' and the column separation delimiter (default: ',') can be chosen. It also can be chosen if the (colum/row) headers should be exported to the string. By default, the produced string is csv conform */ class MITKIMAGESTATISTICSUI_EXPORT QmitkTableModelToStringConverter { public: QmitkTableModelToStringConverter(); - void SetTableModel(QAbstractTableModel* model); + void SetTableModel(QAbstractItemModel* model); /** \brief Returns the string \exception throws exception if model is nullptr */ QString GetString() const; /** \brief If the table should be transposed (iterate over columns instead of rows) */ void SetTransposeOutput(bool transposeOutput); void SetLineDelimiter(QChar lineDelimiter); void SetColumnDelimiter(QChar columnDelimiter); /** \brief If header data (column/row captions) are exported */ void SetIncludeHeaderData(bool includeHeaderData); private: QString GetStringTransposed() const; QString GetStringNonTransposed() const; - QAbstractTableModel* m_tableModel=nullptr; + QAbstractItemModel* m_tableModel=nullptr; bool m_transposeOutput=false; QChar m_lineDelimiter = '\n'; bool m_includeHeaderData = false; QString m_columnDelimiterWithSpace = ", "; }; #endif // QmitkTableModelToQString_H__INCLUDED diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 08aea54837..2db311b104 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -1,84 +1,84 @@ # The entries in the mitk_modules list must be # ordered according to their dependencies. set(MITK_MODULES Core CommandLine AppUtil RDF LegacyIO DataTypesExt Annotation LegacyGL AlgorithmsExt MapperExt DICOMReader DICOMReaderServices DICOMQI DICOMTesting SceneSerializationBase PlanarFigure ImageDenoising ImageExtraction SceneSerialization Gizmo GraphAlgorithms Multilabel Chart ImageStatistics - ImageStatisticsUI ContourModel SurfaceInterpolation Segmentation PlanarFigureSegmentation QtWidgets QtWidgetsExt + ImageStatisticsUI SegmentationUI MatchPointRegistration MatchPointRegistrationUI Classification GPGPU OpenIGTLink IGTBase IGT CameraCalibration OpenCL OpenCVVideoSupport QtOverlays ToFHardware ToFProcessing ToFUI PhotoacousticsHardware PhotoacousticsAlgorithms PhotoacousticsLib US USUI DicomUI Remeshing Python QtPython Persistence OpenIGTLinkUI IGTUI DicomRT RTUI IOExt XNAT TubeGraph BiophotonicsHardware DiffusionImaging TumorInvasionAnalysis BoundingShape RenderWindowManager RenderWindowManagerUI CEST BasicImageProcessing ModelFit ModelFitUI Pharmacokinetics PharmacokineticsUI ) if(MITK_ENABLE_PIC_READER) list(APPEND MITK_MODULES IpPicSupportIO) endif()