diff --git a/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h b/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h index d1824b2f04..10c27f2275 100644 --- a/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h +++ b/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h @@ -1,103 +1,195 @@ /*=================================================================== 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 __MITK_MODEL_FIT_PLOT_DATA_HELPER_H_ #define __MITK_MODEL_FIT_PLOT_DATA_HELPER_H_ #include #include "mitkPoint.h" #include "mitkModelBase.h" +#include "mitkWeakPointer.h" +#include "mitkCommon.h" + +#include "mitkModelFitInfo.h" + +#include "itkMapContainer.h" #include "MitkModelFitExports.h" namespace mitk { class ModelParameterizerBase; class Image; - namespace modelFit - { - class ModelFitInfo; - - } - using PlotDataValues = std::vector>; /** Simple helper structure that represents a curve for plots and its generation time*/ - struct MITKMODELFIT_EXPORT PlotDataCurve + class MITKMODELFIT_EXPORT PlotDataCurve : public ::itk::Object { - /** values of the curve */ - PlotDataValues values; + public: + mitkClassMacroItkParent(PlotDataCurve, itk::Object); + itkFactorylessNewMacro(Self); + + using ValuesType = PlotDataValues; - /** Timestamp indicating the last change of the values.*/ - itk::TimeStamp time; + virtual void SetValues(const ValuesType& _arg); + virtual void SetValues(ValuesType&& _arg); - PlotDataCurve() = default; - PlotDataCurve(const PlotDataValues& values); - PlotDataCurve(const PlotDataCurve& other); - PlotDataCurve(PlotDataCurve&& other); + itkGetConstReferenceMacro(Values, ValuesType); + itkGetMacro(Values, ValuesType); PlotDataCurve& operator=(const PlotDataCurve& rhs); PlotDataCurve& operator=(PlotDataCurve&& rhs) noexcept; void Reset(); + + protected: + PlotDataCurve(); + virtual ~PlotDataCurve() = default; + + private: + /** values of the curve */ + ValuesType m_Values; + + PlotDataCurve(const PlotDataCurve& other) = delete; }; /** Collection of plot curves, e.g. every plot curve for a certain world coordinate position*/ - using PlotDataCurveCollection = std::map; + using PlotDataCurveCollection = itk::MapContainer; - using PlotDataCurveCollectionMap = std::map; + /** Structure containing all information for one model fit */ + struct MITKMODELFIT_EXPORT ModelFitPlotData + { + /** Plots that are related to the world coordinate labeled as current position.*/ + PlotDataCurveCollection::Pointer currentPositionPlots; + + class PositionalLesser { + public: + bool operator()(const mitk::Point3D& lhs, const mitk::Point3D& rhs) const + { + if (lhs[0] < rhs[0]) + { + return true; + } + else if (lhs[0] > rhs[0]) + { + return false; + } + + if (lhs[1] < rhs[1]) + { + return true; + } + else if (lhs[1] > rhs[1]) + { + return false; + } + + if (lhs[2] < rhs[2]) + { + return true; + } + return false; + } + }; + + using PositionalCollectionMap = std::map; + + /** Plot collections that are related to specific world coordinates (inspection position bookmarks).*/ + PositionalCollectionMap positionalPlots; + + /** Plot collection for static plots of the fit that do not depend on some coordinates. */ + PlotDataCurveCollection::Pointer staticPlots; + + /** Pointer to the model fit that correspondens with this plot data.*/ + mitk::modelFit::ModelFitInfo::Pointer fitInfo; + + /** Helper function to get the collection of the current position. + Returns nullptr if no current position exists.*/ + static const PlotDataCurve* GetSamplePlot(const PlotDataCurveCollection* coll); + static const PlotDataCurve* GetSignalPlot(const PlotDataCurveCollection* coll); + static const PlotDataCurve* GetInterpolatedSignalPlot(const PlotDataCurveCollection* coll); + + const PlotDataCurveCollection* GetPositionalPlot(const mitk::Point3D& point) const; + + /**returns the minimum (first element) and maximum (second element) of x of all plot data*/ + PlotDataValues::value_type GetXMinMax() const; + /**returns the minimum (first element) and maximum (second element) of y of all plot data*/ + PlotDataValues::value_type GetYMinMax() const; + + ModelFitPlotData(); + }; + /** Helper function that actualizes min and max by the y values given in data.*/ + void CheckYMinMaxFromPlotDataValues(const PlotDataValues& data, double& min, double& max); + + /** Helper function that actualizes min and max by the x values given in data.*/ + void CheckXMinMaxFromPlotDataValues(const PlotDataValues& data, double& min, double& max); /** Function generates curve data for the signal defined by the passed informations. @param position The position in world coordinates the curve should be generated for. @param fitInfo Pointer to the fit info that defines the model/fit that produces the signal. @param timeGrid Defines the time grid of the generated signal. @param parameterizer Pointer to a parameterizer instance that is used to configure the model to generate the signal. If pointer is not set. The default parameterizer based on the fitInfo instance will be used. @pre position must be within the model fit input image @pre fitInfo must be a valid pointer. */ - MITKMODELFIT_EXPORT PlotDataCurve + MITKMODELFIT_EXPORT PlotDataCurve::Pointer GenerateModelSignalPlotData(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer = nullptr); /** Function generates curve data for all additinal inputs (e.g. ROI signal, AIF) stored in the fit information. The keys in the map are the same keys like in the fitInfo. @param position The position in world coordinates the curve should be generated for. @param fitInfo Pointer to the fit info that defines the model/fit that produces the signal. @param timeGrid Defines the time grid of the generated signal. @pre position must be within the model fit input image @pre fitInfo must be a valid pointer. */ - MITKMODELFIT_EXPORT PlotDataCurveCollection + MITKMODELFIT_EXPORT PlotDataCurveCollection::Pointer GenerateAdditionalModelFitPlotData(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid); /** Function generates curve data for a given image instance. @param position The position in world coordinates the curve should be generated for. @param image Pointer to the image the signal should be extracted from. @param timeGrid Defines the time grid of the generated signal. @pre position must be within the model fit input image @pre image must be a valid pointer. @pre image time steps must equal the timeGrid size. */ - MITKMODELFIT_EXPORT PlotDataCurve + MITKMODELFIT_EXPORT PlotDataCurve::Pointer GenerateImageSamplePlotData(const mitk::Point3D& position, const mitk::Image* image, const mitk::ModelBase::TimeGridType& timeGrid); + /** + * Keyword used in curve collections as key for the sample (extracted from an image) plot. + */ + MITKMODELFIT_EXPORT const std::string MODEL_FIT_PLOT_SAMPLE_NAME(); + + /** + * Keyword used in curve collections as key for the signal (generated by a model) plot. + */ + MITKMODELFIT_EXPORT const std::string MODEL_FIT_PLOT_SIGNAL_NAME(); + + /** + * Keyword used in curve collections as key for the interpolated (hires) signal (generated by a model) plot. + */ + MITKMODELFIT_EXPORT const std::string MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME(); + } #endif diff --git a/Modules/ModelFit/src/Common/mitkModelFitPlotDataHelper.cpp b/Modules/ModelFit/src/Common/mitkModelFitPlotDataHelper.cpp index a01014fc96..cfc165f4cb 100644 --- a/Modules/ModelFit/src/Common/mitkModelFitPlotDataHelper.cpp +++ b/Modules/ModelFit/src/Common/mitkModelFitPlotDataHelper.cpp @@ -1,250 +1,425 @@ /*=================================================================== 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 "mitkModelFitPlotDataHelper.h" #include "mitkExceptionMacro.h" #include "mitkImage.h" #include "mitkModelFitParameterValueExtraction.h" #include "mitkModelGenerator.h" #include "mitkFormulaParser.h" -mitk::PlotDataCurve::PlotDataCurve(const PlotDataValues& values) : values(values) +const std::string mitk::MODEL_FIT_PLOT_SAMPLE_NAME() { - time.Modified(); + return "Sample"; }; -mitk::PlotDataCurve::PlotDataCurve(const PlotDataCurve& other) : values(other.values), time(other.time) +const std::string mitk::MODEL_FIT_PLOT_SIGNAL_NAME() { -}; + return "Signal"; +} -mitk::PlotDataCurve::PlotDataCurve(PlotDataCurve&& other) : values(std::move(other.values)) +const std::string mitk::MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME() { - this->time = other.time; - other.time = itk::TimeStamp(); + return "INTERP_Signal"; }; +void +mitk::PlotDataCurve:: +SetValues(const PlotDataValues& _arg) +{ + if (this->m_Values != _arg) + { + this->m_Values = _arg; + this->Modified(); + } +} + +void +mitk::PlotDataCurve:: +SetValues(PlotDataValues&& _arg) +{ + if (this->m_Values != _arg) + { + this->m_Values = std::move(_arg); + this->Modified(); + } +} + mitk::PlotDataCurve & mitk::PlotDataCurve::operator=(const PlotDataCurve& rhs) { - this->values = rhs.values; - this->time = rhs.time; + this->m_Values = rhs.m_Values; + this->SetTimeStamp(rhs.GetTimeStamp()); return *this; }; mitk::PlotDataCurve& mitk::PlotDataCurve::operator=(PlotDataCurve&& rhs) noexcept { - this->values = std::move(rhs.values); - this->time = rhs.time; - rhs.time = itk::TimeStamp(); + this->m_Values = std::move(rhs.m_Values); + this->SetTimeStamp(rhs.GetTimeStamp()); return *this; }; void mitk::PlotDataCurve::Reset() { - this->values.clear(); - this->time = itk::TimeStamp(); + this->m_Values.clear(); + this->Modified(); +} + +mitk::PlotDataCurve::PlotDataCurve() +{ +} + +const mitk::PlotDataCurve* GetPlotCurve(const mitk::PlotDataCurveCollection* collection, const std::string& key) +{ + if (collection) + { + auto iter = collection->find(key); + if (iter != collection->end()) + { + return iter->second.GetPointer(); + } + } + return nullptr; +}; + +const mitk::PlotDataCurve* mitk::ModelFitPlotData::GetSamplePlot(const PlotDataCurveCollection* coll) +{ + if (coll) + { + return GetPlotCurve(coll, MODEL_FIT_PLOT_SAMPLE_NAME()); + } + return nullptr; +}; + +const mitk::PlotDataCurve* mitk::ModelFitPlotData::GetSignalPlot(const PlotDataCurveCollection* coll) +{ + if (coll) + { + return GetPlotCurve(coll, MODEL_FIT_PLOT_SIGNAL_NAME()); + } + return nullptr; +}; + +const mitk::PlotDataCurve* mitk::ModelFitPlotData::GetInterpolatedSignalPlot(const PlotDataCurveCollection* coll) +{ + if (coll) + { + return GetPlotCurve(coll, MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME()); + } + return nullptr; +}; + +const mitk::PlotDataCurveCollection* mitk::ModelFitPlotData::GetPositionalPlot(const mitk::Point3D& point) const +{ + auto iter = this->positionalPlots.find(point); + if (iter != positionalPlots.end()) + { + return iter->second.GetPointer(); + } + return nullptr; +}; + +mitk::PlotDataValues::value_type mitk::ModelFitPlotData::GetXMinMax() const +{ + double max = itk::NumericTraits::NonpositiveMin(); + double min = itk::NumericTraits::max(); + + //currently we assume that within a model fit, plot data does not exceed + //the sample/signale on the x axis. + auto sample = this->GetSamplePlot(this->currentPositionPlots); + if (sample) + { + CheckXMinMaxFromPlotDataValues(sample->GetValues(), min, max); + } + for (const auto& posCollection : this->positionalPlots) + { + for (const auto& plot : *(posCollection.second)) + { + auto sample = this->GetSamplePlot(posCollection.second); + if (sample) + { + CheckXMinMaxFromPlotDataValues(sample->GetValues(), min, max); + } + } + } + + return std::make_pair(min, max); +}; + +mitk::PlotDataValues::value_type mitk::ModelFitPlotData::GetYMinMax() const +{ + double max = itk::NumericTraits::NonpositiveMin(); + double min = itk::NumericTraits::max(); + + for (const auto& plot : *(this->currentPositionPlots.GetPointer())) + { + CheckYMinMaxFromPlotDataValues(plot.second->GetValues(), min, max); + } + + for (const auto& posCollection : this->positionalPlots) + { + for (const auto& plot : *(posCollection.second)) + { + CheckYMinMaxFromPlotDataValues(plot.second->GetValues(), min, max); + } + } + + for (const auto& plot : *(this->staticPlots)) + { + CheckYMinMaxFromPlotDataValues(plot.second->GetValues(), min, max); + } + + return std::make_pair(min, max); +}; + +mitk::ModelFitPlotData::ModelFitPlotData() +{ + this->currentPositionPlots = PlotDataCurveCollection::New(); + this->staticPlots = PlotDataCurveCollection::New(); +}; + +void mitk::CheckYMinMaxFromPlotDataValues(const PlotDataValues& data, double& min, double& max) +{ + for (const auto & pos : data) + { + if (max < pos.second) + { + max = pos.second; + } + + if (min > pos.second) + { + min = pos.second; + } + } +} + +void mitk::CheckXMinMaxFromPlotDataValues(const PlotDataValues& data, double& min, double& max) +{ + for (const auto & pos : data) + { + if (max < pos.first) + { + max = pos.first; + } + + if (min > pos.first) + { + min = pos.first; + } + } } /** Helper function that generates the curve based on a stored and on the fly parsed function string.*/ -mitk::PlotDataCurve +mitk::PlotDataCurve::Pointer CalcSignalFromFunction(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid) { if (!fitInfo) { mitkThrow() << "Cannot calc model curve from function for given fit. Passed ModelFitInfo instance is nullptr."; } mitk::Image::Pointer inputImage = fitInfo->inputImage; assert(inputImage.IsNotNull()); - mitk::PlotDataCurve result; + mitk::PlotDataCurve::Pointer result = mitk::PlotDataCurve::New(); + mitk::PlotDataCurve::ValuesType values; + values.reserve(timeGrid.size()); // Calculate index ::itk::Index<3> index; mitk::BaseGeometry::Pointer geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep(0); geometry->WorldToIndex(position, index); mitk::ParameterValueMapType parameterMap = mitk::ExtractParameterValueMapFromModelFit(fitInfo, position); mitk::FormulaParser parser(¶meterMap); for (unsigned int t = 0; t < timeGrid.size(); ++t) { // Set up static parameters for (const auto& var : fitInfo->staticParamMap) { const auto& list = var.second; if (list.size() == 1) { parameterMap[var.first] = list.front(); } else { parameterMap[var.first] = list.at(t); } } // Calculate curve data double x = timeGrid[t]; parameterMap[fitInfo->x] = x; double y = parser.parse(fitInfo->function); - result.values.push_back(std::make_pair(x, y)); + values.emplace_back(std::make_pair(x, y)); } - result.time.Modified(); + result->SetValues(std::move(values)); return result; } /** Helper function that generates the curve based on the model specified by the fit info.*/ -mitk::PlotDataCurve +mitk::PlotDataCurve::Pointer CalcSignalFromModel(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelParameterizerBase* parameterizer = nullptr) { assert(fitInfo); if (!parameterizer) { parameterizer = mitk::ModelGenerator::GenerateModelParameterizer(*fitInfo); } mitk::Image::Pointer inputImage = fitInfo->inputImage; assert(inputImage.IsNotNull()); // Calculate index ::itk::Index<3> index; mitk::BaseGeometry::Pointer geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep(0); geometry->WorldToIndex(position, index); //model generation mitk::ModelBase::Pointer model = parameterizer->GenerateParameterizedModel(index); mitk::ParameterValueMapType parameterMap = mitk::ExtractParameterValueMapFromModelFit(fitInfo, position); mitk::ModelBase::ParametersType paramArray = mitk::ConvertParameterMapToParameterVector(parameterMap, model); mitk::ModelBase::ModelResultType curveDataY = model->GetSignal(paramArray); - mitk::PlotDataCurve result; + mitk::PlotDataCurve::Pointer result = mitk::PlotDataCurve::New(); + mitk::ModelBase::TimeGridType timeGrid = model->GetTimeGrid(); + mitk::PlotDataCurve::ValuesType values; + values.reserve(timeGrid.size()); for (unsigned int t = 0; t < timeGrid.size(); ++t) { double x = timeGrid[t]; double y = curveDataY[t]; - result.values.push_back(std::make_pair(x, y)); + values.emplace_back(std::make_pair(x, y)); } - result.time.Modified(); + result->SetValues(std::move(values)); return result; } -mitk::PlotDataCurve +mitk::PlotDataCurve::Pointer mitk::GenerateModelSignalPlotData(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer) { if (!fitInfo) { mitkThrow() << "Cannot calc model curve from function for given fit. Passed ModelFitInfo instance is nullptr."; } - PlotDataCurve result; + mitk::PlotDataCurve::Pointer result; if (!parameterizer) { parameterizer = ModelGenerator::GenerateModelParameterizer(*fitInfo); } if (parameterizer) { // Use model instead of formula parser parameterizer->SetDefaultTimeGrid(timeGrid); result = CalcSignalFromModel(position, fitInfo, parameterizer); } else { // Use formula parser to parse function string try { result = CalcSignalFromFunction(position, fitInfo, timeGrid); } catch (const mitk::FormulaParserException& e) { MITK_ERROR << "Error while parsing modelfit function: " << e.what(); } } return result; } -mitk::PlotDataCurveCollection +mitk::PlotDataCurveCollection::Pointer mitk::GenerateAdditionalModelFitPlotData(const mitk::Point3D& position, const mitk::modelFit::ModelFitInfo* fitInfo, const mitk::ModelBase::TimeGridType& timeGrid) { if (!fitInfo) { mitkThrow() << "Cannot calc model curve from function for given fit. Passed ModelFitInfo instance is nullptr."; } - mitk::PlotDataCurveCollection result; + mitk::PlotDataCurveCollection::Pointer result = mitk::PlotDataCurveCollection::New(); for (const auto& additionalInput : fitInfo->inputData.GetLookupTable()) { if (additionalInput.second.size() != timeGrid.size()) { MITK_ERROR << "Error while refreshing input data for visualization. Size of data and input image time grid differ. Invalid data name: " << additionalInput.first; } else { - mitk::PlotDataCurve pointData; + mitk::PlotDataCurve::Pointer pointData = mitk::PlotDataCurve::New();; + mitk::PlotDataCurve::ValuesType values; + values.reserve(timeGrid.size()); for (unsigned int t = 0; t < timeGrid.size(); ++t) { const double x = timeGrid[t]; const double y = additionalInput.second[t]; - pointData.values.push_back(std::make_pair(x, y)); + values.emplace_back(std::make_pair(x, y)); } - pointData.time.Modified(); - result.emplace(additionalInput.first, std::move(pointData)); + pointData->SetValues(std::move(values)); + result->CastToSTLContainer().emplace(additionalInput.first, std::move(pointData)); } } return result; } -mitk::PlotDataCurve +mitk::PlotDataCurve::Pointer mitk::GenerateImageSamplePlotData(const mitk::Point3D& position, const mitk::Image* image, const mitk::ModelBase::TimeGridType& timeGrid) { if (!image) { mitkThrow() << "Cannot generate sample plot data. Passed image instance is nullptr."; } - mitk::PlotDataCurve result; + mitk::PlotDataCurve::Pointer result = mitk::PlotDataCurve::New(); + mitk::PlotDataCurve::ValuesType values; + values.reserve(timeGrid.size()); for (unsigned int t = 0; t < timeGrid.size(); ++t) { const double x = timeGrid[t]; const double y = ReadVoxel(image, position, t); - result.values.push_back(std::make_pair(x, y)); + values.emplace_back(std::make_pair(x, y)); } - result.time.Modified(); + result->SetValues(std::move(values)); return result; } diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.cpp b/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.cpp new file mode 100644 index 0000000000..f911741038 --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.cpp @@ -0,0 +1,342 @@ +/*=================================================================== + +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 "mitkExceptionMacro.h" +#include "mitkModelFitParameterValueExtraction.h" + +#include "QmitkFitParameterModel.h" + + +QmitkFitParameterModel:: +QmitkFitParameterModel(QObject* parent) : + QAbstractTableModel(parent) +{ + m_CurrentPos.Fill(0.0); +} + +const QmitkFitParameterModel::FitVectorType& +QmitkFitParameterModel:: +getFits() const +{ + return m_Fits; +}; + +mitk::Point3D +QmitkFitParameterModel:: +getCurrentPosition() const +{ + return m_CurrentPos; +}; + +const mitk::PointSet* +QmitkFitParameterModel:: +getPositionBookmarks() const +{ + return m_Bookmarks; +}; + +void +QmitkFitParameterModel:: +setFits(const FitVectorType& fits) +{ + emit beginResetModel(); + m_Fits = fits; + emit endResetModel(); +}; + +void +QmitkFitParameterModel:: +setCurrentPosition(const mitk::Point3D& currentPos) +{ + emit beginResetModel(); + m_CurrentPos = currentPos; + emit endResetModel(); +}; + +void +QmitkFitParameterModel:: +setPositionBookmarks(const mitk::PointSet* bookmarks) +{ + emit beginResetModel(); + m_Bookmarks = bookmarks; + emit endResetModel(); +}; + +bool +QmitkFitParameterModel:: +hasSingleFit() const +{ + return this->m_Fits.size() == 1; +}; + +int +QmitkFitParameterModel:: +rowCount(const QModelIndex& parent) const +{ + if (this->hasSingleFit()) + { + if (parent.isValid()) + { + return 0; + } + else + { + return this->m_Fits.front()->GetParameters().size() + this->m_Fits.front()->staticParamMap.Size(); + } + } + else + { + if (parent.isValid()) + { + assert(parent.row() < this->m_Fits.size()); + return this->m_Fits[parent.row()]->GetParameters().size() + this->m_Fits[parent.row()]->staticParamMap.Size(); + } + else + { + return this->m_Fits.size(); + } + } +} + +std::size_t +QmitkFitParameterModel:: +getBookmarksCount() const +{ + if (m_Bookmarks.IsNotNull()) + { + return m_Bookmarks->GetSize(); + } + return 0; +} + +int +QmitkFitParameterModel:: +columnCount(const QModelIndex& parent) const +{ + return 3 + this->getBookmarksCount(); +} + + +/** Helper function returns the name of the static parameter indicates by the index. + If the index does not indicate a static parameter an empty string will be returned.*/ +std::string GetStaticParameterName(const mitk::modelFit::ModelFitInfo* currentFit, const QModelIndex& index) +{ + const auto paramSize = currentFit->GetParameters().size(); + const auto staticParamSize = currentFit->staticParamMap.Size(); + + std::string staticParamName; + + if (index.row() >= paramSize) + { + int pos = paramSize; + for (const auto& iter : currentFit->staticParamMap) + { + if (pos == index.row()) + { + staticParamName = iter.first; + break; + } + ++pos; + } + } + + return staticParamName; +} + +QVariant +QmitkFitParameterModel:: +data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + QVariant result; + if (!index.parent().isValid() && !this->hasSingleFit()) + { //we need the fit names + if (index.row() < m_Fits.size() && index.column() == 0) + { + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + result = QVariant(QString::fromStdString(m_Fits[index.row()]->fitName)+QString("(") + QString::fromStdString(m_Fits[index.row()]->uid) + QString(")")); + } + else if (role == Qt::ToolTipRole) + { + result = QVariant("Name (UID) of the fit."); + } + } + } + else + { // realy want to get the values of the current fit + const mitk::modelFit::ModelFitInfo* currentFit = nullptr; + + if (this->hasSingleFit() && !index.parent().isValid()) + { + currentFit = m_Fits.front(); + } + else if (index.parent().isValid() && index.parent().row() < m_Fits.size()) + { + currentFit = m_Fits[index.parent().row()]; + } + + if (currentFit) + { + const auto paramSize = currentFit->GetParameters().size(); + const auto staticParamSize = currentFit->staticParamMap.Size(); + + if (index.row() < paramSize + staticParamSize) + { + std::string staticParamName = GetStaticParameterName(currentFit, index); + + switch (index.column()) + { + case 0: + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + if (index.row() < paramSize) + { + const auto& param = currentFit->GetParameters()[index.row()]; + result = QVariant(QString::fromStdString(param->name)); + } + else + { + result = QVariant(QString::fromStdString(staticParamName)); + } + } + else if (role == Qt::ToolTipRole) + { + result = QVariant("Name of the parameter."); + } + break; + + case 1: + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + if (index.row() < paramSize) + { + const auto& param = currentFit->GetParameters()[index.row()]; + std::string paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER(); + + if (param->type == mitk::modelFit::Parameter::DerivedType) + { + paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER(); + } + else if (param->type == mitk::modelFit::Parameter::CriterionType) + { + paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_CRITERION(); + } + else if (param->type == mitk::modelFit::Parameter::EvaluationType) + { + paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_EVALUATION_PARAMETER(); + } + + result = QVariant(QString::fromStdString(paramType)); + } + else + { + result = QVariant("static"); + } + } + else if (role == Qt::ToolTipRole) + { + result = QVariant("Type of the parameter."); + } + break; + + default: + if (index.column() - 2 < this->getBookmarksCount()+1) + { + mitk::Point3D pos = m_CurrentPos; + if (index.column() > 2) + { + pos = m_Bookmarks->GetPoint(index.column() - 3); + } + + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + if (index.row() < paramSize) + { + auto value = mitk::ReadVoxel(currentFit->GetParameters()[index.row()]->image, pos); + result = QVariant(QString::number(value)); + } + else + { + auto value = currentFit->staticParamMap.Get(staticParamName).front(); + result = QVariant(QString::number(value)); + } + } + else if (role == Qt::ToolTipRole) + { + result = QVariant("Value of a (static) fit parameter"); + } + } + break; + } + } + } + } + + return result; +} + +Qt::ItemFlags +QmitkFitParameterModel:: +flags(const QModelIndex& index) const +{ + Qt::ItemFlags flags = QAbstractItemModel::flags(index); + + return flags; +} + +QVariant +QmitkFitParameterModel:: +headerData(int section, Qt::Orientation orientation, int role) const +{ + if ((Qt::DisplayRole == role) && + (Qt::Horizontal == orientation)) + { + if (section == 0) + { + return QVariant("Name"); + } + else if (section == 1) + { + return QVariant("Type"); + } + else if (section == 2) + { + return QVariant("Value"); + } + else if (section - 3 < this->getBookmarksCount()) + { + const auto & pos = m_Bookmarks->GetPoint(section - 3); + std::ostringstream strm; + strm.imbue(std::locale("C")); + strm << std::setprecision(3) << "Value @ Pos " << section -3 << " (" << pos[0] << "|" << pos[1] << "|" << pos[2] << ")"; + return QVariant(QString::fromStdString(strm.str())); + } + } + + return QVariant(); +} + +bool +QmitkFitParameterModel:: +setData(const QModelIndex& index, const QVariant& value, int role) +{ + return false; +}; diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.h b/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.h new file mode 100644 index 0000000000..9808c6e333 --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterModel.h @@ -0,0 +1,81 @@ +/*=================================================================== + +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 QmitkFitParameterModel_h +#define QmitkFitParameterModel_h + +#include + +#include "mitkModelFitInfo.h" +#include "mitkPointSet.h" + +#include "MitkModelFitUIExports.h" + + +/*! +\class QmitkFitParameterModel +Model that can be used to display the parameter values of ModelFitInfo instances for different world coordinate positions. +If more then one ModelFitInfo instance is given the model will use a tree hirarchy. The first level are the fits, +the seconds level are the parameter of the fit. +*/ +class MITKMODELFITUI_EXPORT QmitkFitParameterModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + using FitVectorType = std::vector; + + QmitkFitParameterModel(QObject* parent = NULL); + virtual ~QmitkFitParameterModel() {}; + + const FitVectorType& getFits() const; + + mitk::Point3D getCurrentPosition() const; + + const mitk::PointSet* getPositionBookmarks() const; + + virtual Qt::ItemFlags flags(const QModelIndex& index) const; + virtual QVariant data(const QModelIndex& index, int role) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + +public Q_SLOTS: + + void setFits(const FitVectorType& fits); + + void setCurrentPosition(const mitk::Point3D& currentPos); + + void setPositionBookmarks(const mitk::PointSet* bookmarks); + + +protected: + + std::size_t getBookmarksCount() const; + +private: + + bool hasSingleFit() const; + + FitVectorType m_Fits; + mitk::PointSet::ConstPointer m_Bookmarks; + mitk::Point3D m_CurrentPos; + +}; + +#endif // QmitkFitParameterModel_h + diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.cpp b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.cpp new file mode 100644 index 0000000000..31fa0a98de --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.cpp @@ -0,0 +1,163 @@ +/*=================================================================== + +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 "QmitkFitParameterWidget.h" +#include "QmitkFitParameterModel.h" + +#include +#include +#include + +QmitkFitParameterWidget::QmitkFitParameterWidget(QWidget*) +{ + this->m_Controls.setupUi(this); + + m_InternalModel = new QmitkFitParameterModel(this); + m_Controls.tableFitParameter->setModel(m_InternalModel); + + connect(m_Controls.btnCopyResultsToClipboard, SIGNAL(clicked()), this, SLOT(OnClipboardResultsButtonClicked())); + connect(m_Controls.btnSaveToFile, SIGNAL(clicked()), this, SLOT(OnExportClicked())); +} + +const QmitkFitParameterWidget::FitVectorType& +QmitkFitParameterWidget:: +getFits() const +{ + return m_InternalModel->getFits(); +}; + +mitk::Point3D +QmitkFitParameterWidget:: +getCurrentPosition() const +{ + return m_InternalModel->getCurrentPosition(); +}; + +const mitk::PointSet* +QmitkFitParameterWidget:: +getPositionBookmarks() const +{ + return m_InternalModel->getPositionBookmarks(); +}; + +void +QmitkFitParameterWidget:: +setFits(const FitVectorType& fits) +{ + m_InternalModel->setFits(fits); +}; + +void +QmitkFitParameterWidget:: +setCurrentPosition(const mitk::Point3D& currentPos) +{ + m_InternalModel->setCurrentPosition(currentPos); +}; + +void +QmitkFitParameterWidget:: +setPositionBookmarks(const mitk::PointSet* bookmarks) +{ + m_InternalModel->setPositionBookmarks(bookmarks); +}; + +QmitkFitParameterWidget::~QmitkFitParameterWidget() +{ +} + +std::string QmitkFitParameterWidget::streamModelToString() const +{ + std::ostringstream stream; + stream.imbue(std::locale("C")); + + //head line + const auto colCount = this->m_InternalModel->columnCount(); + for (int col = 0; col < colCount; ++col) + { + if (col != 0) + { + stream << ","; + } + stream << m_InternalModel->headerData(col, Qt::Horizontal, Qt::DisplayRole).toString().toStdString(); + } + stream << std::endl; + + //content + const auto rowCount = this->m_InternalModel->rowCount(); + for (int row = 0; row < rowCount; ++row) + { + QModelIndex index = this->m_InternalModel->index(row, 0); + + const auto childCount = this->m_InternalModel->rowCount(index); + + if (childCount == 0) + { + for (int col = 0; col < colCount; ++col) + { + if (col != 0) + { + stream << ","; + } + stream << m_InternalModel->data(index.siblingAtColumn(col), Qt::DisplayRole).toString().toStdString(); + } + stream << std::endl; + } + else + { + mitkThrow() << "Missing implementation for multiple fits."; + //TODO FIT REFACTOR + } + } + + return stream.str(); +} + +void QmitkFitParameterWidget::OnClipboardResultsButtonClicked() const +{ + QApplication::clipboard()->setText(QString::fromStdString(this->streamModelToString()), QClipboard::Clipboard); + +} + +void QmitkFitParameterWidget::OnExportClicked() const +{ + QString fileName = QFileDialog::getSaveFileName(nullptr, tr("Save fit parameter to csv file")); + + if (fileName.isEmpty()) + { + QMessageBox::critical(nullptr, tr("No file selected!"), + tr("Cannot export pixel dump. Please selected a file.")); + } + else + { + std::ofstream file; + + std::ios_base::openmode iOpenFlag = std::ios_base::out | std::ios_base::trunc; + file.open(fileName.toStdString().c_str(), iOpenFlag); + + if (!file.is_open()) + { + QMessageBox::critical(nullptr, tr("Cannot create/open selected file!"), + tr("Cannot open or create the selected file. Export will be aborted. Selected file name: ") + + fileName); + return; + } + + file << this->streamModelToString(); + + file.close(); + } + +} diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h new file mode 100644 index 0000000000..9b773f0df0 --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h @@ -0,0 +1,77 @@ +/*=================================================================== + +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 QMITK_FIT_PARAMETER_WIDGET_H +#define QMITK_FIT_PARAMETER_WIDGET_H + +#include "mitkModelFitInfo.h" +#include "mitkPointSet.h" + +#include "MitkModelFitUIExports.h" + +#include "ui_QmitkFitParameterWidget.h" +#include + + +class QmitkFitParameterModel; + +/** +* \class QmitkFitParameterWidget +* Widget that displays the parameters of all set ModelFitInfo instances for all given +* world coordinate points. +* In addition it allows to transfer this information as CSV into the clipboard or a file. +*/ +class MITKMODELFITUI_EXPORT QmitkFitParameterWidget : public QWidget +{ + Q_OBJECT + +public: + using FitVectorType = std::vector; + + QmitkFitParameterWidget(QWidget* parent = 0); + ~QmitkFitParameterWidget(); + + const FitVectorType& getFits() const; + + mitk::Point3D getCurrentPosition() const; + + const mitk::PointSet* getPositionBookmarks() const; + +public Q_SLOTS: + + void setFits(const FitVectorType& fits); + + void setCurrentPosition(const mitk::Point3D& currentPos); + + void setPositionBookmarks(const mitk::PointSet* bookmarks); + +protected Q_SLOTS: + void OnExportClicked() const; + + /** @brief Saves the results table to clipboard */ + void OnClipboardResultsButtonClicked() const; + +protected: + std::string streamModelToString() const; + + QmitkFitParameterModel * m_InternalModel; + + Ui::QmitkFitParameterWidget m_Controls; + +}; + +#endif // QmitkFitParameterWidget_H diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.ui b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.ui new file mode 100644 index 0000000000..a6f7e5c461 --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.ui @@ -0,0 +1,76 @@ + + + QmitkFitParameterWidget + + + + 0 + 0 + 723 + 460 + + + + Form + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QAbstractItemView::NoEditTriggers + + + false + + + true + + + false + + + true + + + true + + + + + + + + + Copy to clipboard + + + + + + + Save to file + + + + + + + + + + diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataModel.cpp b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataModel.cpp new file mode 100644 index 0000000000..5dbe46210e --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataModel.cpp @@ -0,0 +1,284 @@ +/*=================================================================== + +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 "mitkExceptionMacro.h" + +#include "QmitkFitPlotDataModel.h" + + +QmitkFitPlotDataModel:: +QmitkFitPlotDataModel(QObject* parent) : + QAbstractTableModel(parent) +{ +} + +const mitk::ModelFitPlotData* +QmitkFitPlotDataModel:: +GetPlotData() const +{ + return &m_PlotData; +}; + +void +QmitkFitPlotDataModel:: +SetPlotData(const mitk::ModelFitPlotData* data) +{ + emit beginResetModel(); + m_PlotData = *data; + emit endResetModel(); +}; + +const std::string& +QmitkFitPlotDataModel:: +GetXName() const +{ + return m_XName; +}; + +void +QmitkFitPlotDataModel:: +SetXName(const std::string& xName) +{ + emit beginResetModel(); + m_XName = xName; + emit endResetModel(); +}; + +int +QmitkFitPlotDataModel:: +rowCount(const QModelIndex& parent) const +{ + //we assume that all plots have the same nr of values (except the interpolated signal which will be ignored). + //Thus we have only to check one plot. Reason: One fit is always derived from one input data and therefore + //all derived data should have the same size. + auto plot = GetCurveByColumn(0); + if (plot.second) + { + return plot.second->GetValues().size(); + } + return 0; +} + +std::pair QmitkFitPlotDataModel::GetPositionalCurvePoint(const mitk::PlotDataCurve* curve) const +{ + mitk::Point3D result; + + for (auto collection : this->m_PlotData.positionalPlots) + { + result = collection.first; + + for (auto aCurve : collection.second->CastToSTLContainer()) + { + if (curve == aCurve.second.GetPointer()) + { + return std::make_pair(true, result); + } + } + } + return std::make_pair(false, result); +} + + +int +QmitkFitPlotDataModel:: +columnCount(const QModelIndex& parent) const +{ + if (parent.isValid()) + { + return 0; + } + else + { + auto size = this->m_PlotData.currentPositionPlots->size() + this->m_PlotData.staticPlots->size(); + if (mitk::ModelFitPlotData::GetInterpolatedSignalPlot(this->m_PlotData.currentPositionPlots)) + { //don't take the interpolated signal into account + size -= 1; + } + if (mitk::ModelFitPlotData::GetInterpolatedSignalPlot(this->m_PlotData.staticPlots)) + { //don't take the interpolated signal into account + size -= 1; + } + + for (const auto& coll : this->m_PlotData.positionalPlots) + { + size += coll.second->size(); + if (mitk::ModelFitPlotData::GetInterpolatedSignalPlot(coll.second)) + { //don't take the interpolated signal into account + size -= 1; + } + } + return size+2; + } +} + +bool GetCurveFromCollection(const mitk::PlotDataCurveCollection* collection, std::size_t index, std::pair& finding, std::size_t& actualCount) +{ + actualCount = 0; + for (const auto& iter : *(collection)) + { + if (iter.first != mitk::MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME()) + { + if (actualCount == index) + { + finding = std::make_pair(iter.first, iter.second.GetPointer()); + return true; + } + ++actualCount; + } + } + return false; +} + +std::pair +QmitkFitPlotDataModel:: +GetCurveByColumn(int col) const +{ + if (col > 1) + { + col -= 2; + } + + std::pair finding; + std::size_t actualCount; + if (GetCurveFromCollection(this->m_PlotData.currentPositionPlots.GetPointer(), col, finding, actualCount)) + { + return finding; + } + col -= actualCount; + + for (auto collection : this->m_PlotData.positionalPlots) + { + if (GetCurveFromCollection(collection.second.GetPointer(), col, finding, actualCount)) + { + return finding; + } + col -= actualCount; + } + + GetCurveFromCollection(this->m_PlotData.staticPlots.GetPointer(), col, finding, actualCount); + return finding; +}; + + +QVariant +QmitkFitPlotDataModel:: +data(const QModelIndex& index, int role) const +{ + if (!index.isValid()) + { + return QVariant(); + } + + QVariant result; + + if (index.column() == 0) + { + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + result = QVariant(QString::number(index.row())); + } + else if (role == Qt::ToolTipRole) + { + result = QVariant("Index in plot."); + } + } + else if (index.column() == 1) + { + auto finding = GetCurveByColumn(index.column()); + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + if (finding.second && index.row() < finding.second->GetValues().size()) + { + result = QVariant(QString::number(finding.second->GetValues()[index.row()].first)); + } + } + else if (role == Qt::ToolTipRole) + { + result = QVariant("x value in plot."); + } + } + else + { + auto finding = GetCurveByColumn(index.column()); + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + if (finding.second && index.row() < finding.second->GetValues().size()) + { + result = QVariant(QString::number(finding.second->GetValues()[index.row()].second)); + } + } + else if (role == Qt::ToolTipRole) + { + result = QVariant("This is a value of the respective curve."); + } + } + + return result; +} + +Qt::ItemFlags +QmitkFitPlotDataModel:: +flags(const QModelIndex& index) const +{ + Qt::ItemFlags flags = QAbstractItemModel::flags(index); + + return flags; +} + +QVariant +QmitkFitPlotDataModel:: +headerData(int section, Qt::Orientation orientation, int role) const +{ + if ((Qt::DisplayRole == role) && + (Qt::Horizontal == orientation)) + { + if (section == 0) + { + return QVariant("#"); + } + else if (section == 1) + { + return QVariant(QString::fromStdString(m_XName)); + } + else + { + auto finding = GetCurveByColumn(section); + + auto pointFinding = GetPositionalCurvePoint(finding.second); + + std::ostringstream nameStrm; + nameStrm.imbue(std::locale("C")); + nameStrm << finding.first; + + if (pointFinding.first) + { + nameStrm << " @ " << std::setprecision(3) << "(" << pointFinding.second[0] << "|" << pointFinding.second[1] << "|" << pointFinding.second[2] << ")"; + } + + return QVariant(QString::fromStdString(nameStrm.str())); + + } + } + + return QVariant(); +} + +bool +QmitkFitPlotDataModel:: +setData(const QModelIndex& index, const QVariant& value, int role) +{ + return false; +}; diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataModel.h b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataModel.h new file mode 100644 index 0000000000..efad1e3f30 --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataModel.h @@ -0,0 +1,65 @@ +/*=================================================================== + +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 QmitkFitPlotDataModel_h +#define QmitkFitPlotDataModel_h + +#include + +#include "mitkModelFitPlotDataHelper.h" +#include "MitkModelFitUIExports.h" + +/*! +\class QmitkFitPlotDataModel +Model that can be used to display the values of an ModelFitPlotData instance. +*/ +class MITKMODELFITUI_EXPORT QmitkFitPlotDataModel : public QAbstractTableModel +{ + Q_OBJECT + +public: + using FitVectorType = std::vector; + + QmitkFitPlotDataModel(QObject* parent = NULL); + virtual ~QmitkFitPlotDataModel() {}; + + const mitk::ModelFitPlotData* GetPlotData() const; + const std::string& GetXName() const; + + virtual Qt::ItemFlags flags(const QModelIndex& index) const; + virtual QVariant data(const QModelIndex& index, int role) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; + virtual int rowCount(const QModelIndex& parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex& parent = QModelIndex()) const; + virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole); + +public Q_SLOTS: + + void SetPlotData(const mitk::ModelFitPlotData* data); + void SetXName(const std::string& xName); + +protected: + std::pair GetCurveByColumn(int col) const; + std::pair GetPositionalCurvePoint(const mitk::PlotDataCurve*) const; + +private: + mitk::ModelFitPlotData m_PlotData; + std::string m_XName; + +}; + +#endif // QmitkFitPlotDataModel_h + diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.cpp b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.cpp new file mode 100644 index 0000000000..b213ac4f43 --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.cpp @@ -0,0 +1,138 @@ +/*=================================================================== + +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 "QmitkFitPlotDataWidget.h" +#include "QmitkFitPlotDataModel.h" + +#include +#include +#include + +QmitkFitPlotDataWidget::QmitkFitPlotDataWidget(QWidget*) +{ + this->m_Controls.setupUi(this); + + m_InternalModel = new QmitkFitPlotDataModel(this); + m_Controls.tablePlotData->setModel(m_InternalModel); + + connect(m_Controls.btnCopyResultsToClipboard, SIGNAL(clicked()), this, SLOT(OnClipboardResultsButtonClicked())); + connect(m_Controls.btnSaveToFile, SIGNAL(clicked()), this, SLOT(OnExportClicked())); +} + +const mitk::ModelFitPlotData* +QmitkFitPlotDataWidget:: +GetPlotData() const +{ + return m_InternalModel->GetPlotData(); +}; + +void +QmitkFitPlotDataWidget:: +SetPlotData(const mitk::ModelFitPlotData* data) +{ + m_InternalModel->SetPlotData(data); +}; + +const std::string& +QmitkFitPlotDataWidget:: +GetXName() const +{ + return m_InternalModel->GetXName(); +}; + +void +QmitkFitPlotDataWidget:: +SetXName(const std::string& xName) +{ + m_InternalModel->SetXName(xName); +}; + +QmitkFitPlotDataWidget::~QmitkFitPlotDataWidget() +{ +} + +std::string QmitkFitPlotDataWidget::StreamModelToString() const +{ + std::ostringstream stream; + stream.imbue(std::locale("C")); + + //head line + const auto colCount = this->m_InternalModel->columnCount(); + for (int col = 0; col < colCount; ++col) + { + if (col != 0) + { + stream << ","; + } + stream << m_InternalModel->headerData(col, Qt::Horizontal, Qt::DisplayRole).toString().toStdString(); + } + stream << std::endl; + + //content + const auto rowCount = this->m_InternalModel->rowCount(); + for (int row = 0; row < rowCount; ++row) + { + for (int col = 0; col < colCount; ++col) + { + QModelIndex index = this->m_InternalModel->index(row, col); + if (col != 0) + { + stream << ","; + } + stream << m_InternalModel->data(index, Qt::DisplayRole).toString().toStdString(); + } + stream << std::endl; + } + + return stream.str(); +} + +void QmitkFitPlotDataWidget::OnClipboardResultsButtonClicked() const +{ + QApplication::clipboard()->setText(QString::fromStdString(this->StreamModelToString()), QClipboard::Clipboard); + +} + +void QmitkFitPlotDataWidget::OnExportClicked() const +{ + QString fileName = QFileDialog::getSaveFileName(nullptr, tr("Save plot data to csv file")); + + if (fileName.isEmpty()) + { + QMessageBox::critical(nullptr, tr("No file selected!"), + tr("Cannot export pixel dump. Please selected a file.")); + } + else + { + std::ofstream file; + + std::ios_base::openmode iOpenFlag = std::ios_base::out | std::ios_base::trunc; + file.open(fileName.toStdString().c_str(), iOpenFlag); + + if (!file.is_open()) + { + QMessageBox::critical(nullptr, tr("Cannot create/open selected file!"), + tr("Cannot open or create the selected file. Export will be aborted. Selected file name: ") + + fileName); + return; + } + + file << this->StreamModelToString(); + + file.close(); + } + +} diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.h b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.h new file mode 100644 index 0000000000..410b1764ca --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.h @@ -0,0 +1,69 @@ +/*=================================================================== + +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 QMITK_FIT_PLOT_DATA_WIDGET_H +#define QMITK_FIT_PLOT_DATA_WIDGET_H + +#include "mitkModelFitPlotDataHelper.h" + +#include "MitkModelFitUIExports.h" + +#include "ui_QmitkFitPlotDataWidget.h" +#include +#include + +class QmitkFitPlotDataModel; + +/** +* \class QmitkFitPlotDataWidget +* Widget that displays the content of a ModelFitPlotData instance. +* In addition it allows to transfer this information as CSV into the clipboard or a file. +*/ +class MITKMODELFITUI_EXPORT QmitkFitPlotDataWidget : public QWidget +{ + Q_OBJECT + +public: + using FitVectorType = std::vector; + + QmitkFitPlotDataWidget(QWidget* parent = 0); + ~QmitkFitPlotDataWidget(); + + const mitk::ModelFitPlotData* GetPlotData() const; + const std::string& GetXName() const; + +public Q_SLOTS: + + void SetPlotData(const mitk::ModelFitPlotData* data); + void SetXName(const std::string& xName); + +protected Q_SLOTS: + void OnExportClicked() const; + + /** @brief Saves the results table to clipboard */ + void OnClipboardResultsButtonClicked() const; + +protected: + std::string StreamModelToString() const; + + QmitkFitPlotDataModel * m_InternalModel; + + Ui::QmitkFitPlotDataWidget m_Controls; + +}; + +#endif // QmitkFitPlotDataWidget_H diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.ui b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.ui new file mode 100644 index 0000000000..c52a6ef7e2 --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.ui @@ -0,0 +1,67 @@ + + + QmitkFitPlotDataWidget + + + + 0 + 0 + 723 + 460 + + + + Form + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QAbstractItemView::NoEditTriggers + + + false + + + true + + + + + + + + + Copy to clipboard + + + + + + + Save to file + + + + + + + + + + diff --git a/Modules/ModelFitUI/Qmitk/QmitkInspectionPositionWidget.cpp b/Modules/ModelFitUI/Qmitk/QmitkInspectionPositionWidget.cpp new file mode 100644 index 0000000000..515f655a0b --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkInspectionPositionWidget.cpp @@ -0,0 +1,92 @@ +/*=================================================================== + +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 "QmitkInspectionPositionWidget.h" + +QmitkInspectionPositionWidget::QmitkInspectionPositionWidget(QWidget*) +{ + this->m_Controls.setupUi(this); + + this->m_CurrentPosition.Fill(0.0); + + m_Controls.btnAdd->setEnabled(false); + + connect(m_Controls.pointlistWidget, SIGNAL(PointListChanged()), this, + SLOT(OnPointListChanged())); + connect(m_Controls.btnAdd, SIGNAL(clicked()), this, + SLOT(OnAddCurrentPositionClicked())); +} + +QmitkInspectionPositionWidget::~QmitkInspectionPositionWidget() +{ +} + +mitk::Point3D +QmitkInspectionPositionWidget:: +GetCurrentPosition() const +{ + return m_CurrentPosition; +}; + +const mitk::PointSet* +QmitkInspectionPositionWidget:: +GetPositionBookmarks() const +{ + return m_Controls.pointlistWidget->GetPointSet(); +}; + +void +QmitkInspectionPositionWidget:: +SetCurrentPosition(const mitk::Point3D& currentPos) +{ + m_CurrentPosition = currentPos; + + std::ostringstream strm; + strm.imbue(std::locale("C")); + strm << currentPos[0] << " | " << currentPos[1] << " | " << currentPos[2]; + m_Controls.lineCurrentPos->setText(QString::fromStdString(strm.str())); +}; + +void +QmitkInspectionPositionWidget:: +SetPositionBookmarkNode(mitk::DataNode *newNode) +{ + m_Controls.pointlistWidget->SetPointSetNode(newNode); + m_Controls.btnAdd->setEnabled(newNode != nullptr); +}; + +mitk::DataNode * +QmitkInspectionPositionWidget::GetPositionBookmarkNode() +{ + return m_Controls.pointlistWidget->GetPointSetNode(); +}; + +void +QmitkInspectionPositionWidget:: +OnPointListChanged() +{ + emit PositionBookmarksChanged(); +}; + +void QmitkInspectionPositionWidget:: +OnAddCurrentPositionClicked() +{ + if (m_Controls.pointlistWidget->GetPointSet()) + { + m_Controls.pointlistWidget->GetPointSet()->InsertPoint(m_CurrentPosition); + emit PositionBookmarksChanged(); + } +} \ No newline at end of file diff --git a/Modules/ModelFitUI/Qmitk/QmitkInspectionPositionWidget.h b/Modules/ModelFitUI/Qmitk/QmitkInspectionPositionWidget.h new file mode 100644 index 0000000000..e1ef9c6326 --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkInspectionPositionWidget.h @@ -0,0 +1,72 @@ +/*=================================================================== + +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 QMITK_INSPECTION_POSITION_WIDGET_H +#define QMITK_INSPECTION_POSITION_WIDGET_H + +#include "mitkModelFitInfo.h" +#include "mitkPointSet.h" + +#include "MitkModelFitUIExports.h" + +#include "ui_QmitkInspectionPositionWidget.h" +#include + + +class QmitkFitParameterModel; + +/** +* \class QmitkInspectionPositionWidget +* \brief Widget that allows manage the positions that should be used to inspect fits. +*/ +class MITKMODELFITUI_EXPORT QmitkInspectionPositionWidget : public QWidget +{ + Q_OBJECT + +public: + using FitVectorType = std::vector; + + QmitkInspectionPositionWidget(QWidget* parent = 0); + ~QmitkInspectionPositionWidget(); + + mitk::Point3D GetCurrentPosition() const; + + const mitk::PointSet* GetPositionBookmarks() const; + + /** @brief assign a point set (contained in a node of DataStorage) for observation */ + void SetPositionBookmarkNode(mitk::DataNode *newNode); + mitk::DataNode *GetPositionBookmarkNode(); + +public Q_SLOTS: + + void SetCurrentPosition(const mitk::Point3D& currentPos); + void OnPointListChanged(); + void OnAddCurrentPositionClicked(); + +Q_SIGNALS: + /** Is emitted as soon as the position bookmarks changed.*/ + void PositionBookmarksChanged(); + +protected: + + mitk::Point3D m_CurrentPosition; + + Ui::QmitkInspectionPositionWidget m_Controls; + +}; + +#endif // QmitkInspectionPositionWidget_H diff --git a/Modules/ModelFitUI/Qmitk/QmitkInspectionPositionWidget.ui b/Modules/ModelFitUI/Qmitk/QmitkInspectionPositionWidget.ui new file mode 100644 index 0000000000..aad2be89e6 --- /dev/null +++ b/Modules/ModelFitUI/Qmitk/QmitkInspectionPositionWidget.ui @@ -0,0 +1,91 @@ + + + QmitkInspectionPositionWidget + + + + 0 + 0 + 723 + 460 + + + + Form + + + + 3 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 0 + + + + + Current position: + + + + + + + Current position in world coordinates + + + + + + true + + + + + + + + + + + + Add as bookmark + + + + + + + + 0 + 0 + + + + + + + + + QmitkPointListWidget + QWidget +
QmitkPointListWidget.h
+ 1 +
+
+ + +
diff --git a/Modules/ModelFitUI/files.cmake b/Modules/ModelFitUI/files.cmake index 7b0311aa62..5f080712fa 100644 --- a/Modules/ModelFitUI/files.cmake +++ b/Modules/ModelFitUI/files.cmake @@ -1,34 +1,47 @@ set(CPP_FILES Qmitk/QmitkParameterFitBackgroundJob.cpp Qmitk/QmitkSimpleBarrierParametersDelegate.cpp Qmitk/QmitkSimpleBarrierTypeDelegate.cpp Qmitk/QmitkSimpleBarrierModel.cpp Qmitk/QmitkSimpleBarrierManagerWidget.cpp Qmitk/QmitkInitialValuesModel.cpp Qmitk/QmitkInitialValuesManagerWidget.cpp Qmitk/QmitkInitialValuesDelegate.cpp Qmitk/QmitkInitialValuesTypeDelegate.cpp + Qmitk/QmitkFitParameterModel.cpp + Qmitk/QmitkFitParameterWidget.cpp + Qmitk/QmitkInspectionPositionWidget.cpp + Qmitk/QmitkFitPlotDataWidget.cpp + Qmitk/QmitkFitPlotDataModel.cpp ) set(H_FILES ) set(TPP_FILES ) set(UI_FILES Qmitk/QmitkSimpleBarrierManagerWidget.ui Qmitk/QmitkInitialValuesManagerWidget.ui + Qmitk/QmitkFitParameterWidget.ui + Qmitk/QmitkInspectionPositionWidget.ui + Qmitk/QmitkFitPlotDataWidget.ui ) set(MOC_H_FILES Qmitk/QmitkSimpleBarrierModel.h Qmitk/QmitkParameterFitBackgroundJob.h Qmitk/QmitkSimpleBarrierParametersDelegate.h Qmitk/QmitkSimpleBarrierTypeDelegate.h Qmitk/QmitkSimpleBarrierManagerWidget.h Qmitk/QmitkInitialValuesModel.h Qmitk/QmitkInitialValuesManagerWidget.h Qmitk/QmitkInitialValuesDelegate.h Qmitk/QmitkInitialValuesTypeDelegate.h + Qmitk/QmitkFitParameterModel.h + Qmitk/QmitkFitParameterWidget.h + Qmitk/QmitkInspectionPositionWidget.h + Qmitk/QmitkFitPlotDataWidget.h + Qmitk/QmitkFitPlotDataModel.h ) diff --git a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp index 8b2b5ca27a..aed026d3d7 100644 --- a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp +++ b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.cpp @@ -1,1203 +1,904 @@ /*=================================================================== 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. ===================================================================*/ // Blueberry #include #include #include // mitk #include // Qt #include -#include #include #include #include #include "QmitkPlotWidget.h" +#include "mitkNodePredicateFunction.h" #include "mitkScalarListLookupTableProperty.h" #include "mitkModelFitConstants.h" #include "mitkExtractTimeGrid.h" #include "mitkModelGenerator.h" #include "mitkModelFitException.h" #include "mitkModelFitParameterValueExtraction.h" #include "mitkModelFitPlotDataHelper.h" #include "ModelFitInspectorView.h" -#include - const std::string ModelFitInspectorView::VIEW_ID = "org.mitk.gui.gt.fit.inspector"; const unsigned int ModelFitInspectorView::INTERPOLATION_STEPS = 100; -ModelFitInspectorView::ObserverInfo::ObserverInfo(mitk::SliceNavigationController* controller, - int observerTag, const std::string& renderWindowName, mitk::IRenderWindowPart* part) : controller(controller), observerTag(observerTag), - renderWindowName(renderWindowName), renderWindowPart(part) -{ -} - ModelFitInspectorView::ModelFitInspectorView() : m_renderWindowPart(nullptr), - m_PendingSliceChangedEvent(false), m_internalUpdateFlag(false), m_currentFit(nullptr), m_currentModelParameterizer(nullptr), m_currentModelProviderService(nullptr), m_currentSelectedTimeStep(0), m_currentSelectedNode(nullptr) { m_currentSelectedPosition.Fill(0.0); m_modelfitList.clear(); } ModelFitInspectorView::~ModelFitInspectorView() { - this->RemoveAllObservers(); -} - - -bool ModelFitInspectorView::InitObservers() -{ - - bool result = true; - - typedef QHash WindowMapType; - WindowMapType windowMap = m_renderWindowPart->GetQmitkRenderWindows(); - - auto i = windowMap.begin(); - - while (i != windowMap.end()) - { - mitk::SliceNavigationController* sliceNavController = - i.value()->GetSliceNavigationController(); - - if (sliceNavController) - { - itk::ReceptorMemberCommand::Pointer cmdSliceEvent = - itk::ReceptorMemberCommand::New(); - cmdSliceEvent->SetCallbackFunction(this, &ModelFitInspectorView::OnSliceChanged); - int tag = sliceNavController->AddObserver( - mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0), - cmdSliceEvent); - - m_ObserverMap.insert(std::make_pair(sliceNavController, ObserverInfo(sliceNavController, tag, - i.key().toStdString(), m_renderWindowPart))); - - itk::ReceptorMemberCommand::Pointer cmdTimeEvent = - itk::ReceptorMemberCommand::New(); - cmdTimeEvent->SetCallbackFunction(this, &ModelFitInspectorView::OnSliceChanged); - tag = sliceNavController->AddObserver( - mitk::SliceNavigationController::GeometryTimeEvent(nullptr, 0), - cmdTimeEvent); - - m_ObserverMap.insert(std::make_pair(sliceNavController, ObserverInfo(sliceNavController, tag, - i.key().toStdString(), m_renderWindowPart))); - - itk::MemberCommand::Pointer cmdDelEvent = - itk::MemberCommand::New(); - cmdDelEvent->SetCallbackFunction(this, - &ModelFitInspectorView::OnSliceNavigationControllerDeleted); - tag = sliceNavController->AddObserver( - itk::DeleteEvent(), cmdDelEvent); - - m_ObserverMap.insert(std::make_pair(sliceNavController, ObserverInfo(sliceNavController, tag, - i.key().toStdString(), m_renderWindowPart))); - } - - ++i; - - result = result && sliceNavController; - } - - return result; -} - -void ModelFitInspectorView::RemoveObservers(const mitk::SliceNavigationController* - deletedSlicer) -{ - - std::pair < ObserverMapType::const_iterator, ObserverMapType::const_iterator> obsRange = - m_ObserverMap.equal_range(deletedSlicer); - - for (ObserverMapType::const_iterator pos = obsRange.first; pos != obsRange.second; ++pos) - { - pos->second.controller->RemoveObserver(pos->second.observerTag); - } - - m_ObserverMap.erase(deletedSlicer); -} - -void ModelFitInspectorView::RemoveAllObservers(mitk::IRenderWindowPart* deletedPart) -{ - for (ObserverMapType::const_iterator pos = m_ObserverMap.begin(); pos != m_ObserverMap.end(); ) - { - ObserverMapType::const_iterator delPos = pos++; - - if (deletedPart == nullptr || deletedPart == delPos->second.renderWindowPart) - { - delPos->second.controller->RemoveObserver(delPos->second.observerTag); - m_ObserverMap.erase(delPos); - } - } -} - -void ModelFitInspectorView::OnSliceNavigationControllerDeleted(const itk::Object* sender, - const itk::EventObject& /*e*/) -{ - const mitk::SliceNavigationController* sendingSlicer = - dynamic_cast(sender); - - this->RemoveObservers(sendingSlicer); } void ModelFitInspectorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_renderWindowPart != renderWindowPart) { m_renderWindowPart = renderWindowPart; - - if (!InitObservers()) - { - QMessageBox::information(nullptr, "Error", "Unable to set up the event observers. The " \ - "plot will not be triggered on changing the crosshair, " \ - "position or time step."); - } } + + this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void ModelFitInspectorView::RenderWindowPartDeactivated( mitk::IRenderWindowPart* renderWindowPart) { m_renderWindowPart = nullptr; - this->RemoveAllObservers(renderWindowPart); + this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void ModelFitInspectorView::CreateQtPartControl(QWidget* parent) { - // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); + + m_SelectionServiceConnector = std::make_unique(); + //m_SelectionServiceConnector->AddPostSelectionListener(this->GetSite()->GetWorkbenchWindow()->GetSelectionService()); + + m_Controls.inputNodeSelector->SetDataStorage(GetDataStorage()); + m_Controls.inputNodeSelector->SetEmptyInfo(QString("Please select input data to be viewed.")); + m_Controls.inputNodeSelector->SetInvalidInfo(QString("No input data is selected")); + m_Controls.inputNodeSelector->SetPopUpTitel(QString("Choose 3D+t input data that should be viewed!")); + m_Controls.inputNodeSelector->SetSelectionIsOptional(false); + m_Controls.inputNodeSelector->SetSelectOnlyVisibleNodes(true); + + auto predicate = mitk::NodePredicateFunction::New( + [](const mitk::DataNode *node) { + return node && node->GetData() && node->GetData()->GetTimeSteps() > 1;}); + m_Controls.inputNodeSelector->SetNodePredicate(predicate); + + connect(m_SelectionServiceConnector.get(), &QmitkSelectionServiceConnector::ServiceSelectionChanged, m_Controls.inputNodeSelector, &QmitkSingleNodeSelectionWidget::SetCurrentSelection); + connect(m_Controls.inputNodeSelector, SIGNAL(CurrentSelectionChanged(QList)), this, SLOT(OnInputChanged(const QList&))); + + this->m_SliceChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); + connect(&m_SliceChangeListener, SIGNAL(SliceChanged()), this, SLOT(OnSliceChanged())); + connect(m_Controls.cmbFit, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFitSelectionChanged(int))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.sbFixMin, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.sbFixMax, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.labelFixMin, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.labelFixMax, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), m_Controls.btnScaleToData, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed, SIGNAL(toggled(bool)), this, SLOT(OnScaleFixedYChecked(bool))); connect(m_Controls.btnScaleToData, SIGNAL(clicked()), this, SLOT(OnScaleToDataYClicked())); connect(m_Controls.sbFixMax, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingYChanged(double))); connect(m_Controls.sbFixMin, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingYChanged(double))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.sbFixMin_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.sbFixMax_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.labelFixMin_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.labelFixMax_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), m_Controls.btnScaleToData_x, SLOT(setEnabled(bool))); connect(m_Controls.radioScaleFixed_x, SIGNAL(toggled(bool)), this, SLOT(OnScaleFixedXChecked(bool))); connect(m_Controls.btnScaleToData_x, SIGNAL(clicked()), this, SLOT(OnScaleToDataXClicked())); connect(m_Controls.sbFixMax_x, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingXChanged(double))); connect(m_Controls.sbFixMin_x, SIGNAL(valueChanged(double)), this, SLOT(OnFixedScalingXChanged(double))); + this->EnsureBookmarkPointSet(); + m_Controls.inspectionPositionWidget->SetPositionBookmarkNode(m_PositionBookmarksNode.Lock()); - connect(m_Controls.btnExport, SIGNAL(clicked()), this, SLOT(OnExportClicked())); - - // Add SIGNAL and SLOT for "Copy to clipboard" functionality - - connect(m_Controls.btnCopyResultsToClipboard, SIGNAL(clicked()), this, SLOT(OnClipboardResultsButtonClicked())); - + connect(m_Controls.inspectionPositionWidget, SIGNAL(PositionBookmarksChanged()), this, SLOT(OnPositionBookmarksChanged())); // For some reason this needs to be called to set the plot widget's minimum width to an // acceptable level (since Qwt 6). // Otherwise it tries to keep both axes equal in length, resulting in a minimum width of // 400-500px which is way too much. m_Controls.widgetPlot->GetPlot()->updateAxes(); m_Controls.cmbFit->clear(); mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); RenderWindowPartActivated(renderWindowPart); } void ModelFitInspectorView::SetFocus() { } void ModelFitInspectorView::OnScaleFixedYChecked(bool checked) { m_Controls.widgetPlot->GetPlot()->setAxisAutoScale(QwtPlot::yLeft, !checked); if (checked) { OnScaleToDataYClicked(); } m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnScaleFixedXChecked(bool checked) { m_Controls.widgetPlot->GetPlot()->setAxisAutoScale(QwtPlot::xBottom, !checked); if (checked) { OnScaleToDataXClicked(); } m_Controls.widgetPlot->GetPlot()->replot(); }; -void CheckYMinMaxFromXYData(const QmitkPlotWidget::XYDataVector& data, double& min, double& max) -{ - for (const auto & pos : data) - { - if (max < pos.second) - { - max = pos.second; - } - - if (min > pos.second) - { - min = pos.second; - } - } -} - -void CheckXMinMaxFromXYData(const QmitkPlotWidget::XYDataVector& data, double& min, double& max) -{ - for (const auto & pos : data) - { - if (max < pos.first) - { - max = pos.first; - } - - if (min > pos.first) - { - min = pos.first; - } - } -} - void ModelFitInspectorView::OnScaleToDataYClicked() { - double max = itk::NumericTraits::NonpositiveMin(); - double min = itk::NumericTraits::max(); - - CheckYMinMaxFromXYData(this->m_ImagePlotCurve.values, min, max); - CheckYMinMaxFromXYData(this->m_ModelPlotCurve.values, min, max); - - for (mitk::PlotDataCurveCollection::const_iterator pos = this->m_InputDataPlotCurves.begin(); - pos != this->m_InputDataPlotCurves.end(); ++pos) - { - CheckYMinMaxFromXYData(pos->second.values, min, max); - } + auto minmax = this->m_PlotCurves.GetYMinMax(); - min -= abs(min) * 0.01; - max += abs(max) * 0.01; + auto min = minmax.first - abs(minmax.first) * 0.01; + auto max = minmax.second + abs(minmax.second) * 0.01; - m_Controls.sbFixMax->setValue(max); m_Controls.sbFixMin->setValue(min); - + m_Controls.sbFixMax->setValue(max); }; void ModelFitInspectorView::OnScaleToDataXClicked() { - double max = itk::NumericTraits::NonpositiveMin(); - double min = itk::NumericTraits::max(); + auto minmax = this->m_PlotCurves.GetXMinMax(); - CheckXMinMaxFromXYData(this->m_ImagePlotCurve.values, min, max); - CheckXMinMaxFromXYData(this->m_ModelPlotCurve.values, min, max); + auto min = minmax.first - abs(minmax.first) * 0.01; + auto max = minmax.second + abs(minmax.second) * 0.01; - for (mitk::PlotDataCurveCollection::const_iterator pos = this->m_InputDataPlotCurves.begin(); - pos != this->m_InputDataPlotCurves.end(); ++pos) - { - CheckXMinMaxFromXYData(pos->second.values, min, max); - } - - min -= abs(min) * 0.01; - max += abs(max) * 0.01; - - m_Controls.sbFixMax_x->setValue(max); m_Controls.sbFixMin_x->setValue(min); - + m_Controls.sbFixMax_x->setValue(max); }; void ModelFitInspectorView::OnFixedScalingYChanged(double /*value*/) { m_Controls.widgetPlot->GetPlot()->setAxisScale(QwtPlot::yLeft, m_Controls.sbFixMin->value(), m_Controls.sbFixMax->value()); m_Controls.widgetPlot->GetPlot()->replot(); }; void ModelFitInspectorView::OnFixedScalingXChanged(double /*value*/) { m_Controls.widgetPlot->GetPlot()->setAxisScale(QwtPlot::xBottom, m_Controls.sbFixMin_x->value(), m_Controls.sbFixMax_x->value()); m_Controls.widgetPlot->GetPlot()->replot(); }; -void ModelFitInspectorView::OnExportClicked() +int ModelFitInspectorView::ActualizeFitSelectionWidget() { - QString fileName = QFileDialog::getSaveFileName(nullptr, tr("Save File")); + mitk::NodeUIDType selectedFitUD = ""; + bool isModelFitNode = this->m_currentSelectedNode->GetData()->GetPropertyList()->GetStringProperty( + mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD); - if (fileName.isEmpty()) - { - QMessageBox::critical(nullptr, tr("No file selected!"), - tr("Cannot export pixel dump. Please selected a file.")); - } - else - { - std::ofstream file; + mitk::DataStorage::Pointer storage = this->GetDataStorage(); - std::ios_base::openmode iOpenFlag = std::ios_base::out | std::ios_base::trunc; - file.open(fileName.toStdString().c_str(), iOpenFlag); + mitk::modelFit::NodeUIDSetType fitUIDs = mitk::modelFit::GetFitUIDsOfNode( + this->m_currentSelectedNode, storage); - if (!file.is_open()) - { - QMessageBox::critical(nullptr, tr("Cannot create/open selected file!"), - tr("Cannot open or create the selected file. Export will be aborted. Selected file name: ") + - fileName); - return; - } + this->m_modelfitList.clear(); + this->m_Controls.cmbFit->clear(); - for (int i = 0; i < m_Controls.tableData->columnCount(); ++i) + for (const auto & fitUID : fitUIDs) + { + mitk::modelFit::ModelFitInfo::ConstPointer info = mitk::modelFit::CreateFitInfoFromNode(fitUID, + storage).GetPointer(); + + if (info.IsNotNull()) { - file << m_Controls.tableData->horizontalHeaderItem(i)->text().toStdString(); + this->m_modelfitList.insert(std::make_pair(info->uid, info)); - if (i < m_Controls.tableData->columnCount() - 1) - { - file << ","; - } + QVariant data(info->uid.c_str()); + m_Controls.cmbFit->addItem(QString::fromStdString(info->modelName), data); + } + else + { + MITK_ERROR << + "Was not able to extract model fit information from storage. Node properties in storage may be invalid. Failed fit UID:" + << fitUID; } + } - file << std::endl; + int cmbIndex = 0; - for (int row = 0; row < m_Controls.tableData->rowCount(); ++row) - { - for (int i = 0; i < m_Controls.tableData->columnCount(); ++i) - { - file << m_Controls.tableData->item(row, i)->text().toStdString(); + if (m_modelfitList.empty()) + { + cmbIndex = -1; + }; - if (i < m_Controls.tableData->columnCount() - 1) - { - file << ","; - } - } + if (isModelFitNode) + { + //model was selected, thus select this one in combobox + QVariant data(selectedFitUD.c_str()); + cmbIndex = m_Controls.cmbFit->findData(data); - file << std::endl; + if (cmbIndex == -1) + { + MITK_WARN << + "Model fit Inspector in invalid state. Selected fit seems to be not avaible in plugin selection. Failed fit UID:" + << selectedFitUD; } + }; - file.close(); - } + m_Controls.cmbFit->setCurrentIndex(cmbIndex); + + return cmbIndex; } -void ModelFitInspectorView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, - const QList& nodes) +void ModelFitInspectorView::OnInputChanged(const QList& nodes) { if (nodes.size() > 0) { if (nodes.front() != this->m_currentSelectedNode) { m_internalUpdateFlag = true; this->m_currentSelectedNode = nodes.front(); - mitk::DataStorage::Pointer storage = this->GetDataStorage(); - mitk::NodeUIDType selectedFitUD = ""; bool isModelFitNode = this->m_currentSelectedNode->GetData()->GetPropertyList()->GetStringProperty( mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD); if (isModelFitNode) { this->m_currentSelectedNode = this->GetParentNode(this->m_currentSelectedNode); } - mitk::modelFit::NodeUIDSetType fitUIDs = mitk::modelFit::GetFitUIDsOfNode( - this->m_currentSelectedNode, storage); - - this->m_modelfitList.clear(); - this->m_Controls.cmbFit->clear(); - - for (const auto & fitUID : fitUIDs) - { - mitk::modelFit::ModelFitInfo::ConstPointer info = mitk::modelFit::CreateFitInfoFromNode(fitUID, - storage).GetPointer(); - - if (info.IsNotNull()) - { - this->m_modelfitList.insert(std::make_pair(info->uid, info)); - - QVariant data(info->uid.c_str()); - m_Controls.cmbFit->addItem(QString::fromStdString(info->modelName), data); - } - else - { - MITK_ERROR << - "Was not able to extract model fit information from storage. Node properties in storage may be invalid. Failed fit UID:" - << fitUID; - } - } - - int cmbIndex = 0; - - if (m_modelfitList.empty()) - { - cmbIndex = -1; - }; - - if (isModelFitNode) - { - //model was selected, thus select this one in combobox - QVariant data(selectedFitUD.c_str()); - cmbIndex = m_Controls.cmbFit->findData(data); - - if (cmbIndex == -1) - { - MITK_WARN << - "Model fit Inspector in invalid state. Selected fit seems to be not avaible in plugin selection. Failed fit UID:" - << selectedFitUD; - } - }; - - m_Controls.cmbFit->setCurrentIndex(cmbIndex); + auto cmbIndex = ActualizeFitSelectionWidget(); m_internalUpdateFlag = false; m_selectedNodeTime.Modified(); if (cmbIndex == -1) { //only raw 4D data selected. Just update plots for current position m_currentFit = nullptr; m_currentFitTime.Modified(); - OnSliceChangedDelayed(); + OnSliceChanged(); } else { //refresh fit selection (and implicitly update plots) OnFitSelectionChanged(cmbIndex); } } } else { if (this->m_currentSelectedNode.IsNotNull()) { m_internalUpdateFlag = true; this->m_currentSelectedNode = nullptr; this->m_currentFit = nullptr; this->m_modelfitList.clear(); this->m_Controls.cmbFit->clear(); m_internalUpdateFlag = false; m_selectedNodeTime.Modified(); OnFitSelectionChanged(0); } } } mitk::DataNode::ConstPointer ModelFitInspectorView::GetParentNode(mitk::DataNode::ConstPointer node) { if (node.IsNotNull()) { mitk::DataStorage::SetOfObjects::ConstPointer parentNodeList = GetDataStorage()->GetSources(node); if (parentNodeList->size() > 0) { return parentNodeList->front().GetPointer(); } } return mitk::DataNode::ConstPointer(); } void ModelFitInspectorView::ValidateAndSetCurrentPosition() { mitk::Point3D currentSelectedPosition = GetRenderWindowPart()->GetSelectedPosition(nullptr); unsigned int currentSelectedTimestep = m_renderWindowPart->GetTimeNavigationController()->GetTime()-> GetPos(); if (m_currentSelectedPosition != currentSelectedPosition || m_currentSelectedTimeStep != currentSelectedTimestep || m_selectedNodeTime > m_currentPositionTime) { //the current position has been changed or the selected node has been changed since the last position validation -> check position m_currentSelectedPosition = currentSelectedPosition; m_currentSelectedTimeStep = currentSelectedTimestep; m_currentPositionTime.Modified(); m_validSelectedPosition = false; mitk::Image::Pointer inputImage = this->GetCurrentInputImage(); if (inputImage.IsNull()) { return; } mitk::BaseGeometry::Pointer geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep( m_currentSelectedTimeStep); // check for invalid time step if (geometry.IsNull()) { geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep(0); } if (geometry.IsNull()) { return; } m_validSelectedPosition = geometry->IsInside(m_currentSelectedPosition); } } mitk::Image::Pointer ModelFitInspectorView::GetCurrentInputImage() const { mitk::Image::Pointer result = nullptr; if (this->m_currentFit.IsNotNull()) { result = m_currentFit->inputImage; } else if (this->m_currentSelectedNode.IsNotNull()) { result = dynamic_cast(this->m_currentSelectedNode->GetData()); if (result.IsNotNull() && result->GetTimeSteps() <= 1) { //if the image is not dynamic, we can't use it. result = nullptr; } } return result; }; const mitk::ModelBase::TimeGridType ModelFitInspectorView::GetCurrentTimeGrid() const { if (m_currentModelProviderService && m_currentFit.IsNotNull()) { return m_currentModelProviderService->GetVariableGrid(m_currentFit); } else { //fall back if there is no model provider we assume to use the normal time grid. return ExtractTimeGrid(GetCurrentInputImage()); } }; -void ModelFitInspectorView::OnSliceChanged(const itk::EventObject&) +void ModelFitInspectorView::OnSliceChanged() { - // Taken from QmitkStdMultiWidget::HandleCrosshairPositionEvent(). - // Since there are always 3 events arriving (one for each render window) every time the slice - // or time changes, the slot OnSliceChangedDelayed is triggered - and only if it hasn't been - // triggered yet - so it is only executed once for every slice/time change. - if (!m_PendingSliceChangedEvent) - { - m_PendingSliceChangedEvent = true; - QTimer::singleShot(0, this, SLOT(OnSliceChangedDelayed())); - } -} - -void ModelFitInspectorView::OnSliceChangedDelayed() -{ - m_PendingSliceChangedEvent = false; - ValidateAndSetCurrentPosition(); m_Controls.widgetPlot->setEnabled(m_validSelectedPosition); if (m_currentSelectedNode.IsNotNull()) { + m_Controls.inspectionPositionWidget->SetCurrentPosition(m_currentSelectedPosition); + if (RefreshPlotData()) { RenderPlot(); RenderFitInfo(); } } } +void ModelFitInspectorView::OnPositionBookmarksChanged() +{ + if (RefreshPlotData()) + { + RenderPlot(); + RenderFitInfo(); + } +} + /** Super sample passed time grid with the factor INTERPOLATION_STEPS and interpolates linear in between.*/ mitk::ModelBase::TimeGridType GenerateInterpolatedTimeGrid(const mitk::ModelBase::TimeGridType& grid, const unsigned int interpolation_steps = 100) { unsigned int origGridSize = grid.size(); mitk::ModelBase::TimeGridType interpolatedTimeGrid(((origGridSize - 1) * interpolation_steps) + 1); for (unsigned int t = 0; t < origGridSize - 1; ++t) { double delta = (grid[t + 1] - grid[t]) / interpolation_steps; for (unsigned int i = 0; i < interpolation_steps; ++i) { interpolatedTimeGrid[(t * interpolation_steps) + i] = grid[t] + i * delta; } } interpolatedTimeGrid[interpolatedTimeGrid.size() - 1] = grid[grid.size() - 1]; return interpolatedTimeGrid; }; void ModelFitInspectorView::OnFitSelectionChanged(int index) { if (!m_internalUpdateFlag) { MITK_DEBUG << "selected fit index: " << index; std::string uid = ""; if (m_Controls.cmbFit->count() > index) { uid = m_Controls.cmbFit->itemData(index).toString().toStdString(); } mitk::modelFit::ModelFitInfo::ConstPointer newFit = nullptr; ModelFitInfoListType::iterator finding = m_modelfitList.find(uid); if (finding != m_modelfitList.end()) { newFit = finding->second; } if (m_currentFit != newFit) { m_currentModelParameterizer = nullptr; m_currentModelProviderService = nullptr; if (newFit.IsNotNull()) { m_currentModelParameterizer = mitk::ModelGenerator::GenerateModelParameterizer(*newFit); m_currentModelProviderService = mitk::ModelGenerator::GetProviderService(newFit->functionClassID); } m_currentFit = newFit; m_currentFitTime.Modified(); - OnSliceChangedDelayed(); + auto name = m_currentFit->xAxisName; + if (!m_currentFit->xAxisUnit.empty()) + { + name += " [" + m_currentFit->xAxisUnit + "]"; + } + m_Controls.plotDataWidget->SetXName(name); + + OnSliceChanged(); } } } +mitk::PlotDataCurveCollection::Pointer ModelFitInspectorView::RefreshPlotDataCurveCollection(const mitk::Point3D& position, + const mitk::Image* input, const mitk::modelFit::ModelFitInfo* fitInfo, + const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer) +{ + mitk::PlotDataCurveCollection::Pointer result = mitk::PlotDataCurveCollection::New(); + + //sample curve + if (input) + { + result->InsertElement(mitk::MODEL_FIT_PLOT_SAMPLE_NAME(), GenerateImageSamplePlotData(position, input, timeGrid)); + } + + //model signal curve + if (fitInfo) + { + // Interpolate time grid (x values) so the curve looks smooth + const mitk::ModelBase::TimeGridType interpolatedTimeGrid = GenerateInterpolatedTimeGrid(timeGrid, INTERPOLATION_STEPS); + auto hires_curve = mitk::GenerateModelSignalPlotData(position, fitInfo, interpolatedTimeGrid, parameterizer); + result->InsertElement(mitk::MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME(), hires_curve); + auto curve = mitk::GenerateModelSignalPlotData(position, fitInfo, timeGrid, parameterizer); + result->InsertElement(mitk::MODEL_FIT_PLOT_SIGNAL_NAME(), curve); + } + + return result; +}; + bool ModelFitInspectorView::RefreshPlotData() { bool changed = false; if (m_currentSelectedNode.IsNull()) { - this->m_ImagePlotCurve.Reset(); - this->m_ModelPlotCurve.Reset(); - this->m_InputDataPlotCurves.clear(); + this->m_PlotCurves = mitk::ModelFitPlotData(); changed = m_selectedNodeTime > m_lastRefreshTime; m_lastRefreshTime.Modified(); } else { assert(GetRenderWindowPart() != NULL); const mitk::Image* input = GetCurrentInputImage(); const mitk::ModelBase::TimeGridType timeGrid = GetCurrentTimeGrid(); - //image data curve - if (m_selectedNodeTime > m_lastRefreshTime || m_currentFitTime > m_lastRefreshTime - || m_currentPositionTime > m_lastRefreshTime) + if (m_currentFitTime > m_lastRefreshTime || m_currentPositionTime > m_lastRefreshTime) { - m_ImagePlotCurve.Reset(); - - if (input && m_validSelectedPosition) + if (m_validSelectedPosition) { - m_ImagePlotCurve = GenerateImageSamplePlotData(m_currentSelectedPosition, input, timeGrid); + m_PlotCurves.currentPositionPlots = RefreshPlotDataCurveCollection(m_currentSelectedPosition,input,m_currentFit, timeGrid, m_currentModelParameterizer); + } + else + { + m_PlotCurves.currentPositionPlots = mitk::PlotDataCurveCollection::New(); } changed = true; } - //model curve - if (m_currentFitTime > m_lastRefreshTime || m_currentPositionTime > m_lastRefreshTime) + auto bookmarks = m_PositionBookmarks.Lock(); + if (bookmarks.IsNotNull()) { - m_ModelPlotCurve.Reset(); - - if (m_currentFit.IsNotNull() && m_validSelectedPosition) + if (m_currentFitTime > m_lastRefreshTime || bookmarks->GetMTime() > m_lastRefreshTime) { + m_PlotCurves.positionalPlots.clear(); - // Interpolate time grid (x values) so the curve looks smooth - const mitk::ModelBase::TimeGridType interpolatedTimeGrid = GenerateInterpolatedTimeGrid(timeGrid, INTERPOLATION_STEPS); + auto endIter = bookmarks->End(); + for (auto iter = bookmarks->Begin(); iter != endIter; iter++) + { + auto collection = RefreshPlotDataCurveCollection(iter.Value(), input, m_currentFit, timeGrid, m_currentModelParameterizer); + m_PlotCurves.positionalPlots.emplace(iter.Value(), collection); + } - m_ModelPlotCurve = GenerateModelSignalPlotData(m_currentSelectedPosition, m_currentFit, interpolatedTimeGrid, m_currentModelParameterizer); + changed = true; } - - changed = true; + } + else + { + m_PlotCurves.positionalPlots.clear(); } // input data curve if (m_currentFitTime > m_lastRefreshTime) { - m_InputDataPlotCurves.clear(); + m_PlotCurves.staticPlots->clear(); if (m_currentFit.IsNotNull()) { - m_InputDataPlotCurves = GenerateAdditionalModelFitPlotData(m_currentSelectedPosition, m_currentFit, timeGrid); + m_PlotCurves.staticPlots = GenerateAdditionalModelFitPlotData(m_currentSelectedPosition, m_currentFit, timeGrid); } changed = true; } - if (m_selectedNodeTime > m_lastRefreshTime || m_currentFitTime > m_lastRefreshTime - || m_currentPositionTime > m_lastRefreshTime) + if (changed) { - InitDataTable(); + m_Controls.plotDataWidget->SetPlotData(&m_PlotCurves); } m_lastRefreshTime.Modified(); } return changed; } void ModelFitInspectorView::RenderFitInfo() { - assert(m_renderWindowPart != nullptr); - - // configure fit information - - if (m_currentFit.IsNull()) - { - m_Controls.lFitType->setText(""); - m_Controls.lFitUID->setText(""); - m_Controls.lModelName->setText(""); - m_Controls.lModelType->setText(""); - } - else - { - m_Controls.lFitType->setText(QString::fromStdString(m_currentFit->fitType)); - m_Controls.lFitUID->setText(QString::fromStdString(m_currentFit->uid)); - m_Controls.lModelName->setText(QString::fromStdString(m_currentFit->modelName)); - m_Controls.lModelType->setText(QString::fromStdString(m_currentFit->modelType)); - } - - // print results - std::stringstream infoOutput; - - m_Controls.grpParams->setVisible(false); - m_Controls.groupSettings->setVisible(false); - m_Controls.tableResults->clearContents(); - - if (m_currentFit.IsNull()) - { - infoOutput << "No fit selected. Only raw image data is plotted."; - } - else if (!m_validSelectedPosition) - { - infoOutput << + assert(m_renderWindowPart != nullptr); + + // configure fit information + + if (m_currentFit.IsNull()) + { + m_Controls.lFitType->setText(""); + m_Controls.lFitUID->setText(""); + m_Controls.lModelName->setText(""); + m_Controls.lModelType->setText(""); + } + else + { + m_Controls.lFitType->setText(QString::fromStdString(m_currentFit->fitType)); + m_Controls.lFitUID->setText(QString::fromStdString(m_currentFit->uid)); + m_Controls.lModelName->setText(QString::fromStdString(m_currentFit->modelName)); + m_Controls.lModelType->setText(QString::fromStdString(m_currentFit->modelType)); + } + + // print results + std::stringstream infoOutput; + + m_Controls.fitParametersWidget->setVisible(false); + m_Controls.groupSettings->setVisible(false); + + if (m_currentFit.IsNull()) + { + infoOutput << "No fit selected. Only raw image data is plotted."; + } + else if (!m_validSelectedPosition) + { + infoOutput << "Current position is outside of the input image of the selected fit.\nInspector is deactivated."; - } - else - { - m_Controls.grpParams->setVisible(true); - // Set sorting to false. Wait with sorting until all parameters are set in the table. - - m_Controls.tableResults->setSortingEnabled(false); - m_Controls.tableResults->setRowCount(m_currentFit->GetParameters().size() + - m_currentFit->staticParamMap.Size()); - - unsigned int timestep = m_renderWindowPart->GetTimeNavigationController()->GetTime()->GetPos(); - unsigned int rowIndex = 0; + } + else + { + m_Controls.fitParametersWidget->setVisible(true); + m_Controls.fitParametersWidget->setFits({ m_currentFit }); - assert(GetRenderWindowPart() != NULL); + m_Controls.fitParametersWidget->setPositionBookmarks(m_PositionBookmarks.Lock()); + m_Controls.fitParametersWidget->setCurrentPosition(m_currentSelectedPosition); + } + + // configure data table + m_Controls.tableInputData->clearContents(); + + if (m_currentFit.IsNull()) + { + infoOutput << "No fit selected. Only raw image data is plotted."; + } + else + { + m_Controls.groupSettings->setVisible(true); + m_Controls.tableInputData->setRowCount(m_PlotCurves.staticPlots->size()); + + unsigned int rowIndex = 0; + + for (mitk::PlotDataCurveCollection::const_iterator pos = m_PlotCurves.staticPlots->begin(); + pos != m_PlotCurves.staticPlots->end(); ++pos, ++rowIndex) + { + QColor dataColor; + + if (pos->first == "ROI") + { + dataColor = QColor(0, 190, 0); + } + else + { + //Use HSV schema of QColor to calculate a different color depending on the + //number of already existing free iso lines. + dataColor.setHsv(((rowIndex + 1) * 85) % 360, 255, 255); + } + + QTableWidgetItem* newItem = new QTableWidgetItem(QString::fromStdString(pos->first)); + m_Controls.tableInputData->setItem(rowIndex, 0, newItem); + newItem = new QTableWidgetItem(); + newItem->setBackgroundColor(dataColor); + m_Controls.tableInputData->setItem(rowIndex, 1, newItem); + } + } + + m_Controls.lInfo->setText(QString::fromStdString(infoOutput.str())); +} + +void ModelFitInspectorView::RenderPlotCurve(const mitk::PlotDataCurveCollection* curveCollection, const QColor& sampleColor, const QColor& signalColor, const std::string& posString) +{ + auto sampleCurve = mitk::ModelFitPlotData::GetSamplePlot(curveCollection); + if (sampleCurve) + { + std::string name = mitk::MODEL_FIT_PLOT_SAMPLE_NAME() + posString; + unsigned int curveId = m_Controls.widgetPlot->InsertCurve(name.c_str()); + m_Controls.widgetPlot->SetCurveData(curveId, sampleCurve->GetValues()); + m_Controls.widgetPlot->SetCurvePen(curveId, QPen(Qt::NoPen)); + + // QwtSymbol needs to passed as a real pointer from MITK v2013.09.0 on + // (QwtPlotCurve deletes it on destruction and assignment). + QwtSymbol* dataSymbol = new QwtSymbol(QwtSymbol::Diamond, sampleColor, sampleColor, QSize(8, 8)); + m_Controls.widgetPlot->SetCurveSymbol(curveId, dataSymbol); + + // Again, there is no way to set a curve's legend attributes via QmitkPlotWidget so this + // gets unnecessarily complicated. + QwtPlotCurve* measurementCurve = dynamic_cast(m_Controls.widgetPlot-> + GetPlot()->itemList(QwtPlotItem::Rtti_PlotCurve).back()); + measurementCurve->setLegendAttribute(QwtPlotCurve::LegendShowSymbol); + measurementCurve->setLegendIconSize(QSize(8, 8)); + } + + //draw model curve + auto signalCurve = mitk::ModelFitPlotData::GetInterpolatedSignalPlot(curveCollection); + if (signalCurve) + { + std::string name = mitk::MODEL_FIT_PLOT_SIGNAL_NAME() + posString; + QPen pen; + pen.setColor(signalColor); + pen.setWidth(2); + unsigned int curveId = m_Controls.widgetPlot->InsertCurve(name.c_str()); + m_Controls.widgetPlot->SetCurveData(curveId, signalCurve->GetValues()); + m_Controls.widgetPlot->SetCurvePen(curveId, pen); + + // Manually set the legend attribute to use the symbol as the legend icon and alter its + // size. Otherwise it would revert to default which is drawing a square which is the color + // of the curve's pen, so in this case none which defaults to black. + // Unfortunately, QmitkPlotWidget offers no way to set the legend attribute and icon size so + // this looks a bit hacky. + QwtPlotCurve* fitCurve = dynamic_cast(m_Controls.widgetPlot->GetPlot()-> + itemList(QwtPlotItem::Rtti_PlotCurve).back()); + fitCurve->setLegendAttribute(QwtPlotCurve::LegendShowLine); + } - for (mitk::modelFit::ModelFitInfo::ConstIterType paramIter = m_currentFit->GetParameters().begin(); - paramIter != m_currentFit->GetParameters().end(); - ++paramIter, ++rowIndex) - { - mitk::modelFit::Parameter::ConstPointer p = - static_cast(*paramIter); - double value = ReadVoxel(p->image, m_currentSelectedPosition, timestep); - - std::string paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER(); - - if (p->type == mitk::modelFit::Parameter::DerivedType) - { - paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER(); - } - else if (p->type == mitk::modelFit::Parameter::CriterionType) - { - paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_CRITERION(); - } - else if (p->type == mitk::modelFit::Parameter::EvaluationType) - { - paramType = mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_EVALUATION_PARAMETER(); - } - - QTableWidgetItem* newItem = new QTableWidgetItem(QString::fromStdString(p->name)); - m_Controls.tableResults->setItem(rowIndex, 0, newItem); - newItem = new QTableWidgetItem(QString::number(value)); - m_Controls.tableResults->setItem(rowIndex, 1, newItem); - newItem = new QTableWidgetItem(QString::fromStdString(paramType)); - m_Controls.tableResults->setItem(rowIndex, 2, newItem); - } - - foreach(const mitk::modelFit::StaticParameterMap::StaticParameterType& var, - m_currentFit->staticParamMap) - { - const std::string& name = var.first; - const mitk::modelFit::StaticParameterMap::ValueType& list = var.second; - - QTableWidgetItem* newItem = new QTableWidgetItem(QString::fromStdString(name)); - m_Controls.tableResults->setItem(rowIndex, 0, newItem); - newItem = new QTableWidgetItem(QString::number(list.front())); - m_Controls.tableResults->setItem(rowIndex, 1, newItem); - newItem = new QTableWidgetItem(tr("static")); - m_Controls.tableResults->setItem(rowIndex, 2, newItem); - ++rowIndex; - } - // Here the sorting function is enabled and the table entries are sorted alphabetically in descending order. - // This way the parameters are always displayed in the same order. - - m_Controls.tableResults->setSortingEnabled(true); - m_Controls.tableResults->sortItems(0, Qt::DescendingOrder); - - - } - - // configure data table - m_Controls.tableInputData->clearContents(); - - if (m_currentFit.IsNull()) - { - infoOutput << "No fit selected. Only raw image data is plotted."; - } - else - { - m_Controls.groupSettings->setVisible(true); - m_Controls.tableInputData->setRowCount(m_InputDataPlotCurves.size()); - - unsigned int rowIndex = 0; - - for (mitk::PlotDataCurveCollection::const_iterator pos = m_InputDataPlotCurves.begin(); - pos != m_InputDataPlotCurves.end(); ++pos, ++rowIndex) - { - QColor dataColor; - - if (pos->first == "ROI") - { - dataColor = QColor(0, 190, 0); - } - else - { - //Use HSV schema of QColor to calculate a different color depending on the - //number of already existing free iso lines. - dataColor.setHsv(((rowIndex + 1) * 85) % 360, 255, 255); - } - - QTableWidgetItem* newItem = new QTableWidgetItem(QString::fromStdString(pos->first)); - m_Controls.tableInputData->setItem(rowIndex, 0, newItem); - newItem = new QTableWidgetItem(); - newItem->setBackgroundColor(dataColor); - m_Controls.tableInputData->setItem(rowIndex, 1, newItem); - } - } - - m_Controls.lInfo->setText(QString::fromStdString(infoOutput.str())); } void ModelFitInspectorView::RenderPlot() { m_Controls.widgetPlot->Clear(); std::string xAxis = "Time [s]"; std::string yAxis = "Intensity"; std::string plotTitle = "Raw data plot: no data"; if (m_currentSelectedNode.IsNotNull()) { plotTitle = "Raw data plot: " + m_currentSelectedNode->GetName(); } if (m_currentFit.IsNotNull()) { plotTitle = m_currentFit->modelName.c_str(); xAxis = m_currentFit->xAxisName; if (!m_currentFit->xAxisUnit.empty()) { xAxis += " [" + m_currentFit->xAxisUnit + "]"; } yAxis = m_currentFit->yAxisName; if (!m_currentFit->yAxisUnit.empty()) { yAxis += " [" + m_currentFit->yAxisUnit + "]"; } } m_Controls.widgetPlot->SetAxisTitle(QwtPlot::xBottom, xAxis.c_str()); m_Controls.widgetPlot->SetAxisTitle(QwtPlot::yLeft, yAxis.c_str()); m_Controls.widgetPlot->SetPlotTitle(plotTitle.c_str()); - // Draw input data points + // Draw static curves unsigned int colorIndex = 0; - for (mitk::PlotDataCurveCollection::const_iterator pos = m_InputDataPlotCurves.begin(); - pos != m_InputDataPlotCurves.end(); ++pos) + for (mitk::PlotDataCurveCollection::const_iterator pos = m_PlotCurves.staticPlots->begin(); + pos != m_PlotCurves.staticPlots->end(); ++pos) { QColor dataColor; unsigned int curveId = m_Controls.widgetPlot->InsertCurve(pos->first.c_str()); - m_Controls.widgetPlot->SetCurveData(curveId, pos->second.values); + m_Controls.widgetPlot->SetCurveData(curveId, pos->second->GetValues()); if (pos->first == "ROI") { dataColor = QColor(0, 190, 0); QPen pen; pen.setColor(dataColor); pen.setStyle(Qt::SolidLine); m_Controls.widgetPlot->SetCurvePen(curveId, pen); } else { //Use HSV schema of QColor to calculate a different color depending on the - //number of already existing free iso lines. + //number of already existing curves. dataColor.setHsv((++colorIndex * 85) % 360, 255, 150); m_Controls.widgetPlot->SetCurvePen(curveId, QPen(Qt::NoPen)); } // QwtSymbol needs to passed as a real pointer from MITK v2013.09.0 on // (QwtPlotCurve deletes it on destruction and assignment). QwtSymbol* dataSymbol = new QwtSymbol(QwtSymbol::Triangle, dataColor, dataColor, QSize(8, 8)); m_Controls.widgetPlot->SetCurveSymbol(curveId, dataSymbol); // Again, there is no way to set a curve's legend attributes via QmitkPlotWidget so this // gets unnecessarily complicated. QwtPlotCurve* measurementCurve = dynamic_cast(m_Controls.widgetPlot-> GetPlot()->itemList(QwtPlotItem::Rtti_PlotCurve).back()); measurementCurve->setLegendAttribute(QwtPlotCurve::LegendShowSymbol); measurementCurve->setLegendIconSize(QSize(8, 8)); } - // Draw image points - if (!m_ImagePlotCurve.values.empty()) + // Draw positional curves + for (const auto& posIter : this->m_PlotCurves.positionalPlots) { - unsigned int curveId = m_Controls.widgetPlot->InsertCurve("measurement"); - m_Controls.widgetPlot->SetCurveData(curveId, m_ImagePlotCurve.values); - m_Controls.widgetPlot->SetCurvePen(curveId, QPen(Qt::NoPen)); - - // QwtSymbol needs to passed as a real pointer from MITK v2013.09.0 on - // (QwtPlotCurve deletes it on destruction and assignment). - QwtSymbol* redSymbol = new QwtSymbol(QwtSymbol::Diamond, QColor(Qt::red), QColor(Qt::red), - QSize(8, 8)); - m_Controls.widgetPlot->SetCurveSymbol(curveId, redSymbol); - - // Again, there is no way to set a curve's legend attributes via QmitkPlotWidget so this - // gets unnecessarily complicated. - QwtPlotCurve* measurementCurve = dynamic_cast(m_Controls.widgetPlot-> - GetPlot()->itemList(QwtPlotItem::Rtti_PlotCurve).back()); - measurementCurve->setLegendAttribute(QwtPlotCurve::LegendShowSymbol); - measurementCurve->setLegendIconSize(QSize(8, 8)); - - // Highlight the current time step - unsigned int timestep = m_renderWindowPart->GetTimeNavigationController()->GetTime()-> - GetPos(); - - if (timestep < m_ImagePlotCurve.values.size()) - { - QwtSymbol* blueSymbol = new QwtSymbol(QwtSymbol::Diamond, QColor(Qt::blue), - QColor(Qt::blue), QSize(8, 8)); - QwtPlotMarker* marker = new QwtPlotMarker(); - marker->setSymbol(blueSymbol); - marker->setValue(m_ImagePlotCurve.values[timestep].first, m_ImagePlotCurve.values[timestep].second); - marker->attach(m_Controls.widgetPlot->GetPlot()); - } + QColor dataColor; + dataColor.setHsv((++colorIndex * 85) % 360, 255, 150); + std::ostringstream plotPosStrm; + plotPosStrm.imbue(std::locale("C")); + plotPosStrm << " @ " << std::setprecision(3) << "(" << posIter.first[0] << "|" << posIter.first[1] << "|" << posIter.first[2] << ")"; - for (QmitkPlotWidget::XYDataVector::size_type i = 0; i < m_ImagePlotCurve.values.size(); ++i) - { - m_Controls.tableData->item(i, 1)->setText(QString::number(m_ImagePlotCurve.values[i].second)); - } + this->RenderPlotCurve(posIter.second, dataColor, dataColor, plotPosStrm.str()); } - //draw model curve - if (!m_ModelPlotCurve.values.empty()) - { - QPen pen; - pen.setColor(QColor(Qt::black)); - pen.setWidth(2); - unsigned int curveId = m_Controls.widgetPlot->InsertCurve("fit"); - m_Controls.widgetPlot->SetCurveData(curveId, m_ModelPlotCurve.values); - m_Controls.widgetPlot->SetCurvePen(curveId, pen); - - // Manually set the legend attribute to use the symbol as the legend icon and alter its - // size. Otherwise it would revert to default which is drawing a square which is the color - // of the curve's pen, so in this case none which defaults to black. - // Unfortunately, QmitkPlotWidget offers no way to set the legend attribute and icon size so - // this looks a bit hacky. - QwtPlotCurve* fitCurve = dynamic_cast(m_Controls.widgetPlot->GetPlot()-> - itemList(QwtPlotItem::Rtti_PlotCurve).back()); - fitCurve->setLegendAttribute(QwtPlotCurve::LegendShowLine); - - for (QmitkPlotWidget::XYDataVector::size_type i = 0; i < m_ImagePlotCurve.values.size(); ++i) - { - m_Controls.tableData->item(i, 2)->setText(QString::number(m_ModelPlotCurve.values[i * - INTERPOLATION_STEPS].second)); - } - } + // Draw current pos curve + this->RenderPlotCurve(m_PlotCurves.currentPositionPlots, QColor(Qt::red), QColor(Qt::black), ""); QwtLegend* legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); m_Controls.widgetPlot->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.widgetPlot->Replot(); } -void ModelFitInspectorView::InitDataTable() +void ModelFitInspectorView::EnsureBookmarkPointSet() { - QStringList headers; - headers.push_back(QString("Time")); - headers.push_back(QString("Input image")); - - if (!this->m_ModelPlotCurve.values.empty()) - { - headers.push_back(QString("Model")); - } - - for (mitk::PlotDataCurveCollection::const_iterator pos = this->m_InputDataPlotCurves.begin(); - pos != this->m_InputDataPlotCurves.end(); ++pos) - { - headers.push_back(QString::fromStdString(pos->first)); - } - - m_Controls.tableData->setRowCount(m_ImagePlotCurve.values.size()); - m_Controls.tableData->setColumnCount(headers.size()); - - m_Controls.tableData->setHorizontalHeaderLabels(headers); - m_Controls.tableData->horizontalHeader()->resizeSections(QHeaderView::ResizeToContents); - - for (QmitkPlotWidget::XYDataVector::size_type i = 0; i < m_ImagePlotCurve.values.size(); ++i) + if (m_PositionBookmarks.IsExpired() || m_PositionBookmarksNode.IsExpired()) { - int column = 0; - QTableWidgetItem* newItem = new QTableWidgetItem(QString::number(m_ImagePlotCurve.values[i].first)); - m_Controls.tableData->setItem(i, column++, newItem); - newItem = new QTableWidgetItem(QString::number(m_ImagePlotCurve.values[i].second)); - m_Controls.tableData->setItem(i, column++, newItem); + const char* nodeName = "org.mitk.gui.qt.fit.inspector.positions"; + mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode(nodeName); - if (!m_ModelPlotCurve.values.empty()) + if (!node) { - newItem = new QTableWidgetItem(QString::number(m_ModelPlotCurve.values[i * INTERPOLATION_STEPS].second)); - m_Controls.tableData->setItem(i, column++, newItem); + node = mitk::DataNode::New(); + node->SetName(nodeName); + node->SetBoolProperty("helper object", true); + this->GetDataStorage()->Add(node); } + m_PositionBookmarksNode = node; - for (mitk::PlotDataCurveCollection::const_iterator pos = this->m_InputDataPlotCurves.begin(); - pos != this->m_InputDataPlotCurves.end(); ++pos, ++column) + mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); + if (pointSet.IsNull()) { - newItem = new QTableWidgetItem(QString::number(pos->second.values[i].second)); - m_Controls.tableData->setItem(i, column, newItem); + pointSet = mitk::PointSet::New(); + node->SetData(pointSet); } - } -} - -void ModelFitInspectorView::OnClipboardResultsButtonClicked() -{ - QLocale tempLocal; - QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); - std::stringstream infoOutput; - QString clipboard; - QVector< QVector > resultsTable; - if (m_currentFit.IsNotNull()) - { - int rowNumber = m_currentFit->GetParameters().size() + m_currentFit->staticParamMap.Size(); - - // set headline as in results table: "Name", "Value", "Type" - QStringList headlineText; - QVector headline; - // Create Headline - headlineText << "Name" << "Value" << "Type"; - - for (int i = 0; i < headlineText.size(); i++) - { - headline.append(headlineText.at(i)); - } - resultsTable.append(headline); - - // fill table with values from the displayed results table - for (int i = 0; i < rowNumber; i++) - { - QVector rowValues; - for (int j = 0; j < 3; j++) - { - rowValues.append((m_Controls.tableResults->item(i, j)->text())); - - } - resultsTable.append(rowValues); - - } - - // Create output string for clipboard - for (int i = 0; i < resultsTable.size(); i++) - { - for (int t = 0; t < resultsTable.at(i).size(); t++) - { - clipboard.append(resultsTable.at(i).at(t)); - clipboard.append("\t"); - } - clipboard.append("\n"); - } - QApplication::clipboard()->setText(clipboard, QClipboard::Clipboard); - } - else - { - infoOutput << "Results table is empty."; - } - QLocale::setDefault(tempLocal); -} + m_PositionBookmarks = pointSet; + } +} \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.h b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.h index bca597b2a4..9b104fcb53 100644 --- a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.h +++ b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorView.h @@ -1,227 +1,201 @@ /*=================================================================== 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 ModelFitInspectorView_h #define ModelFitInspectorView_h // Blueberry //#include #include // mitk #include #include +#include "QmitkSliceNavigationListener.h" #include "mitkModelFitStaticParameterMap.h" #include "mitkModelParameterizerBase.h" #include "mitkModelFitInfo.h" #include "mitkIModelFitProvider.h" #include "mitkModelFitPlotDataHelper.h" +#include "QmitkSelectionServiceConnector.h" +#include "QmitkFitParameterModel.h" // Qt #include "ui_ModelFitInspectorViewControls.h" /** * @brief View class defining the UI part of the ModelFitInspector plug-in. */ class ModelFitInspectorView : public QmitkAbstractView, public mitk::IRenderWindowPartListener { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: ModelFitInspectorView(); ~ModelFitInspectorView(); static const std::string VIEW_ID; protected slots: + void OnSliceChanged(); + /** * @brief Triggered when the selection of the "Modelfit" combo box changes. * Sets the selected fit as the current one. * @param index The index (in the combo box) of the selected item. */ void OnFitSelectionChanged(int index); - /** - * @brief Triggered when the voxel or time step selection changes. - * Calculates the curve and points for the current fit if the visualization is running. - */ - void OnSliceChangedDelayed(); + void OnInputChanged(const QList& nodes); + + void OnPositionBookmarksChanged(); /** Triggered when the selection of "fixed" y axis scaling changes*/ void OnScaleFixedYChecked(bool checked); void OnScaleToDataYClicked(); void OnFixedScalingYChanged(double value); /** Triggered when the selection of "fixed" x axis scaling changes*/ void OnScaleFixedXChecked(bool checked); void OnScaleToDataXClicked(); void OnFixedScalingXChanged(double value); - void OnExportClicked(); - - - /** @brief Saves the results table to clipboard */ - void OnClipboardResultsButtonClicked(); - - protected: virtual void CreateQtPartControl(QWidget* parent); virtual void SetFocus(); - /** @brief called by QmitkFunctionality when DataManager's selection has changed */ - virtual void OnSelectionChanged(berry::IWorkbenchPart::Pointer source, - const QList& nodes); - - /** @brief Calls OnSliceChangedDelayed so the event isn't triggered multiple times. */ - void OnSliceChanged(const itk::EventObject& e); - - void OnSliceNavigationControllerDeleted(const itk::Object* sender, const itk::EventObject& /*e*/); + /** Helper that actualizes the fit selection widget and returns the index of the currently selected + * fit.*/ + int ActualizeFitSelectionWidget(); virtual void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart); virtual void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart); - /** Initializes and sets the observers that are used to monitor changes in the selected position - or time point in order to actualize the view.h*/ - bool InitObservers(); - void RemoveObservers(const mitk::SliceNavigationController* deletedSlicer); - /** Removes all observers of the deletedPart. If null pointer is passed all observers will be removed.*/ - void RemoveAllObservers(mitk::IRenderWindowPart* deletedPart = NULL); - /** * @brief Calculates the curve data using the current fit's model and parameterizer. * @param position Indicating the point in the input image for which the model curve should be calculated. * @return A vector of points for the curve data. */ QmitkPlotWidget::XYDataVector CalcCurveFromModel(const mitk::Point3D& position); /** * @brief Calculates the curve data using the current fit's function string. * @param timeGrid The time grid containing the (interpolated) x values for the curve data. * @return A vector of points for the curve data. */ QmitkPlotWidget::XYDataVector CalcCurveFromFunction(const mitk::Point3D& position, const mitk::ModelBase::TimeGridType& timeGrid); /** * @brief Returns the parent node of the given node if it exists. * @param node The node whose parent node should be returned. * @return The parent node of the given node or NULL if it doesn't exist. */ mitk::DataNode::ConstPointer GetParentNode(mitk::DataNode::ConstPointer node); /** Sets m_currentSelectedPosition to the current selection and validates if this position is valid * for the input image of the currently selected fit. If it is valid, m_validSelectedPosition is set to true. * If the fit, his input image or geometry is not specified, it will also handled as invalid.*/ void ValidateAndSetCurrentPosition(); /** Returns the current input image. If a current fit is set it will be its input image. * Otherwise it will be the image stored in the currently selected node. If the node is not set, contains no image * or the image is not 4D, NULL will be returned.*/ mitk::Image::Pointer GetCurrentInputImage() const; /** Returns the variable/time grid of the GetCurrentInputImage(). If a model fit is selected its provider will be used to get the correct grid, otherwise just a simple time grid will be extracted.*/ const mitk::ModelBase::TimeGridType GetCurrentTimeGrid() const; Ui::ModelFitInspectorViewControls m_Controls; mitk::IRenderWindowPart* m_renderWindowPart; - // Needed for observing the events for when a slice or time step is changed. - bool m_PendingSliceChangedEvent; - - /**Helper structure to manage the registered observer events.*/ - struct ObserverInfo - { - mitk::SliceNavigationController* controller; - int observerTag; - std::string renderWindowName; - mitk::IRenderWindowPart* renderWindowPart; - - ObserverInfo(mitk::SliceNavigationController* controller, int observerTag, - const std::string& renderWindowName, mitk::IRenderWindowPart* part); - }; - - typedef std::multimap ObserverMapType; - ObserverMapType m_ObserverMap; - /** @brief Is a visualization currently running? */ bool m_internalUpdateFlag; /** @brief List of modelfits currently in the data manager */ typedef std::map ModelFitInfoListType; ModelFitInfoListType m_modelfitList; /** @brief The currently selected modelfit */ mitk::modelFit::ModelFitInfo::ConstPointer m_currentFit; /** @brief Pointer to the instance of the model parameterizer for the current fit */ mitk::ModelParameterizerBase::Pointer m_currentModelParameterizer; mitk::IModelFitProvider* m_currentModelProviderService; /** @brief currently valid selected position in the inspector*/ mitk::Point3D m_currentSelectedPosition; /** @brief indicates if the currently selected position is valid for the currently selected fit. * This it is within the input image */ bool m_validSelectedPosition; /** @brief currently selected time step of the selected node for the visualization logic*/ unsigned int m_currentSelectedTimeStep; /** @brief currently selected node for the visualization logic*/ mitk::DataNode::ConstPointer m_currentSelectedNode; + mitk::WeakPointer m_PositionBookmarksNode; + mitk::WeakPointer m_PositionBookmarks; + /** @brief Number of interpolation steps between two x values */ static const unsigned int INTERPOLATION_STEPS; /*************************************/ /* Members for visualizing the model */ itk::TimeStamp m_selectedNodeTime; itk::TimeStamp m_currentFitTime; itk::TimeStamp m_currentPositionTime; itk::TimeStamp m_lastRefreshTime; - mitk::PlotDataCurve m_ImagePlotCurve; - mitk::PlotDataCurve m_ModelPlotCurve; - mitk::PlotDataCurveCollection m_InputDataPlotCurves; + mitk::ModelFitPlotData m_PlotCurves; + + std::unique_ptr m_SelectionServiceConnector; + QmitkFitParameterModel* m_FitParameterModel; + + QmitkSliceNavigationListener m_SliceChangeListener; /** Check and updates the plot data if needed. * @return indicates if something was refreshed (true)*/ bool RefreshPlotData(); - void RenderPlot(); - void RenderFitInfo(); + void RenderPlotCurve(const mitk::PlotDataCurveCollection* curveCollection, const QColor& sampleColor, const QColor& signalColor, const std::string& posString); - /** (re)initializes the headers of the data table*/ - void InitDataTable(); + void RenderFitInfo(); + void EnsureBookmarkPointSet(); + static mitk::PlotDataCurveCollection::Pointer RefreshPlotDataCurveCollection(const mitk::Point3D& position, + const mitk::Image* input, const mitk::modelFit::ModelFitInfo* fitInfo, + const mitk::ModelBase::TimeGridType& timeGrid, mitk::ModelParameterizerBase* parameterizer); }; #endif // ModelFitInspectorView_h diff --git a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorViewControls.ui b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorViewControls.ui index fb53fa3ea7..6976906ecb 100644 --- a/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorViewControls.ui +++ b/Plugins/org.mitk.gui.qt.fit.inspector/src/internal/ModelFitInspectorViewControls.ui @@ -1,784 +1,732 @@ ModelFitInspectorViewControls 0 0 - 349 - 843 + 721 + 852 0 0 QmitkTemplate 5 5 5 5 5 - - - Modelfit: + + + 6 - - - - + + 6 + + + + + Modelfit: + + + + + + + + + + + + + Input + + + + true 0 0 0 400 true - 1 + 0 Fit info 50 16777215 Fit type 0 0 Fit uid: 0 0 Model name: Model type: - Results + Fit parameter - Information of the fit + Parameter used for the fit. 3 5 5 5 5 - - - Parameters - - - - 5 - - - 5 - - - 5 - - - 5 - - - 5 - - - - - - 8 - - - - QAbstractItemView::NoEditTriggers - - - false - - - QAbstractItemView::NoSelection - - - true - - - 110 - - - false - - - true - - - true - - - false - - - 20 - - - 20 - - - - Name - - - - - - - - Value - - - - - - - - Type - - - - - - - - - - - - - Copy to Clipboard - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - + + + + + + + Inspection positions + + + + 3 + + + 5 + + + 5 + + + 5 + + + 5 + + + View settings Settings for the visualization of fittings + + 3 + + + 5 + + + 5 + + + 5 + + + 5 + X-axis scaling: true automatic true true fixed false false 40 16777215 min: false -9999.000000000000000 9999.000000000000000 Qt::Horizontal 40 20 false 40 16777215 max: false -9999.000000000000000 9999.000000000000000 Qt::Horizontal 40 20 false Sets the min and max to the current range of data +-10% Scale to data Y-axis scaling: true automatic true true fixed false false 40 16777215 min: false -9999.000000000000000 9999.000000000000000 Qt::Horizontal 40 20 false 40 16777215 max: false -9999.000000000000000 9999.000000000000000 Qt::Horizontal 40 20 false Sets the min and max to the current range of data +-10% Scale to data Additional input data visibility: 5 5 5 5 5 8 150 false true false 20 20 Name Color Qt::Vertical 20 40 - Data / Export + Plot data export - Export options for the current data/fitting information + Display and export option for the current data samples, model signals and additinal fitting informations of the plot. - 5 + 3 5 5 5 5 - - - - 0 - 0 - - - - Export to csv file - - - - - + 80 0 - - true - - - 45 - - - true - QmitkPlotWidget QWidget
QmitkPlotWidget.h
+ + QmitkSingleNodeSelectionWidget + QWidget +
QmitkSingleNodeSelectionWidget.h
+ 1 +
+ + QmitkFitParameterWidget + QWidget +
QmitkFitParameterWidget.h
+
+ + QmitkInspectionPositionWidget + QWidget +
QmitkInspectionPositionWidget.h
+
+ + QmitkFitPlotDataWidget + QWidget +
QmitkFitPlotDataWidget.h
+
radioScaleFixed clicked(bool) sbFixMin setEnabled(bool) 66 528 86 559 radioScaleFixed clicked(bool) sbFixMax setEnabled(bool) 208 535 195 559 radioScaleFixed clicked(bool) btnScaleToData setEnabled(bool) 299 530 292 557
diff --git a/Plugins/org.mitk.gui.qt.matchpoint.evaluator/src/internal/QmitkMatchPointRegistrationEvaluator.h b/Plugins/org.mitk.gui.qt.matchpoint.evaluator/src/internal/QmitkMatchPointRegistrationEvaluator.h index 725cbbaf72..9127e8bf1a 100644 --- a/Plugins/org.mitk.gui.qt.matchpoint.evaluator/src/internal/QmitkMatchPointRegistrationEvaluator.h +++ b/Plugins/org.mitk.gui.qt.matchpoint.evaluator/src/internal/QmitkMatchPointRegistrationEvaluator.h @@ -1,123 +1,123 @@ /*=================================================================== 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 __Q_MITK_MATCHPOINT_REGISTRATION_EVALUATOR_H #define __Q_MITK_MATCHPOINT_REGISTRATION_EVALUATOR_H #include #include #include #include "ui_QmitkMatchPointRegistrationEvaluator.h" /*! \brief QmitkMatchPointRegistrationEvaluator \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. \sa QmitkFunctionality \ingroup ${plugin_target}_internal */ class QmitkMatchPointRegistrationEvaluator : public QmitkAbstractView, public mitk::IRenderWindowPartListener { // this is needed for all Qt objects that should have a Qt meta-object // (everything that derives from QObject and wants to have signal/slots) Q_OBJECT public: static const std::string VIEW_ID; /** * Creates smartpointer typedefs */ berryObjectMacro(QmitkMatchPointRegistrationEvaluator) QmitkMatchPointRegistrationEvaluator(); ~QmitkMatchPointRegistrationEvaluator(); virtual void CreateQtPartControl(QWidget *parent); - protected slots: +protected slots: /// \brief Called when the user clicks the GUI button void OnEvalBtnPushed(); void OnStopBtnPushed(); void OnSettingsChanged(mitk::DataNode*); - void OnSliceChanged(); + void OnSliceChanged(); protected: /// \brief called by QmitkFunctionality when DataManager's selection has changed virtual void OnSelectionChanged( berry::IWorkbenchPart::Pointer source, const QList& nodes) override; virtual void NodeRemoved(const mitk::DataNode* node) override; virtual void SetFocus(); virtual void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart); virtual void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart); Ui::MatchPointRegistrationEvaluatorControls m_Controls; private: QWidget *m_Parent; void Error(QString msg); /** Methods returns a list of all eval nodes in the data manager.*/ QList GetEvalNodes(); /** * Checks if appropriated nodes are selected in the data manager. If nodes are selected, * they are stored m_spSelectedRegNode, m_spSelectedInputNode and m_spSelectedRefNode. * They are also checked for vadility and stored in m_ValidInput,... . * It also sets the info lables accordingly.*/ void CheckInputs(); /** * Updates the state of controls regarding to selected eval object.*/ void ConfigureControls(); mitk::DataNode::Pointer m_selectedEvalNode; QmitkSliceNavigationListener m_SliceChangeListener; itk::TimeStamp m_selectedNodeTime; itk::TimeStamp m_currentPositionTime; bool m_activeEvaluation; bool m_autoMoving; bool m_autoTarget; /** @brief currently valid selected position in the inspector*/ mitk::Point3D m_currentSelectedPosition; /** @brief indicates if the currently selected position is valid for the currently selected fit. * This it is within the input image */ unsigned int m_currentSelectedTimeStep; mitk::DataNode::Pointer m_spSelectedRegNode; mitk::DataNode::Pointer m_spSelectedMovingNode; mitk::DataNode::Pointer m_spSelectedTargetNode; static const std::string HelperNodeName; }; #endif // MatchPoint_h