diff --git a/Plugins/org.mitk.gui.qt.exampleplugin/src/internal/ExampleViewControls.ui b/Plugins/org.mitk.gui.qt.exampleplugin/src/internal/ExampleViewControls.ui index 37427f6..f96e7ee 100644 --- a/Plugins/org.mitk.gui.qt.exampleplugin/src/internal/ExampleViewControls.ui +++ b/Plugins/org.mitk.gui.qt.exampleplugin/src/internal/ExampleViewControls.ui @@ -1,106 +1,110 @@ ExampleViewControls 0 0 - 220 - 160 + 227 + 356 Example View - - - - - - Please select an image. - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Offset - - - - - - - - 0 - 0 - - - - 9999 - - - - - - - - - - Process selected image - - - Add Offset - - + + + + + + 0 + 0 + + + + 9999 + + + + + + + Process selected image + + + Add Offset + + + + + + + Offset + + + + + + + + + + Image + + + + + + + + 0 + 40 + + + + + CTRL-click in result images to paint. true Qt::Vertical QSizePolicy::Expanding 20 220 + + + QmitkSingleNodeSelectionWidget + QWidget +
QmitkSingleNodeSelectionWidget.h
+ 1 +
+
diff --git a/Plugins/org.mitk.gui.qt.exampleplugin/src/internal/QmitkExampleView.cpp b/Plugins/org.mitk.gui.qt.exampleplugin/src/internal/QmitkExampleView.cpp index 9c2fcbe..8b03ff0 100644 --- a/Plugins/org.mitk.gui.qt.exampleplugin/src/internal/QmitkExampleView.cpp +++ b/Plugins/org.mitk.gui.qt.exampleplugin/src/internal/QmitkExampleView.cpp @@ -1,176 +1,151 @@ /*============================================================================ 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 +#include +#include + #include #include #include #include #include "QmitkExampleView.h" namespace { // Helper function to create a fully set up instance of our // ExampleImageInteractor, based on the state machine specified in Paint.xml // as well as its configuration in PaintConfig.xml. Both files are compiled - // into ExtExampleModule as resources. + // into MitkExampleModule as resources. static ExampleImageInteractor::Pointer CreateExampleImageInteractor() { auto exampleModule = us::ModuleRegistry::GetModule("MitkExampleModule"); if (nullptr != exampleModule) { auto interactor = ExampleImageInteractor::New(); interactor->LoadStateMachine("Paint.xml", exampleModule); interactor->SetEventConfig("PaintConfig.xml", exampleModule); return interactor; } return nullptr; } } // Don't forget to initialize the VIEW_ID. const std::string QmitkExampleView::VIEW_ID = "org.mitk.views.exampleview"; void QmitkExampleView::CreateQtPartControl(QWidget* parent) { // Setting up the UI is a true pleasure when using .ui files, isn't it? m_Controls.setupUi(parent); + m_Controls.selectionWidget->SetDataStorage(this->GetDataStorage()); + m_Controls.selectionWidget->SetSelectionIsOptional(true); + m_Controls.selectionWidget->SetEmptyInfo(QStringLiteral("Select an image")); + m_Controls.selectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New( + mitk::TNodePredicateDataType::New(), + mitk::NodePredicateNot::New(mitk::NodePredicateOr::New( + mitk::NodePredicateProperty::New("helper object"), + mitk::NodePredicateProperty::New("hidden object"))))); + // Wire up the UI widgets with our functionality. + connect(m_Controls.selectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkExampleView::OnImageChanged); connect(m_Controls.processImageButton, SIGNAL(clicked()), this, SLOT(ProcessSelectedImage())); + + // Make sure to have a consistent UI state at the very beginning. + this->OnImageChanged(m_Controls.selectionWidget->GetSelectedNodes()); } void QmitkExampleView::SetFocus() { m_Controls.processImageButton->setFocus(); } -void QmitkExampleView::OnSelectionChanged(berry::IWorkbenchPart::Pointer, const QList& dataNodes) +void QmitkExampleView::OnImageChanged(const QmitkSingleNodeSelectionWidget::NodeList&) { - for (const auto& dataNode : dataNodes) - { - // Write robust code. Always check pointers before using them. If the - // data node pointer is null, the second half of our condition isn't - // even evaluated and we're safe (C++ short-circuit evaluation). - if (dataNode.IsNotNull() && nullptr != dynamic_cast(dataNode->GetData())) - { - m_Controls.selectImageLabel->setVisible(false); - return; - } - } + this->EnableWidgets(m_Controls.selectionWidget->GetSelectedNode().IsNotNull()); +} - // Nothing is selected or the selection doesn't contain an image. - m_Controls.selectImageLabel->setVisible(true); +void QmitkExampleView::EnableWidgets(bool enable) +{ + m_Controls.processImageButton->setEnabled(enable); } void QmitkExampleView::ProcessSelectedImage() { - // Before we even think about processing something, we need to make sure - // that we have valid input. Don't be sloppy, this is a main reason - // for application crashes if neglected. - - auto selectedDataNodes = this->GetDataManagerSelection(); - - if (selectedDataNodes.empty()) - return; + auto selectedDataNode = m_Controls.selectionWidget->GetSelectedNode(); + auto data = selectedDataNode->GetData(); - auto firstSelectedDataNode = selectedDataNodes.front(); + // We don't use the auto keyword here, which would evaluate to a native + // image pointer. Instead, we want a smart pointer to ensure that + // the image isn't deleted somewhere else while we're using it. + mitk::Image::Pointer image = dynamic_cast(data); - if (firstSelectedDataNode.IsNull()) - { - QMessageBox::information(nullptr, "Example View", "Please load and select an image before starting image processing."); - return; - } - - auto data = firstSelectedDataNode->GetData(); - - // Something is selected, but does it contain data? - if (data != nullptr) - { - // We don't use the auto keyword here, which would evaluate to a native - // image pointer. Instead, we want a smart pointer in order to ensure that - // the image isn't deleted somewhere else while we're using it. - mitk::Image::Pointer image = dynamic_cast(data); + auto imageName = selectedDataNode->GetName(); + auto offset = m_Controls.offsetSpinBox->value(); - // Something is selected and it contains data, but is it an image? - if (image.IsNotNull()) - { - auto imageName = firstSelectedDataNode->GetName(); - auto offset = m_Controls.offsetSpinBox->value(); - - MITK_INFO << "Process image \"" << imageName << "\" ..."; - - // We're finally using the ExampleImageFilter from ExtExampleModule. - auto filter = ExampleImageFilter::New(); - filter->SetInput(image); - filter->SetOffset(offset); + MITK_INFO << "Process image \"" << imageName << "\" ..."; - filter->Update(); + // We're finally using the ExampleImageFilter from MitkExampleModule. + auto filter = ExampleImageFilter::New(); + filter->SetInput(image); + filter->SetOffset(offset); - mitk::Image::Pointer processedImage = filter->GetOutput(); + filter->Update(); - if (processedImage.IsNull() || !processedImage->IsInitialized()) - return; + mitk::Image::Pointer processedImage = filter->GetOutput(); - MITK_INFO << " done"; + if (processedImage.IsNull() || !processedImage->IsInitialized()) + return; - // Stuff the resulting image into a data node, set some properties, - // and add it to the data storage, which will eventually display the - // image in the application. - auto processedImageDataNode = mitk::DataNode::New(); - processedImageDataNode->SetData(processedImage); + MITK_INFO << " done"; - QString name = QString("%1 (Offset: %2)").arg(imageName.c_str()).arg(offset); - processedImageDataNode->SetName(name.toStdString()); + // Stuff the resulting image into a data node, set some properties, + // and add it to the data storage, which will eventually display the + // image in the application. + auto processedImageDataNode = mitk::DataNode::New(); + processedImageDataNode->SetData(processedImage); - // We don't really need to copy the level window, but if we wouldn't - // do it, the new level window would be initialized to display the image - // with optimal contrast in order to capture the whole range of pixel - // values. This is also true for the input image as long as one didn't - // modify its level window manually. Thus, the images would appear - // identical unless you compare the level window widget for both images. - mitk::LevelWindow levelWindow; + QString name = QString("%1 (Offset: %2)").arg(imageName.c_str()).arg(offset); + processedImageDataNode->SetName(name.toStdString()); - if (firstSelectedDataNode->GetLevelWindow(levelWindow)) - processedImageDataNode->SetLevelWindow(levelWindow); + // We don't really need to copy the level window, but if we wouldn't + // do it, the new level window would be initialized to display the image + // with optimal contrast in order to capture the whole range of pixel + // values. This is also true for the input image as long as one didn't + // modify its level window manually. Thus, the images would appear + // identical unless you compare the level window widget for both images. + mitk::LevelWindow levelWindow; - // We also attach our ExampleImageInteractor, which allows us to paint - // on the resulting images by using the mouse as long as the CTRL key - // is pressed. - auto interactor = CreateExampleImageInteractor(); + if (selectedDataNode->GetLevelWindow(levelWindow)) + processedImageDataNode->SetLevelWindow(levelWindow); - if (interactor.IsNotNull()) - interactor->SetDataNode(processedImageDataNode); + // We also attach our ExampleImageInteractor, which allows us to paint + // on the resulting images by using the mouse as long as the CTRL key + // is pressed. + auto interactor = CreateExampleImageInteractor(); - this->GetDataStorage()->Add(processedImageDataNode); - } - } + if (interactor.IsNotNull()) + interactor->SetDataNode(processedImageDataNode); - // Now it's your turn. This class/method has lots of room for improvements, - // for example: - // - // - What happens when multiple items are selected but the first one isn't - // an image? - There isn't any feedback for the user at all. - // - What's the front item of a selection? Does it depend on the order - // of selection or the position in the Data Manager? - Isn't it - // better to process all selected images? Don't forget to adjust the - // titles of the UI widgets. - // - In addition to the the displayed label, it's probably a good idea to - // enable or disable the button depending on the selection. + this->GetDataStorage()->Add(processedImageDataNode); } diff --git a/Plugins/org.mitk.gui.qt.exampleplugin/src/internal/QmitkExampleView.h b/Plugins/org.mitk.gui.qt.exampleplugin/src/internal/QmitkExampleView.h index f84d5df..658f7e3 100644 --- a/Plugins/org.mitk.gui.qt.exampleplugin/src/internal/QmitkExampleView.h +++ b/Plugins/org.mitk.gui.qt.exampleplugin/src/internal/QmitkExampleView.h @@ -1,65 +1,63 @@ /*============================================================================ 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. ============================================================================*/ #ifndef QmitkExampleView_h #define QmitkExampleView_h #include #include +#include // There's an item "ExampleViewControls.ui" in the UI_FILES list in // files.cmake. The Qt UI Compiler will parse this file and generate a // header file prefixed with "ui_", which is located in the build directory. // Use Qt Creator to view and edit .ui files. The generated header file // provides a class that contains all of the UI widgets. #include // All views in MITK derive from QmitkAbstractView. You have to override // at least the two methods CreateQtPartControl() and SetFocus(). class QmitkExampleView : public QmitkAbstractView { // As ExampleView derives from QObject and we want to use the Qt // signal and slot mechanism, we must not forget the Q_OBJECT macro. // This header file also has to be listed in MOC_H_FILES in files.cmake // so that the Qt Meta-Object compiler can find and process this // class declaration. Q_OBJECT public: // This is a tricky one and will give you some headache later on in // your debug sessions if it has been forgotten. Also, don't forget // to initialize it in the implementation file. static const std::string VIEW_ID; // In this method we initialize the GUI components and connect the // associated signals and slots. void CreateQtPartControl(QWidget* parent) override; private slots: + void OnImageChanged(const QmitkSingleNodeSelectionWidget::NodeList& nodes); void ProcessSelectedImage(); private: // Typically a one-liner. Set the focus to the default widget. void SetFocus() override; - // This method is conveniently called whenever the selection of Data Manager - // items changes. - void OnSelectionChanged( - berry::IWorkbenchPart::Pointer source, - const QList& dataNodes) override; + void EnableWidgets(bool enable); // Generated from the associated UI file, it encapsulates all the widgets // of our view. Ui::ExampleViewControls m_Controls; }; #endif