diff --git a/Modules/QtOverlays/QmitkTextOverlay.cpp b/Modules/QtOverlays/QmitkTextOverlay.cpp index ebdd4d7005..512e849693 100644 --- a/Modules/QtOverlays/QmitkTextOverlay.cpp +++ b/Modules/QtOverlays/QmitkTextOverlay.cpp @@ -1,159 +1,159 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkTextOverlay.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkPropertyList.h" #include QmitkTextOverlay::QmitkTextOverlay(const char *id) : QmitkOverlay(id), m_ObservedProperty(nullptr), m_ObserverTag(0) { m_Widget = m_Label = new QLabel(); QmitkOverlay::AddDropShadow(m_Widget); } QmitkTextOverlay::~QmitkTextOverlay() { m_PropertyList->GetProperty(m_Id)->RemoveObserver(m_ObserverTag); } void QmitkTextOverlay::GenerateData(mitk::PropertyList::Pointer pl) { if (pl.IsNull()) return; m_PropertyList = pl; if (m_PropertyList.IsNotNull()) { this->SetupCallback(m_PropertyList->GetProperty(m_Id)); this->UpdateFontProperties(pl); this->UpdateDisplayedTextFromProperties(); } else { MITK_ERROR << "invalid propList"; } } void QmitkTextOverlay::UpdateDisplayedTextFromProperties() { std::string text; if (m_PropertyList.IsNull() || !m_PropertyList->GetStringProperty(m_Id, text)) { MITK_DEBUG << "Property " << m_Id << " could not be found"; } if (text != m_Label->text().toStdString()) { m_Label->setText(text.c_str()); m_Label->repaint(); } } void QmitkTextOverlay::UpdateFontProperties(mitk::PropertyList::Pointer pl) { if (pl.IsNull()) return; mitk::PropertyList::Pointer propertyList = pl; QPalette palette = QPalette(); QFont font = QFont(); // get the desired color of the textOverlays mitk::ColorProperty::Pointer colorProp = dynamic_cast(propertyList->GetProperty("overlay.color")); if (colorProp.IsNull()) { colorProp = mitk::ColorProperty::New(127.0, 196.0, 232.0); } mitk::Color color = colorProp->GetColor(); - palette.setColor(QPalette::Foreground, QColor(color[0], color[1], color[2], 255)); + palette.setColor(QPalette::WindowText, QColor(color[0], color[1], color[2], 255)); palette.setColor(QPalette::Window, Qt::transparent); m_Label->setPalette(palette); // get the desired opacity of the overlays // mitk::FloatProperty::Pointer opacityProperty = // dynamic_cast( propertyList->GetProperty( "overlay.opacity" ) ); // if ( opacityProperty.IsNull() ) //{ // m_Label->setWindowOpacity( 1 ); //} // else //{ // m_Label->setWindowOpacity( opacityProperty->GetValue() ); //} // set the desired font-size of the overlays int fontSize = 0; if (!propertyList->GetIntProperty("overlay.fontSize", fontSize)) { fontSize = 9; } font.setPointSize(fontSize); bool useKerning = false; if (!propertyList->GetBoolProperty("overlay.kerning", useKerning)) { useKerning = true; } font.setKerning(useKerning); std::string fontFamily = ""; if (!propertyList->GetStringProperty("overlay.fontFamily", fontFamily)) { fontFamily = "Verdana"; } font.setFamily(QString(fontFamily.c_str())); m_Label->setFont(font); } void QmitkTextOverlay::SetupCallback(mitk::BaseProperty::Pointer prop) { if (m_ObservedProperty != prop && m_ObserverTag == 0) { if (prop.IsNotNull()) { if (m_ObservedProperty.IsNotNull()) { m_ObservedProperty->RemoveObserver(m_ObserverTag); } typedef itk::SimpleMemberCommand MemberCommandType; MemberCommandType::Pointer propModifiedCommand; propModifiedCommand = MemberCommandType::New(); propModifiedCommand->SetCallbackFunction(this, &QmitkTextOverlay::UpdateDisplayedTextFromProperties); m_ObserverTag = prop->AddObserver(itk::ModifiedEvent(), propModifiedCommand); } m_ObservedProperty = prop; } else { MITK_DEBUG << "invalid property"; } } QSize QmitkTextOverlay::GetNeededSize() { QFont font = m_Label->font(); QFontMetrics fm(font); return fm.size(Qt::TextSingleLine, m_Label->text()); } diff --git a/Modules/QtWidgets/src/QmitkSliderLevelWindowWidget.cpp b/Modules/QtWidgets/src/QmitkSliderLevelWindowWidget.cpp index ef8fb9e4d0..61eaf48f09 100644 --- a/Modules/QtWidgets/src/QmitkSliderLevelWindowWidget.cpp +++ b/Modules/QtWidgets/src/QmitkSliderLevelWindowWidget.cpp @@ -1,550 +1,550 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include // mitk core #include // mitk qt widgets #include // qt #include #include #include #include // itk #include // c++ #include QmitkSliderLevelWindowWidget::QmitkSliderLevelWindowWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f) { m_Manager = mitk::LevelWindowManager::New(); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSliderLevelWindowWidget::OnPropertyModified); m_ObserverTag = m_Manager->AddObserver(itk::ModifiedEvent(), command); m_IsObserverTagSet = true; setMouseTracking(true); m_Resize = false; m_Bottom = false; m_CtrlPressed = false; m_MouseDown = false; m_Font.setPointSize(6); m_MoveHeight = height() - 25; m_ScaleVisible = true; m_Contextmenu = new QmitkLevelWindowWidgetContextMenu(this); this->hide(); Update(); } QmitkSliderLevelWindowWidget::~QmitkSliderLevelWindowWidget() { if (m_IsObserverTagSet) { m_Manager->RemoveObserver(m_ObserverTag); m_IsObserverTagSet = false; } } void QmitkSliderLevelWindowWidget::SetLevelWindowManager(mitk::LevelWindowManager *levelWindowManager) { if (m_IsObserverTagSet) { m_Manager->RemoveObserver(m_ObserverTag); m_IsObserverTagSet = false; } m_Manager = levelWindowManager; if (m_Manager.IsNotNull()) { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSliderLevelWindowWidget::OnPropertyModified); m_ObserverTag = m_Manager->AddObserver(itk::ModifiedEvent(), command); m_IsObserverTagSet = true; } } void QmitkSliderLevelWindowWidget::OnPropertyModified(const itk::EventObject &) { try { m_LevelWindow = m_Manager->GetLevelWindow(); this->show(); Update(); } catch (...) { try { this->hide(); } catch (...) { } } } void QmitkSliderLevelWindowWidget::paintEvent(QPaintEvent *itkNotUsed(e)) { QPixmap pm(width(), height()); pm.fill(this->palette().color(this->backgroundRole())); QPainter painter(&pm); painter.setFont(m_Font); painter.setPen(this->palette().color(this->foregroundRole())); QColor c(51, 153, 204); - QColor cl = c.light(); - QColor cd = c.dark(); + QColor cl = c.lighter(); + QColor cd = c.darker(); painter.setBrush(c); painter.drawRect(m_Rect); mitk::ScalarType mr = m_LevelWindow.GetRange(); float smallestLevelableValue = 1e-9; //This check is needed as safe guard. LevelWindow is refactored to only deduce finite ranges //from images, but old scene serialization may contain infinite ranges that overwrite the new //business logic. This roots in two many "jobs" LevelWindow" is used for; see also T24962. //Until LevelWindow and this widget is refactored the check was the minimal invasive fix. if (!std::isfinite(mr)) { mr = m_LevelWindow.GetWindow(); } // avoiding a division by 0 while still enabling small level windows if (mr < smallestLevelableValue) mr = smallestLevelableValue; mitk::ScalarType fact = m_MoveHeight / mr; // begin draw scale if (m_ScaleVisible) { mitk::ScalarType minRange = m_LevelWindow.GetRangeMin(); mitk::ScalarType maxRange = m_LevelWindow.GetRangeMax(); //This check is needed as safe guard. LevelWindow is refactored to only deduce finite ranges //from images, but old scene serialization may contain infinite ranges that overwrite the new //business logic. This roots in two many "jobs" LevelWindow" is used for; see also T24962. //Until LevelWindow and this widget is refactored the check was the minimal invasive fix. if (!std::isfinite(minRange)) { minRange = m_LevelWindow.GetLowerWindowBound(); } //This check is needed as safe guard. LevelWindow is refactored to only deduce finite ranges //from images, but old scene serialization may contain infinite ranges that overwrite the new //business logic. This roots in two many "jobs" LevelWindow" is used for; see also T24962. //Until LevelWindow and this widget is refactored the check was the minimal invasive fix. if (!std::isfinite(maxRange)) { maxRange = m_LevelWindow.GetUpperWindowBound(); } int yValue = m_MoveHeight + static_cast(minRange * fact); QString s = " 0"; if (minRange < 0 && maxRange > 0) { painter.drawLine(5, yValue, 15, yValue); painter.drawText(21, yValue + 3, s); } int count = 1; int k = 5; bool enoughSpace = false; bool enoughSpace2 = false; double dStepSize = pow(10, floor(log10(mr / 100)) + 1); for (int i = m_MoveHeight + static_cast(minRange * fact); i < m_MoveHeight;) // negative { if (-count * dStepSize < minRange) { break; } yValue = m_MoveHeight + static_cast((minRange + count * dStepSize) * fact); s = QString::number(-count * dStepSize); if (count % k && ((dStepSize * fact) > 2.5)) { painter.drawLine(8, yValue, 12, yValue); enoughSpace = true; } else if (!(count % k)) { if ((k * dStepSize * fact) > 7) { painter.drawLine(5, yValue, 15, yValue); painter.drawText(21, yValue + 3, s); enoughSpace2 = true; } else { k += 5; } } if (enoughSpace) { i = yValue; count++; } else if (enoughSpace2) { i = yValue; count += k; } else { i = yValue; count = k; } } count = 1; k = 5; enoughSpace = false; enoughSpace2 = false; for (int i = m_MoveHeight + static_cast(minRange * fact); i >= 0;) { if (count * dStepSize > maxRange) { break; } yValue = m_MoveHeight + static_cast((minRange - count * dStepSize) * fact); s = QString::number(count * dStepSize); if (count % k && ((dStepSize * fact) > 2.5)) { if (!(minRange > 0 && (count * dStepSize) < minRange)) painter.drawLine(8, yValue, 12, yValue); enoughSpace = true; } else if (!(count % k)) { if ((k * dStepSize * fact) > 7) { if (!(minRange > 0 && (count * dStepSize) < minRange)) { painter.drawLine(5, yValue, 15, yValue); painter.drawText(21, yValue + 3, s); } enoughSpace2 = true; } else { k += 5; } } if (enoughSpace) { i = yValue; count++; } else if (enoughSpace2) { i = yValue; count += k; } else { i = yValue; count = k; } } } // end draw scale painter.setPen(cl); painter.drawLine(m_Rect.topLeft(), m_Rect.topRight()); painter.drawLine(m_Rect.topLeft(), m_Rect.bottomLeft()); painter.setPen(cd); painter.drawLine(m_Rect.topRight(), m_Rect.bottomRight()); painter.drawLine(m_Rect.bottomRight(), m_Rect.bottomLeft()); painter.end(); QPainter p(this); p.drawPixmap(0, 0, pm); } void QmitkSliderLevelWindowWidget::mouseMoveEvent(QMouseEvent *mouseEvent) { if (!mouseEvent) return; if (m_LevelWindow.IsFixed()) return; if (!m_MouseDown) { if (mouseEvent->pos().y() >= 0 && mouseEvent->pos().y() <= (m_Rect.topLeft().y() + 3)) { setCursor(Qt::SizeVerCursor); m_UpperBound.setRect(m_Rect.topLeft().x(), m_Rect.topLeft().y() - 3, 17, 7); this->setToolTip("Ctrl + left click to change only upper bound"); m_Resize = true; } else if (mouseEvent->pos().y() >= (m_Rect.bottomLeft().y() - 3)) { setCursor(Qt::SizeVerCursor); m_LowerBound.setRect(m_Rect.bottomLeft().x(), m_Rect.bottomLeft().y() - 3, 17, 7); this->setToolTip("Ctrl + left click to change only lower bound"); m_Resize = true; m_Bottom = true; } else { setCursor(Qt::ArrowCursor); this->setToolTip("Left click and mouse move to adjust the slider"); m_Resize = false; m_Bottom = false; } } else { mitk::ScalarType fact = m_MoveHeight / m_LevelWindow.GetRange(); if (m_Leftbutton) { if (m_Resize && !m_CtrlPressed) { mitk::ScalarType diff = (mouseEvent->pos().y()) / fact; diff -= (m_StartPos.y()) / fact; m_StartPos = mouseEvent->pos(); if (diff == 0) return; mitk::ScalarType value; if (m_Bottom) value = m_LevelWindow.GetWindow() + ((2 * diff)); else value = m_LevelWindow.GetWindow() - ((2 * diff)); if (value < 0) value = 0; m_LevelWindow.SetLevelWindow(m_LevelWindow.GetLevel(), value); } else if (m_Resize && m_CtrlPressed) { if (!m_Bottom) { mitk::ScalarType diff = (mouseEvent->pos().y()) / fact; diff -= (m_StartPos.y()) / fact; m_StartPos = mouseEvent->pos(); if (diff == 0) return; mitk::ScalarType value; value = m_LevelWindow.GetWindow() - ((diff)); if (value < 0) value = 0; mitk::ScalarType oldWindow; mitk::ScalarType oldLevel; mitk::ScalarType newLevel; oldWindow = m_LevelWindow.GetWindow(); oldLevel = m_LevelWindow.GetLevel(); newLevel = oldLevel + (value - oldWindow) / 2; if (!((newLevel + value / 2) > m_LevelWindow.GetRangeMax())) m_LevelWindow.SetLevelWindow(newLevel, value); } else { mitk::ScalarType diff = (mouseEvent->pos().y()) / fact; diff -= (m_StartPos.y()) / fact; m_StartPos = mouseEvent->pos(); if (diff == 0) return; mitk::ScalarType value; value = m_LevelWindow.GetWindow() + ((diff)); if (value < 0) value = 0; mitk::ScalarType oldWindow; mitk::ScalarType oldLevel; mitk::ScalarType newLevel; oldWindow = m_LevelWindow.GetWindow(); oldLevel = m_LevelWindow.GetLevel(); newLevel = oldLevel - (value - oldWindow) / 2; if (!((newLevel - value / 2) < m_LevelWindow.GetRangeMin())) m_LevelWindow.SetLevelWindow(newLevel, value); } } else { const mitk::ScalarType minv = m_LevelWindow.GetRangeMin(); const mitk::ScalarType level = (m_MoveHeight - mouseEvent->pos().y()) / fact + minv; mitk::ScalarType diff = (mouseEvent->pos().x()) / fact; diff -= (m_StartPos.x()) / fact; m_StartPos = mouseEvent->pos(); mitk::ScalarType window; if (m_Bottom) window = m_LevelWindow.GetWindow() + ((2 * diff)); else window = m_LevelWindow.GetWindow() - ((2 * diff)); if (window < 0) window = 0; m_LevelWindow.SetLevelWindow(level, window); } m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } } void QmitkSliderLevelWindowWidget::enterEvent(QEvent * /*event*/) { QPoint p = QCursor::pos(); p = this->mapFromGlobal(p); QMouseEvent ev(QEvent::MouseMove, p, Qt::NoButton, Qt::NoButton, Qt::NoModifier); this->mouseMoveEvent(&ev); } void QmitkSliderLevelWindowWidget::mousePressEvent(QMouseEvent *mouseEvent) { if (m_LevelWindow.IsFixed()) return; m_MouseDown = true; m_StartPos = mouseEvent->pos(); if (mouseEvent->button() == Qt::LeftButton) { if (mouseEvent->modifiers() == Qt::ControlModifier || mouseEvent->modifiers() == Qt::ShiftModifier) { m_CtrlPressed = true; } else { m_CtrlPressed = false; } m_Leftbutton = true; } else m_Leftbutton = false; mouseMoveEvent(mouseEvent); } void QmitkSliderLevelWindowWidget::resizeEvent(QResizeEvent *event) { m_MoveHeight = event->size().height() - 25; Update(); } void QmitkSliderLevelWindowWidget::mouseReleaseEvent(QMouseEvent *) { if (m_LevelWindow.IsFixed()) return; m_MouseDown = false; } void QmitkSliderLevelWindowWidget::Update() { int rectWidth; if (m_ScaleVisible) { rectWidth = 17; setMinimumSize(QSize(50, 50)); setMaximumSize(QSize(50, 2000)); setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); } else { rectWidth = 26; setMinimumSize(QSize(40, 50)); setMaximumSize(QSize(50, 2000)); setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding)); } mitk::ScalarType mr = m_LevelWindow.GetRange(); if (mr < 1e-9) mr = 1e-9; mitk::ScalarType fact = m_MoveHeight / mr; mitk::ScalarType rectHeight = m_LevelWindow.GetWindow() * fact; if (rectHeight < 15) rectHeight = 15; if (m_LevelWindow.GetLowerWindowBound() < 0) m_Rect.setRect(2, static_cast(m_MoveHeight - (m_LevelWindow.GetUpperWindowBound() - m_LevelWindow.GetRangeMin()) * fact), rectWidth, static_cast(rectHeight)); else m_Rect.setRect(2, static_cast(m_MoveHeight - (m_LevelWindow.GetUpperWindowBound() - m_LevelWindow.GetRangeMin()) * fact), rectWidth, static_cast(rectHeight)); QWidget::repaint(); } void QmitkSliderLevelWindowWidget::contextMenuEvent(QContextMenuEvent *) { m_Contextmenu->SetLevelWindowManager(m_Manager.GetPointer()); auto contextMenu = new QMenu(this); Q_CHECK_PTR(contextMenu); if (m_ScaleVisible) contextMenu->addAction(tr("Hide Scale"), this, SLOT(HideScale())); else contextMenu->addAction(tr("Show Scale"), this, SLOT(ShowScale())); contextMenu->addSeparator(); m_Contextmenu->GetContextMenu(contextMenu); // Fix: Bug #13327 we need to reset the m_MouseDown value // otherwise the cursor is not correctly restored afterwards m_MouseDown = false; } void QmitkSliderLevelWindowWidget::HideScale() { m_ScaleVisible = false; Update(); } void QmitkSliderLevelWindowWidget::ShowScale() { m_ScaleVisible = true; Update(); } void QmitkSliderLevelWindowWidget::SetDataStorage(mitk::DataStorage *ds) { m_Manager->SetDataStorage(ds); } mitk::LevelWindowManager *QmitkSliderLevelWindowWidget::GetManager() { return m_Manager.GetPointer(); } diff --git a/Modules/QtWidgetsExt/src/QmitkColorPropertyView.cpp b/Modules/QtWidgetsExt/src/QmitkColorPropertyView.cpp index 95789bc8e4..62407ef96a 100644 --- a/Modules/QtWidgetsExt/src/QmitkColorPropertyView.cpp +++ b/Modules/QtWidgetsExt/src/QmitkColorPropertyView.cpp @@ -1,51 +1,51 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkColorPropertyView.h" #include #define ROUND_P(x) ((int)((x) + 0.5)) QmitkColorPropertyView::QmitkColorPropertyView(const mitk::ColorProperty *property, QWidget *parent) : QLabel(parent), PropertyView(property), m_ColorProperty(property) { setText(" "); // two spaces for some minimun height setMinimumSize(15, 15); PropertyChanged(); m_WidgetPalette = QWidget::palette(); QWidget::setPalette(m_WidgetPalette); QWidget::setAutoFillBackground(true); } QmitkColorPropertyView::~QmitkColorPropertyView() { } void QmitkColorPropertyView::PropertyChanged() { if (m_Property) DisplayColor(); } void QmitkColorPropertyView::PropertyRemoved() { m_Property = nullptr; m_ColorProperty = nullptr; } void QmitkColorPropertyView::DisplayColor() { const mitk::Color &tmp_col(m_ColorProperty->GetColor()); QColor color(ROUND_P(tmp_col[0] * 255.0), ROUND_P(tmp_col[1] * 255.0), ROUND_P(tmp_col[2] * 255.0)); - m_WidgetPalette.setColor(QPalette::Background, color); + m_WidgetPalette.setColor(QPalette::Window, color); } diff --git a/Modules/QtWidgetsExt/src/QmitkHistogram.cpp b/Modules/QtWidgetsExt/src/QmitkHistogram.cpp index c2a7db8f5b..be0e9bfc8b 100644 --- a/Modules/QtWidgetsExt/src/QmitkHistogram.cpp +++ b/Modules/QtWidgetsExt/src/QmitkHistogram.cpp @@ -1,179 +1,179 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include "QmitkHistogram.h" class QmitkHistogram::HistogramData { public: QwtIntervalSeriesData data; QColor color; double reference; }; QmitkHistogram::QmitkHistogram(const QwtText &title) : QwtPlotItem(title) { init(); } QmitkHistogram::QmitkHistogram(const QString &title) : QwtPlotItem(QwtText(title)) { init(); } QmitkHistogram::~QmitkHistogram() { delete m_Data; } void QmitkHistogram::init() { m_Data = new HistogramData(); m_Data->reference = 0.0; setItemAttribute(QwtPlotItem::AutoScale, true); setItemAttribute(QwtPlotItem::Legend, true); setZ(20.0); } void QmitkHistogram::setBaseline(double reference) { if (m_Data->reference != reference) { m_Data->reference = reference; itemChanged(); } } double QmitkHistogram::baseline() const { return m_Data->reference; } void QmitkHistogram::setData(const QwtIntervalSeriesData &data) { m_Data->data.setSamples(data.samples()); itemChanged(); } const QwtIntervalSeriesData &QmitkHistogram::data() const { return m_Data->data; } void QmitkHistogram::setColor(const QColor &color) { if (m_Data->color != color) { m_Data->color = color; itemChanged(); } } QColor QmitkHistogram::color() const { return m_Data->color; } QRectF QmitkHistogram::boundingRect() const { QRectF rect = m_Data->data.boundingRect(); if (!rect.isValid()) return rect; if (rect.bottom() < m_Data->reference) rect.setBottom(m_Data->reference); else if (rect.top() > m_Data->reference) rect.setTop(m_Data->reference); return rect; } void QmitkHistogram::draw(QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, const QRectF &) const { const QwtIntervalSeriesData &iData = m_Data->data; painter->setPen(QPen(m_Data->color)); const int y0 = yMap.transform(baseline()); for (int i = 0; i < (int)iData.size(); i++) { const int y2 = yMap.transform(iData.sample(i).value); if (y2 == y0) continue; int x1 = xMap.transform(iData.sample(i).interval.minValue()); int x2 = xMap.transform(iData.sample(i).interval.maxValue()); if (x1 > x2) qSwap(x1, x2); if (i < (int)iData.size() - 2) { const int xx1 = xMap.transform(iData.sample(i + 1).interval.minValue()); const int xx2 = xMap.transform(iData.sample(i + 1).interval.maxValue()); if (x2 == qMin(xx1, xx2)) { const int yy2 = yMap.transform(iData.sample(i + 1).value); if (yy2 != y0 && ((yy2 < y0 && y2 < y0) || (yy2 > y0 && y2 > y0))) { // One pixel distance between neighbored bars x2--; } } } drawBar(painter, Qt::Vertical, QRect(x1, y0, x2 - x1, y2 - y0)); } } void QmitkHistogram::drawBar(QPainter *painter, Qt::Orientation, const QRect &rect) const { painter->save(); const QColor color(painter->pen().color()); const QRect r = rect.normalized(); const int factor = 125; - const QColor light(color.light(factor)); - const QColor dark(color.dark(factor)); + const QColor light(color.lighter(factor)); + const QColor dark(color.darker(factor)); painter->setBrush(color); painter->setPen(Qt::NoPen); QwtPainter::drawRect(painter, r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2); painter->setBrush(Qt::NoBrush); painter->setPen(QPen(light, 2)); QwtPainter::drawLine(painter, r.left() + 1, r.top() + 2, r.right() + 1, r.top() + 2); painter->setPen(QPen(dark, 2)); QwtPainter::drawLine(painter, r.left() + 1, r.bottom(), r.right() + 1, r.bottom()); painter->setPen(QPen(light, 1)); QwtPainter::drawLine(painter, r.left(), r.top() + 1, r.left(), r.bottom()); QwtPainter::drawLine(painter, r.left() + 1, r.top() + 2, r.left() + 1, r.bottom() - 1); painter->setPen(QPen(dark, 1)); QwtPainter::drawLine(painter, r.right() + 1, r.top() + 1, r.right() + 1, r.bottom()); QwtPainter::drawLine(painter, r.right(), r.top() + 2, r.right(), r.bottom() - 1); painter->restore(); } diff --git a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp index fddbc1637d..b554ebd685 100644 --- a/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkLesionTreeModel.cpp @@ -1,298 +1,298 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations UI module #include "QmitkLesionTreeModel.h" // semantic relations module #include #include #include #include #include #include // qt #include QmitkLesionTreeModel::QmitkLesionTreeModel(QObject* parent/* = nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_LastSegmentation(nullptr) , m_RootItem(std::make_shared(mitk::LesionData())) { // nothing here } ////////////////////////////////////////////////////////////////////////// // overridden virtual functions from QAbstractItemModel ////////////////////////////////////////////////////////////////////////// QModelIndex QmitkLesionTreeModel::index(int row, int column, const QModelIndex& itemIndex) const { if (!hasIndex(row, column, itemIndex)) { return QModelIndex(); } auto childItem = GetItemByIndex(itemIndex)->GetChildInRow(row); if (nullptr == childItem) { return QModelIndex(); } return createIndex(row, column, childItem.get()); } QModelIndex QmitkLesionTreeModel::parent(const QModelIndex& itemIndex) const { if (!itemIndex.isValid()) { return QModelIndex(); } auto parentItem = GetItemByIndex(itemIndex)->GetParent(); if (parentItem.expired()) { return QModelIndex(); } auto sharedParent = parentItem.lock(); if (sharedParent == m_RootItem) { return QModelIndex(); } return createIndex(sharedParent->GetRow(), 0, sharedParent.get()); } int QmitkLesionTreeModel::rowCount(const QModelIndex& itemIndex/* = QModelIndex()*/) const { return GetItemByIndex(itemIndex)->ChildCount(); } int QmitkLesionTreeModel::columnCount(const QModelIndex&/* itemIndex = QModelIndex() */) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display columns return 0; } return m_ControlPoints.size() + 1; } QVariant QmitkLesionTreeModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } if (index.column() < 0 || index.column() > static_cast(m_ControlPoints.size())) { return QVariant(); } QmitkLesionTreeItem* currentItem = GetItemByIndex(index); if (Qt::DisplayRole == role) { if (currentItem->GetParent().expired()) { return QVariant(); } auto parentItem = currentItem->GetParent().lock(); // parent exists and is the root item -> 1. item of a lesion entry if (m_RootItem == parentItem) { // display role fills the first columns with the lesion UID / name if (0 == index.column()) { std::string itemString = currentItem->GetData().GetLesionName(); if (itemString.empty()) { itemString = currentItem->GetData().GetLesionUID(); } return QString::fromStdString(itemString); } else { // display role fills other columns with the lesion presence info const auto lesionPresence = currentItem->GetData().GetLesionPresence(); if (index.column() - 1 > static_cast(lesionPresence.size())) { return "N/A"; } if (lesionPresence.at(index.column() - 1)) { return QString::fromStdString("present"); } return QString::fromStdString("not present"); } } } - if (Qt::BackgroundColorRole == role) + if (Qt::BackgroundRole == role) { auto it = m_DataNodePresence.find(currentItem->GetData().GetLesion().UID); if (it != m_DataNodePresence.end()) { return it->second ? QVariant(QColor(Qt::darkGreen)) : QVariant(QColor(Qt::transparent)); } return QVariant(QColor(Qt::transparent)); } if (Qt::UserRole == role) { return QVariant::fromValue(currentItem); } return QVariant(); } QVariant QmitkLesionTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { if (0 == m_RootItem->ChildCount()) { // no lesion items stored, no need to display the header return QVariant(); } if (Qt::Horizontal == orientation && Qt::DisplayRole == role) { if (0 == section) { return QVariant("Lesion"); } if (static_cast(m_ControlPoints.size()) >= section) { mitk::SemanticTypes::ControlPoint currentControlPoint = m_ControlPoints.at(section-1); return QVariant(QString::fromStdString(currentControlPoint.ToString())); } } return QVariant(); } const mitk::DataNode* QmitkLesionTreeModel::GetLastSegmentation() const { return m_LastSegmentation; } void QmitkLesionTreeModel::NodeAdded(const mitk::DataNode* dataNode) { if (mitk::NodePredicates::GetSegmentationPredicate()->CheckNode(dataNode)) { m_LastSegmentation = dataNode; } } void QmitkLesionTreeModel::SetData() { m_RootItem = std::make_shared(mitk::LesionData()); // get all control points of current case m_ControlPoints = mitk::RelationStorage::GetAllControlPointsOfCase(m_CaseID); // sort the vector of control points for the timeline std::sort(m_ControlPoints.begin(), m_ControlPoints.end()); SetLesionData(); SetSelectedDataNodesPresence(); } void QmitkLesionTreeModel::SetLesionData() { m_CurrentLesions = mitk::RelationStorage::GetAllLesionsOfCase(m_CaseID); for (auto& lesion : m_CurrentLesions) { AddLesion(lesion); } } void QmitkLesionTreeModel::AddLesion(const mitk::SemanticTypes::Lesion& lesion) { auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNull()) { return; } // create new lesion tree item data and modify it according to the control point data mitk::LesionData lesionData(lesion); mitk::ComputeLesionPresence(lesionData, m_CaseID); // add the top-level lesion item to the root item std::shared_ptr newLesionTreeItem = std::make_shared(lesionData); m_RootItem->AddChild(newLesionTreeItem); } void QmitkLesionTreeModel::SetSelectedDataNodesPresence() { m_DataNodePresence.clear(); for (const auto& dataNode : qAsConst(m_SelectedDataNodes)) { if (!mitk::SemanticRelationsInference::InstanceExists(dataNode)) { continue; } for (const auto& lesion : m_CurrentLesions) { if (!mitk::SemanticRelationsInference::InstanceExists(m_CaseID, lesion)) { continue; } try { // set the lesion presence for the current node bool dataNodePresence = mitk::SemanticRelationsInference::IsLesionPresent(lesion, dataNode); SetDataNodePresenceOfLesion(&lesion, dataNodePresence); } catch (const mitk::SemanticRelationException&) { continue; } } } } void QmitkLesionTreeModel::SetDataNodePresenceOfLesion(const mitk::SemanticTypes::Lesion* lesion, bool dataNodePresence) { std::map::iterator iter = m_DataNodePresence.find(lesion->UID); if (iter != m_DataNodePresence.end()) { // key already existing, overwrite already stored bool value iter->second = dataNodePresence; } else { m_DataNodePresence.insert(std::make_pair(lesion->UID, dataNodePresence)); } } QmitkLesionTreeItem* QmitkLesionTreeModel::GetItemByIndex(const QModelIndex& index) const { if (index.isValid()) { auto item = static_cast(index.internalPointer()); if (nullptr != item) { return item; } } return m_RootItem.get(); } diff --git a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp index c60140b1c6..ff9e330c38 100644 --- a/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp +++ b/Modules/SemanticRelationsUI/src/QmitkPatientTableModel.cpp @@ -1,346 +1,346 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations UI module #include "QmitkPatientTableModel.h" #include "QmitkPatientTableHeaderView.h" #include "QmitkSemanticRelationsUIHelper.h" // semantic relations module #include #include #include #include #include #include #include // qt #include // c++ #include #include QmitkPatientTableModel::QmitkPatientTableModel(QObject* parent /*= nullptr*/) : QmitkAbstractSemanticRelationsStorageModel(parent) , m_SelectedNodeType("Image") { m_HeaderModel = new QStandardItemModel(this); } QmitkPatientTableModel::~QmitkPatientTableModel() { // nothing here } QModelIndex QmitkPatientTableModel::index(int row, int column, const QModelIndex& parent/* = QModelIndex()*/) const { if (hasIndex(row, column, parent)) { return createIndex(row, column); } return QModelIndex(); } QModelIndex QmitkPatientTableModel::parent(const QModelIndex& /*child*/) const { return QModelIndex(); } int QmitkPatientTableModel::rowCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return m_InformationTypes.size(); } int QmitkPatientTableModel::columnCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return m_ExaminationPeriods.size(); } QVariant QmitkPatientTableModel::data(const QModelIndex& index, int role/* = Qt::DisplayRole*/) const { // special role for returning the horizontal header if (QmitkPatientTableHeaderView::HorizontalHeaderDataRole == role) { return QVariant::fromValue(m_HeaderModel); } if (!index.isValid()) { return QVariant(); } if (index.row() < 0 || index.row() >= static_cast(m_InformationTypes.size()) || index.column() < 0 || index.column() >= static_cast(m_ExaminationPeriods.size())) { return QVariant(); } mitk::DataNode* dataNode = GetCurrentDataNode(index); if (Qt::DecorationRole == role) { auto it = m_PixmapMap.find(dataNode); if (it != m_PixmapMap.end()) { return QVariant(it->second); } auto emptyPixmap = QPixmap(120, 120); emptyPixmap.fill(Qt::transparent); return emptyPixmap; } - if (Qt::BackgroundColorRole == role) + if (Qt::BackgroundRole == role) { auto it = m_LesionPresence.find(dataNode); if (it != m_LesionPresence.end()) { return it->second ? QVariant(QColor(Qt::darkGreen)) : QVariant(QColor(Qt::transparent)); } return QVariant(QColor(Qt::transparent)); } if (QmitkDataNodeRole == role) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } if (QmitkDataNodeRawPointerRole == role) { return QVariant::fromValue(dataNode); } return QVariant(); } QVariant QmitkPatientTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (Qt::Vertical == orientation && Qt::DisplayRole == role) { if (static_cast(m_InformationTypes.size()) > section) { mitk::SemanticTypes::InformationType currentInformationType = m_InformationTypes.at(section); return QVariant(QString::fromStdString(currentInformationType)); } } return QVariant(); } Qt::ItemFlags QmitkPatientTableModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags; mitk::DataNode* dataNode = GetCurrentDataNode(index); if (nullptr != dataNode) { flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; } return flags; } void QmitkPatientTableModel::SetNodeType(const std::string& nodeType) { m_SelectedNodeType = nodeType; UpdateModelData(); } void QmitkPatientTableModel::NodePredicateChanged() { UpdateModelData(); } void QmitkPatientTableModel::SetData() { // get all examination periods of current case m_ExaminationPeriods = mitk::RelationStorage::GetAllExaminationPeriodsOfCase(m_CaseID); // sort all examination periods for the timeline mitk::SortAllExaminationPeriods(m_CaseID, m_ExaminationPeriods); // rename examination periods according to their new order std::string examinationPeriodName = "Baseline"; for (size_t i = 0; i < m_ExaminationPeriods.size(); ++i) { auto& examinationPeriod = m_ExaminationPeriods.at(i); examinationPeriod.name = examinationPeriodName; mitk::RelationStorage::RenameExaminationPeriod(m_CaseID, examinationPeriod); examinationPeriodName = "Follow-up " + std::to_string(i); } // get all information types points of current case m_InformationTypes = mitk::RelationStorage::GetAllInformationTypesOfCase(m_CaseID); if ("Image" == m_SelectedNodeType) { m_CurrentDataNodes = m_SemanticRelationsDataStorageAccess->GetAllImagesOfCase(m_CaseID); } else if ("Segmentation" == m_SelectedNodeType) { m_CurrentDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSegmentationsOfCase(m_CaseID); } SetHeaderModel(); SetPixmaps(); SetLesionPresences(); } void QmitkPatientTableModel::SetHeaderModel() { m_HeaderModel->clear(); QStandardItem* rootItem = new QStandardItem("Timeline"); QList standardItems; for (const auto& examinationPeriod : m_ExaminationPeriods) { QStandardItem* examinationPeriodItem = new QStandardItem(QString::fromStdString(examinationPeriod.name)); standardItems.push_back(examinationPeriodItem); rootItem->appendColumn(standardItems); standardItems.clear(); } m_HeaderModel->setItem(0, 0, rootItem); } void QmitkPatientTableModel::SetPixmaps() { m_PixmapMap.clear(); for (const auto& dataNode : m_CurrentDataNodes) { // set the pixmap for the current node QPixmap pixmapFromImage = QmitkSemanticRelationsUIHelper::GetPixmapFromImageNode(dataNode); SetPixmapOfNode(dataNode, &pixmapFromImage); } } void QmitkPatientTableModel::SetPixmapOfNode(const mitk::DataNode* dataNode, QPixmap* pixmapFromImage) { if (nullptr == dataNode) { return; } std::map::iterator iter = m_PixmapMap.find(dataNode); if (iter != m_PixmapMap.end()) { // key already existing if (nullptr != pixmapFromImage) { // overwrite already stored pixmap iter->second = pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio); } else { // remove key if no pixmap is given m_PixmapMap.erase(iter); } } else { m_PixmapMap.insert(std::make_pair(dataNode, pixmapFromImage->scaled(120, 120, Qt::IgnoreAspectRatio))); } } void QmitkPatientTableModel::SetLesionPresences() { m_LesionPresence.clear(); if (!mitk::SemanticRelationsInference::InstanceExists(m_CaseID, m_Lesion)) { return; } for (const auto& dataNode : m_CurrentDataNodes) { if (!mitk::SemanticRelationsInference::InstanceExists(dataNode)) { continue; } // set the lesion presence for the current node bool lesionPresence = mitk::SemanticRelationsInference::IsLesionPresent(m_Lesion, dataNode); SetLesionPresenceOfNode(dataNode, lesionPresence); } } void QmitkPatientTableModel::SetLesionPresenceOfNode(const mitk::DataNode* dataNode, bool lesionPresence) { std::map::iterator iter = m_LesionPresence.find(dataNode); if (iter != m_LesionPresence.end()) { // key already existing, overwrite already stored bool value iter->second = lesionPresence; } else { m_LesionPresence.insert(std::make_pair(dataNode, lesionPresence)); } } mitk::DataNode* QmitkPatientTableModel::GetCurrentDataNode(const QModelIndex& index) const { if (!index.isValid()) { return nullptr; } auto examinationPeriod = m_ExaminationPeriods.at(index.column()); auto currentInformationType = m_InformationTypes.at(index.row()); auto controlPointsOfExaminationPeriod = examinationPeriod.controlPointUIDs; for (const auto& controlPointUID : controlPointsOfExaminationPeriod) { auto currentControlPoint = mitk::GetControlPointByUID(m_CaseID, controlPointUID); try { std::vector filteredDataNodes; if ("Image" == m_SelectedNodeType) { filteredDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSpecificImages(m_CaseID, currentControlPoint, currentInformationType); } else if ("Segmentation" == m_SelectedNodeType) { filteredDataNodes = m_SemanticRelationsDataStorageAccess->GetAllSpecificSegmentations(m_CaseID, currentControlPoint, currentInformationType); } if (filteredDataNodes.empty()) { // try next control point continue; } else { // found a specific image return filteredDataNodes.front(); } } catch (const mitk::SemanticRelationException&) { return nullptr; } } // could not find a specif image return nullptr; } diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryQtTracker.cpp b/Plugins/org.blueberry.ui.qt/src/internal/berryQtTracker.cpp index 456b947984..2241519b18 100755 --- a/Plugins/org.blueberry.ui.qt/src/internal/berryQtTracker.cpp +++ b/Plugins/org.blueberry.ui.qt/src/internal/berryQtTracker.cpp @@ -1,328 +1,327 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "berryTweaklets.h" #include "berryQtTracker.h" #include "berryConstants.h" #include "berryIDropTarget.h" #include "berryDragUtil.h" #include "berryGuiWidgetsTweaklet.h" #include #include #include #include #include namespace berry { CursorType QtDragManager::PositionToCursorType(int positionConstant) { if (positionConstant == Constants::LEFT) return CURSOR_LEFT; if (positionConstant == Constants::RIGHT) return CURSOR_RIGHT; if (positionConstant == Constants::TOP) return CURSOR_TOP; if (positionConstant == Constants::BOTTOM) return CURSOR_BOTTOM; if (positionConstant == Constants::CENTER) return CURSOR_CENTER; return CURSOR_INVALID; } int QtDragManager::CursorTypeToPosition(CursorType dragCursorId) { switch (dragCursorId) { case CURSOR_LEFT: return Constants::LEFT; case CURSOR_RIGHT: return Constants::RIGHT; case CURSOR_TOP: return Constants::TOP; case CURSOR_BOTTOM: return Constants::BOTTOM; case CURSOR_CENTER: return Constants::CENTER; default: return Constants::DEFAULT; } } bool QtDragManager::eventFilter(QObject* o, QEvent* e) { if (beingCancelled) { if (e->type() == QEvent::KeyRelease && ((QKeyEvent*) e)->key() == Qt::Key_Escape) { QApplication::instance()->removeEventFilter(this); beingCancelled = false; eventLoop->exit(); return true; // block the key release } return false; } if (!o->isWidgetType()) return false; if (e->type() == QEvent::MouseMove) { QMouseEvent* me = (QMouseEvent *) e; this->Move(me->globalPos()); return true; } else if (e->type() == QEvent::MouseButtonRelease) { //DEBUG("pre drop"); QApplication::instance()->removeEventFilter(this); beingCancelled = false; eventLoop->exit(); return true; } if (e->type() == QEvent::KeyPress || e->type() == QEvent::KeyRelease) { QKeyEvent *ke = ((QKeyEvent*) e); if (ke->key() == Qt::Key_Escape && e->type() == QEvent::KeyPress) { this->Cancel(); QApplication::instance()->removeEventFilter(this); //beingCancelled = false; eventLoop->exit(); } else { // move(QCursor::pos()); } return true; // Eat all key events } // ### We bind modality to widgets, so we have to do this // ### "manually". // DnD is modal - eat all other interactive events switch (e->type()) { case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: case QEvent::KeyPress: case QEvent::KeyRelease: case QEvent::Wheel: case QEvent::ShortcutOverride: #ifdef QT3_SUPPORT case QEvent::Accel: case QEvent::AccelAvailable: #endif return true; default: return false; } } void QtDragManager::Cancel() { beingCancelled = true; } void QtDragManager::Move(const QPoint& globalPos) { emit tracker->Moved(tracker, globalPos); } bool QtDragManager::Drag(QtTracker* tracker) { if (tracker == nullptr) return false; this->tracker = tracker; beingCancelled = false; QApplication::instance()->installEventFilter(this); // if (!QWidget::mouseGrabber()) // rubberBand->grabMouse(); eventLoop = new QEventLoop; eventLoop->exec(); delete eventLoop; eventLoop = nullptr; return !beingCancelled; } QtTracker::QtTracker() : rubberBand(nullptr), dragManager(nullptr), cursorOverride(0) { rubberBand = new QRubberBand(QRubberBand::Rectangle); QPalette rubberPalette(rubberBand->palette()); //rubberPalette.setColor(QPalette::Button, QColor(Qt::darkRed)); - rubberPalette.setBrush(QPalette::Foreground, QBrush(Qt::darkRed)); + rubberPalette.setBrush(QPalette::WindowText, QBrush(Qt::darkRed)); rubberPalette.setBrush(QPalette::Window, QBrush(Qt::darkRed)); - rubberPalette.setBrush(QPalette::Background, QBrush(Qt::darkRed)); rubberPalette.setBrush(QPalette::Base, QBrush(Qt::darkRed)); rubberPalette.setBrush(QPalette::Text, QBrush(Qt::darkRed)); rubberBand->setPalette(rubberPalette); rubberBand->ensurePolished(); QPixmap pixCursorTop(":/org.blueberry.ui.qt/cursor_top.xpm"); auto cursorTop = new QCursor(pixCursorTop, 15, 8); cursorMap.insert(CURSOR_TOP, cursorTop); QPixmap pixCursorRight(":/org.blueberry.ui.qt/cursor_right.xpm"); auto cursorRight = new QCursor(pixCursorRight, 23, 15); cursorMap.insert(CURSOR_RIGHT, cursorRight); QPixmap pixCursorBottom(":/org.blueberry.ui.qt/cursor_bottom.xpm"); auto cursorBottom = new QCursor(pixCursorBottom, 16, 23); cursorMap.insert(CURSOR_BOTTOM, cursorBottom); QPixmap pixCursorLeft(":/org.blueberry.ui.qt/cursor_left.xpm"); auto cursorLeft = new QCursor(pixCursorLeft, 8, 15); cursorMap.insert(CURSOR_LEFT, cursorLeft); QPixmap pixCursorCenter(":/org.blueberry.ui.qt/cursor_center.xpm"); auto cursorCenter = new QCursor(pixCursorCenter, 15, 15); cursorMap.insert(CURSOR_CENTER, cursorCenter); QPixmap pixCursorOffscreen(":/org.blueberry.ui.qt/cursor_offscreen.xpm"); auto cursorOffscreen = new QCursor(pixCursorOffscreen, 15, 15); cursorMap.insert(CURSOR_OFFSCREEN, cursorOffscreen); auto cursorInvalid = new QCursor(Qt::ForbiddenCursor); cursorMap.insert(CURSOR_INVALID, cursorInvalid); } QtTracker::~QtTracker() { delete rubberBand; for (QHash::iterator iter = cursorMap.begin(); iter != cursorMap.end(); ++iter) { delete iter.value(); } } QRect QtTracker::GetRectangle() const { return rubberBand->geometry(); } void QtTracker::SetRectangle(const QRect& rectangle) { rubberBand->setGeometry(rectangle); } void QtTracker::SetCursor(CursorType cursorType) { QCursor* cursor = cursorMap[cursorType]; if (!cursor) return; if (cursorOverride > 0) { QApplication::changeOverrideCursor(*cursor); } else { ++cursorOverride; QApplication::setOverrideCursor(*cursor); } } bool QtTracker::Open() { rubberBand->show(); dragManager = new QtDragManager(); bool result = dragManager->Drag(this); delete dragManager; rubberBand->hide(); while (cursorOverride > 0) { QApplication::restoreOverrideCursor(); --cursorOverride; } return result; } QtTrackerMoveListener::QtTrackerMoveListener(Object::Pointer draggedItem, const QRect& sourceBounds, const QPoint& initialLocation, bool allowSnapping) : allowSnapping(allowSnapping) , draggedItem(draggedItem) , sourceBounds(sourceBounds) , initialLocation(initialLocation) { } void QtTrackerMoveListener::Moved(QtTracker* tracker, const QPoint& location) { // Select a drop target; use the global one by default IDropTarget::Pointer target; QWidget* targetControl = Tweaklets::Get(GuiWidgetsTweaklet::KEY)->GetCursorControl(); // Get the drop target for this location target = DragUtil::GetDropTarget(targetControl, draggedItem, location, tracker->GetRectangle()); // Set up the tracker feedback based on the target QRect snapTarget; if (target != 0) { snapTarget = target->GetSnapRectangle(); tracker->SetCursor(target->GetCursor()); } else { tracker->SetCursor(CURSOR_INVALID); } // If snapping then reset the tracker's rectangle based on the current drop target if (allowSnapping) { if (snapTarget.width() <= 0 || snapTarget.height() <= 0) { snapTarget = QRect(sourceBounds.x() + location.x() - initialLocation.x(), sourceBounds.y() + location.y() - initialLocation.y(), sourceBounds.width(), sourceBounds.height()); } // Try to prevent flicker: don't change the rectangles if they're already in // the right location QRect currentRectangle = tracker->GetRectangle(); if (!(currentRectangle == snapTarget)) { tracker->SetRectangle(snapTarget); } } } } diff --git a/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/QmitkDeformableClippingPlaneView.cpp b/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/QmitkDeformableClippingPlaneView.cpp index 2d2ffa0a09..6f72a2c5df 100644 --- a/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/QmitkDeformableClippingPlaneView.cpp +++ b/Plugins/org.mitk.gui.qt.deformableclippingplane/src/internal/QmitkDeformableClippingPlaneView.cpp @@ -1,584 +1,584 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkDeformableClippingPlaneView.h" #include // mitk core #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkDeformableClippingPlaneView::VIEW_ID = "org.mitk.views.deformableclippingplane"; QmitkDeformableClippingPlaneView::QmitkDeformableClippingPlaneView() : QmitkAbstractView() , m_Controls(new Ui::QmitkDeformableClippingPlaneViewControls) , m_WorkingNode(nullptr) { auto isImage = mitk::TNodePredicateDataType::New(); auto isNotHelperObject = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true))); m_IsImagePredicate = mitk::NodePredicateAnd::New(isImage, isNotHelperObject); m_IsClippingPlanePredicate = mitk::NodePredicateProperty::New("clippingPlane", mitk::BoolProperty::New(true)); } QmitkDeformableClippingPlaneView::~QmitkDeformableClippingPlaneView() { if (m_WorkingNode.IsNotNull()) m_WorkingNode->SetDataInteractor(nullptr); } void QmitkDeformableClippingPlaneView::SetFocus() { m_Controls->createNewPlanePushButton->setFocus(); } void QmitkDeformableClippingPlaneView::CreateQtPartControl(QWidget *parent) { m_Controls->setupUi(parent); m_Controls->imageSelectionWidget->SetDataStorage(GetDataStorage()); m_Controls->imageSelectionWidget->SetNodePredicate(m_IsImagePredicate); m_Controls->imageSelectionWidget->SetSelectionIsOptional(false); m_Controls->imageSelectionWidget->SetInvalidInfo("Select an image or segmentation."); m_Controls->imageSelectionWidget->SetPopUpTitel("Select an image or segmentation."); m_Controls->clippingPlaneSelector->SetDataStorage(this->GetDataStorage()); m_Controls->clippingPlaneSelector->SetPredicate(m_IsClippingPlanePredicate); m_Controls->volumeGroupBox->setEnabled(false); m_Controls->interactionSelectionBox->setEnabled(false); m_Controls->planesWarningLabel->hide(); this->CreateConnections(); m_Controls->imageSelectionWidget->SetAutoSelectNewNodes(true); } void QmitkDeformableClippingPlaneView::OnCurrentSelectionChanged(const QList& /*nodes*/) { this->UpdateView(); } void QmitkDeformableClippingPlaneView::OnComboBoxSelectionChanged(const mitk::DataNode* node) { this->DeactivateInteractionButtons(); auto selectedNode = const_cast(node); if(nullptr != selectedNode) { if(m_WorkingNode.IsNotNull()) selectedNode->SetDataInteractor(m_WorkingNode->GetDataInteractor()); m_WorkingNode = selectedNode; } this->UpdateView(); } void QmitkDeformableClippingPlaneView::OnCreateNewClippingPlane() { this->DeactivateInteractionButtons(); auto plane = mitk::Surface::New(); auto planeSource = vtkSmartPointer::New(); planeSource->SetOrigin(-32.0, -32.0, 0.0); planeSource->SetPoint1(32.0, -32.0, 0.0); planeSource->SetPoint2(-32.0, 32.0, 0.0); planeSource->SetResolution(128, 128); planeSource->Update(); plane->SetVtkPolyData(planeSource->GetOutput()); mitk::ScalarType imageDiagonal = 200.0; auto selectedNode = m_Controls->imageSelectionWidget->GetSelectedNode(); if (selectedNode.IsNotNull()) { auto selectedImage = dynamic_cast(selectedNode->GetData()); if (nullptr != selectedImage) { // check if user wants a surface model if (m_Controls->surfaceModelCheckBox->isChecked()) { //Check if there is a surface node from the image. If not, create one bool createSurfaceFromImage = true; auto isSurface = mitk::NodePredicateDataType::New("Surface"); auto childNodes = GetDataStorage()->GetDerivations(selectedNode, isSurface, true); for (mitk::DataStorage::SetOfObjects::ConstIterator it = childNodes->Begin(); it != childNodes->End(); it++) { if (it.Value().IsNotNull() && it->Value()->GetName() == selectedNode->GetName()) { createSurfaceFromImage = false; it.Value()->SetVisibility(true); } } if (createSurfaceFromImage) { //Lsg 2: Surface for the 3D-perspective auto surfaceFilter = mitk::ImageToSurfaceFilter::New(); surfaceFilter->SetInput(selectedImage); surfaceFilter->SetThreshold(1); surfaceFilter->SetSmooth(true); //Downsampling surfaceFilter->SetDecimate(mitk::ImageToSurfaceFilter::DecimatePro); auto surfaceNode = mitk::DataNode::New(); surfaceNode->SetData(surfaceFilter->GetOutput()); surfaceNode->SetProperty("color", selectedNode->GetProperty("color")); surfaceNode->SetOpacity(0.5); surfaceNode->SetName(selectedNode->GetName()); this->GetDataStorage()->Add(surfaceNode, selectedNode); } } //If an image is selected trim the plane to this. imageDiagonal = selectedImage->GetGeometry()->GetDiagonalLength(); plane->SetOrigin(selectedImage->GetGeometry()->GetCenter()); // Rotate plane mitk::Vector3D rotationAxis; mitk::FillVector3D(rotationAxis, 0.0, 1.0, 0.0); mitk::RotationOperation op(mitk::OpROTATE, selectedImage->GetGeometry()->GetCenter(), rotationAxis, 90.0); plane->GetGeometry()->ExecuteOperation(&op); } } // equivalent to the extent and resolution function of the clipping plane const auto x = imageDiagonal * 0.9; planeSource->SetOrigin(-x / 2.0, -x / 2.0, 0.0); planeSource->SetPoint1(x / 2.0, -x / 2.0, 0.0); planeSource->SetPoint2(-x / 2.0, x / 2.0, 0.0); planeSource->SetResolution(64, 64); planeSource->Update(); plane->SetVtkPolyData(planeSource->GetOutput()); // Set scalars (for colorization of plane) vtkFloatArray *scalars = vtkFloatArray::New(); scalars->SetName("Distance"); scalars->SetNumberOfComponents(1); const auto numerOfPoints = plane->GetVtkPolyData(0)->GetNumberOfPoints(); for (std::remove_const_t i = 0; i < plane->GetVtkPolyData(0)->GetNumberOfPoints(); ++i) { scalars->InsertNextValue(-1.0); } plane->GetVtkPolyData(0)->GetPointData()->SetScalars(scalars); plane->GetVtkPolyData(0)->GetPointData()->Update(); auto planeNode = mitk::DataNode::New(); planeNode->SetData(plane); std::stringstream planeName; planeName << "ClippingPlane "; planeName << this->GetAllClippingPlanes()->Size() + 1; planeNode->SetName(planeName.str()); planeNode->AddProperty("clippingPlane", mitk::BoolProperty::New(true)); // Make plane pickable planeNode->SetBoolProperty("pickable", true); mitk::SurfaceVtkMapper3D::SetDefaultProperties(planeNode); // Don't include plane in bounding box! planeNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); // Set lookup table for plane surface visualization auto lookupTablevtk = vtkSmartPointer::New(); lookupTablevtk->SetHueRange(0.6, 0.0); lookupTablevtk->SetSaturationRange(1.0, 1.0); lookupTablevtk->SetValueRange(1.0, 1.0); lookupTablevtk->SetTableRange(-1.0, 1.0); lookupTablevtk->Build(); auto lookupTable = mitk::LookupTable::New(); lookupTable->SetVtkLookupTable(lookupTablevtk); auto prop = mitk::LookupTableProperty::New(lookupTable); planeNode->SetProperty("LookupTable", prop); planeNode->SetBoolProperty("scalar visibility", true); planeNode->SetBoolProperty("color mode", true); planeNode->SetFloatProperty("ScalarsRangeMinimum", -1.0); planeNode->SetFloatProperty("ScalarsRangeMaximum", 1.0); // Configure material so that only scalar colors are shown planeNode->SetColor(0.0f, 0.0f, 0.0f); planeNode->SetOpacity(1.0f); planeNode->SetFloatProperty("material.wireframeLineWidth", 2.0f); //Set view of plane to wireframe planeNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME)); //Add the plane to data storage this->GetDataStorage()->Add(planeNode); //Change the index of the selector to the new generated node m_Controls->clippingPlaneSelector->setCurrentIndex(m_Controls->clippingPlaneSelector->Find(planeNode)); m_Controls->interactionSelectionBox->setEnabled(true); if (auto renderWindowPart = dynamic_cast(this->GetRenderWindowPart())) { renderWindowPart->EnableSlicingPlanes(false); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDeformableClippingPlaneView::OnCalculateClippingVolume() { auto selectedNode = m_Controls->imageSelectionWidget->GetSelectedNode(); if (selectedNode.IsNull()) { MITK_ERROR << "No segmentation selected! Can't calculate volume"; return; } bool isSegmentation = false; selectedNode->GetBoolProperty("binary", isSegmentation); if (!isSegmentation) { MITK_ERROR << "No segmentation selected! Can't calculate volume"; return; } std::vector clippingPlanes; mitk::DataStorage::SetOfObjects::ConstPointer allClippingPlanes = this->GetAllClippingPlanes(); for (mitk::DataStorage::SetOfObjects::ConstIterator itPlanes = allClippingPlanes->Begin(); itPlanes != allClippingPlanes->End(); itPlanes++) { bool isVisible = false; itPlanes.Value()->GetBoolProperty("visible", isVisible); auto plane = dynamic_cast(itPlanes.Value()->GetData()); if (isVisible && nullptr != plane) clippingPlanes.push_back(plane); } if (clippingPlanes.empty()) { MITK_ERROR << "No clipping plane selected! Can't calculate volume"; return; } // deactivate Tools this->DeactivateInteractionButtons(); //Clear the list of volumes, before calculating the new values m_Controls->volumeList->clear(); selectedNode->SetBoolProperty("visible", false); //set some properties for clipping the image-->Output: labeled Image mitk::HeightFieldSurfaceClipImageFilter::Pointer surfaceClipFilter = mitk::HeightFieldSurfaceClipImageFilter::New(); surfaceClipFilter->SetInput(dynamic_cast(selectedNode->GetData())); surfaceClipFilter->SetClippingModeToMultiPlaneValue(); surfaceClipFilter->SetClippingSurfaces(clippingPlanes); surfaceClipFilter->Update(); //delete the old clipped image node mitk::DataStorage::SetOfObjects::ConstPointer oldClippedNode = this->GetDataStorage()->GetSubset(mitk::NodePredicateProperty::New("name", mitk::StringProperty::New("Clipped Image"))); if (oldClippedNode.IsNotNull()) this->GetDataStorage()->Remove(oldClippedNode); //add the new clipped image node auto clippedNode = mitk::DataNode::New(); mitk::Image::Pointer clippedImage = surfaceClipFilter->GetOutput(); clippedImage->DisconnectPipeline(); clippedNode->SetData(clippedImage); clippedNode->SetName("Clipped Image"); clippedNode->SetColor(1.0, 1.0, 1.0); // color property will not be used, labeled image lookuptable will be used instead clippedNode->SetProperty("use color", mitk::BoolProperty::New(false)); clippedNode->SetProperty("reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_NEAREST)); clippedNode->SetOpacity(0.4); this->GetDataStorage()->Add(clippedNode); auto volumeCalculator = mitk::LabeledImageVolumeCalculator::New(); volumeCalculator->SetImage(clippedImage); volumeCalculator->Calculate(); auto volumes = volumeCalculator->GetVolumes(); auto lookupTable = mitk::LabeledImageLookupTable::New(); int lablesWithVolume = 0; const auto numberOfVolumes = volumes.size(); for (std::remove_const_t i = 1; i < numberOfVolumes; ++i) { if (0 != volumes[0]) { lablesWithVolume++; mitk::Color color(GetLabelColor(lablesWithVolume)); lookupTable->SetColorForLabel(i, color.GetRed(), color.GetGreen(), color.GetBlue(), 1.0); QColor qcolor; qcolor.setRgbF(color.GetRed(), color.GetGreen(), color.GetBlue(), 0.7); //output volume as string "x.xx ml" std::stringstream stream; stream << std::fixed << std::setprecision(2) << 0.001 * volumes[i] << " ml"; stream << " ml"; auto item = new QListWidgetItem(); item->setText(QString::fromStdString(stream.str())); - item->setBackgroundColor(qcolor); + item->setBackground(qcolor); m_Controls->volumeList->addItem(item); } } //set the rendering mode to use the lookup table and level window clippedNode->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR)); mitk::LookupTableProperty::Pointer lutProp = mitk::LookupTableProperty::New(lookupTable.GetPointer()); clippedNode->SetProperty("LookupTable", lutProp); // it is absolutely important, to use the LevelWindow settings provided by // the LUT generator, otherwise, it is not guaranteed, that colors show // up correctly. clippedNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(lookupTable->GetLevelWindow())); } void QmitkDeformableClippingPlaneView::OnTranslationMode(bool check) { if (check) { //uncheck all other buttons m_Controls->rotationPushButton->setChecked(false); m_Controls->deformationPushButton->setChecked(false); mitk::ClippingPlaneInteractor3D::Pointer affineDataInteractor = mitk::ClippingPlaneInteractor3D::New(); affineDataInteractor->LoadStateMachine("ClippingPlaneInteraction3D.xml", us::ModuleRegistry::GetModule("MitkDataTypesExt")); affineDataInteractor->SetEventConfig("ClippingPlaneTranslationConfig.xml", us::ModuleRegistry::GetModule("MitkDataTypesExt")); affineDataInteractor->SetDataNode(m_WorkingNode); } else m_WorkingNode->SetDataInteractor(nullptr); } void QmitkDeformableClippingPlaneView::OnRotationMode(bool check) { if (check) { //uncheck all other buttons m_Controls->translationPushButton->setChecked(false); m_Controls->deformationPushButton->setChecked(false); mitk::ClippingPlaneInteractor3D::Pointer affineDataInteractor = mitk::ClippingPlaneInteractor3D::New(); affineDataInteractor->LoadStateMachine("ClippingPlaneInteraction3D.xml", us::ModuleRegistry::GetModule("MitkDataTypesExt")); affineDataInteractor->SetEventConfig("ClippingPlaneRotationConfig.xml", us::ModuleRegistry::GetModule("MitkDataTypesExt")); affineDataInteractor->SetDataNode(m_WorkingNode); } else m_WorkingNode->SetDataInteractor(nullptr); } void QmitkDeformableClippingPlaneView::OnDeformationMode(bool check) { if (check) { //uncheck all other buttons m_Controls->translationPushButton->setChecked(false); m_Controls->rotationPushButton->setChecked(false); mitk::SurfaceDeformationDataInteractor3D::Pointer surfaceDataInteractor = mitk::SurfaceDeformationDataInteractor3D::New(); surfaceDataInteractor->LoadStateMachine("ClippingPlaneInteraction3D.xml", us::ModuleRegistry::GetModule("MitkDataTypesExt")); surfaceDataInteractor->SetEventConfig("ClippingPlaneDeformationConfig.xml", us::ModuleRegistry::GetModule("MitkDataTypesExt")); surfaceDataInteractor->SetDataNode(m_WorkingNode); } else m_WorkingNode->SetDataInteractor(nullptr); } void QmitkDeformableClippingPlaneView::CreateConnections() { connect(m_Controls->imageSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkDeformableClippingPlaneView::OnCurrentSelectionChanged); connect(m_Controls->translationPushButton, &QPushButton::toggled, this, &QmitkDeformableClippingPlaneView::OnTranslationMode); connect(m_Controls->rotationPushButton, &QPushButton::toggled, this, &QmitkDeformableClippingPlaneView::OnRotationMode); connect(m_Controls->deformationPushButton, &QPushButton::toggled, this, &QmitkDeformableClippingPlaneView::OnDeformationMode); connect(m_Controls->createNewPlanePushButton, &QPushButton::clicked, this, &QmitkDeformableClippingPlaneView::OnCreateNewClippingPlane); connect(m_Controls->updateVolumePushButton, &QPushButton::clicked, this, &QmitkDeformableClippingPlaneView::OnCalculateClippingVolume); connect(m_Controls->clippingPlaneSelector, &QmitkDataStorageComboBox::OnSelectionChanged, this, &QmitkDeformableClippingPlaneView::OnComboBoxSelectionChanged); } void QmitkDeformableClippingPlaneView::NodeRemoved(const mitk::DataNode* node) { if (m_IsClippingPlanePredicate->CheckNode(node)) { if (this->GetAllClippingPlanes()->Size() <= 1) { m_WorkingNode = nullptr; this->UpdateView(); } } } void::QmitkDeformableClippingPlaneView::NodeChanged(const mitk::DataNode*) { this->UpdateView(); } void QmitkDeformableClippingPlaneView::UpdateView() { auto selectedNode = m_Controls->imageSelectionWidget->GetSelectedNode(); if (selectedNode.IsNotNull()) { m_Controls->selectedReferenceImageLabel->setText(QString::fromUtf8(selectedNode->GetName().c_str())); if (m_WorkingNode.IsNotNull()) { bool isSegmentation = false; selectedNode->GetBoolProperty("binary", isSegmentation); m_Controls->interactionSelectionBox->setEnabled(true); m_Controls->volumeGroupBox->setEnabled(isSegmentation); //clear list --> than search for all shown clipping plans (max 7 planes) m_Controls->selectedClippingPlanesLabel->setText(""); m_Controls->planesWarningLabel->hide(); int volumePlanes = 0; auto allClippingPlanes = this->GetAllClippingPlanes(); for (mitk::DataStorage::SetOfObjects::ConstIterator itPlanes = allClippingPlanes->Begin(); itPlanes != allClippingPlanes->End(); itPlanes++) { bool isVisible = false; itPlanes.Value()->GetBoolProperty("visible", isVisible); if (isVisible) { if (volumePlanes < 7) { ++volumePlanes; m_Controls->selectedClippingPlanesLabel->setText(m_Controls->selectedClippingPlanesLabel->text().append(QString::fromStdString(itPlanes.Value()->GetName()+"\n"))); } else { m_Controls->planesWarningLabel->show(); return; } } } } else { m_Controls->volumeGroupBox->setEnabled(false); m_Controls->interactionSelectionBox->setEnabled(false); m_Controls->selectedClippingPlanesLabel->setText(""); m_Controls->volumeList->clear(); } } else { m_Controls->volumeGroupBox->setEnabled(false); m_Controls->selectedReferenceImageLabel->setText(""); m_Controls->selectedClippingPlanesLabel->setText(""); m_Controls->planesWarningLabel->hide(); m_Controls->interactionSelectionBox->setEnabled(m_WorkingNode.IsNotNull()); } } mitk::DataStorage::SetOfObjects::ConstPointer QmitkDeformableClippingPlaneView::GetAllClippingPlanes() { auto allPlanes = GetDataStorage()->GetSubset(m_IsClippingPlanePredicate); return allPlanes; } mitk::Color QmitkDeformableClippingPlaneView::GetLabelColor(int label) { std::array color = { 0.0f, 0.0f, 0.0f }; switch (label % 6) { case 0: // red color[0] = 1.0f; break; case 1: // green color[1] = 1.0f; break; case 2: // blue color[2] = 1.0f; break; case 3: // yellow color[0] = 1.0f; color[1] = 1.0f; break; case 4: // magenta color[0] = 1.0f; color[2] = 1.0f; break; case 5: // cyan color[1] = 1.0f; color[2] = 1.0f; default: // black break; } int outerCycleNr = label / 6; int cycleSize = std::min(1, static_cast(std::pow(2.0, std::log(outerCycleNr) / std::log(2.0)))); int insideCycleCounter = outerCycleNr % cycleSize; float factor; if (0 == outerCycleNr) { factor = 255.0f; } else { factor = 256.0f / (2.0f * cycleSize) + insideCycleCounter * (256.0f / cycleSize); } color = { std::min(1.0f, color[0] / 256.0f * factor), std::min(1.0f, color[1] / 256.0f * factor), std::min(1.0f, color[2] / 256.0f * factor) }; return mitk::Color(color.data()); } void QmitkDeformableClippingPlaneView::DeactivateInteractionButtons() { m_Controls->translationPushButton->setChecked(false); m_Controls->rotationPushButton->setChecked(false); m_Controls->deformationPushButton->setChecked(false); } 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 bb5465eb1b..b7351fe0f3 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,918 +1,918 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "ModelFitInspectorView.h" // Blueberry #include #include #include // mitk #include // Qt #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const std::string ModelFitInspectorView::VIEW_ID = "org.mitk.views.fit.inspector"; 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_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) { bool isModelFitNode = node->GetData() && node->GetData()->GetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str()).IsNotNull(); return isModelFitNode || (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, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &ModelFitInspectorView::OnInputChanged); 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))); connect(m_Controls.btnFullPlot, SIGNAL(clicked(bool)), this, SLOT(OnFullPlotClicked(bool))); 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 - std::abs(minmax.first) * 0.01; auto max = minmax.second + std::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 - std::abs(minmax.first) * 0.01; auto max = minmax.second + std::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(); }; void ModelFitInspectorView::OnFullPlotClicked(bool checked) { m_Controls.tabWidget->setVisible(!checked); }; int ModelFitInspectorView::ActualizeFitSelectionWidget() { mitk::modelFit::ModelFitInfo::UIDType selectedFitUD = ""; bool isModelFitNode = false; if (this->m_Controls.inputNodeSelector->GetSelectedNode().IsNotNull()) { isModelFitNode = this->m_Controls.inputNodeSelector->GetSelectedNode()->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)); std::ostringstream nameStrm; if (info->fitName.empty()) { nameStrm << info->uid; } else { nameStrm << info->fitName; } nameStrm << " (" << info->modelName << ")"; QVariant data(info->uid.c_str()); m_Controls.cmbFit->addItem(QString::fromStdString(nameStrm.str()), 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 available 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::modelFit::ModelFitInfo::UIDType selectedFitUD = ""; bool isModelFitNode = this->m_currentSelectedNode->GetData()->GetPropertyList()->GetStringProperty( mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), selectedFitUD); if (isModelFitNode) { this->m_currentSelectedNode = this->GetInputNode(this->m_currentSelectedNode); if (this->m_currentSelectedNode.IsNull()) { MITK_WARN << "Model fit Inspector in invalid state. Input image for selected fit cannot be found in data storage. Failed fit UID:" << selectedFitUD; } } 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::GetInputNode(mitk::DataNode::ConstPointer node) { if (node.IsNotNull()) { std::string selectedFitUD = ""; auto rule = mitk::ModelFitResultRelationRule::New(); auto predicate = rule->GetDestinationsDetector(node); mitk::DataStorage::SetOfObjects::ConstPointer parentNodeList = GetDataStorage()->GetSubset(predicate); if (parentNodeList->size() > 0) { return parentNodeList->front().GetPointer(); } } return mitk::DataNode::ConstPointer(); } void ModelFitInspectorView::ValidateAndSetCurrentPosition() { const mitk::Point3D currentSelectedPosition = GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(nullptr); const unsigned int currentSelectedTimestep = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimeStep(); 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; auto inputImage = this->GetCurrentInputImage(); if (inputImage.IsNull()) { return; } mitk::BaseGeometry::ConstPointer geometry = inputImage->GetTimeGeometry()->GetGeometryForTimeStep( m_currentSelectedTimeStep).GetPointer(); // 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::ConstPointer ModelFitInspectorView::GetCurrentInputImage() const { mitk::Image::ConstPointer 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(); } } 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 = mitk::GenerateSupersampledTimeGrid(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.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; } 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); + newItem->setBackground(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 = 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); 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; } } diff --git a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUSNavigationCalibrationsDataModel.cpp b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUSNavigationCalibrationsDataModel.cpp index 9e6f972eef..ac12f4f207 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUSNavigationCalibrationsDataModel.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUSNavigationCalibrationsDataModel.cpp @@ -1,244 +1,244 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkUSNavigationCalibrationsDataModel.h" #include #include #include #include #include #include QmitkUSNavigationCalibrationsDataModel::QmitkUSNavigationCalibrationsDataModel(QObject *parent) : QAbstractTableModel(parent), m_ListenerDeviceChanged(this, &QmitkUSNavigationCalibrationsDataModel::OnDeviceChanged) { } QmitkUSNavigationCalibrationsDataModel::~QmitkUSNavigationCalibrationsDataModel() { if ( m_CombinedModality.IsNotNull() ) { m_CombinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_ListenerDeviceChanged); } } void QmitkUSNavigationCalibrationsDataModel::SetCombinedModality(mitk::AbstractUltrasoundTrackerDevice::Pointer combinedModality) { if ( m_CombinedModality.IsNotNull() && m_CombinedModality->GetUltrasoundDevice().IsNotNull() ) { m_CombinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_ListenerDeviceChanged); } m_CombinedModality = combinedModality; if ( m_CombinedModality.IsNotNull() ) { m_ControlInterfaceBMode = m_CombinedModality->GetControlInterfaceBMode(); // make sure that the combined modality is active as this may be // necessary to get the available depths if ( m_CombinedModality->GetUltrasoundDevice()->GetDeviceState() < mitk::USDevice::State_Connected ) { m_CombinedModality->GetUltrasoundDevice()->Connect(); } if ( m_CombinedModality->GetUltrasoundDevice()->GetDeviceState() == mitk::USDevice::State_Connected ) { m_CombinedModality->GetUltrasoundDevice()->Activate(); } if ( m_CombinedModality->GetUltrasoundDevice().IsNotNull() ) { m_CombinedModality->GetUltrasoundDevice()->AddPropertyChangedListener(m_ListenerDeviceChanged); } } // as the combined modality was changed, an old table model // would not be valid anymore this->beginResetModel(); this->endResetModel(); } void QmitkUSNavigationCalibrationsDataModel::OnDeviceChanged(const std::string&, const std::string&) { this->beginResetModel(); this->endResetModel(); } /** \brief Return number of rows of the model. */ int QmitkUSNavigationCalibrationsDataModel::rowCount ( const QModelIndex & ) const { if ( m_ControlInterfaceBMode.IsNull() ) { return 1; // only one default depth can be assumed } else { return m_ControlInterfaceBMode->GetScanningDepthValues().size(); } } /** \brief Return number of columns (3) of the model. */ int QmitkUSNavigationCalibrationsDataModel::columnCount ( const QModelIndex & ) const { return 3; } /** \brief Return names for the columns, numbers for the rows and invalid for DisplayRole. */ QVariant QmitkUSNavigationCalibrationsDataModel::headerData ( int section, Qt::Orientation orientation, int role ) const { if ( role != Qt::DisplayRole ) { return QVariant(QVariant::Invalid); } if ( orientation == Qt::Horizontal ) { switch ( section ) { case 0: return QVariant("Depth"); case 1: return QVariant("Calibrated"); case 2: return QVariant(""); } } return QVariant(QVariant::Invalid); } /** \brief Return selectable and enabled for column 1 (size); selectable, enabled and editable for every other column. */ Qt::ItemFlags QmitkUSNavigationCalibrationsDataModel::flags ( const QModelIndex & ) const { return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } /** \brief Return model data of the selected cell. */ QVariant QmitkUSNavigationCalibrationsDataModel::data ( const QModelIndex & index, int role ) const { if ( m_CombinedModality.IsNull() ) { return QVariant(QVariant::Invalid); } std::vector scanningDepthValues = m_ControlInterfaceBMode.IsNull() ? std::vector(1,0) : m_ControlInterfaceBMode->GetScanningDepthValues(); // make sure that row and column index fit data borders if (index.row() >= this->rowCount() || index.column() >= this->columnCount()) { return QVariant(QVariant::Invalid); } double currentDepth = 0; if ( m_ControlInterfaceBMode.IsNotNull() ) { currentDepth = m_ControlInterfaceBMode->GetScanningDepth(); } bool isCalibratedForCurrentDepth = m_CombinedModality->GetCalibration(QString::number(scanningDepthValues.at(index.row())).toStdString()).IsNotNull(); switch (role) { - case Qt::BackgroundColorRole: + case Qt::BackgroundRole: { if ( isCalibratedForCurrentDepth ) { return QVariant(QBrush(QColor(125, 255, 125))); } else { return QVariant(QBrush(QColor(255, 125, 125))); } } case Qt::FontRole: { if ( scanningDepthValues.at(index.row()) == currentDepth ) { QFont qFont; qFont.setBold(true); return qFont; } else { return QVariant::Invalid; } } case Qt::DecorationRole: { if ( index.column() == 2 ) { if ( isCalibratedForCurrentDepth ) { return QIcon(":/USNavigation/process-stop.png"); } } break; } case Qt::EditRole: case Qt::DisplayRole: { switch ( index.column() ) { case 0: { return QVariant::fromValue(scanningDepthValues.at(index.row())); } case 1: { if ( m_ControlInterfaceBMode.IsNull() ) { // use the current zoom level (which is assumed to be the only one), // when no b mode controls are available return QVariant(m_CombinedModality->GetCalibration().IsNotNull()); } else { return QVariant(isCalibratedForCurrentDepth); } } case 2: { return QVariant(""); } } break; } case Qt::ToolTipRole: { if ( index.column() == 2 && isCalibratedForCurrentDepth ) { return QVariant(QString("Remove calibration for depth ") + QString::number(scanningDepthValues.at(index.row())) + " on mouse click."); } break; } } return QVariant(QVariant::Invalid); } /** \brief Set model data for the selected cell. */ bool QmitkUSNavigationCalibrationsDataModel::setData ( const QModelIndex & index, const QVariant & value, int ) { if ( m_CombinedModality.IsNull() || index.column() != 2 || value != false ) return false; if ( m_ControlInterfaceBMode.IsNull() ) { m_CombinedModality->RemoveCalibration(); } else { m_CombinedModality->RemoveCalibration(QString::number(m_ControlInterfaceBMode->GetScanningDepthValues().at(index.row())).toStdString()); } emit dataChanged(this->index(index.row(), 0), this->index(index.row(), 1)); return true; } /** \brief Remove given rows from the model. * \param removeFromDataStorage zone nodes are removed from the data storage too, if this is set to true */ bool QmitkUSNavigationCalibrationsDataModel::removeRows ( int, int, const QModelIndex&, bool ) { return false; } diff --git a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUSZonesDataModel.cpp b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUSZonesDataModel.cpp index 9b75e10e90..0427f253f8 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUSZonesDataModel.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUSZonesDataModel.cpp @@ -1,327 +1,327 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkUSZonesDataModel.h" #include #include "mitkMessage.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "Interactors/mitkUSZonesInteractor.h" QmitkUSZonesDataModel::QmitkUSZonesDataModel(QObject *parent) : QAbstractTableModel(parent), m_ListenerAddNode(this, &QmitkUSZonesDataModel::AddNode), m_ListenerChangeNode(this, &QmitkUSZonesDataModel::ChangeNode), m_ListenerRemoveNode(this, &QmitkUSZonesDataModel::RemoveNode) { } QmitkUSZonesDataModel::~QmitkUSZonesDataModel() { if ( m_DataStorage.IsNotNull() ) { m_DataStorage->AddNodeEvent.RemoveListener(m_ListenerAddNode); m_DataStorage->ChangedNodeEvent.RemoveListener(m_ListenerChangeNode); m_DataStorage->InteractorChangedNodeEvent.RemoveListener(m_ListenerChangeNode); m_DataStorage->RemoveNodeEvent.RemoveListener(m_ListenerRemoveNode); } } void QmitkUSZonesDataModel::SetDataStorage(mitk::DataStorage::Pointer dataStorage, mitk::DataNode::Pointer baseNode) { m_DataStorage = dataStorage; m_BaseNode = baseNode; if ( m_DataStorage.IsNotNull() ) { m_DataStorage->AddNodeEvent.AddListener(m_ListenerAddNode); m_DataStorage->RemoveNodeEvent.AddListener(m_ListenerRemoveNode); m_DataStorage->ChangedNodeEvent.AddListener(m_ListenerChangeNode); m_DataStorage->InteractorChangedNodeEvent.AddListener(m_ListenerChangeNode); } } void QmitkUSZonesDataModel::AddNode(const mitk::DataNode* node) { if ( m_DataStorage.IsNull() ) { MITK_ERROR << "DataStorage has to be set before adding the first zone node."; mitkThrow() << "DataStorage has to be set before adding the first zone node."; } // do not add nodes, which aren't fully created yet bool boolValue; if ( ! (node->GetBoolProperty(mitk::USZonesInteractor::DATANODE_PROPERTY_CREATED, boolValue) && boolValue) ) { return; } // get source node of given node and test if m_BaseNode is a source node mitk::DataStorage::SetOfObjects::ConstPointer sourceNodes = m_DataStorage->GetSources(node); mitk::DataStorage::SetOfObjects::ConstIterator baseNodeIt = sourceNodes->Begin(); while ( baseNodeIt != sourceNodes->End() && baseNodeIt->Value() != m_BaseNode ) { ++baseNodeIt; } // only nodes below m_BaseNode should be added to the model if ( baseNodeIt == sourceNodes->End() ) { return; } int newRowIndex = this->rowCount(); this->insertRow(newRowIndex); m_ZoneNodes.at(newRowIndex) = const_cast(node); // get row of the changed node and emit signal that the data of this row changed emit dataChanged(this->index(newRowIndex, 0), this->index(newRowIndex, this->columnCount())); } void QmitkUSZonesDataModel::RemoveNode(const mitk::DataNode* node) { // find index of the given node in the nodes vector unsigned int index = 0; DataNodeVector::iterator current = m_ZoneNodes.begin(); while (current != m_ZoneNodes.end()) { if ( *current == node ) { break; } ++index; ++current; } // remove node from model if it was found if ( current != m_ZoneNodes.end() ) { // remove node from the model and make sure that there will be no // recursive removing calls (as removing node from data storage inside // removeRows function will lead into another call of RemoveNode). this->removeRows(index, 1, QModelIndex(), false); } } void QmitkUSZonesDataModel::ChangeNode(const mitk::DataNode* node) { if ( static_cast >(node).IsNull() ) { return; } DataNodeVector::iterator oldNodeIt = find (m_ZoneNodes.begin(), m_ZoneNodes.end(), node); if (oldNodeIt == m_ZoneNodes.end()) { // if node was not added yet, but it's creation is finished -> add it now bool boolValue; if ( node->GetBoolProperty(mitk::USZonesInteractor::DATANODE_PROPERTY_CREATED, boolValue) && boolValue ) { this->AddNode(node); } return; } // get row of the changed node and emit signal that the data of this row changed unsigned int row = oldNodeIt - m_ZoneNodes.begin(); emit dataChanged(this->index(row, 0), this->index(row, this->columnCount())); } int QmitkUSZonesDataModel::rowCount ( const QModelIndex& /*parent*/ ) const { return m_ZoneNodes.size(); } int QmitkUSZonesDataModel::columnCount ( const QModelIndex& /*parent*/ ) const { return 3; } QVariant QmitkUSZonesDataModel::headerData ( int section, Qt::Orientation orientation, int role ) const { if ( role != Qt::DisplayRole ) { return QVariant(QVariant::Invalid); } if ( orientation == Qt::Horizontal ) { switch ( section ) { case 0: return QVariant("Name"); case 1: return QVariant("Size"); case 2: return QVariant("Color"); } } else { return QVariant(section+1); } return QVariant(QVariant::Invalid); } Qt::ItemFlags QmitkUSZonesDataModel::flags ( const QModelIndex& index ) const { if (index.column() == 1 || index.column() == 2) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled; } QVariant QmitkUSZonesDataModel::data ( const QModelIndex& index, int role ) const { // make sure that row and column index fit data borders if (static_cast(index.row()) >= m_ZoneNodes.size() || index.column() >= this->columnCount()) { return QVariant(QVariant::Invalid); } mitk::DataNode::Pointer curNode = m_ZoneNodes.at(index.row()); switch (role) { - case Qt::BackgroundColorRole: + case Qt::BackgroundRole: { float color[3]; if ( curNode->GetColor(color) ) { QColor qColor(color[0] * 255, color[1] * 255, color[2] * 255); if (qColor.isValid()) { return QVariant(QBrush(qColor)); } } break; } case Qt::EditRole: case Qt::DisplayRole: { switch ( index.column() ) { case 0: { return QString::fromStdString(curNode->GetName()); } case 1: { float floatValue; if ( curNode->GetFloatProperty(mitk::USZonesInteractor::DATANODE_PROPERTY_SIZE, floatValue) ) { return static_cast(floatValue); } else { return QVariant(QVariant::Invalid); } } case 2: { float color[3]; if ( curNode->GetColor(color) ) { QColor qColor(color[0] * 255, color[1] * 255, color[2] * 255); if (qColor == Qt::darkGreen) { return QVariant("Green"); } else if (qColor == Qt::red) { return QVariant("Red"); } else if (qColor == Qt::blue) { return QVariant("Blue"); } else if (qColor == Qt::yellow) { return QVariant("Yellow"); } else { return QVariant(qColor.name()); } } else { return QVariant(QVariant::Invalid); } } } break; } } return QVariant(QVariant::Invalid); } bool QmitkUSZonesDataModel::setData ( const QModelIndex & index, const QVariant & value, int role ) { if (role == Qt::EditRole) { if (static_cast(index.row()) >= m_ZoneNodes.size() || index.column() >= this->columnCount()) { return false; } mitk::DataNode::Pointer curNode = m_ZoneNodes.at(index.row()); switch ( index.column() ) { case 0: { curNode->SetName(value.toString().toStdString()); break; } case 1: { curNode->SetFloatProperty(mitk::USZonesInteractor::DATANODE_PROPERTY_SIZE, value.toFloat()); if (curNode->GetData() != nullptr) { curNode->SetFloatProperty(mitk::USZonesInteractor::DATANODE_PROPERTY_SIZE, value.toFloat()); mitk::USZonesInteractor::UpdateSurface(curNode); } break; } case 2: { QColor color(value.toString()); curNode->SetColor(color.redF(), color.greenF(), color.blueF()); break; } default: return false; } emit dataChanged(index, index); } // update the RenderWindow to show new points mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return true; } bool QmitkUSZonesDataModel::insertRows ( int row, int count, const QModelIndex & parent ) { this->beginInsertRows(parent, row, row+count-1); for ( int n = 0; n < count; ++n ) { m_ZoneNodes.insert(m_ZoneNodes.begin()+row, mitk::DataNode::New()); } this->endInsertRows(); return true; } bool QmitkUSZonesDataModel::removeRows ( int row, int count, const QModelIndex & parent ) { return this->removeRows(row, count, parent, true); } bool QmitkUSZonesDataModel::removeRows ( int row, int count, const QModelIndex & parent, bool removeFromDataStorage ) { if ( static_cast(row+count) > m_ZoneNodes.size() ) { return false; } this->beginRemoveRows(parent, row, row+count-1); for ( int n = count-1; n >= 0; --n ) { DataNodeVector::iterator it = m_ZoneNodes.begin()+row+n; mitk::DataNode::Pointer curNode = *it; m_ZoneNodes.erase(it); if ( removeFromDataStorage && m_DataStorage.IsNotNull() ) { m_DataStorage->Remove(curNode); } } this->endRemoveRows(); return true; }