diff --git a/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h b/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h index 10c27f2275..c9d1c61e1f 100644 --- a/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h +++ b/Modules/ModelFit/include/mitkModelFitPlotDataHelper.h @@ -1,195 +1,171 @@ /*=================================================================== 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 "mitkPointSet.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; using PlotDataValues = std::vector>; /** Simple helper structure that represents a curve for plots and its generation time*/ class MITKMODELFIT_EXPORT PlotDataCurve : public ::itk::Object { public: mitkClassMacroItkParent(PlotDataCurve, itk::Object); itkFactorylessNewMacro(Self); using ValuesType = PlotDataValues; virtual void SetValues(const ValuesType& _arg); virtual void SetValues(ValuesType&& _arg); 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 = itk::MapContainer; /** 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; + using PositionalCurveCollection = std::pair; + 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); + /** Helper function that generates a humand readable name for the passed value of a positional collection map.*/ + static std::string GetPositionalCollectionName(const PositionalCollectionMap::value_type& mapValue); + const PlotDataCurveCollection* GetPositionalPlot(const mitk::Point3D& point) const; + const PlotDataCurveCollection* GetPositionalPlot(mitk::PointSet::PointIdentifier id) 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::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::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::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 cfc165f4cb..196cae80f4 100644 --- a/Modules/ModelFit/src/Common/mitkModelFitPlotDataHelper.cpp +++ b/Modules/ModelFit/src/Common/mitkModelFitPlotDataHelper.cpp @@ -1,425 +1,447 @@ /*=================================================================== 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" const std::string mitk::MODEL_FIT_PLOT_SAMPLE_NAME() { return "Sample"; }; const std::string mitk::MODEL_FIT_PLOT_SIGNAL_NAME() { return "Signal"; } const std::string mitk::MODEL_FIT_PLOT_INTERPOLATED_SIGNAL_NAME() { 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->m_Values = rhs.m_Values; this->SetTimeStamp(rhs.GetTimeStamp()); return *this; }; mitk::PlotDataCurve& mitk::PlotDataCurve::operator=(PlotDataCurve&& rhs) noexcept { this->m_Values = std::move(rhs.m_Values); this->SetTimeStamp(rhs.GetTimeStamp()); return *this; }; void mitk::PlotDataCurve::Reset() { 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; }; +std::string mitk::ModelFitPlotData::GetPositionalCollectionName(const PositionalCollectionMap::value_type& mapValue) +{ + std::ostringstream nameStrm; + nameStrm.imbue(std::locale("C")); + nameStrm << "Pos " << mapValue.first << std::endl << std::setprecision(3) << "(" << mapValue.second.first[0] << "|" << mapValue.second.first[1] << "|" << mapValue.second.first[2] << ")"; + return nameStrm.str(); +}; + + const mitk::PlotDataCurveCollection* mitk::ModelFitPlotData::GetPositionalPlot(const mitk::Point3D& point) const { - auto iter = this->positionalPlots.find(point); + auto predicate = [point](const PositionalCollectionMap::value_type& value) {return value.second.first == point; }; + + auto iter = std::find_if(std::begin(this->positionalPlots), std::end(this->positionalPlots), predicate); if (iter != positionalPlots.end()) { - return iter->second.GetPointer(); + return iter->second.second.GetPointer(); } return nullptr; }; +const mitk::PlotDataCurveCollection* mitk::ModelFitPlotData::GetPositionalPlot(mitk::PointSet::PointIdentifier id) const +{ + auto iter = this->positionalPlots.find(id); + if (iter != positionalPlots.end()) + { + return iter->second.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)) + for (const auto& plot : *(posCollection.second.second)) { - auto sample = this->GetSamplePlot(posCollection.second); + auto sample = this->GetSamplePlot(posCollection.second.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)) + for (const auto& plot : *(posCollection.second.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::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::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); values.emplace_back(std::make_pair(x, y)); } result->SetValues(std::move(values)); return result; } /** Helper function that generates the curve based on the model specified by the fit info.*/ 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::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]; values.emplace_back(std::make_pair(x, y)); } result->SetValues(std::move(values)); return result; } 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."; } 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::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::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::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]; values.emplace_back(std::make_pair(x, y)); } pointData->SetValues(std::move(values)); result->CastToSTLContainer().emplace(additionalInput.first, std::move(pointData)); } } return result; } 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::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); values.emplace_back(std::make_pair(x, y)); } result->SetValues(std::move(values)); return result; } diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.cpp b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.cpp index 31fa0a98de..58c676392f 100644 --- a/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.cpp +++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.cpp @@ -1,163 +1,171 @@ /*=================================================================== 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 SanatizeString(std::string str) +{ + std::replace(std::begin(str), std::end(str), ',', ' '); + std::replace(std::begin(str), std::end(str), '\n', ' '); + std::replace(std::begin(str), std::end(str), '\r', ' '); + return str; +} + 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 << SanatizeString(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 << SanatizeString(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 index 9b773f0df0..8584c08110 100644 --- a/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h +++ b/Modules/ModelFitUI/Qmitk/QmitkFitParameterWidget.h @@ -1,77 +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 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; }; +/** Helper function to sanatize strings before used in a csv export + Moved to header in order to be reusabel for other ModelFitUI widgets.*/ +std::string SanatizeString(std::string str); + #endif // QmitkFitParameterWidget_H diff --git a/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataModel.cpp b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataModel.cpp index 5dbe46210e..cc0fa6bc2a 100644 --- a/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataModel.cpp +++ b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataModel.cpp @@ -1,284 +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 +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()) + for (auto aCurve : collection.second.second->CastToSTLContainer()) { if (curve == aCurve.second.GetPointer()) { - return std::make_pair(true, result); + return std::make_pair(true, collection); } } } - return std::make_pair(false, result); + return std::make_pair(false, mitk::ModelFitPlotData::PositionalCollectionMap::value_type()); } 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)) + size += coll.second.second->size(); + if (mitk::ModelFitPlotData::GetInterpolatedSignalPlot(coll.second.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) - { + if (col == 1) + { /*the x axis is needed so just get the first signal.*/ + col = 0; + } + else if (col > 1) + { /*a normal signal is requested, correct the column index to ignore the timestep and timepoint column*/ 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)) + if (GetCurveFromCollection(collection.second.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] << ")"; + nameStrm << " @ " << mitk::ModelFitPlotData::GetPositionalCollectionName(pointFinding.second); } 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 index efad1e3f30..80d22308c1 100644 --- a/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataModel.h +++ b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataModel.h @@ -1,65 +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; + 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 index b213ac4f43..637b4e06a0 100644 --- a/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.cpp +++ b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.cpp @@ -1,138 +1,139 @@ /*=================================================================== 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 "QmitkFitParameterWidget.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 << SanatizeString(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 << SanatizeString(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.ui b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.ui index c52a6ef7e2..ab83fa5350 100644 --- a/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.ui +++ b/Modules/ModelFitUI/Qmitk/QmitkFitPlotDataWidget.ui @@ -1,67 +1,70 @@ QmitkFitPlotDataWidget 0 0 723 460 Form 3 0 0 0 0 QAbstractItemView::NoEditTriggers false true + + false + Copy to clipboard Save to file 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 aed026d3d7..2a25f3b757 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,904 +1,913 @@ /*=================================================================== 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 "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" const std::string ModelFitInspectorView::VIEW_ID = "org.mitk.gui.gt.fit.inspector"; -const unsigned int ModelFitInspectorView::INTERPOLATION_STEPS = 100; +const unsigned int ModelFitInspectorView::INTERPOLATION_STEPS = 10; +const std::string DEFAULT_X_AXIS = "Time [s]"; ModelFitInspectorView::ModelFitInspectorView() : m_renderWindowPart(nullptr), 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() { } void ModelFitInspectorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_renderWindowPart != renderWindowPart) { m_renderWindowPart = renderWindowPart; } this->m_SliceChangeListener.RenderWindowPartActivated(renderWindowPart); } void ModelFitInspectorView::RenderWindowPartDeactivated( mitk::IRenderWindowPart* renderWindowPart) { m_renderWindowPart = nullptr; this->m_SliceChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void ModelFitInspectorView::CreateQtPartControl(QWidget* parent) { m_Controls.setupUi(parent); m_SelectionServiceConnector = std::make_unique(); - //m_SelectionServiceConnector->AddPostSelectionListener(this->GetSite()->GetWorkbenchWindow()->GetSelectionService()); + 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.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::NodeRemoved(const mitk::DataNode* node) +{ + if (node == this->m_currentSelectedNode) + { + QmitkSingleNodeSelectionWidget::NodeList emptylist; + this->m_Controls.inputNodeSelector->SetCurrentSelection(emptylist); + } +} + 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 ModelFitInspectorView::OnScaleToDataYClicked() { auto minmax = this->m_PlotCurves.GetYMinMax(); auto min = minmax.first - abs(minmax.first) * 0.01; auto max = minmax.second + abs(minmax.second) * 0.01; m_Controls.sbFixMin->setValue(min); m_Controls.sbFixMax->setValue(max); }; void ModelFitInspectorView::OnScaleToDataXClicked() { auto minmax = this->m_PlotCurves.GetXMinMax(); auto min = minmax.first - abs(minmax.first) * 0.01; auto max = minmax.second + abs(minmax.second) * 0.01; 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(); }; int ModelFitInspectorView::ActualizeFitSelectionWidget() { mitk::NodeUIDType selectedFitUD = ""; bool isModelFitNode = this->m_currentSelectedNode->GetData()->GetPropertyList()->GetStringProperty( mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD); mitk::DataStorage::Pointer storage = this->GetDataStorage(); 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); return cmbIndex; } 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::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); } 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(); OnSliceChanged(); + m_Controls.plotDataWidget->SetXName(DEFAULT_X_AXIS); } 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); + RefreshPlotData(); + m_Controls.plotDataWidget->SetPlotData(&(this->m_PlotCurves)); + m_Controls.fitParametersWidget->setFits(QmitkFitParameterModel::FitVectorType()); + RenderPlot(); } } } 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() { ValidateAndSetCurrentPosition(); m_Controls.widgetPlot->setEnabled(m_validSelectedPosition); if (m_currentSelectedNode.IsNotNull()) { m_Controls.inspectionPositionWidget->SetCurrentPosition(m_currentSelectedPosition); if (RefreshPlotData()) { RenderPlot(); + m_Controls.plotDataWidget->SetPlotData(&m_PlotCurves); RenderFitInfo(); } } } void ModelFitInspectorView::OnPositionBookmarksChanged() { if (RefreshPlotData()) { RenderPlot(); + m_Controls.plotDataWidget->SetPlotData(&m_PlotCurves); 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(); 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_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(); if (m_currentFitTime > m_lastRefreshTime || m_currentPositionTime > m_lastRefreshTime) { if (m_validSelectedPosition) { m_PlotCurves.currentPositionPlots = RefreshPlotDataCurveCollection(m_currentSelectedPosition,input,m_currentFit, timeGrid, m_currentModelParameterizer); } else { m_PlotCurves.currentPositionPlots = mitk::PlotDataCurveCollection::New(); } changed = true; } auto bookmarks = m_PositionBookmarks.Lock(); if (bookmarks.IsNotNull()) { if (m_currentFitTime > m_lastRefreshTime || bookmarks->GetMTime() > m_lastRefreshTime) { m_PlotCurves.positionalPlots.clear(); 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_PlotCurves.positionalPlots.emplace(iter.Index(), std::make_pair(iter.Value(), collection)); } changed = true; } } else { m_PlotCurves.positionalPlots.clear(); } // input data curve if (m_currentFitTime > m_lastRefreshTime) { m_PlotCurves.staticPlots->clear(); if (m_currentFit.IsNotNull()) { m_PlotCurves.staticPlots = GenerateAdditionalModelFitPlotData(m_currentSelectedPosition, m_currentFit, timeGrid); } changed = true; } - if (changed) - { - 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.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.fitParametersWidget->setVisible(true); m_Controls.fitParametersWidget->setFits({ m_currentFit }); 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); } } void ModelFitInspectorView::RenderPlot() { m_Controls.widgetPlot->Clear(); - std::string xAxis = "Time [s]"; + std::string xAxis = DEFAULT_X_AXIS; 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 static curves unsigned int colorIndex = 0; 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->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 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 positional curves for (const auto& posIter : this->m_PlotCurves.positionalPlots) { 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] << ")"; - this->RenderPlotCurve(posIter.second, dataColor, dataColor, plotPosStrm.str()); + this->RenderPlotCurve(posIter.second.second, dataColor, dataColor, " @ "+mitk::ModelFitPlotData::GetPositionalCollectionName(posIter)); } // 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::EnsureBookmarkPointSet() { if (m_PositionBookmarks.IsExpired() || m_PositionBookmarksNode.IsExpired()) { const char* nodeName = "org.mitk.gui.qt.fit.inspector.positions"; mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode(nodeName); if (!node) { node = mitk::DataNode::New(); node->SetName(nodeName); node->SetBoolProperty("helper object", true); this->GetDataStorage()->Add(node); } m_PositionBookmarksNode = node; mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNull()) { pointSet = mitk::PointSet::New(); node->SetData(pointSet); } 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 9b104fcb53..d62523f17e 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,201 +1,203 @@ /*=================================================================== 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); 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); protected: - virtual void CreateQtPartControl(QWidget* parent); + virtual void CreateQtPartControl(QWidget* parent) override; - virtual void SetFocus(); + virtual void SetFocus() override; + + virtual void NodeRemoved(const mitk::DataNode* node) override; /** 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); /** * @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; /** @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::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 RenderPlotCurve(const mitk::PlotDataCurveCollection* curveCollection, const QColor& sampleColor, const QColor& signalColor, const std::string& posString); 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