diff --git a/Modules/Chart/resource/empty.html b/Modules/Chart/resource/empty.html
index 011d8e9980..43703a99c6 100644
--- a/Modules/Chart/resource/empty.html
+++ b/Modules/Chart/resource/empty.html
@@ -1,16 +1,14 @@
-
-
\ No newline at end of file
diff --git a/Plugins/org.blueberry.ui.qt/src/berryQtStyleManager.cpp b/Plugins/org.blueberry.ui.qt/src/berryQtStyleManager.cpp
index 34bb198c61..576f5f0fcd 100644
--- a/Plugins/org.blueberry.ui.qt/src/berryQtStyleManager.cpp
+++ b/Plugins/org.blueberry.ui.qt/src/berryQtStyleManager.cpp
@@ -1,409 +1,413 @@
/*===================================================================
BlueBerry Platform
Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.
See LICENSE.txt or http://www.mitk.org for details.
===================================================================*/
#include "berryQtStyleManager.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "berryQtPreferences.h"
#include "berryWorkbenchPlugin.h"
namespace berry
{
static QString ParseColor(const QString &subject, const QString &pattern, const QString &fallback)
{
QRegularExpression re(pattern, QRegularExpression::CaseInsensitiveOption);
auto match = re.match(subject);
return match.hasMatch()
? match.captured(1)
: fallback;
}
QIcon QtStyleManager::ThemeIcon(const QByteArray &originalSVG)
{
auto styleSheet = qApp->styleSheet();
if (styleSheet.isEmpty())
return QPixmap::fromImage(QImage::fromData(originalSVG));
auto iconColor = ParseColor(styleSheet,
QStringLiteral("iconColor\\s*[=:]\\s*(#[0-9a-f]{6})"),
QStringLiteral("#000000"));
auto iconAccentColor = ParseColor(styleSheet,
QStringLiteral("iconAccentColor\\s*[=:]\\s*(#[0-9a-f]{6})"),
QStringLiteral("#ffffff"));
auto themedSVG = QString(originalSVG).replace(QStringLiteral("#00ff00"), iconColor, Qt::CaseInsensitive);
themedSVG = themedSVG.replace(QStringLiteral("#ff00ff"), iconAccentColor, Qt::CaseInsensitive);
return QPixmap::fromImage(QImage::fromData(themedSVG.toLatin1()));
}
QIcon QtStyleManager::ThemeIcon(const QString &resourcePath)
{
QFile resourceFile(resourcePath);
if (resourceFile.open(QIODevice::ReadOnly))
{
auto originalSVG = resourceFile.readAll();
return ThemeIcon(originalSVG);
}
BERRY_WARN << "Could not read " << resourcePath;
return QIcon();
}
QtStyleManager::QtStyleManager()
{
AddDefaultStyle();
AddDefaultFonts();
ReadPreferences();
}
void QtStyleManager::ReadPreferences()
{
IPreferencesService* prefService = WorkbenchPlugin::GetDefault()->GetPreferencesService();
IPreferences::Pointer stylePref = prefService->GetSystemPreferences()->Node(QtPreferences::QT_STYLES_NODE);
QString paths = stylePref->Get(QtPreferences::QT_STYLE_SEARCHPATHS, "");
QStringList pathList = paths.split(";", QString::SkipEmptyParts);
QStringListIterator it(pathList);
while (it.hasNext())
{
AddStyles(it.next());
}
QString styleName = stylePref->Get(QtPreferences::QT_STYLE_NAME, "");
// if a style is contributed via the Qt resource mechanism, it may not be
// registered yet.
if (Contains(styleName))
+ {
// do not update the style in the QApplication instance,
// since it might not be created yet
SetStyle(styleName, false);
+ }
else
+ {
SetDefaultStyle(false);
+ }
}
QtStyleManager::~QtStyleManager()
{
for (FileNameToStyleMap::const_iterator i = styles.begin(); i != styles.end(); ++i)
{
delete i.value();
}
}
void QtStyleManager::AddDefaultStyle()
{
#ifndef _APPLE_
AddStyle(":/org.blueberry.ui.qt/darkstyle.qss", "Dark");
AddStyle(":/org.blueberry.ui.qt/lightstyle.qss", "Light");
defaultStyle = styles[":/org.blueberry.ui.qt/darkstyle.qss"];
#endif
}
void QtStyleManager::AddDefaultFonts()
{
m_customFontNames.append(QString("<>"));
m_customFontNames.append(QString("Fira Sans"));
QFontDatabase::addApplicationFont(":/org.blueberry.ui.qt/fonts/FiraSans/FiraSans.ttf");
m_customFontNames.append(QString("Light Fira Sans"));
QFontDatabase::addApplicationFont(":/org.blueberry.ui.qt/fonts/LightFiraSans/LightFiraSans.ttf");
m_customFontNames.append(QString("Roboto"));
QFontDatabase::addApplicationFont(":/org.blueberry.ui.qt/fonts/Roboto/Roboto.ttf");
m_customFontNames.push_back(QString("Open Sans"));
QFontDatabase::addApplicationFont(":/org.blueberry.ui.qt/fonts/OpenSans/OpenSans-Regular.ttf");
m_customFontNames.push_back(QString("xkcd"));
QFontDatabase::addApplicationFont(":/org.blueberry.ui.qt/fonts/xkcd/xkcd.ttf");
}
void QtStyleManager::ClearStyles()
{
for (FileNameToStyleMap::iterator i = styles.begin(); i != styles.end(); )
{
if (!i.value()->fileName.startsWith(':'))
{
delete i.value();
i = styles.erase(i);
}
else ++i;
}
SetDefaultStyle();
}
QtStyleManager::Style QtStyleManager::GetStyle() const
{
return Style(currentStyle->name, currentStyle->fileName);
}
QString QtStyleManager::GetStylesheet() const
{
return currentStyle->stylesheet;
}
QString QtStyleManager::GetActiveTabStylesheet() const
{
return currentStyle->activeTabStylesheet;
}
QString QtStyleManager::GetTabStylesheet() const
{
return currentStyle->tabStylesheet;
}
void QtStyleManager::AddStyle(const QString& styleFileName,
const QString& styleName)
{
auto newStyle = new ExtStyle();
if (styleName.isEmpty())
{
QFileInfo info(styleFileName);
newStyle->name = info.completeBaseName();
}
else
{
newStyle->name = styleName;
}
newStyle->fileName = styleFileName;
styles.insert(newStyle->fileName, newStyle);
}
void QtStyleManager::GetFonts(QStringList& fontNames) const
{
fontNames = m_customFontNames;
}
QString QtStyleManager::GetFont() const
{
return m_currentFont;
}
void QtStyleManager::AddStyles(const QString& path)
{
QDirIterator dirIt(path);
while (dirIt.hasNext())
{
QString current = dirIt.next();
QFileInfo info = dirIt.fileInfo();
if (info.isFile() && info.isReadable())
{
QString fileName = info.fileName();
if (fileName.endsWith("-tab.qss") || fileName.endsWith("-activetab.qss"))
continue;
if (fileName.endsWith(".qss"))
AddStyle(current);
}
}
}
void QtStyleManager::ReadStyleData(ExtStyle* style)
{
QString tabStyleFileName(style->fileName);
QString activeTabStyleFileName(style->fileName);
int index = style->fileName.lastIndexOf(".qss");
tabStyleFileName.replace(index, 4, "-tab.qss");
activeTabStyleFileName.replace(index, 4, "-activetab.qss");
QFile styleFile(style->fileName);
if (styleFile.open(QIODevice::ReadOnly))
{
QTextStream in(&styleFile);
style->stylesheet = in.readAll();
}
else
{
BERRY_WARN << "Could not read " << style->fileName.toStdString();
}
QFile tabStyleFile(tabStyleFileName);
if (tabStyleFile.open(QIODevice::ReadOnly))
{
QTextStream in(&tabStyleFile);
style->tabStylesheet = in.readAll();
}
else
{
BERRY_WARN << "Could not read " << tabStyleFileName.toStdString();
}
QFile activeTabStyleFile(activeTabStyleFileName);
if (activeTabStyleFile.open(QIODevice::ReadOnly))
{
QTextStream in(&activeTabStyleFile);
style->activeTabStylesheet = in.readAll();
}
else
{
BERRY_WARN << "Could not read " << activeTabStyleFileName.toStdString();
}
}
void QtStyleManager::RemoveStyle(const QString& styleFileName)
{
if (currentStyle->fileName == styleFileName)
{
SetDefaultStyle();
}
delete styles.take(styleFileName);
}
void QtStyleManager::RemoveStyles(const QString& repo)
{
if (repo.isEmpty())
{
ClearStyles();
return;
}
for (FileNameToStyleMap::iterator i = styles.begin(); i != styles.end();)
{
ExtStyle* style = i.value();
QFileInfo info(style->fileName);
if (info.absolutePath() == repo)
{
if (style->name == currentStyle->name)
{
SetDefaultStyle();
}
i = styles.erase(i);
delete style;
}
else
{
++i;
}
}
}
void QtStyleManager::GetStyles(StyleList& styleNames) const
{
for (FileNameToStyleMap::const_iterator i = styles.begin(); i != styles.end(); ++i)
styleNames.push_back(Style(i.value()->name, i.value()->fileName));
}
void QtStyleManager::SetStyle(const QString& fileName)
{
SetStyle(fileName, true);
}
void QtStyleManager::SetStyle(const QString& fileName, bool update)
{
if (fileName.isEmpty())
{
SetDefaultStyle();
return;
}
FileNameToStyleMap::const_iterator i = styles.find(fileName);
ExtStyle* style = nullptr;
if (i == styles.end())
{
BERRY_WARN << "Style " + fileName.toStdString() << " does not exist";
style = defaultStyle;
}
else
{
style = i.value();
}
currentStyle = style;
ReadStyleData(style);
if (update)
{
qApp->setStyleSheet(currentStyle->stylesheet);
PlatformUI::GetWorkbench()->UpdateTheme();
}
}
void QtStyleManager::SetFont(const QString& fontName)
{
m_currentFont = fontName;
}
void QtStyleManager::SetFontSize(const int fontSize)
{
m_currentFontSize = fontSize;
}
void QtStyleManager::UpdateWorkbenchFont()
{
if( m_currentFont == QString( "<>" ) || m_currentFont == QString( "" ))
{
qApp->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));
}
else
{
QFont font;
font.setFamily(m_currentFont);
font.setPointSize(m_currentFontSize);
qApp->setFont(font);
}
qApp->setStyleSheet(currentStyle->stylesheet);
PlatformUI::GetWorkbench()->UpdateTheme();
}
QtStyleManager::Style QtStyleManager::GetDefaultStyle() const
{
return Style(defaultStyle->name, defaultStyle->fileName);
}
void QtStyleManager::SetDefaultStyle()
{
SetDefaultStyle(true);
}
void QtStyleManager::SetDefaultStyle(bool update)
{
SetStyle(defaultStyle->fileName, update);
}
bool QtStyleManager::Contains(const QString& fileName) const
{
return styles.contains(fileName);
}
}
diff --git a/Plugins/org.blueberry.ui.qt/src/internal/berryQtStylePreferencePage.cpp b/Plugins/org.blueberry.ui.qt/src/internal/berryQtStylePreferencePage.cpp
index 074756829e..6adf98efe3 100644
--- a/Plugins/org.blueberry.ui.qt/src/internal/berryQtStylePreferencePage.cpp
+++ b/Plugins/org.blueberry.ui.qt/src/internal/berryQtStylePreferencePage.cpp
@@ -1,264 +1,275 @@
/*===================================================================
BlueBerry Platform
Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.
See LICENSE.txt or http://www.mitk.org for details.
===================================================================*/
#include "berryQtStylePreferencePage.h"
#include
#include
#include "berryWorkbenchPlugin.h"
#include
#include
#include
namespace berry
{
QtStylePreferencePage::QtStylePreferencePage()
{
}
void QtStylePreferencePage::Init(IWorkbench::Pointer )
{
}
void QtStylePreferencePage::CreateQtControl(QWidget* parent)
{
mainWidget = new QWidget(parent);
controls.setupUi(mainWidget);
berry::IPreferencesService* prefService = berry::WorkbenchPlugin::GetDefault()->GetPreferencesService();
ctkPluginContext* context = berry::WorkbenchPlugin::GetDefault()->GetPluginContext();
ctkServiceReference styleManagerRef = context->getServiceReference();
if (styleManagerRef)
{
styleManager = context->getService(styleManagerRef);
}
m_StylePref = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE);
Update();
- connect(controls.m_StylesCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(StyleChanged(int)));
+ //connect(controls.m_StylesCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(StyleChanged(int)));
connect(controls.m_FontComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(FontChanged(int)));
connect(controls.m_FontSizeSpinBox, SIGNAL(valueChanged(int)), this, SLOT(FontChanged(int)));
connect(controls.m_PathList, SIGNAL(itemSelectionChanged()), this, SLOT(UpdatePathListButtons()));
connect(controls.m_AddButton, SIGNAL(clicked(bool)), this, SLOT(AddPathClicked(bool)));
connect(controls.m_EditButton, SIGNAL(clicked(bool)), this, SLOT(EditPathClicked(bool)));
connect(controls.m_RemoveButton, SIGNAL(clicked(bool)), this, SLOT(RemovePathClicked(bool)));
}
void QtStylePreferencePage::FillStyleCombo(const berry::IQtStyleManager::Style& currentStyle)
{
controls.m_StylesCombo->clear();
styles.clear();
styleManager->GetStyles(styles);
qSort(styles);
for (int i = 0; i < styles.size(); ++i)
{
controls.m_StylesCombo->addItem(styles.at(i).name, QVariant(styles.at(i).fileName));
}
controls.m_StylesCombo->setCurrentIndex(styles.indexOf(currentStyle));
}
void QtStylePreferencePage::FillFontCombo(const QString& currentFont)
{
controls.m_FontComboBox->clear();
QStringList fonts;
styleManager->GetFonts(fonts);
for (int i = 0; i < fonts.size(); ++i)
{
controls.m_FontComboBox->addItem(fonts.at(i));
}
controls.m_FontComboBox->setCurrentIndex(fonts.indexOf(currentFont));
if (currentFont == QString("<>"))
{
controls.m_FontSizeSpinBox->setEnabled(false);
}
else
{
controls.m_FontSizeSpinBox->setEnabled(true);
}
}
void QtStylePreferencePage::AddPath(const QString& path, bool updateCombo)
{
if (!controls.m_PathList->findItems(path, Qt::MatchCaseSensitive).isEmpty()) return;
new QListWidgetItem(path, controls.m_PathList);
styleManager->AddStyles(path);
if (updateCombo)
FillStyleCombo(oldStyle);
}
void QtStylePreferencePage::StyleChanged(int /*index*/)
{
QString fileName = controls.m_StylesCombo->itemData(controls.m_StylesCombo->currentIndex()).toString();
styleManager->SetStyle(fileName);
}
void QtStylePreferencePage::FontChanged(int /*index*/)
{
QString fontName = controls.m_FontComboBox->currentText();
int fontSize = controls.m_FontSizeSpinBox->value();
if (fontName == QString("<>"))
{
controls.m_FontSizeSpinBox->setEnabled(false);
}
else
{
controls.m_FontSizeSpinBox->setEnabled(true);
}
styleManager->SetFont(fontName);
styleManager->SetFontSize(fontSize);
styleManager->UpdateWorkbenchFont();
}
void QtStylePreferencePage::AddPathClicked(bool /*checked*/)
{
QListWidgetItem* item = controls.m_PathList->currentItem();
QString initialDir;
if (item) initialDir = item->text();
QString dir = QFileDialog::getExistingDirectory(mainWidget, "", initialDir);
if (!dir.isEmpty()) this->AddPath(dir, true);
}
void QtStylePreferencePage::RemovePathClicked(bool /*checked*/)
{
QList selection = controls.m_PathList->selectedItems();
QListIterator it(selection);
while (it.hasNext())
{
QListWidgetItem* item = it.next();
QString dir = item->text();
controls.m_PathList->takeItem(controls.m_PathList->row(item));
delete item;
styleManager->RemoveStyles(dir);
}
if (!styleManager->Contains(oldStyle.fileName))
{
oldStyle = styleManager->GetDefaultStyle();
}
FillStyleCombo(oldStyle);
}
void QtStylePreferencePage::EditPathClicked(bool checked)
{
QListWidgetItem* item = controls.m_PathList->currentItem();
QString initialDir = item->text();
QString dir = QFileDialog::getExistingDirectory(mainWidget, "", initialDir);
if (!dir.isEmpty())
{
this->RemovePathClicked(checked);
this->AddPath(dir, true);
}
}
void QtStylePreferencePage::UpdatePathListButtons()
{
int s = controls.m_PathList->selectedItems().size();
if (s == 0)
{
controls.m_EditButton->setEnabled(false);
controls.m_RemoveButton->setEnabled(false);
}
else if (s == 1)
{
controls.m_EditButton->setEnabled(true);
controls.m_RemoveButton->setEnabled(true);
}
else
{
controls.m_EditButton->setEnabled(false);
controls.m_RemoveButton->setEnabled(true);
}
}
QWidget* QtStylePreferencePage::GetQtControl() const
{
return mainWidget;
}
bool QtStylePreferencePage::PerformOk()
{
- m_StylePref->Put(berry::QtPreferences::QT_STYLE_NAME,
- controls.m_StylesCombo->itemData(controls.m_StylesCombo->currentIndex()).toString());
+ /*
+ IPreferencesService* prefService = WorkbenchPlugin::GetDefault()->GetPreferencesService();
+ IPreferences::Pointer stylePref = prefService->GetSystemPreferences()->Node(QtPreferences::QT_STYLES_NODE);
+ stylePref->Put(QtPreferences::QT_STYLE_NAME, defaultStyle->fileName);
+ */
+ QString fileName = controls.m_StylesCombo->itemData(controls.m_StylesCombo->currentIndex()).toString();
+ styleManager->SetStyle(fileName);
+
+ m_StylePref->Put(berry::QtPreferences::QT_STYLE_NAME, fileName);
QString paths;
for (int i = 0; i < controls.m_PathList->count(); ++i)
{
QString path = controls.m_PathList->item(i)->text() + ";";
paths += path;
}
m_StylePref->Put(berry::QtPreferences::QT_STYLE_SEARCHPATHS, paths);
m_StylePref->Put(berry::QtPreferences::QT_FONT_NAME,
controls.m_FontComboBox->currentText());
m_StylePref->Put(berry::QtPreferences::QT_FONT_SIZE,
QString::number(controls.m_FontSizeSpinBox->value()));
return true;
}
void QtStylePreferencePage::PerformCancel()
{
Update();
}
void QtStylePreferencePage::Update()
{
- styleManager->RemoveStyles();
-
+ // get current style preferences
QString paths = m_StylePref->Get(berry::QtPreferences::QT_STYLE_SEARCHPATHS, "");
+ QString styleName = m_StylePref->Get(berry::QtPreferences::QT_STYLE_NAME, "");
+ QString fontName = m_StylePref->Get(berry::QtPreferences::QT_FONT_NAME, "");
+ int fontSize = m_StylePref->Get(berry::QtPreferences::QT_FONT_SIZE, "").toInt();
+
+ // clean style manager
+ //styleManager->RemoveStyles();
+
+ // use style preferences to set style
QStringList pathList = paths.split(";", QString::SkipEmptyParts);
QStringListIterator it(pathList);
while (it.hasNext())
{
AddPath(it.next(), false);
}
- QString styleName = m_StylePref->Get(berry::QtPreferences::QT_STYLE_NAME, "");
- styleManager->SetStyle(styleName);
+ //styleManager->SetStyle(styleName);
oldStyle = styleManager->GetStyle();
FillStyleCombo(oldStyle);
- QString fontName = m_StylePref->Get(berry::QtPreferences::QT_FONT_NAME, "");
styleManager->SetFont(fontName);
-
- int fontSize = m_StylePref->Get(berry::QtPreferences::QT_FONT_SIZE, "").toInt();
+
styleManager->SetFontSize(fontSize);
controls.m_FontSizeSpinBox->setValue(fontSize);
styleManager->UpdateWorkbenchFont();
FillFontCombo(styleManager->GetFont());
}
}
diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp
index e403667a1b..abfa4ccb47 100644
--- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp
+++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp
@@ -1,1375 +1,1377 @@
/*===================================================================
The Medical Imaging Interaction Toolkit (MITK)
Copyright (c) German Cancer Research Center,
Division of Medical and Biological Informatics.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.
See LICENSE.txt or http://www.mitk.org for details.
===================================================================*/
#include "QmitkImageStatisticsView.h"
// Qt includes
#include
#include
#include
// berry includes
#include
// mitk includes
#include "mitkNodePredicateDataType.h"
#include "mitkNodePredicateOr.h"
#include "mitkPlanarFigureInteractor.h"
#include "mitkImageTimeSelector.h"
#include
#include
// itk includes
#include "itksys/SystemTools.hxx"
#include
#include "itkImageRegionConstIteratorWithIndex.h"
#include
//blueberry includes
#include
#include
const std::string QmitkImageStatisticsView::VIEW_ID = "org.mitk.views.imagestatistics";
const int QmitkImageStatisticsView::STAT_TABLE_BASE_HEIGHT = 180;
QmitkImageStatisticsView::QmitkImageStatisticsView(QObject* /*parent*/, const char* /*name*/)
: m_Controls( nullptr ),
m_TimeStepperAdapter( nullptr ),
m_SelectedImage( nullptr ),
m_SelectedImageMask( nullptr ),
m_SelectedPlanarFigure( nullptr ),
m_ImageObserverTag( -1 ),
m_ImageMaskObserverTag( -1 ),
m_PlanarFigureObserverTag( -1 ),
m_TimeObserverTag( -1 ),
m_CurrentStatisticsValid( false ),
m_StatisticsUpdatePending( false ),
m_DataNodeSelectionChanged ( false ),
m_Visible(false)
{
this->m_CalculationThread = new QmitkImageStatisticsCalculationThread;
}
QmitkImageStatisticsView::~QmitkImageStatisticsView()
{
if ( m_SelectedImage != nullptr )
m_SelectedImage->RemoveObserver( m_ImageObserverTag );
if ( m_SelectedImageMask != nullptr )
m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag );
if ( m_SelectedPlanarFigure != nullptr )
m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag );
while(this->m_CalculationThread->isRunning()) // wait until thread has finished
{
itksys::SystemTools::Delay(100);
}
delete this->m_CalculationThread;
}
void QmitkImageStatisticsView::CreateQtPartControl(QWidget *parent)
{
if (m_Controls == nullptr)
{
m_Controls = new Ui::QmitkImageStatisticsViewControls;
m_Controls->setupUi(parent);
CreateConnections();
m_Controls->m_ErrorMessageLabel->hide();
m_Controls->m_StatisticsWidgetStack->setCurrentIndex(0);
m_Controls->m_BinSizeFrame->setEnabled(false);
+
+ this->OnPageSuccessfullyLoaded();
}
}
void QmitkImageStatisticsView::OnPageSuccessfullyLoaded()
{
berry::IPreferencesService* prefService = berry::WorkbenchPlugin::GetDefault()->GetPreferencesService();
m_StylePref = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE);
QString styleName = m_StylePref->Get(berry::QtPreferences::QT_STYLE_NAME, "");
if (styleName == ":/org.blueberry.ui.qt/darkstyle.qss")
{
this->m_Controls->m_JSHistogram->SetTheme(QmitkChartWidget::ChartStyle::darkstyle);
}
else
{
this->m_Controls->m_JSHistogram->SetTheme(QmitkChartWidget::ChartStyle::lightstyle);
}
}
void QmitkImageStatisticsView::CreateConnections()
{
if ( m_Controls )
{
connect( (QObject*)(this->m_Controls->m_ButtonCopyHistogramToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(OnClipboardHistogramButtonClicked()) );
connect( (QObject*)(this->m_Controls->m_ButtonCopyStatisticsToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(OnClipboardStatisticsButtonClicked()) );
connect( (QObject*)(this->m_Controls->m_IgnoreZerosCheckbox), SIGNAL(clicked()),(QObject*) this, SLOT(OnIgnoreZerosCheckboxClicked()) );
connect( (QObject*) this->m_CalculationThread, SIGNAL(finished()),this, SLOT( OnThreadedStatisticsCalculationEnds()),Qt::QueuedConnection);
connect( (QObject*) this, SIGNAL(StatisticsUpdate()),this, SLOT( RequestStatisticsUpdate()), Qt::QueuedConnection);
connect( (QObject*) this->m_Controls->m_StatisticsTable, SIGNAL(cellDoubleClicked(int,int)),this, SLOT( JumpToCoordinates(int,int)) );
connect((QObject*)(this->m_Controls->m_barRadioButton), SIGNAL(clicked()), (QObject*)(this), SLOT(OnBarRadioButtonSelected()));
connect((QObject*)(this->m_Controls->m_lineRadioButton), SIGNAL(clicked()), (QObject*)(this), SLOT(OnLineRadioButtonSelected()));
connect( (QObject*) (this->m_Controls->m_HistogramBinSizeSpinbox), SIGNAL(editingFinished()), this, SLOT(OnHistogramBinSizeBoxValueChanged()));
connect((QObject*)(this->m_Controls->m_UseDefaultBinSizeBox), SIGNAL(clicked()), (QObject*) this, SLOT(OnDefaultBinSizeBoxChanged()));
connect((QObject*)(this->m_Controls->m_ShowSubchartCheckBox), SIGNAL(clicked()), (QObject*) this, SLOT(OnShowSubchartBoxChanged()));
connect((QObject*)(this->m_Controls->m_JSHistogram), SIGNAL(PageSuccessfullyLoaded()), (QObject*) this, SLOT(OnPageSuccessfullyLoaded()));
}
}
void QmitkImageStatisticsView::OnDefaultBinSizeBoxChanged()
{
m_Controls->m_BinSizeFrame->setEnabled(!m_Controls->m_UseDefaultBinSizeBox->isChecked());
if (m_CalculationThread != nullptr){
m_Controls->m_HistogramBinSizeSpinbox->setValue(m_CalculationThread->GetHistogramBinSize());
m_CalculationThread->SetUseDefaultNBins(m_Controls->m_UseDefaultBinSizeBox->isChecked());
}
this->UpdateStatistics();
}
void QmitkImageStatisticsView::OnShowSubchartBoxChanged()
{
bool showSubchart = this->m_Controls->m_ShowSubchartCheckBox->isChecked();
this->m_Controls->m_JSHistogram->Reload(showSubchart);
}
void QmitkImageStatisticsView::OnBarRadioButtonSelected()
{
this->m_Controls->m_JSHistogram->SetChartTypeForAllDataAndReload(QmitkChartWidget::ChartType::bar);
}
void QmitkImageStatisticsView::OnLineRadioButtonSelected()
{
this->m_Controls->m_JSHistogram->SetChartTypeForAllDataAndReload(QmitkChartWidget::ChartType::line);
}
void QmitkImageStatisticsView::PartClosed(const berry::IWorkbenchPartReference::Pointer& )
{
}
void QmitkImageStatisticsView::OnTimeChanged(const itk::EventObject& e)
{
if (this->m_SelectedDataNodes.isEmpty() || this->m_SelectedImage == nullptr)
return;
const mitk::SliceNavigationController::GeometryTimeEvent* timeEvent =
dynamic_cast(&e);
assert(timeEvent != nullptr);
int timestep = timeEvent->GetPos();
if (this->m_SelectedImage->GetTimeSteps() > 1)
{
for (int x = 0; x < this->m_Controls->m_StatisticsTable->columnCount(); x++)
{
for (int y = 0; y < this->m_Controls->m_StatisticsTable->rowCount(); y++)
{
QTableWidgetItem* item = this->m_Controls->m_StatisticsTable->item(y, x);
if (item == nullptr)
break;
if (x == timestep)
{
item->setBackgroundColor(Qt::yellow);
}
else
{
if (y % 2 == 0)
item->setBackground(this->m_Controls->m_StatisticsTable->palette().base());
else
item->setBackground(this->m_Controls->m_StatisticsTable->palette().alternateBase());
}
}
}
this->m_Controls->m_StatisticsTable->viewport()->update();
}
if ((this->m_SelectedImage->GetTimeSteps() == 1 && timestep == 0) ||
this->m_SelectedImage->GetTimeSteps() > 1)
{
// display histogram for selected timestep
this->m_Controls->m_JSHistogram->Clear();
QmitkImageStatisticsCalculationThread::HistogramType::ConstPointer histogram =
(QmitkImageStatisticsCalculationThread::HistogramType::ConstPointer)this->m_CalculationThread->GetTimeStepHistogram(timestep);
if (histogram.IsNotNull())
{
bool closedFigure = this->m_CalculationThread->GetStatisticsUpdateSuccessFlag();
if (closedFigure)
{
auto imageNameLabel = m_Controls->m_SelectedFeatureImageLabel->text().toStdString();
this->m_Controls->m_JSHistogram->AddData2D(ConvertHistogramToMap(histogram), imageNameLabel);
if (this->m_Controls->m_lineRadioButton->isChecked())
{
this->m_Controls->m_JSHistogram->SetChartType(imageNameLabel, QmitkChartWidget::ChartType::line);
}
else
{
this->m_Controls->m_JSHistogram->SetChartType(imageNameLabel, QmitkChartWidget::ChartType::bar);
}
this->m_Controls->m_JSHistogram->SetXAxisLabel("Grey value");
this->m_Controls->m_JSHistogram->SetYAxisLabel("Frequency");
this->m_Controls->m_JSHistogram->Show(this->m_Controls->m_ShowSubchartCheckBox->isChecked());
}
}
}
}
void QmitkImageStatisticsView::JumpToCoordinates(int row ,int col)
{
if(m_SelectedDataNodes.isEmpty())
{
MITK_WARN("QmitkImageStatisticsView") << "No data node selected for statistics calculation." ;
return;
}
mitk::Point3D world;
if (row==5 && !m_WorldMinList.empty())
world = m_WorldMinList[col];
else if (row==4 && !m_WorldMaxList.empty())
world = m_WorldMaxList[col];
else
return;
mitk::IRenderWindowPart* part = this->GetRenderWindowPart();
if (part)
{
part->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SelectSliceByPoint(world);
part->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()->SelectSliceByPoint(world);
part->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()->SelectSliceByPoint(world);
mitk::SliceNavigationController::GeometryTimeEvent timeEvent(this->m_SelectedImage->GetTimeGeometry(), col);
part->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SetGeometryTime(timeEvent);
}
}
void QmitkImageStatisticsView::OnIgnoreZerosCheckboxClicked()
{
emit StatisticsUpdate();
}
void QmitkImageStatisticsView::OnClipboardHistogramButtonClicked()
{
if ( m_CurrentStatisticsValid && !( m_SelectedPlanarFigure != nullptr))
{
const unsigned int t = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->GetPos();
typedef mitk::ImageStatisticsCalculator::HistogramType HistogramType;
const HistogramType *histogram = this->m_CalculationThread->GetTimeStepHistogram(t).GetPointer();
QString clipboard( "Measurement \t Frequency\n" );
for ( HistogramType::ConstIterator it = histogram->Begin();
it != histogram->End();
++it )
{
if( m_Controls->m_HistogramBinSizeSpinbox->value() == 1.0)
{
clipboard = clipboard.append( "%L1 \t %L2\n" )
.arg( it.GetMeasurementVector()[0], 0, 'f', 0 )
.arg( it.GetFrequency() );
}
else
{
clipboard = clipboard.append( "%L1 \t %L2\n" )
.arg( it.GetMeasurementVector()[0], 0, 'f', 2 )
.arg( it.GetFrequency() );
}
}
QApplication::clipboard()->setText(
clipboard, QClipboard::Clipboard );
}
// If a (non-closed) PlanarFigure is selected, display a line profile widget
else if ( m_CurrentStatisticsValid && (m_SelectedPlanarFigure != nullptr ))
{
/*auto intensity = m_Controls->m_JSHistogram->GetFrequency();
auto pixel = m_Controls->m_JSHistogram->GetMeasurement();
QString clipboard( "Pixel \t Intensity\n" );
auto j = pixel.begin();
for (auto i = intensity.begin(); i < intensity.end(); i++)
{
assert(j != pixel.end());
clipboard = clipboard.append( "%L1 \t %L2\n" )
.arg( (*j).toString())
.arg( (*i).toString());
j++;
}
QApplication::clipboard()->setText(
clipboard, QClipboard::Clipboard );
*/
}
else
{
QApplication::clipboard()->clear();
}
}
void QmitkImageStatisticsView::OnClipboardStatisticsButtonClicked()
{
QLocale tempLocal;
QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates));
if ( m_CurrentStatisticsValid && !( m_SelectedPlanarFigure != nullptr))
{
const std::vector &statistics =
this->m_CalculationThread->GetStatisticsData();
// Set time borders for for loop ;)
unsigned int startT, endT;
if(this->m_Controls->m_CheckBox4dCompleteTable->checkState()==Qt::CheckState::Unchecked)
{
startT = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->
GetPos();
endT = startT+1;
}
else
{
startT = 0;
endT = statistics.size();
}
QVector< QVector > statisticsTable;
QStringList headline;
// Create Headline
headline << " "
<< "Mean"
<< "Median"
<< "StdDev"
<< "RMS"
<< "Max"
<< "Min"
<< "NumberOfVoxels"
<< "Skewness"
<< "Kurtosis"
<< "Uniformity"
<< "Entropy"
<< "MPP"
<< "UPP"
<< "V [mm³]";
for(int i=0;i row;
row.append(headline.at(i));
statisticsTable.append(row);
}
// Fill Table
for(unsigned int t=startT;tGetMean())
<< QString::number(statistics[t]->GetMedian())
<< QString::number(statistics[t]->GetStd())
<< QString::number(statistics[t]->GetRMS())
<< QString::number(statistics[t]->GetMax())
<< QString::number(statistics[t]->GetMin())
<< QString::number(statistics[t]->GetN())
<< QString::number(statistics[t]->GetSkewness())
<< QString::number(statistics[t]->GetKurtosis())
<< QString::number(statistics[t]->GetUniformity())
<< QString::number(statistics[t]->GetEntropy())
<< QString::number(statistics[t]->GetMPP())
<< QString::number(statistics[t]->GetUPP())
<< QString::number(m_Controls->m_StatisticsTable->item(7, 0)->data(Qt::DisplayRole).toDouble());
for(int z=0;zsetText(clipboard, QClipboard::Clipboard);
}
else
{
QApplication::clipboard()->clear();
}
QLocale::setDefault(tempLocal);
}
void QmitkImageStatisticsView::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*part*/,
const QList &nodes )
{
if (this->m_Visible)
{
this->SelectionChanged( nodes );
}
else
{
this->m_DataNodeSelectionChanged = true;
}
}
void QmitkImageStatisticsView::SelectionChanged(const QList &selectedNodes)
{
//Clear Histogram if data node is deselected
m_Controls->m_JSHistogram->Clear();
if( this->m_StatisticsUpdatePending )
{
this->m_DataNodeSelectionChanged = true;
return; // not ready for new data now!
}
if (selectedNodes.size() == this->m_SelectedDataNodes.size())
{
int i = 0;
for (; i < selectedNodes.size(); ++i)
{
if (selectedNodes.at(i) != this->m_SelectedDataNodes.at(i))
{
break;
}
}
// node selection did not change
if (i == selectedNodes.size()) return;
}
//reset the feature image and image mask field
m_Controls->m_SelectedFeatureImageLabel->setText("None");
m_Controls->m_SelectedMaskLabel->setText("None");
this->ReinitData();
if (selectedNodes.isEmpty())
{
m_Controls->m_lineRadioButton->setEnabled(true);
m_Controls->m_barRadioButton->setEnabled(true);
m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true);
m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true);
m_Controls->m_UseDefaultBinSizeBox->setEnabled(true);
m_Controls->m_InfoLabel->setText("");
m_Controls->groupBox->setEnabled(false);
m_Controls->groupBox_3->setEnabled(false);
}
else
{
m_Controls->groupBox->setEnabled(true);
m_Controls->groupBox_3->setEnabled(true);
m_Controls->m_barRadioButton->setChecked(true);
}
if(selectedNodes.size() == 1 || selectedNodes.size() == 2)
{
bool isBinary = false;
selectedNodes.value(0)->GetBoolProperty("binary",isBinary);
mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage");
isBinary |= isLabelSet->CheckNode(selectedNodes.value(0));
if(isBinary)
{
m_Controls->m_lineRadioButton->setEnabled(true);
m_Controls->m_barRadioButton->setEnabled(true);
m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true);
m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true);
m_Controls->m_UseDefaultBinSizeBox->setEnabled(true);
m_Controls->m_InfoLabel->setText("");
}
for (int i= 0; i< selectedNodes.size(); ++i)
{
this->m_SelectedDataNodes.push_back(selectedNodes.at(i));
}
this->m_DataNodeSelectionChanged = false;
this->m_Controls->m_ErrorMessageLabel->setText( "" );
this->m_Controls->m_ErrorMessageLabel->hide();
emit StatisticsUpdate();
}
else
{
this->m_DataNodeSelectionChanged = false;
}
}
void QmitkImageStatisticsView::ReinitData()
{
while( this->m_CalculationThread->isRunning()) // wait until thread has finished
{
itksys::SystemTools::Delay(100);
}
if(this->m_SelectedImage != nullptr)
{
this->m_SelectedImage->RemoveObserver( this->m_ImageObserverTag);
this->m_SelectedImage = nullptr;
}
if(this->m_SelectedImageMask != nullptr)
{
this->m_SelectedImageMask->RemoveObserver( this->m_ImageMaskObserverTag);
this->m_SelectedImageMask = nullptr;
}
if(this->m_SelectedPlanarFigure != nullptr)
{
this->m_SelectedPlanarFigure->RemoveObserver( this->m_PlanarFigureObserverTag);
this->m_SelectedPlanarFigure = nullptr;
}
this->m_SelectedDataNodes.clear();
this->m_StatisticsUpdatePending = false;
m_Controls->m_ErrorMessageLabel->setText( "" );
m_Controls->m_ErrorMessageLabel->hide();
this->InvalidateStatisticsTableView();
m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 );
}
void QmitkImageStatisticsView::OnThreadedStatisticsCalculationEnds()
{
std::stringstream message;
message << "";
m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() );
m_Controls->m_ErrorMessageLabel->hide();
this->WriteStatisticsToGUI();
}
void QmitkImageStatisticsView::UpdateStatistics()
{
mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart();
if ( renderPart == nullptr )
{
this->m_StatisticsUpdatePending = false;
return;
}
m_WorldMinList.clear();
m_WorldMaxList.clear();
// classify selected nodes
mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image");
mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage");
mitk::NodePredicateOr::Pointer imagePredicate = mitk::NodePredicateOr::New(isImage, isLabelSet);
std::string maskName;
std::string maskType;
std::string featureImageName;
unsigned int maskDimension = 0;
// reset data from last run
ITKCommandType::Pointer changeListener = ITKCommandType::New();
changeListener->SetCallbackFunction( this, &QmitkImageStatisticsView::SelectedDataModified );
mitk::DataNode::Pointer planarFigureNode;
for( int i= 0 ; i < this->m_SelectedDataNodes.size(); ++i)
{
mitk::PlanarFigure::Pointer planarFig = dynamic_cast(this->m_SelectedDataNodes.at(i)->GetData());
if( imagePredicate->CheckNode(this->m_SelectedDataNodes.at(i)) )
{
bool isMask = false;
this->m_SelectedDataNodes.at(i)->GetPropertyValue("binary", isMask);
isMask |= isLabelSet->CheckNode(this->m_SelectedDataNodes.at(i));
if( this->m_SelectedImageMask == nullptr && isMask)
{
this->m_SelectedImageMask = dynamic_cast(this->m_SelectedDataNodes.at(i)->GetData());
this->m_ImageMaskObserverTag = this->m_SelectedImageMask->AddObserver(itk::ModifiedEvent(), changeListener);
maskName = this->m_SelectedDataNodes.at(i)->GetName();
maskType = m_SelectedImageMask->GetNameOfClass();
maskDimension = 3;
}
else if( !isMask )
{
if(this->m_SelectedImage == nullptr)
{
this->m_SelectedImage = static_cast(this->m_SelectedDataNodes.at(i)->GetData());
this->m_ImageObserverTag = this->m_SelectedImage->AddObserver(itk::ModifiedEvent(), changeListener);
}
featureImageName = this->m_SelectedDataNodes.at(i)->GetName();
}
}
else if (planarFig.IsNotNull())
{
if(this->m_SelectedPlanarFigure == nullptr)
{
this->m_SelectedPlanarFigure = planarFig;
this->m_PlanarFigureObserverTag =
this->m_SelectedPlanarFigure->AddObserver(mitk::EndInteractionPlanarFigureEvent(), changeListener);
maskName = this->m_SelectedDataNodes.at(i)->GetName();
maskType = this->m_SelectedPlanarFigure->GetNameOfClass();
maskDimension = 2;
planarFigureNode = m_SelectedDataNodes.at(i);
}
}
else
{
std::stringstream message;
message << "" << "Invalid data node type!" << "";
m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() );
m_Controls->m_ErrorMessageLabel->show();
}
}
if(maskName == "")
{
maskName = "None";
maskType = "";
maskDimension = 0;
}
if(featureImageName == "")
{
featureImageName = "None";
}
if (m_SelectedPlanarFigure != nullptr && m_SelectedImage == nullptr)
{
mitk::DataStorage::SetOfObjects::ConstPointer parentSet = this->GetDataStorage()->GetSources(planarFigureNode);
for (unsigned int i=0; iSize(); i++)
{
mitk::DataNode::Pointer node = parentSet->ElementAt(i);
if( imagePredicate->CheckNode(node) )
{
bool isMask = false;
node->GetPropertyValue("binary", isMask);
isMask |= isLabelSet->CheckNode(node);
if( !isMask )
{
if(this->m_SelectedImage == nullptr)
{
this->m_SelectedImage = static_cast(node->GetData());
this->m_ImageObserverTag = this->m_SelectedImage->AddObserver(itk::ModifiedEvent(), changeListener);
}
}
}
}
}
unsigned int timeStep = renderPart->GetTimeNavigationController()->GetTime()->GetPos();
if ( m_SelectedImage != nullptr && m_SelectedImage->IsInitialized())
{
// Check if a the selected image is a multi-channel image. If yes, statistics
// cannot be calculated currently.
if ( m_SelectedImage->GetPixelType().GetNumberOfComponents() > 1 )
{
std::stringstream message;
message << "Multi-component images not supported.";
m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() );
m_Controls->m_ErrorMessageLabel->show();
this->InvalidateStatisticsTableView();
m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 );
m_CurrentStatisticsValid = false;
this->m_StatisticsUpdatePending = false;
m_Controls->m_lineRadioButton->setEnabled(true);
m_Controls->m_barRadioButton->setEnabled(true);
m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true);
m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true);
m_Controls->m_UseDefaultBinSizeBox->setEnabled(true);
m_Controls->m_InfoLabel->setText("");
return;
}
std::stringstream maskLabel;
maskLabel << maskName;
if ( maskDimension > 0 )
{
maskLabel << " [" << maskDimension << "D " << maskType << "]";
}
m_Controls->m_SelectedMaskLabel->setText( maskLabel.str().c_str() );
m_Controls->m_SelectedFeatureImageLabel->setText(featureImageName.c_str());
// check time step validity
if(m_SelectedImage->GetDimension() <= 3 && timeStep > m_SelectedImage->GetDimension(3)-1)
{
timeStep = m_SelectedImage->GetDimension(3)-1;
}
// Add the used mask time step to the mask label so the user knows which mask time step was used
// if the image time step is bigger than the total number of mask time steps (see
// ImageStatisticsCalculator::ExtractImageAndMask)
if (m_SelectedImageMask != nullptr)
{
unsigned int maskTimeStep = timeStep;
if (maskTimeStep >= m_SelectedImageMask->GetTimeSteps())
{
maskTimeStep = m_SelectedImageMask->GetTimeSteps() - 1;
}
m_Controls->m_SelectedMaskLabel->setText(m_Controls->m_SelectedMaskLabel->text() +
QString(" (t=") +
QString::number(maskTimeStep) +
QString(")"));
}
// check if the segmentation mask is empty
if (m_SelectedImageMask != NULL)
{
typedef itk::Image ItkImageType;
typedef itk::ImageRegionConstIteratorWithIndex< ItkImageType > IteratorType;
ItkImageType::Pointer itkImage;
mitk::CastToItkImage( m_SelectedImageMask, itkImage );
bool empty = true;
IteratorType it( itkImage, itkImage->GetLargestPossibleRegion() );
while ( !it.IsAtEnd() )
{
ItkImageType::ValueType val = it.Get();
if ( val != 0 )
{
empty = false;
break;
}
++it;
}
if ( empty )
{
std::stringstream message;
message << "Empty segmentation mask selected...";
m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() );
m_Controls->m_ErrorMessageLabel->show();
return;
}
}
//// initialize thread and trigger it
this->m_CalculationThread->SetIgnoreZeroValueVoxel( m_Controls->m_IgnoreZerosCheckbox->isChecked() );
this->m_CalculationThread->Initialize( m_SelectedImage, m_SelectedImageMask, m_SelectedPlanarFigure );
this->m_CalculationThread->SetTimeStep( timeStep );
std::stringstream message;
message << "Calculating statistics...";
m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() );
m_Controls->m_ErrorMessageLabel->show();
try
{
// Compute statistics
// this->m_CalculationThread->SetUseDefaultBinSize(m_Controls->m_UseDefaultBinSizeBox->isChecked());
this->m_CalculationThread->start();
}
catch ( const mitk::Exception& e)
{
std::stringstream message;
message << "" << e.GetDescription() << "";
m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() );
m_Controls->m_ErrorMessageLabel->show();
this->m_StatisticsUpdatePending = false;
}
catch ( const std::runtime_error &e )
{
// In case of exception, print error message on GUI
std::stringstream message;
message << "" << e.what() << "";
m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() );
m_Controls->m_ErrorMessageLabel->show();
this->m_StatisticsUpdatePending = false;
}
catch ( const std::exception &e )
{
MITK_ERROR << "Caught exception: " << e.what();
// In case of exception, print error message on GUI
std::stringstream message;
message << "Error! Unequal Dimensions of Image and Segmentation. No recompute possible ";
m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() );
m_Controls->m_ErrorMessageLabel->show();
this->m_StatisticsUpdatePending = false;
}
AdaptBinSizeCheckboxStepsize(m_SelectedImage);
}
else
{
this->m_StatisticsUpdatePending = false;
}
}
void QmitkImageStatisticsView::SelectedDataModified()
{
if( !m_StatisticsUpdatePending )
{
emit StatisticsUpdate();
}
}
void QmitkImageStatisticsView::NodeRemoved(const mitk::DataNode *node)
{
while(this->m_CalculationThread->isRunning()) // wait until thread has finished
{
itksys::SystemTools::Delay(100);
}
if (node->GetData() == m_SelectedImage)
{
m_SelectedImage = nullptr;
}
}
void QmitkImageStatisticsView::RequestStatisticsUpdate()
{
if ( !m_StatisticsUpdatePending )
{
if(this->m_DataNodeSelectionChanged)
{
this->SelectionChanged(this->GetCurrentSelection());
}
else
{
this->m_StatisticsUpdatePending = true;
this->UpdateStatistics();
}
}
if (this->GetRenderWindowPart())
this->GetRenderWindowPart()->RequestUpdate();
}
void QmitkImageStatisticsView::OnHistogramBinSizeBoxValueChanged()
{
if (m_Controls->m_HistogramBinSizeSpinbox->value() != m_HistogramBinSize)
{
m_HistogramBinSize = m_Controls->m_HistogramBinSizeSpinbox->value();
this->m_CalculationThread->SetHistogramBinSize(m_Controls->m_HistogramBinSizeSpinbox->value());
this->UpdateStatistics();
}
}
void QmitkImageStatisticsView::WriteStatisticsToGUI()
{
m_Controls->m_JSHistogram->Clear();
//Disconnect OnLineRadioButtonSelected() to prevent reloading chart when radiobutton is checked programmatically
disconnect((QObject*)(this->m_Controls->m_JSHistogram), SIGNAL(PageSuccessfullyLoaded()), 0, 0);
connect((QObject*)(this->m_Controls->m_JSHistogram), SIGNAL(PageSuccessfullyLoaded()), (QObject*) this, SLOT(OnPageSuccessfullyLoaded()));
m_Controls->m_lineRadioButton->setEnabled(true);
m_Controls->m_barRadioButton->setEnabled(true);
m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true);
m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true);
m_Controls->m_InfoLabel->setText("");
if (m_DataNodeSelectionChanged)
{
this->m_StatisticsUpdatePending = false;
this->RequestStatisticsUpdate();
return; // stop visualization of results and calculate statistics of new selection
}
if (this->m_CalculationThread->GetStatisticsUpdateSuccessFlag())
{
if (this->m_CalculationThread->GetStatisticsChangedFlag())
{
// Do not show any error messages
m_Controls->m_ErrorMessageLabel->hide();
m_CurrentStatisticsValid = true;
}
if (m_SelectedImage != nullptr) {
//all statistics are now computed also on planar figures (lines, paths...)!
// If a (non-closed) PlanarFigure is selected, display a line profile widget
if (m_SelectedPlanarFigure != nullptr) {
// Check if the (closed) planar figure is out of bounds and so no image mask could be calculated--> Intensity Profile can not be calculated
bool outOfBounds = false;
if (m_SelectedPlanarFigure->IsClosed() && m_SelectedImageMask == nullptr)
{
outOfBounds = true;
const QString message("Planar figure is on a rotated image plane or outside the image bounds.");
m_Controls->m_InfoLabel->setText(message);
}
// check whether PlanarFigure is initialized
const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_SelectedPlanarFigure->GetPlaneGeometry();
if (!(planarFigurePlaneGeometry == nullptr || outOfBounds))
{
unsigned int timeStep = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->GetPos();
mitk::Image::Pointer image;
if (this->m_CalculationThread->GetStatisticsImage()->GetDimension() == 4)
{
mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New();
timeSelector->SetInput(this->m_CalculationThread->GetStatisticsImage());
timeSelector->SetTimeNr(timeStep);
timeSelector->Update();
image = timeSelector->GetOutput();
}
else
{
image = this->m_CalculationThread->GetStatisticsImage();
}
mitk::IntensityProfile::ConstPointer intensityProfile = (mitk::IntensityProfile::ConstPointer)mitk::ComputeIntensityProfile(image, m_SelectedPlanarFigure);
auto intensityProfileList = ConvertIntensityProfileToVector(intensityProfile);
auto lineDataLabel = "Intensity profile " + m_Controls->m_SelectedMaskLabel->text().toStdString();
m_Controls->m_JSHistogram->SetChartType(lineDataLabel, QmitkChartWidget::ChartType::line);
m_Controls->m_JSHistogram->AddData1D(intensityProfileList, lineDataLabel);
m_Controls->m_JSHistogram->SetXAxisLabel("Distance");
m_Controls->m_JSHistogram->SetYAxisLabel("Intensity");
m_Controls->m_JSHistogram->Show(m_Controls->m_ShowSubchartCheckBox->isChecked());
m_Controls->m_lineRadioButton->setChecked(true);
m_Controls->m_lineRadioButton->setEnabled(false);
m_Controls->m_barRadioButton->setEnabled(false);
m_Controls->m_HistogramBinSizeSpinbox->setEnabled(false);
m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(false);
m_Controls->m_UseDefaultBinSizeBox->setEnabled(false);
//Reconnect OnLineRadioButtonSelected()
connect((QObject*)(this->m_Controls->m_JSHistogram), SIGNAL(PageSuccessfullyLoaded()), (QObject*) this, SLOT(OnLineRadioButtonSelected()));
auto statisticsVector = this->m_CalculationThread->GetStatisticsData();
//only one entry (current timestep)
this->FillLinearProfileStatisticsTableView(statisticsVector.front().GetPointer(), this->m_CalculationThread->GetStatisticsImage());
QString message("Only linegraph available for an intensity profile!");
if (this->m_CalculationThread->GetStatisticsImage()->GetDimension() == 4) {
message += "Only current timestep displayed!";
}
message += "";
m_Controls->m_InfoLabel->setText(message);
m_CurrentStatisticsValid = true;
}
else
{
// Clear statistics, histogram, and GUI
this->InvalidateStatisticsTableView();
m_Controls->m_StatisticsWidgetStack->setCurrentIndex(0);
m_CurrentStatisticsValid = false;
m_Controls->m_ErrorMessageLabel->hide();
m_Controls->m_SelectedMaskLabel->setText("None");
this->m_StatisticsUpdatePending = false;
m_Controls->m_lineRadioButton->setEnabled(true);
m_Controls->m_barRadioButton->setEnabled(true);
m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true);
m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true);
if (!outOfBounds)
m_Controls->m_InfoLabel->setText("");
return;
}
}
else
{
m_Controls->m_StatisticsWidgetStack->setCurrentIndex(0);
m_Controls->m_HistogramBinSizeSpinbox->setValue(this->m_CalculationThread->GetHistogramBinSize());
auto histogram = this->m_CalculationThread->GetTimeStepHistogram(this->m_CalculationThread->GetTimeStep()).GetPointer();
auto imageLabelName = m_Controls->m_SelectedFeatureImageLabel->text().toStdString();
m_Controls->m_JSHistogram->AddData2D(ConvertHistogramToMap(histogram), imageLabelName);
m_Controls->m_JSHistogram->SetChartType(imageLabelName, QmitkChartWidget::ChartType::bar);
this->m_Controls->m_JSHistogram->SetXAxisLabel("Gray value");
this->m_Controls->m_JSHistogram->SetYAxisLabel("Frequency");
m_Controls->m_UseDefaultBinSizeBox->setEnabled(true);
m_Controls->m_JSHistogram->Show(this->m_Controls->m_ShowSubchartCheckBox->isChecked());
auto currentTime = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->GetPos();
this->AdaptBinSizeCheckBoxMinMax((this->m_CalculationThread->GetStatisticsData()).at(currentTime).GetPointer(), this->m_CalculationThread->GetStatisticsImage()->GetPixelType().GetComponentType());
this->FillStatisticsTableView(this->m_CalculationThread->GetStatisticsData(), this->m_CalculationThread->GetStatisticsImage());
}
m_CurrentStatisticsValid = true;
}
}
else
{
m_Controls->m_SelectedMaskLabel->setText("None");
m_Controls->m_ErrorMessageLabel->setText(m_CalculationThread->GetLastErrorMessage().c_str());
m_Controls->m_ErrorMessageLabel->show();
// Clear statistics and histogram
this->InvalidateStatisticsTableView();
m_Controls->m_StatisticsWidgetStack->setCurrentIndex(0);
m_CurrentStatisticsValid = false;
}
berry::IPreferencesService* prefService = berry::WorkbenchPlugin::GetDefault()->GetPreferencesService();
m_StylePref = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE);
this->m_StatisticsUpdatePending = false;
}
void QmitkImageStatisticsView::FillStatisticsTableView(
const std::vector &statistics,
const mitk::Image *image )
{
this->m_Controls->m_StatisticsTable->setColumnCount(image->GetTimeSteps());
this->m_Controls->m_StatisticsTable->horizontalHeader()->setVisible(image->GetTimeSteps() > 1);
// Set Checkbox for complete copy of statistic table
if(image->GetTimeSteps()>1)
{
this->m_Controls->m_CheckBox4dCompleteTable->setEnabled(true);
}
else
{
this->m_Controls->m_CheckBox4dCompleteTable->setEnabled(false);
this->m_Controls->m_CheckBox4dCompleteTable->setChecked(false);
}
for (unsigned int t = 0; t < image->GetTimeSteps(); t++)
{
this->m_Controls->m_StatisticsTable->setHorizontalHeaderItem(t,
new QTableWidgetItem(QString::number(t)));
if (statistics.at(t)->GetMaxIndex().size()==3)
{
mitk::Point3D index, max, min;
index[0] = statistics.at(t)->GetMaxIndex()[0];
index[1] = statistics.at(t)->GetMaxIndex()[1];
index[2] = statistics.at(t)->GetMaxIndex()[2];
m_SelectedImage->GetGeometry()->IndexToWorld(index, max);
this->m_WorldMaxList.push_back(max);
index[0] = statistics.at(t)->GetMinIndex()[0];
index[1] = statistics.at(t)->GetMinIndex()[1];
index[2] = statistics.at(t)->GetMinIndex()[2];
m_SelectedImage->GetGeometry()->IndexToWorld(index, min);
this->m_WorldMinList.push_back(min);
}
auto statisticsVector = AssembleStatisticsIntoVector(statistics.at(t).GetPointer(), image);
unsigned int count = 0;
for (const auto& entry : statisticsVector) {
auto item = new QTableWidgetItem(entry);
this->m_Controls->m_StatisticsTable->setItem(count, t, item);
count++;
}
}
this->m_Controls->m_StatisticsTable->resizeColumnsToContents();
int height = STAT_TABLE_BASE_HEIGHT;
if (this->m_Controls->m_StatisticsTable->horizontalHeader()->isVisible())
height += this->m_Controls->m_StatisticsTable->horizontalHeader()->height();
if (this->m_Controls->m_StatisticsTable->horizontalScrollBar()->isVisible())
height += this->m_Controls->m_StatisticsTable->horizontalScrollBar()->height();
this->m_Controls->m_StatisticsTable->setMinimumHeight(height);
// make sure the current timestep's column is highlighted (and the correct histogram is displayed)
unsigned int t = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->
GetPos();
mitk::SliceNavigationController::GeometryTimeEvent timeEvent(this->m_SelectedImage->GetTimeGeometry(),
t);
this->OnTimeChanged(timeEvent);
t = std::min(image->GetTimeSteps() - 1, t);
// See bug 18340
/*QString hotspotMean; hotspotMean.append(QString("%1").arg(s[t].GetHotspotStatistics().GetMean(), 0, 'f', decimals));
hotspotMean += " (";
for (int i=0; im_Controls->m_StatisticsTable->setItem( 7, t, new QTableWidgetItem( hotspotMean ) );
QString hotspotMax; hotspotMax.append(QString("%1").arg(s[t].GetHotspotStatistics().GetMax(), 0, 'f', decimals));
hotspotMax += " (";
for (int i=0; im_Controls->m_StatisticsTable->setItem( 8, t, new QTableWidgetItem( hotspotMax ) );
QString hotspotMin; hotspotMin.append(QString("%1").arg(s[t].GetHotspotStatistics().GetMin(), 0, 'f', decimals));
hotspotMin += " (";
for (int i=0; im_Controls->m_StatisticsTable->setItem( 9, t, new QTableWidgetItem( hotspotMin ) );*/
}
std::vector QmitkImageStatisticsView::AssembleStatisticsIntoVector(mitk::ImageStatisticsCalculator::StatisticsContainer::ConstPointer statistics, mitk::Image::ConstPointer image, bool noVolumeDefined) const
{
std::vector result;
unsigned int decimals = 2;
//statistics of higher order should have 5 decimal places because they used to be very small
unsigned int decimalsHigherOrderStatistics = 5;
if (image->GetPixelType().GetComponentType() == itk::ImageIOBase::DOUBLE || image->GetPixelType().GetComponentType() == itk::ImageIOBase::FLOAT)
{
decimals = 5;
}
result.push_back(GetFormattedString(statistics->GetMean(), decimals));
result.push_back(GetFormattedString(statistics->GetMedian(), decimals));
result.push_back(GetFormattedString(statistics->GetStd(), decimals));
result.push_back(GetFormattedString(statistics->GetRMS(), decimals));
result.push_back(GetFormattedString(statistics->GetMax(), decimals) + " " + GetFormattedIndex(statistics->GetMaxIndex()));
result.push_back(GetFormattedString(statistics->GetMin(), decimals) + " " + GetFormattedIndex(statistics->GetMinIndex()));
//to prevent large negative values of empty image statistics
if (statistics->GetN() != std::numeric_limits::min()) {
result.push_back(GetFormattedString(statistics->GetN(), 0));
const mitk::BaseGeometry *geometry = image->GetGeometry();
if (geometry != NULL && !noVolumeDefined)
{
const mitk::Vector3D &spacing = image->GetGeometry()->GetSpacing();
double volume = spacing[0] * spacing[1] * spacing[2] * static_cast(statistics->GetN());
result.push_back(GetFormattedString(volume, decimals));
}
else {
result.push_back("NA");
}
}
else {
result.push_back("NA");
result.push_back("NA");
}
result.push_back(GetFormattedString(statistics->GetSkewness(), decimalsHigherOrderStatistics));
result.push_back(GetFormattedString(statistics->GetKurtosis(), decimalsHigherOrderStatistics));
result.push_back(GetFormattedString(statistics->GetUniformity(), decimalsHigherOrderStatistics));
result.push_back(GetFormattedString(statistics->GetEntropy(), decimalsHigherOrderStatistics));
result.push_back(GetFormattedString(statistics->GetMPP(), decimals));
result.push_back(GetFormattedString(statistics->GetUPP(), decimalsHigherOrderStatistics));
return result;
}
void QmitkImageStatisticsView::FillLinearProfileStatisticsTableView(mitk::ImageStatisticsCalculator::StatisticsContainer::ConstPointer statistics,
const mitk::Image *image)
{
this->m_Controls->m_StatisticsTable->setColumnCount(1);
this->m_Controls->m_StatisticsTable->horizontalHeader()->setVisible(false);
m_PlanarFigureStatistics = this->AssembleStatisticsIntoVector(statistics, image, true);
for (unsigned int i = 0; i< m_PlanarFigureStatistics.size(); i++)
{
this->m_Controls->m_StatisticsTable->setItem( i, 0, new QTableWidgetItem(m_PlanarFigureStatistics[i] ));
}
this->m_Controls->m_StatisticsTable->resizeColumnsToContents();
int height = STAT_TABLE_BASE_HEIGHT;
if (this->m_Controls->m_StatisticsTable->horizontalHeader()->isVisible())
height += this->m_Controls->m_StatisticsTable->horizontalHeader()->height();
if (this->m_Controls->m_StatisticsTable->horizontalScrollBar()->isVisible())
height += this->m_Controls->m_StatisticsTable->horizontalScrollBar()->height();
this->m_Controls->m_StatisticsTable->setMinimumHeight(height);
}
void QmitkImageStatisticsView::InvalidateStatisticsTableView()
{
this->m_Controls->m_StatisticsTable->horizontalHeader()->setVisible(false);
this->m_Controls->m_StatisticsTable->setColumnCount(1);
for ( int i = 0; i < this->m_Controls->m_StatisticsTable->rowCount(); ++i )
{
{
this->m_Controls->m_StatisticsTable->setItem( i, 0, new QTableWidgetItem( "NA" ) );
}
}
this->m_Controls->m_StatisticsTable->setMinimumHeight(STAT_TABLE_BASE_HEIGHT);
}
void QmitkImageStatisticsView::Activated()
{
}
void QmitkImageStatisticsView::Deactivated()
{
}
void QmitkImageStatisticsView::Visible()
{
m_Visible = true;
mitk::IRenderWindowPart* renderWindow = GetRenderWindowPart();
if (renderWindow)
{
itk::ReceptorMemberCommand::Pointer cmdTimeEvent =
itk::ReceptorMemberCommand::New();
cmdTimeEvent->SetCallbackFunction(this, &QmitkImageStatisticsView::OnTimeChanged);
// It is sufficient to add the observer to the axial render window since the GeometryTimeEvent
// is always triggered by all views.
m_TimeObserverTag = renderWindow->GetQmitkRenderWindow("axial")->
GetSliceNavigationController()->
AddObserver(mitk::SliceNavigationController::GeometryTimeEvent(nullptr, 0), cmdTimeEvent);
}
if (m_DataNodeSelectionChanged)
{
if (this->IsCurrentSelectionValid())
{
this->SelectionChanged(this->GetCurrentSelection());
}
else
{
this->SelectionChanged(this->GetDataManagerSelection());
}
m_DataNodeSelectionChanged = false;
}
}
void QmitkImageStatisticsView::Hidden()
{
m_Visible = false;
// The slice navigation controller observer is removed here instead of in the destructor.
// If it was called in the destructor, the application would freeze because the view's
// destructor gets called after the render windows have been destructed.
if ( m_TimeObserverTag != 0 )
{
mitk::IRenderWindowPart* renderWindow = GetRenderWindowPart();
if (renderWindow)
{
renderWindow->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->
RemoveObserver( m_TimeObserverTag );
}
m_TimeObserverTag = 0;
}
}
void QmitkImageStatisticsView::SetFocus()
{
}
void QmitkImageStatisticsView::AdaptBinSizeCheckboxStepsize(mitk::Image::ConstPointer image)
{
auto componentType = image->GetPixelType().GetComponentType();
if (componentType == itk::ImageIOBase::DOUBLE || componentType == itk::ImageIOBase::FLOAT) {
m_Controls->m_HistogramBinSizeSpinbox->setDecimals(2);
m_Controls->m_HistogramBinSizeSpinbox->setSingleStep(.01);
}
else {
m_Controls->m_HistogramBinSizeSpinbox->setDecimals(0);
m_Controls->m_HistogramBinSizeSpinbox->setSingleStep(1);
}
}
void QmitkImageStatisticsView::AdaptBinSizeCheckBoxMinMax(mitk::ImageStatisticsCalculator::StatisticsContainer::ConstPointer statistics, int componentType)
{
auto minValue = statistics->GetMin();
auto maxValue = statistics->GetMax();
//10 bins are minimum as defined in ImageStatisticsCalculator::GetStatistics
const unsigned int minHistogramBins = 10;
//10000 bins are just a maximum arbitrary number to keep computation time reasonable
const unsigned int maxHistogramBins = 10000;
double minHistogramBinSize = static_cast((maxValue - minValue) / maxHistogramBins);
double maxHistogramBinSize = static_cast((maxValue - minValue) / minHistogramBins);
if (componentType == itk::ImageIOBase::DOUBLE || componentType == itk::ImageIOBase::FLOAT) {
//smallest value for double/float images
const double minHistogramBinSizeValid = 0.01;
m_Controls->m_HistogramBinSizeSpinbox->setMinimum(std::max(minHistogramBinSize, minHistogramBinSizeValid));
m_Controls->m_HistogramBinSizeSpinbox->setMaximum(maxHistogramBinSize);
}
else {
//smallest valid value for int/short images
const double minHistogramBinSizeValid = 1;
m_Controls->m_HistogramBinSizeSpinbox->setMinimum(std::max(static_cast(minHistogramBinSize), static_cast(minHistogramBinSizeValid)));
m_Controls->m_HistogramBinSizeSpinbox->setMaximum(static_cast(maxHistogramBinSize));
}
}
std::map QmitkImageStatisticsView::ConvertHistogramToMap(itk::Statistics::Histogram::ConstPointer histogram) const
{
std::map histogramMap;
auto endIt = histogram->End();
auto it = histogram->Begin();
// generating Lists of measurement and frequencies
for (; it != endIt; ++it)
{
double frequency = it.GetFrequency();
double measurement = it.GetMeasurementVector()[0];
histogramMap.emplace(measurement, frequency);
}
return histogramMap;
}
std::vector QmitkImageStatisticsView::ConvertIntensityProfileToVector(mitk::IntensityProfile::ConstPointer intensityProfile) const
{
std::vector intensityProfileList;
auto end = intensityProfile->End();
for (auto it = intensityProfile->Begin(); it != end; ++it)
{
intensityProfileList.push_back(it.GetMeasurementVector()[0]);
}
return intensityProfileList;
}
QString QmitkImageStatisticsView::GetFormattedString(double value, unsigned int decimals) const
{
typedef mitk::ImageStatisticsCalculator::StatisticsContainer::RealType RealType;
RealType maxVal = std::numeric_limits::max();
if (value == maxVal)
{
return QString("NA");
}
else
{
return QString("%1").arg(value, 0, 'f', decimals);
}
}
QString QmitkImageStatisticsView::GetFormattedIndex(const vnl_vector& vector) const
{
if (vector.empty()) {
return QString();
}
QString formattedIndex("(");
for (const auto& entry : vector)
{
formattedIndex += QString::number(entry);
formattedIndex += ",";
}
formattedIndex.chop(1);
formattedIndex += ")";
return formattedIndex;
}