diff --git a/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidget.cpp b/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidget.cpp index db30798301..324b7471c9 100644 --- a/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidget.cpp +++ b/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidget.cpp @@ -1,233 +1,259 @@ /*============================================================================ 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 "QmitkBooleanOperationsWidget.h" #include <ui_QmitkBooleanOperationsWidgetControls.h> #include <mitkDataStorage.h> #include <mitkException.h> #include <mitkRenderingManager.h> #include <mitkMultiLabelPredicateHelper.h> #include <mitkBooleanOperation.h> #include <mitkProgressBar.h> #include <mitkLabelSetImageHelper.h> QmitkBooleanOperationsWidget::QmitkBooleanOperationsWidget(mitk::DataStorage* dataStorage, QWidget* parent) : QWidget(parent) { m_Controls = new Ui::QmitkBooleanOperationsWidgetControls; m_Controls->setupUi(this); m_Controls->label1st->setText("<img width=16 height=16 src=\":/Qmitk/BooleanLabelA_32x32.png\"/>"); m_Controls->label1st->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); m_Controls->label2nd->setText("<img width=16 height=16 src=\":/Qmitk/BooleanLabelB_32x32.png\"/>"); m_Controls->label2nd->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); m_Controls->segNodeSelector->SetDataStorage(dataStorage); m_Controls->segNodeSelector->SetNodePredicate(mitk::GetMultiLabelSegmentationPredicate()); m_Controls->segNodeSelector->SetSelectionIsOptional(false); m_Controls->segNodeSelector->SetInvalidInfo(QStringLiteral("Please select segmentation for extraction.")); m_Controls->segNodeSelector->SetPopUpTitel(QStringLiteral("Select segmentation")); m_Controls->segNodeSelector->SetPopUpHint(QStringLiteral("Select the segmentation that should be used as source for extraction.")); m_Controls->labelInspector->SetMultiSelectionMode(true); connect(m_Controls->segNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkBooleanOperationsWidget::OnSegSelectionChanged); connect(m_Controls->labelInspector, &QmitkMultiLabelInspector::CurrentSelectionChanged, this, &QmitkBooleanOperationsWidget::OnLabelSelectionChanged); connect(m_Controls->differenceButton, SIGNAL(clicked()), this, SLOT(OnDifferenceButtonClicked())); connect(m_Controls->intersectionButton, SIGNAL(clicked()), this, SLOT(OnIntersectionButtonClicked())); connect(m_Controls->unionButton, SIGNAL(clicked()), this, SLOT(OnUnionButtonClicked())); m_Controls->segNodeSelector->SetAutoSelectNewNodes(true); + this->ConfigureWidgets(); } QmitkBooleanOperationsWidget::~QmitkBooleanOperationsWidget() { m_Controls->labelInspector->SetMultiLabelNode(nullptr); } void QmitkBooleanOperationsWidget::OnSegSelectionChanged(QmitkAbstractNodeSelectionWidget::NodeList /*nodes*/) { auto node = m_Controls->segNodeSelector->GetSelectedNode(); m_Controls->labelInspector->SetMultiLabelNode(node); this->ConfigureWidgets(); } void QmitkBooleanOperationsWidget::OnLabelSelectionChanged(mitk::LabelSetImage::LabelValueVectorType labels) { this->ConfigureWidgets(); } +namespace +{ + std::string GenerateLabelHTML(const mitk::Label* label) + { + std::stringstream stream; + auto color = label->GetColor(); + stream << "<span style='color: #" << std::hex << std::setfill('0') + << std::setw(2) << static_cast<int>(color.GetRed()*255) + << std::setw(2) << static_cast<int>(color.GetGreen()*255) + << std::setw(2) << static_cast<int>(color.GetBlue()*255) + << "; font-size: 20px '>■</span>" << std::dec; + + stream << "<font class=\"normal\"> " << label->GetName()<< "</font>"; + return stream.str(); + } +} void QmitkBooleanOperationsWidget::ConfigureWidgets() { auto selectedLabelValues = m_Controls->labelInspector->GetSelectedLabels(); + auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation(); + + auto styleSheet = qApp->styleSheet(); + + m_Controls->line1stLabel->document()->setDefaultStyleSheet(styleSheet); + m_Controls->lineOtherLabels->document()->setDefaultStyleSheet(styleSheet); if (selectedLabelValues.empty()) { - m_Controls->line1stLabel->setText(""); + m_Controls->line1stLabel->setHtml(QStringLiteral("<font class=\"warning\">Select 1st label to proceed.</font>")); } else { - m_Controls->line1stLabel->setText(QString::fromStdString(std::to_string(selectedLabelValues.front()))); + auto label = seg->GetLabel(selectedLabelValues.front()); + m_Controls->line1stLabel->setText(QString::fromStdString(GenerateLabelHTML(label))); } if (selectedLabelValues.size() < 2) { - m_Controls->lineOtherLabels->setText(""); + m_Controls->lineOtherLabels->setHtml(QStringLiteral("<font class=\"warning\">Select secondary label(s) to proceed.</font>")); } else { std::stringstream stream; for (mitk::LabelSetImage::LabelValueVectorType::iterator iter = selectedLabelValues.begin() + 1; iter != selectedLabelValues.end(); ++iter) { - stream << *iter << "; "; + auto label = seg->GetLabel(*iter); + if (stream.rdbuf()->in_avail() != 0) stream << "; "; + stream << GenerateLabelHTML(label); } m_Controls->lineOtherLabels->setText(QString::fromStdString(stream.str())); } m_Controls->differenceButton->setEnabled(selectedLabelValues.size()>1); m_Controls->intersectionButton->setEnabled(selectedLabelValues.size() > 1); m_Controls->unionButton->setEnabled(selectedLabelValues.size() > 1); } void QmitkBooleanOperationsWidget::OnDifferenceButtonClicked() { QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); mitk::ProgressBar::GetInstance()->Reset(); mitk::ProgressBar::GetInstance()->AddStepsToDo(110); unsigned int currentProgress = 0; auto progressCallback = [¤tProgress](float filterProgress) { auto delta = (filterProgress * 100) - currentProgress; if (delta > 0) { currentProgress += delta; mitk::ProgressBar::GetInstance()->Progress(delta); } }; auto selectedLabelValues = m_Controls->labelInspector->GetSelectedLabels(); auto minuend = selectedLabelValues.front(); auto subtrahends = mitk::LabelSetImage::LabelValueVectorType(selectedLabelValues.begin() + 1, selectedLabelValues.end()); auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation(); auto resultMask = mitk::BooleanOperation::GenerateDifference(seg, minuend, subtrahends, progressCallback); std::stringstream name; name << "Difference " << seg->GetLabel(minuend)->GetName() << " - "; for (auto label : subtrahends) { name << " " << seg->GetLabel(label)->GetName(); } this->SaveResultLabelMask(resultMask, name.str()); mitk::ProgressBar::GetInstance()->Reset(); QApplication::restoreOverrideCursor(); } void QmitkBooleanOperationsWidget::OnIntersectionButtonClicked() { QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); mitk::ProgressBar::GetInstance()->Reset(); mitk::ProgressBar::GetInstance()->AddStepsToDo(110); unsigned int currentProgress = 0; auto progressCallback = [¤tProgress](float filterProgress) { auto delta = (filterProgress * 100) - currentProgress; if (delta > 0) { currentProgress += delta; mitk::ProgressBar::GetInstance()->Progress(delta); } }; auto selectedLabelValues = m_Controls->labelInspector->GetSelectedLabels(); auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation(); auto resultMask = mitk::BooleanOperation::GenerateIntersection(seg, selectedLabelValues, progressCallback); std::stringstream name; name << "Intersection"; for (auto label : selectedLabelValues) { name << " " << seg->GetLabel(label)->GetName(); } this->SaveResultLabelMask(resultMask, name.str()); mitk::ProgressBar::GetInstance()->Reset(); QApplication::restoreOverrideCursor(); } void QmitkBooleanOperationsWidget::OnUnionButtonClicked() { QApplication::setOverrideCursor(QCursor(Qt::BusyCursor)); mitk::ProgressBar::GetInstance()->Reset(); mitk::ProgressBar::GetInstance()->AddStepsToDo(110); unsigned int currentProgress = 0; auto progressCallback = [¤tProgress](float filterProgress) { auto delta = (filterProgress * 100) - currentProgress; if (delta > 0) { currentProgress += delta; mitk::ProgressBar::GetInstance()->Progress(delta); } }; auto selectedLabelValues = m_Controls->labelInspector->GetSelectedLabels(); auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation(); auto resultMask = mitk::BooleanOperation::GenerateUnion(seg, selectedLabelValues, progressCallback); std::stringstream name; name << "Union"; for (auto label : selectedLabelValues) { name << " " << seg->GetLabel(label)->GetName(); } this->SaveResultLabelMask(resultMask, name.str()); mitk::ProgressBar::GetInstance()->Reset(); QApplication::restoreOverrideCursor(); } void QmitkBooleanOperationsWidget::SaveResultLabelMask(const mitk::Image* resultMask, const std::string& labelName) const { auto seg = m_Controls->labelInspector->GetMultiLabelSegmentation(); if (seg == nullptr) mitkThrow() << "Widget is in invalid state. Processing was triggered with no segmentation selected."; auto labels = m_Controls->labelInspector->GetSelectedLabels(); if (labels.empty()) mitkThrow() << "Widget is in invalid state. Processing was triggered with no label selected."; auto groupID = seg->AddLayer(); auto newLabel = mitk::LabelSetImageHelper::CreateNewLabel(seg, labelName, true); seg->AddLabelWithContent(newLabel, resultMask, groupID, 1); m_Controls->labelInspector->GetMultiLabelSegmentation()->Modified(); m_Controls->labelInspector->GetMultiLabelNode()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } diff --git a/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidgetControls.ui b/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidgetControls.ui index 8b26bce4a2..b9c623ea2b 100644 --- a/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidgetControls.ui +++ b/Modules/SegmentationUI/SegmentationUtilities/QmitkBooleanOperationsWidgetControls.ui @@ -1,235 +1,291 @@ <?xml version="1.0" encoding="UTF-8"?> <ui version="4.0"> <class>QmitkBooleanOperationsWidgetControls</class> <widget class="QWidget" name="QmitkBooleanOperationsWidgetControls"> <property name="geometry"> <rect> <x>0</x> <y>0</y> <width>249</width> - <height>227</height> + <height>293</height> </rect> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> <widget class="QmitkSingleNodeSelectionWidget" name="segNodeSelector" native="true"/> </item> <item> <widget class="QmitkMultiLabelInspector" name="labelInspector" native="true"/> </item> <item> <widget class="QGroupBox" name="groupBox"> <property name="title"> <string>Selected operands:</string> </property> <layout class="QGridLayout" name="gridLayout"> <property name="leftMargin"> <number>6</number> </property> <property name="topMargin"> <number>0</number> </property> <property name="rightMargin"> <number>6</number> </property> <property name="bottomMargin"> <number>6</number> </property> - <item row="1" column="0"> + <item row="4" column="0"> <widget class="QLabel" name="label2nd"> <property name="text"> <string>TextLabel</string> </property> </widget> </item> - <item row="0" column="0"> + <item row="2" column="1"> + <widget class="QTextEdit" name="line1stLabel"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>26</height> + </size> + </property> + <property name="acceptDrops"> + <bool>false</bool> + </property> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="lineWrapMode"> + <enum>QTextEdit::NoWrap</enum> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + <property name="html"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> +p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +</style></head><body style=" font-family:'Segoe UI'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Test text</p></body></html></string> + </property> + </widget> + </item> + <item row="2" column="0"> <widget class="QLabel" name="label1st"> <property name="text"> <string>TextLabel</string> </property> </widget> </item> - <item row="0" column="1"> - <widget class="QLineEdit" name="line1stLabel"> - <property name="readOnly"> - <bool>true</bool> + <item row="4" column="1"> + <widget class="QTextEdit" name="lineOtherLabels"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Minimum"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> </property> - <property name="placeholderText"> - <string>select 1st label</string> + <property name="maximumSize"> + <size> + <width>16777215</width> + <height>26</height> + </size> + </property> + <property name="contextMenuPolicy"> + <enum>Qt::NoContextMenu</enum> + </property> + <property name="acceptDrops"> + <bool>false</bool> + </property> + <property name="verticalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="horizontalScrollBarPolicy"> + <enum>Qt::ScrollBarAlwaysOff</enum> + </property> + <property name="lineWrapMode"> + <enum>QTextEdit::NoWrap</enum> </property> - </widget> - </item> - <item row="1" column="1"> - <widget class="QLineEdit" name="lineOtherLabels"> <property name="readOnly"> <bool>true</bool> </property> - <property name="placeholderText"> - <string>select label</string> - </property> </widget> </item> </layout> </widget> </item> <item> <layout class="QHBoxLayout" name="horizontalLayout"> <item> <widget class="QToolButton" name="differenceButton"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Subtracts first segmentation from the second one</string> </property> <property name="text"> <string>Difference</string> </property> <property name="icon"> <iconset resource="../resources/SegmentationUI.qrc"> <normaloff>:/Qmitk/BooleanDifference_48x48.png</normaloff>:/Qmitk/BooleanDifference_48x48.png</iconset> </property> <property name="iconSize"> <size> <width>24</width> <height>24</height> </size> </property> <property name="checkable"> <bool>false</bool> </property> <property name="checked"> <bool>false</bool> </property> <property name="toolButtonStyle"> <enum>Qt::ToolButtonTextUnderIcon</enum> </property> <property name="arrowType"> <enum>Qt::NoArrow</enum> </property> </widget> </item> <item> <widget class="QToolButton" name="intersectionButton"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Keeps just the overlapping areas of two the segmentations</string> </property> <property name="text"> <string>Intersection</string> </property> <property name="icon"> <iconset resource="../resources/SegmentationUI.qrc"> <normaloff>:/Qmitk/BooleanIntersection_48x48.png</normaloff>:/Qmitk/BooleanIntersection_48x48.png</iconset> </property> <property name="iconSize"> <size> <width>24</width> <height>24</height> </size> </property> <property name="checkable"> <bool>false</bool> </property> <property name="checked"> <bool>false</bool> </property> <property name="toolButtonStyle"> <enum>Qt::ToolButtonTextUnderIcon</enum> </property> <property name="arrowType"> <enum>Qt::NoArrow</enum> </property> </widget> </item> <item> <widget class="QToolButton" name="unionButton"> <property name="enabled"> <bool>false</bool> </property> <property name="sizePolicy"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <horstretch>0</horstretch> <verstretch>0</verstretch> </sizepolicy> </property> <property name="toolTip"> <string>Combines the two segmentations</string> </property> <property name="text"> <string>Union</string> </property> <property name="icon"> <iconset resource="../resources/SegmentationUI.qrc"> <normaloff>:/Qmitk/BooleanUnion_48x48.png</normaloff>:/Qmitk/BooleanUnion_48x48.png</iconset> </property> <property name="iconSize"> <size> <width>24</width> <height>24</height> </size> </property> <property name="checkable"> <bool>false</bool> </property> <property name="checked"> <bool>false</bool> </property> <property name="toolButtonStyle"> <enum>Qt::ToolButtonTextUnderIcon</enum> </property> <property name="arrowType"> <enum>Qt::NoArrow</enum> </property> </widget> </item> </layout> </item> <item> <spacer name="verticalSpacer"> <property name="orientation"> <enum>Qt::Vertical</enum> </property> <property name="sizeHint" stdset="0"> <size> <width>20</width> <height>40</height> </size> </property> </spacer> </item> </layout> </widget> <customwidgets> <customwidget> <class>QmitkSingleNodeSelectionWidget</class> <extends>QWidget</extends> <header location="global">QmitkSingleNodeSelectionWidget.h</header> <container>1</container> </customwidget> <customwidget> <class>QmitkMultiLabelInspector</class> <extends>QWidget</extends> <header location="global">QmitkMultiLabelInspector.h</header> <container>1</container> </customwidget> </customwidgets> <resources> <include location="../resources/SegmentationUI.qrc"/> </resources> <connections/> </ui>