diff --git a/Modules/QtWidgets/files.cmake b/Modules/QtWidgets/files.cmake index cc23808ef6..cac3b8c1ae 100644 --- a/Modules/QtWidgets/files.cmake +++ b/Modules/QtWidgets/files.cmake @@ -1,174 +1,181 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES QmitkAbstractDataStorageModel.cpp QmitkAbstractMultiWidget.cpp QmitkAbstractNodeSelectionWidget.cpp QmitkApplicationCursor.cpp QmitkDataStorageComboBox.cpp QmitkDataStorageDefaultListModel.cpp QmitkDataStorageHistoryModel.cpp QmitkDataStorageListModel.cpp QmitkDataStorageTableModel.cpp QmitkDataStorageSimpleTreeModel.cpp QmitkDataStorageTreeModel.cpp QmitkDataStorageTreeModelInternalItem.cpp QmitkDnDDataNodeWidget.cpp QmitkFileReaderOptionsDialog.cpp QmitkFileReaderWriterOptionsWidget.cpp QmitkFileWriterOptionsDialog.cpp QmitkInteractionSchemeToolBar.cpp QmitkIOUtil.cpp QmitkLevelWindowPresetDefinitionDialog.cpp QmitkLevelWindowRangeChangeDialog.cpp QmitkLevelWindowWidgetContextMenu.cpp QmitkLevelWindowWidget.cpp QmitkLineEditLevelWindowWidget.cpp QmitkMemoryUsageIndicatorView.cpp QmitkMimeTypes.cpp QmitkMultiNodeSelectionWidget.cpp QmitkMultiWidgetConfigurationToolBar.cpp QmitkMultiWidgetLayoutManager.cpp QmitkMultiWidgetLayoutSelectionWidget.cpp QmitkNodeDescriptor.cpp QmitkNodeSelectionButton.cpp QmitkNodeSelectionConstants.cpp QmitkNodeSelectionDialog.cpp QmitkNodeSelectionListItemWidget.cpp QmitkNodeSelectionPreferenceHelper.cpp QmitkNodeDescriptor.cpp QmitkColoredNodeDescriptor.cpp QmitkNodeDescriptorManager.cpp QmitkProgressBar.cpp QmitkPropertiesTableEditor.cpp QmitkPropertiesTableModel.cpp QmitkPropertyDelegate.cpp QmitkRegisterClasses.cpp QmitkRenderingManager.cpp QmitkRenderWindowDataStorageTreeModel.cpp QmitkRenderingManagerFactory.cpp QmitkRenderWindow.cpp QmitkRenderWindowMenu.cpp QmitkRenderWindowUtilityWidget.cpp QmitkRenderWindowWidget.cpp QmitkRenderWindowContextDataStorageInspector.cpp mitkRenderWindowLayerController.cpp mitkRenderWindowLayerUtilities.cpp mitkRenderWindowViewDirectionController.cpp QmitkServiceListWidget.cpp QmitkSingleNodeSelectionWidget.cpp QmitkSliceNavigationWidget.cpp QmitkSliderLevelWindowWidget.cpp QmitkStdMultiWidget.cpp QmitkStepperAdapter.cpp QmitkMxNMultiWidget.cpp QmitkDataStorageComboBoxWithSelectNone.cpp QmitkDataStorageFilterProxyModel.cpp QmitkPropertyItem.cpp QmitkPropertyItemDelegate.cpp QmitkPropertyItemModel.cpp QmitkStyleManager.cpp QmitkAbstractDataStorageInspector.cpp QmitkDataStorageFavoriteNodesInspector.cpp QmitkDataStorageListInspector.cpp QmitkDataStorageTreeInspector.cpp QmitkDataStorageSelectionHistoryInspector.cpp QmitkModelViewSelectionConnector.cpp mitkIDataStorageInspectorProvider.cpp mitkQtWidgetsActivator.cpp mitkDataStorageInspectorGenerator.cpp QmitkOverlayWidget.cpp QmitkSimpleTextOverlayWidget.cpp QmitkButtonOverlayWidget.cpp QmitkNodeDetailsDialog.cpp + QmitkRenderWindowDataNodeTableModel.cpp + QmitkSynchronizedNodeSelectionWidget.cpp + QmitkSynchronizedWidgetConnector.cpp ) set(MOC_H_FILES include/QmitkAbstractDataStorageModel.h include/QmitkAbstractMultiWidget.h include/QmitkAbstractNodeSelectionWidget.h include/QmitkDataStorageComboBox.h include/QmitkDataStorageTableModel.h include/QmitkDataStorageTreeModel.h include/QmitkDataStorageSimpleTreeModel.h include/QmitkDataStorageDefaultListModel.h include/QmitkDnDDataNodeWidget.h include/QmitkFileReaderOptionsDialog.h include/QmitkFileReaderWriterOptionsWidget.h include/QmitkFileWriterOptionsDialog.h include/QmitkInteractionSchemeToolBar.h include/QmitkLevelWindowPresetDefinitionDialog.h include/QmitkLevelWindowRangeChangeDialog.h include/QmitkLevelWindowWidgetContextMenu.h include/QmitkLevelWindowWidget.h include/QmitkLineEditLevelWindowWidget.h include/QmitkMemoryUsageIndicatorView.h include/QmitkMultiNodeSelectionWidget.h include/QmitkMultiWidgetConfigurationToolBar.h include/QmitkMultiWidgetLayoutManager.h include/QmitkMultiWidgetLayoutSelectionWidget.h include/QmitkNodeDescriptor.h include/QmitkNodeSelectionButton.h include/QmitkNodeSelectionDialog.h include/QmitkNodeSelectionListItemWidget.h include/QmitkColoredNodeDescriptor.h include/QmitkNodeDescriptorManager.h include/QmitkProgressBar.h include/QmitkPropertiesTableEditor.h include/QmitkPropertyDelegate.h include/QmitkRenderingManager.h include/QmitkRenderWindow.h include/QmitkRenderWindowDataStorageTreeModel.h include/QmitkRenderWindowMenu.h include/QmitkRenderWindowUtilityWidget.h include/QmitkRenderWindowWidget.h include/QmitkRenderWindowContextDataStorageInspector.h include/mitkRenderWindowLayerController.h include/mitkRenderWindowLayerUtilities.h include/mitkRenderWindowViewDirectionController.h include/QmitkServiceListWidget.h include/QmitkSingleNodeSelectionWidget.h include/QmitkSliceNavigationWidget.h include/QmitkSliderLevelWindowWidget.h include/QmitkStdMultiWidget.h include/QmitkMxNMultiWidget.h include/QmitkStepperAdapter.h include/QmitkDataStorageComboBoxWithSelectNone.h include/QmitkPropertyItemDelegate.h include/QmitkPropertyItemModel.h include/QmitkAbstractDataStorageInspector.h include/QmitkDataStorageFavoriteNodesInspector.h include/QmitkDataStorageListInspector.h include/QmitkDataStorageTreeInspector.h include/QmitkDataStorageHistoryModel.h include/QmitkDataStorageSelectionHistoryInspector.h include/QmitkModelViewSelectionConnector.h include/QmitkOverlayWidget.h include/QmitkSimpleTextOverlayWidget.h include/QmitkButtonOverlayWidget.h include/QmitkNodeDetailsDialog.h + include/QmitkRenderWindowDataNodeTableModel.h + include/QmitkSynchronizedNodeSelectionWidget.h + include/QmitkSynchronizedWidgetConnector.h ) set(UI_FILES src/QmitkFileReaderOptionsDialog.ui src/QmitkFileWriterOptionsDialog.ui src/QmitkLevelWindowPresetDefinition.ui src/QmitkLevelWindowWidget.ui src/QmitkLevelWindowRangeChange.ui src/QmitkMemoryUsageIndicator.ui src/QmitkMultiNodeSelectionWidget.ui src/QmitkMultiWidgetLayoutSelectionWidget.ui src/QmitkNodeSelectionDialog.ui src/QmitkNodeSelectionListItemWidget.ui src/QmitkRenderWindowContextDataStorageInspector.ui src/QmitkServiceListWidgetControls.ui src/QmitkSingleNodeSelectionWidget.ui src/QmitkSliceNavigationWidget.ui src/QmitkDataStorageListInspector.ui src/QmitkDataStorageTreeInspector.ui src/QmitkDataStorageSelectionHistoryInspector.ui + src/QmitkSynchronizedNodeSelectionWidget.ui ) set(QRC_FILES resource/Qmitk.qrc ) diff --git a/Modules/QtWidgets/include/QmitkAbstractNodeSelectionWidget.h b/Modules/QtWidgets/include/QmitkAbstractNodeSelectionWidget.h index a3ee95aece..fda201043c 100644 --- a/Modules/QtWidgets/include/QmitkAbstractNodeSelectionWidget.h +++ b/Modules/QtWidgets/include/QmitkAbstractNodeSelectionWidget.h @@ -1,258 +1,266 @@ /*============================================================================ 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 QmitkAbstractNodeSelectionWidget_h #define QmitkAbstractNodeSelectionWidget_h #include #include #include #include #include class QmitkAbstractDataStorageModel; /** * \class QmitkAbstractNodeSelectionWidget * \brief Abstract base class for the selection of data from a data storage. */ class MITKQTWIDGETS_EXPORT QmitkAbstractNodeSelectionWidget : public QWidget { Q_OBJECT public: explicit QmitkAbstractNodeSelectionWidget(QWidget* parent = nullptr); virtual ~QmitkAbstractNodeSelectionWidget() override; /** * @brief Sets the data storage that will be used / monitored by widget. * * @par dataStorage A pointer to the data storage to set. */ void SetDataStorage(mitk::DataStorage* dataStorage); /** * Sets the node predicate and updates the widget, according to the node predicate. * Implement OnNodePredicateChange() for custom actualization of a derived widget class. * * @par nodePredicate A pointer to node predicate. */ void SetNodePredicate(const mitk::NodePredicateBase* nodePredicate); const mitk::NodePredicateBase* GetNodePredicate() const; QString GetInvalidInfo() const; QString GetEmptyInfo() const; QString GetPopUpTitel() const; QString GetPopUpHint() const; bool GetSelectionIsOptional() const; bool GetSelectOnlyVisibleNodes() const; using NodeList = QList; /** Other node container type often used in the code base.*/ using ConstNodeStdVector = std::vector; /** Returns the selected nodes, as emitted with CurrentSelectionChanged*/ NodeList GetSelectedNodes() const; /** Convinience method that returns the selected nodes as ConstNodeStdVector. This is a type also often used in the mitk code base.*/ ConstNodeStdVector GetSelectedNodesStdVector() const; Q_SIGNALS: /** * @brief A signal that will be emitted if the selected node has changed. * * @par nodes A list of data nodes that are newly selected. */ void CurrentSelectionChanged(NodeList nodes); public Q_SLOTS: /** * @brief Change the selection modus of the item view's selection model. * * If true, an incoming selection will be filtered (reduced) to only those nodes that are visible by the current view. * An outgoing selection can then at most contain the filtered nodes. * If false, the incoming non-visible selection will be stored and later added to the outgoing selection, * to include the original selection that could not be modified. * The part of the original selection, that is non-visible are the nodes, that do not fullfill the predicate. * * @par selectOnlyVisibleNodes The bool value to define the selection modus. */ void SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes); /** * @brief Transform a list of data nodes (a selection) into a model selection and set this as a new selection of the * selection model of the private member item view. * * The function filters the given list of nodes according to the 'm_SelectOnlyVisibleNodes' member variable. If * necessary, the non-visible nodes are stored. This is done if 'm_SelectOnlyVisibleNodes' is false: In this case * the selection may be filtered and only a subset of the selected nodes may be visible and therefore (de-)selectable * in the data storage viewer. By storing the non-visible nodes it is possible to send the new, modified selection * but also include the selected nodes from the original selection that could not be modified (see 'SetSelectOnlyVisibleNodes'). * * @par nodes A list of data nodes that should be newly selected. */ void SetCurrentSelection(NodeList selectedNodes); /** Set the info text that should be displayed if no (valid) node is selected, * but a selection is mandatory. * The string can contain HTML code, if desired. */ void SetInvalidInfo(QString info); /** Set the info text that should be displayed if no (valid) node is selected, * but a selection is optional. * The string can contain HTML code, if desired. */ void SetEmptyInfo(QString info); /** Set the caption of the popup that is displayed to alter the selection. * The string can contain HTML code, if desired. */ void SetPopUpTitel(QString info); /** Set the hint text of the popup that is displayed to alter the selection. * The string can contain HTML code, if desired. */ void SetPopUpHint(QString info); /** Set the widget into an optional mode. Optional means that the selection of no valid * node does not mean an invalid state. Thus no node is a valid "node" selection too. */ void SetSelectionIsOptional(bool isOptional); protected Q_SLOTS: /** Call to remove a node from the current selection. If the node is part of the current selection, * this will trigger ReviseSelectionChanged(), AllowEmissionOfSelection() and if there is really a change, * will also emit CurrentSelectionChanged. */ void RemoveNodeFromSelection(const mitk::DataNode* node); protected: /** Method is called if the display of the selected nodes should be updated (e.g. because the selection changed). */ virtual void UpdateInfo() = 0; /** Method is called if the predicate has changed, before the selection will be updated according to the new predicate. * The default implementation does nothing. * @remark If you are only interested to know when the selection has changed, overwrite OnInternalSelectionChange(). */ virtual void OnNodePredicateChanged(); /** Method is called if the data storage has changed. The selection will be automatically be reseted afterwards. * The default implementation does nothing. */ virtual void OnDataStorageChanged(); /** This member function will called when ever a new internal selection has been determined. This can be * used to update the state of internal widgets. The default implementation does nothing. */ virtual void OnInternalSelectionChanged(); /** Method is called when a node is added to the storage. Default implementation does nothing. * Derived widgets can override the method if they want to react on new nodes in the storage. */ virtual void OnNodeAddedToStorage(const mitk::DataNode* node); /** Method is called when a node is removed from the storage. The removed node is passed as - * variable. This member is called directly before the node will be removed from the current selection if - * he was a part. Default implementation does nothing. + * variable. This member is called directly before the node will be removed from the current selection. + * Default implementation does nothing. + * Derived widgets can override the method if they want to handle to-be-removed nodes before. */ virtual void OnNodeRemovedFromStorage(const mitk::DataNode* node); + /** Method is called when a node is modified. The modified node is passed as 'caller' variable. + * Default implementation handles changes that are related to the node predicate: + * - If the node does not fit the node predicate anymore, it will be removed. + * - If the node was part of the external selection and now fits the node predicate, + * a new selection is compiled and emitted. + * Derived widgets can override the method if they want to react on modified nodes. + */ + virtual void OnNodeModified(const itk::Object* caller, const itk::EventObject& event); + /** Method is called if the internal selection has changed. It will call following methods, that can be overriden to change * behavior in derived classes: * - pre internal selection change: ReviseSelectionChanged() * - post internal selection change: OnInternalSelectionChanged(), UpdateInfo() and AllowEmissionOfSelection() (via EmitSelection()). * If the emission is needed and allowed it will also trigger the emission via EmitSelection(). */ void HandleChangeOfInternalSelection(NodeList newInternalSelection); /** Compiles the list of node that would be emitted. It always contains the internal selection. * Depending on SelectOnlyVisibleNodes it also adds all external select nodes that weren't visible (failed the predicate). */ NodeList CompileEmitSelection() const; /** This member function is called if the internal selection is about to be changed by the base implementation. * This is the slot where derived classes can revise and change the internal selection before widget updates, * signal emissions and other things are triggered. Default implementation does nothing, thus it keeps the * passed internal selection as compiled by the base implementation. */ virtual void ReviseSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection); /** This function will be called before the CurrentSelectionChanged signal is emitted. The return value indicates * if the signal should be emitted (true = emission; false = no emission). The default implementation always * returns true. * @param emissionCandidates The nodes that will be emitted if the function returns true. */ virtual bool AllowEmissionOfSelection(const NodeList& emissionCandidates) const; /** Checks if the new emission differs from the last emission. If this is the case and AllowEmissionOfSelection() * returns true the new selection will be emited. */ void EmitSelection(const NodeList& emissionCandidates); void SetCurrentInternalSelection(NodeList selectedNodes); const NodeList& GetCurrentInternalSelection() const; const NodeList& GetCurrentExternalSelection() const; mitk::WeakPointer m_DataStorage; mitk::NodePredicateBase::ConstPointer m_NodePredicate; QString m_InvalidInfo; QString m_EmptyInfo; QString m_PopUpTitel; QString m_PopUpHint; /** See documentation of SetSelectOnlyVisibleNodes for details*/ bool m_IsOptional; /** See documentation of SetSelectionIsOptional for details*/ bool m_SelectOnlyVisibleNodes; private: /** Helper triggered on the storage delete event */ void SetDataStorageDeleted(); /**Member is called when a node is added to the storage. Derived widgets can override the method OnNodeAddedToStorage if they want to react on new nodes in the storage.*/ void NodeAddedToStorage(const mitk::DataNode* node); /**Member is called when a node is removed from the storage. It calls OnNodeRemovedFromStorage() and afterwards it removes the removed node form the selection (if it is part of the current selection). Derived classes can override OnNodeRemovedFromStorage() to react on the fact that a node might be removed and their selection might change, because the removed node is part of there selection.*/ void NodeRemovedFromStorage(const mitk::DataNode* node); - void OnNodeModified(const itk::Object * /*caller*/, const itk::EventObject &); - void AddNodeObserver(mitk::DataNode* node); void RemoveNodeObserver(mitk::DataNode* node); unsigned long m_DataStorageDeletedTag; NodeList m_CurrentInternalSelection; NodeList m_CurrentExternalSelection; NodeList m_LastEmission; bool m_LastEmissionAllowance; using NodeObserverTagMapType = std::map; NodeObserverTagMapType m_NodeObserverTags; /** Help to prevent recursions due to signal loops when emitting selections.*/ bool m_RecursionGuard; }; #endif diff --git a/Modules/QtWidgets/include/QmitkMxNMultiWidget.h b/Modules/QtWidgets/include/QmitkMxNMultiWidget.h index dbc9613c5d..bbb622e0ff 100644 --- a/Modules/QtWidgets/include/QmitkMxNMultiWidget.h +++ b/Modules/QtWidgets/include/QmitkMxNMultiWidget.h @@ -1,119 +1,125 @@ /*============================================================================ 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 QmitkMxNMultiWidget_h #define QmitkMxNMultiWidget_h -// qt widgets module #include "MitkQtWidgetsExports.h" + +// qt widgets module #include "QmitkAbstractMultiWidget.h" +#include +#include /** * @brief The 'QmitkMxNMultiWidget' is a 'QmitkAbstractMultiWidget' that is used to display multiple render windows at once. * Render windows can dynamically be added and removed to change the layout of the multi widget. This * is done by using the 'SetLayout'-function to define a layout. This will automatically add or remove * the appropriate number of render window widgets. */ class MITKQTWIDGETS_EXPORT QmitkMxNMultiWidget : public QmitkAbstractMultiWidget { Q_OBJECT public: QmitkMxNMultiWidget(QWidget* parent = nullptr, Qt::WindowFlags f = 0, const QString& multiWidgetName = "mxnmulti"); ~QmitkMxNMultiWidget(); void InitializeMultiWidget() override; void Synchronize(bool synchronized) override; QmitkRenderWindow* GetRenderWindow(const QString& widgetName) const override; QmitkRenderWindow* GetRenderWindow(const mitk::AnatomicalPlane& orientation) const override; void SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget) override; /** * @brief Initialize the active render windows of the MxNMultiWidget to the given geometry. * * @param geometry The geometry to be used to initialize / update the * active render window's time and slice navigation controller. * @param resetCamera If true, the camera and crosshair will be reset to the default view (centered, no zoom). * If false, the current crosshair position and the camera zoom will be stored and reset * after the reference geometry has been updated. */ void InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera) override; /** * @brief Forward the given time geometry to all base renderers, so that they can store it as their * interaction reference geometry. * This will update the alignment status of the reference geometry for each base renderer. * For more details, see 'BaseRenderer::SetInteractionReferenceGeometry'. * Overridem from 'QmitkAbstractMultiWidget'. */ void SetInteractionReferenceGeometry(const mitk::TimeGeometry* referenceGeometry) override; /** * @brief Returns true if the render windows are coupled; false if not. * * For the MxNMultiWidget the render windows are typically decoupled. */ bool HasCoupledRenderWindows() const override; void SetSelectedPosition(const mitk::Point3D& newPosition, const QString& widgetName) override; const mitk::Point3D GetSelectedPosition(const QString& widgetName) const override; void SetCrosshairVisibility(bool visible) override; bool GetCrosshairVisibility() const override; void SetCrosshairGap(unsigned int gapSize) override; void ResetCrosshair() override; void SetWidgetPlaneMode(int userMode) override; mitk::SliceNavigationController* GetTimeNavigationController(); void AddPlanesToDataStorage(); void RemovePlanesFromDataStorage(); public Q_SLOTS: // mouse events void wheelEvent(QWheelEvent* e) override; void mousePressEvent(QMouseEvent* e) override; void moveEvent(QMoveEvent* e) override; Q_SIGNALS: void WheelMoved(QWheelEvent *); void Moved(); protected: void RemoveRenderWindowWidget() override; private: void SetLayoutImpl() override; void SetInteractionSchemeImpl() override { } void CreateRenderWindowWidget(); + void SetInitialSelection(); + void ToggleSynchronization(QmitkSynchronizedNodeSelectionWidget* synchronizedWidget); mitk::SliceNavigationController* m_TimeNavigationController; + std::unique_ptr m_SynchronizedWidgetConnector; bool m_CrosshairVisibility; }; #endif diff --git a/Modules/QtWidgets/include/QmitkRenderWindowContextDataStorageInspector.h b/Modules/QtWidgets/include/QmitkRenderWindowContextDataStorageInspector.h index 2434015247..f4d17a5f8d 100644 --- a/Modules/QtWidgets/include/QmitkRenderWindowContextDataStorageInspector.h +++ b/Modules/QtWidgets/include/QmitkRenderWindowContextDataStorageInspector.h @@ -1,90 +1,88 @@ /*============================================================================ 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 QmitkRenderWindowContextDataStorageInspector_h #define QmitkRenderWindowContextDataStorageInspector_h // qt widgets module #include "MitkQtWidgetsExports.h" #include "ui_QmitkRenderWindowContextDataStorageInspector.h" // render window manager module #include #include // qt widgets module #include // mitk core #include /** * The 'QmitkRenderWindowContextDataStorageInspector' offers a GUI to manipulate the base renderer / render windows of the MITK workbench. * * In order to use this widget, a (e.g.) plugin has to set the controlled renderer, which will be forwarded to * a render window view direction controller. */ class MITKQTWIDGETS_EXPORT QmitkRenderWindowContextDataStorageInspector : public QmitkAbstractDataStorageInspector { Q_OBJECT public: QmitkRenderWindowContextDataStorageInspector(QWidget* parent = nullptr, mitk::BaseRenderer* renderer = nullptr); // override from 'QmitkAbstractDataStorageInspector' /** * @brief See 'QmitkAbstractDataStorageInspector' */ QAbstractItemView* GetView() override; /** * @brief See 'QmitkAbstractDataStorageInspector' */ const QAbstractItemView* GetView() const override; /** * @brief See 'QmitkAbstractDataStorageInspector' */ void SetSelectionMode(SelectionMode mode) override; /** * @brief See 'QmitkAbstractDataStorageInspector' */ SelectionMode GetSelectionMode() const override; QItemSelectionModel* GetDataNodeSelectionModel() const; Q_SIGNALS: void ReinitAction(QList selectedNodes); void ResetAction(QList selectedNodes); private Q_SLOTS: void ModelRowsInserted(const QModelIndex& parent, int start, int end); - void ResetRenderer(); - void OnContextMenuRequested(const QPoint& pos); void OnReinit(); void OnReset(); private: void Initialize() override; void SetUpConnections(); Ui::QmitkRenderWindowContextDataStorageInspector m_Controls; std::unique_ptr m_StorageModel; std::unique_ptr m_RenderWindowLayerController; }; #endif diff --git a/Modules/QtWidgets/include/QmitkRenderWindowDataNodeTableModel.h b/Modules/QtWidgets/include/QmitkRenderWindowDataNodeTableModel.h new file mode 100644 index 0000000000..e0c8d3f4b0 --- /dev/null +++ b/Modules/QtWidgets/include/QmitkRenderWindowDataNodeTableModel.h @@ -0,0 +1,87 @@ +/*============================================================================ + +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 QmitkRenderWindowDataNodeTableModel_h +#define QmitkRenderWindowDataNodeTableModel_h + +#include + +//mitk core +#include +#include +#include +#include + +// qt widgets module +#include +#include +#include + +#include + +/* +* @brief The 'QmitkRenderWindowDataNodeTableModel' is a table model that extends the 'QAbstractItemModel'. +*/ +class MITKQTWIDGETS_EXPORT QmitkRenderWindowDataNodeTableModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + + QmitkRenderWindowDataNodeTableModel(QObject* parent = nullptr); + + void UpdateModelData(); + + void SetDataStorage(mitk::DataStorage* dataStorage); + void SetCurrentRenderer(mitk::BaseRenderer* baseRenderer); + mitk::BaseRenderer::Pointer GetCurrentRenderer() const; + + using NodeList = QList; + void SetCurrentSelection(NodeList selectedNodes); + NodeList GetCurrentSelection() const; + + // override from 'QAbstractItemModel' + QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& child) const override; + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + + QVariant data(const QModelIndex& index, int role) const override; + bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; + + Qt::ItemFlags flags(const QModelIndex& index) const override; + + Qt::DropActions supportedDropActions() const override; + Qt::DropActions supportedDragActions() const override; + QStringList mimeTypes() const override; + QMimeData* mimeData(const QModelIndexList& indexes) const override; + + bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; + +Q_SIGNALS: + + void ModelUpdated(); + +private: + + std::unique_ptr m_RenderWindowLayerController; + mitk::WeakPointer m_BaseRenderer; + NodeList m_CurrentSelection; + + QIcon m_VisibleIcon; + QIcon m_InvisibleIcon; + QIcon m_ArrowIcon; + QIcon m_TimesIcon; +}; + +#endif diff --git a/Modules/QtWidgets/include/QmitkRenderWindowDataStorageTreeModel.h b/Modules/QtWidgets/include/QmitkRenderWindowDataStorageTreeModel.h index 6fae6f6630..b29ac1e636 100644 --- a/Modules/QtWidgets/include/QmitkRenderWindowDataStorageTreeModel.h +++ b/Modules/QtWidgets/include/QmitkRenderWindowDataStorageTreeModel.h @@ -1,132 +1,131 @@ /*============================================================================ 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 QmitkRenderWindowDataStorageTreeModel_h #define QmitkRenderWindowDataStorageTreeModel_h // render window manager UI model #include "MitkQtWidgetsExports.h" // render window manager module #include "mitkRenderWindowLayerController.h" #include "mitkRenderWindowLayerUtilities.h" //mitk core #include // qt widgets module #include #include /* * @brief The 'QmitkRenderWindowDataStorageTreeModel' is a tree model derived from the 'QmitkAbstractDataStorageModel'. */ class MITKQTWIDGETS_EXPORT QmitkRenderWindowDataStorageTreeModel : public QmitkAbstractDataStorageModel { Q_OBJECT public: QmitkRenderWindowDataStorageTreeModel(QObject* parent = nullptr); // override from 'QmitkAbstractDataStorageModel' /** * @brief See 'QmitkAbstractDataStorageModel' */ void DataStorageChanged() override; /** * @brief See 'QmitkAbstractDataStorageModel' */ void NodePredicateChanged() override; /** * @brief See 'QmitkAbstractDataStorageModel' */ void NodeAdded(const mitk::DataNode* node) override; /** * @brief See 'QmitkAbstractDataStorageModel' */ void NodeChanged(const mitk::DataNode* node) override; /** * @brief See 'QmitkAbstractDataStorageModel' */ void NodeRemoved(const mitk::DataNode* node) override; // override from 'QAbstractItemModel' QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex& parent) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex& parent = QModelIndex()) const override; QVariant data(const QModelIndex& index, int role) const override; bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole) override; Qt::ItemFlags flags(const QModelIndex& index) const override; Qt::DropActions supportedDropActions() const override; Qt::DropActions supportedDragActions() const override; QStringList mimeTypes() const override; QMimeData* mimeData(const QModelIndexList& indexes) const override; bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) override; void SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer); void SetCurrentRenderer(mitk::BaseRenderer* baseRenderer); mitk::BaseRenderer::Pointer GetCurrentRenderer() const; private: void ResetTree(); void UpdateModelData(); /** * @brief Adjust the layer property according to the current tree. * The function will set the "layer" property of each underlying data node so that it fits the * the actual hierarchy represented by the current tree. */ void AdjustLayerProperty(); /** * @brief Fill a vector of tree items in a depth-first order (child-first). */ void TreeToVector(QmitkDataStorageTreeModelInternalItem* parent, std::vector& treeAsVector) const; /** * @brief Add the given data node to the tree of the given renderer. * The given renderer specifies the "layer"-property that is used for adding the new tree item * to the tree. The "layer"-property may be different for each renderer resulting in a * different tree for each renderer. * * @param dataNode The data node that should be added. * @param renderer The base renderer to which the data node should be added. */ void AddNodeInternal(const mitk::DataNode* dataNode, const mitk::BaseRenderer* renderer); /** * @brief Remove the tree item that contains the given data node. Removing an item may * leave the child items of the removed item without a parent. In this case * the children have to be moved inside the tree so the tree has to be rebuild * according to the current status of the data storage. * * @param dataNode The data node that should be removed. */ void RemoveNodeInternal(const mitk::DataNode* dataNode); mitk::DataNode* GetParentNode(const mitk::DataNode* node) const; QmitkDataStorageTreeModelInternalItem* GetItemByIndex(const QModelIndex& index) const; QModelIndex GetIndexByItem(QmitkDataStorageTreeModelInternalItem* item) const; std::unique_ptr m_RenderWindowLayerController; - mitk::RenderWindowLayerUtilities::RendererVector m_ControlledRenderer; QmitkDataStorageTreeModelInternalItem* m_Root; mitk::WeakPointer m_BaseRenderer; }; #endif diff --git a/Modules/QtWidgets/include/QmitkRenderWindowUtilityWidget.h b/Modules/QtWidgets/include/QmitkRenderWindowUtilityWidget.h index 05cde2fb5e..9a2d0089cf 100644 --- a/Modules/QtWidgets/include/QmitkRenderWindowUtilityWidget.h +++ b/Modules/QtWidgets/include/QmitkRenderWindowUtilityWidget.h @@ -1,70 +1,74 @@ /*============================================================================ 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 QmitkRenderWindowUtilityWidget_h #define QmitkRenderWindowUtilityWidget_h -// qt widgets module #include "MitkQtWidgetsExports.h" -#include "QmitkSliceNavigationWidget.h" -#include "QmitkStepperAdapter.h" -#include "QmitkRenderWindow.h" -#include "QmitkRenderWindowContextDataStorageInspector.h" -#include "mitkRenderWindowViewDirectionController.h" -// mitk core -#include "mitkDataStorage.h" +// qt widgets module +#include +#include +#include +#include +#include // qt #include #include #include #include +namespace mitk +{ + class DataStorage; +} + +class QmitkRenderWindow; + class MITKQTWIDGETS_EXPORT QmitkRenderWindowUtilityWidget : public QWidget { Q_OBJECT public: + QmitkRenderWindowUtilityWidget( QWidget* parent = nullptr, QmitkRenderWindow* renderWindow = nullptr, mitk::DataStorage* dataStorage = nullptr ); ~QmitkRenderWindowUtilityWidget() override; - void SetInvertedSliceNavigation(bool inverted); + void ToggleSynchronization(bool synchronized); + + void SetGeometry(const itk::EventObject& event); Q_SIGNALS: - void ReinitAction(QList selectedNodes); - void ResetAction(QList selectedNodes); + void SynchronizationToggled(QmitkSynchronizedNodeSelectionWidget* synchronizedWidget); private: - QHBoxLayout* m_Layout; - QMenuBar* m_MenuBar; - - QmitkRenderWindow* m_RenderWindow; - mitk::DataStorage* m_DataStorage; - - QmitkRenderWindowContextDataStorageInspector* m_RenderWindowInspector; + mitk::BaseRenderer* m_BaseRenderer; + QmitkSynchronizedNodeSelectionWidget* m_NodeSelectionWidget; QmitkSliceNavigationWidget* m_SliceNavigationWidget; QmitkStepperAdapter* m_StepperAdapter; + std::unique_ptr m_RenderWindowLayerController; std::unique_ptr m_RenderWindowViewDirectionController; QComboBox* m_ViewDirectionSelector; void ChangeViewDirection(const QString& viewDirection); + }; #endif diff --git a/Modules/QtWidgets/include/QmitkRenderWindowWidget.h b/Modules/QtWidgets/include/QmitkRenderWindowWidget.h index c3420b49ef..6716bdf177 100644 --- a/Modules/QtWidgets/include/QmitkRenderWindowWidget.h +++ b/Modules/QtWidgets/include/QmitkRenderWindowWidget.h @@ -1,130 +1,123 @@ /*============================================================================ 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 QmitkRenderWindowWidget_h #define QmitkRenderWindowWidget_h -// qt widgets module #include "MitkQtWidgetsExports.h" -#include "QmitkRenderWindow.h" -#include "QmitkRenderWindowUtilityWidget.h" + +// qt widgets module +#include // mitk core #include #include #include // qt #include #include +#include class vtkCornerAnnotation; /** * @brief The 'QmitkRenderWindowWidget' is a QFrame that holds a render window * and some associates properties, e.g. decorations. * Decorations are corner annotation (text and color), frame color or background color * and can be set using this class. * The 'QmitkRenderWindowWidget' is used inside a 'QmitkAbstractMultiWidget', where a map contains * several render window widgets to create the multi widget display. * This class uses a CrosshairManager, which allows to use plane geometries as crosshair. */ class MITKQTWIDGETS_EXPORT QmitkRenderWindowWidget : public QFrame { Q_OBJECT public: QmitkRenderWindowWidget( QWidget* parent = nullptr, const QString& widgetName = "", - mitk::DataStorage* dataStorage = nullptr, - bool windowControls = false); + mitk::DataStorage* dataStorage = nullptr); ~QmitkRenderWindowWidget() override; void SetDataStorage(mitk::DataStorage* dataStorage); const QString& GetWidgetName() const { return m_WidgetName; }; QmitkRenderWindow* GetRenderWindow() const { return m_RenderWindow; }; mitk::SliceNavigationController* GetSliceNavigationController() const; void RequestUpdate(); void ForceImmediateUpdate(); + void AddUtilityWidget(QWidget* utilityWidget); + void SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower); void ShowGradientBackground(bool enable); std::pair GetGradientBackgroundColors() const { return m_GradientBackgroundColors; }; bool IsGradientBackgroundOn() const; void SetDecorationColor(const mitk::Color& color); mitk::Color GetDecorationColor() const { return m_DecorationColor; }; void ShowColoredRectangle(bool show); bool IsColoredRectangleVisible() const; void ShowCornerAnnotation(bool show); bool IsCornerAnnotationVisible() const; void SetCornerAnnotationText(const std::string& cornerAnnotation); std::string GetCornerAnnotationText() const; bool IsRenderWindowMenuActivated() const; void SetCrosshairVisibility(bool visible); bool GetCrosshairVisibility(); void SetCrosshairGap(unsigned int gapSize); void AddPlanesToDataStorage(); void RemovePlanesFromDataStorage(); void SetCrosshairPosition(const mitk::Point3D& newPosition); mitk::Point3D GetCrosshairPosition() const; void SetGeometry(const itk::EventObject& event); void SetGeometrySlice(const itk::EventObject& event); -private Q_SLOTS: - - void OnReinitAction(QList selectedNodes); - - void OnResetAction(QList selectedNodes); +public Q_SLOTS: void OnResetGeometry(); private: void InitializeGUI(); void InitializeDecorations(); - void ComputeInvertedSliceNavigation(); void ResetGeometry(const mitk::TimeGeometry* referenceGeometry); QString m_WidgetName; QVBoxLayout* m_Layout; mitk::DataStorage* m_DataStorage; QmitkRenderWindow* m_RenderWindow; mitk::CrosshairManager::Pointer m_CrosshairManager; std::pair m_GradientBackgroundColors; mitk::Color m_DecorationColor; vtkSmartPointer m_CornerAnnotation; - QmitkRenderWindowUtilityWidget* m_UtilityWidget; - - bool m_WindowControls; - }; #endif diff --git a/Modules/QtWidgets/include/QmitkSynchronizedNodeSelectionWidget.h b/Modules/QtWidgets/include/QmitkSynchronizedNodeSelectionWidget.h new file mode 100644 index 0000000000..875df531b2 --- /dev/null +++ b/Modules/QtWidgets/include/QmitkSynchronizedNodeSelectionWidget.h @@ -0,0 +1,100 @@ +/*============================================================================ + +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 QmitkSynchronizedNodeSelectionWidget_h +#define QmitkSynchronizedNodeSelectionWidget_h + +#include + +#include "ui_QmitkSynchronizedNodeSelectionWidget.h" + +// mitk core +#include + +// qt widgets module +#include +#include + +/* +* @brief The 'QmitkSynchronizedNodeSelectionWidget' implements the 'QmitkAbstractNodeSelectionWidget' +* by providing a table view, using a 'QmitkRenderWindowDataNodeTableModel' and extending it +* with base renderer-specific functionality. +* +* Given a base renderer, the selection widget is able to display and access render window specific properties +* of the selected nodes, making it possible to switch between a "synchronized" and "desynchronized" selection +* state. +* The widget can be used to decide if all data nodes of the data storage should be selected or +* only an individually selected set of nodes, defined by a 'QmitkNodeSelectionDialog'. +* If individual nodes are selected / removed from the selection, the widget can inform other +* 'QmitkSynchronizedNodeSelectionWidget' about the current selection, if desired. +* Additionally the widget allows to reinitialize the corresponding base renderer with a specific +* data node geometry. +*/ +class MITKQTWIDGETS_EXPORT QmitkSynchronizedNodeSelectionWidget : public QmitkAbstractNodeSelectionWidget +{ + Q_OBJECT + +public: + + QmitkSynchronizedNodeSelectionWidget(QWidget* parent); + ~QmitkSynchronizedNodeSelectionWidget(); + + using NodeList = QmitkAbstractNodeSelectionWidget::NodeList; + + void SetBaseRenderer(mitk::BaseRenderer* baseRenderer); + + void SetSelectAll(bool selectAll); + bool GetSelectAll() const; + void SetSynchronized(bool synchronize); + bool IsSynchronized() const; + +Q_SIGNALS: + + void SelectionModeChanged(bool selectAll); + +private Q_SLOTS: + + void OnModelUpdated(); + void OnSelectionModeChanged(bool selectAll); + void OnEditSelection(); + void OnTableClicked(const QModelIndex& index); + +protected: + + void SetUpConnections(); + void Initialize(); + + void UpdateInfo() override; + void OnDataStorageChanged() override; + void OnNodePredicateChanged() override; + void ReviseSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection) override; + void OnInternalSelectionChanged() override; + bool AllowEmissionOfSelection(const NodeList& emissionCandidates) const override; + void OnNodeAddedToStorage(const mitk::DataNode* node) override; + void OnNodeModified(const itk::Object* caller, const itk::EventObject& event) override; + +private: + + void ReviseSynchronizedSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection); + void ReviseDesynchronizedSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection); + void ReinitNode(const mitk::DataNode* dataNode); + void RemoveFromInternalSelection(mitk::DataNode* dataNode); + void DeselectNode(mitk::DataNode* dataNode); + + Ui::QmitkSynchronizedNodeSelectionWidget m_Controls; + mitk::WeakPointer m_BaseRenderer; + + std::unique_ptr m_StorageModel; + +}; + +#endif diff --git a/Modules/QtWidgets/include/QmitkSynchronizedWidgetConnector.h b/Modules/QtWidgets/include/QmitkSynchronizedWidgetConnector.h new file mode 100644 index 0000000000..ad672d5223 --- /dev/null +++ b/Modules/QtWidgets/include/QmitkSynchronizedWidgetConnector.h @@ -0,0 +1,150 @@ +/*============================================================================ + +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 QmitkSynchronizedWidgetConnector_h +#define QmitkSynchronizedWidgetConnector_h + +#include + +// mitk core +#include + +// mitk qt widgets +#include + +// qt +#include + +/* +* @brief This class connects different 'QmitkSynchronizedNodeSelectionWidget', such that +* they can synchronize their current node selection and their current selection mode. +* +* In order to synchronize a new node selection widget with other already connected +* node selection widgets, 'ConnectWidget(const QmitkSynchronizedNodeSelectionWidget*)' has to be used. +* In order to desynchronize a node selection widget, +* 'DisconnectWidget(const QmitkSynchronizedNodeSelectionWidget*)' has to be used. +* If a new node selection has been connected / synchronized, +* 'SynchronizeWidget(QmitkSynchronizedNodeSelectionWidget*' can be used to initialy set +* the current selection and the current selection mode. +* For this, both values are stored in this class internally. +*/ +class MITKQTWIDGETS_EXPORT QmitkSynchronizedWidgetConnector : public QObject +{ + Q_OBJECT + +public: + + using NodeList = QList; + + QmitkSynchronizedWidgetConnector(); + + /* + * @brief This function connects the different signals and slots of this instance and the given + * given node selection widget, such that changes to the current list of nodes + * and the selection mode can be forwarded or received. + * The connections are as follows: + * - QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged + * -> QmitkSynchronizedWidgetConnector::ChangeSelection + * - QmitkSynchronizedWidgetConnector::NodeSelectionChanged + * -> QmitkAbstractNodeSelectionWidget::SetCurrentSelection + * - QmitkSynchronizedNodeSelectionWidget::SelectionModeChanged + * -> QmitkSynchronizedWidgetConnector::ChangeSelectionMode + * - QmitkSynchronizedWidgetConnector::SelectionModeChanged + * -> QmitkSynchronizedNodeSelectionWidget::SetSelectAll + * + * @param nodeSelectionWidget The synchronized node selection widget to be connected / synchronized. + */ + void ConnectWidget(const QmitkSynchronizedNodeSelectionWidget* nodeSelectionWidget) const; + /* + * @brief This function disconnects the different signals and slot of this instance and the given + * given node selection widget, such that changes to the current list of nodes + * and the selection mode cannot be forwarded or received anymore. + * + * @param nodeSelectionWidget The synchronized node selection widget to be disconnected / desynchronized. + */ + void DisconnectWidget(const QmitkSynchronizedNodeSelectionWidget* nodeSelectionWidget) const; + /* + * @brief This function sets the current selection and the selection mode of the given node selection widget + * to the values of this instance. The required values are stored in this class internally. + * It can be used to newly initialize the given node selection widget. + * + * @param nodeSelectionWidget The synchronized node selection widget for which the + * current selection and the selection mode should be set. + */ + void SynchronizeWidget(QmitkSynchronizedNodeSelectionWidget* nodeSelectionWidget) const; + /* + * @brief Get the current internal node selection. + * + * @return NodeList The current internal node selection stored as a member variable. + */ + NodeList GetNodeSelection() const; + /* + * @brief Get the current internal selection mode. + * + * @return The current internal selection mode stored as a member variable. + */ + bool GetSelectionMode() const; + +Q_SIGNALS: + /* + * @brief A signal that will be emitted by the 'ChangeSelection'-slot. + * This happens if a new selection / list of nodes is set from outside of this class, + * e.g. from a QmitkSynchronizedNodeSelectionWidget. + * This signal is connected to the 'SetCurrentSelection'-slot of each + * QmitkSynchronizedNodeSelectionWidget to propagate the new selection. + * + * @param nodes A list of data nodes that are newly selected. + */ + void NodeSelectionChanged(NodeList nodes); + /* + * @brief A signal that will be emitted by the 'ChangeSelectionMode'-slot. + * This happens if the selection mode is change from outside of this class, + * e.g. from a QmitkSynchronizedNodeSelectionWidget. + * This signal is connected to the 'SetSelectAll'-slot of each + * QmitkSynchronizedNodeSelectionWidget to propagate the selection mode. + * + * @param selectAll True, if the selection mode is changed to "select all" nodes. + * False otherwise. + */ + void SelectionModeChanged(bool selectAll); + +public Q_SLOTS: + /* + * @brief Set a new internal selection and send this new selection to connected + * QmitkSynchronizedNodeSelectionWidgets using the 'NodeSelectionChanged'-signal. + * + * This slot itself is connected to the 'CurrentSelectionChanged'-signal of each + * QmitkSynchronizedNodeSelectionWidget to receive a new selection. + * + * @param nodes A list of data nodes that are newly selected. + */ + void ChangeSelection(NodeList nodes); + /* + * @brief Set a new selection mode and send this new selection mode to connected + * QmitkSynchronizedNodeSelectionWidgets using the 'SelectionModeChanged'-signal. + * + * This slot itself is connected to the 'SelectionModeChanged'-signal of each + * QmitkSynchronizedNodeSelectionWidget to receive a new selection mode. + * + * @param selectAll True, if the selection mode is changed to "select all" nodes. + * False otherwise. + */ + void ChangeSelectionMode(bool selectAll); + +private: + + NodeList m_InternalSelection; + bool m_SelectAll; + +}; + +#endif diff --git a/Modules/QtWidgets/include/mitkRenderWindowLayerController.h b/Modules/QtWidgets/include/mitkRenderWindowLayerController.h index 86a48c5e75..7df2a26072 100644 --- a/Modules/QtWidgets/include/mitkRenderWindowLayerController.h +++ b/Modules/QtWidgets/include/mitkRenderWindowLayerController.h @@ -1,174 +1,115 @@ /*============================================================================ 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 mitkRenderWindowLayerController_h #define mitkRenderWindowLayerController_h // qt widgets module #include "MitkQtWidgetsExports.h" #include "mitkRenderWindowLayerUtilities.h" // mitk core #include #include #include namespace mitk { /** - * The RenderWindowLayerController is used to manipulate the 'layer', 'fixedLayer' and 'visible' property of a given data node. + * The RenderWindowLayerController is used to manipulate the 'layer' and 'visible' property of a given data node. * The 'layer' property is used to denote the layer level of a data node. Data from nodes on higher layer level are rendered * on top of data from nodes on lower layer level. It can be changed using the 'MoveNode*'-functions. * * To view the data of a data node only in a specific renderer, the "InsertLayerNode'-function should be used. It inserts the * given node into the specified renderer and sets the corresponding properties. - * To hide the data in the common renderer view (all renderer), the 'HideDataNodeInAllRenderer'-function can be used. * Inserting and showing a data node in a specific renderer / render window, will overwrite the properties of the common renderer view. * * For more information about the data node properties for specific renderer, see mitk::DataNode- and mitk::PropertyList-classes. * * Functions with 'mitk::BaseRenderer* renderer' have 'nullptr' as their default argument. Using the nullptr - * these functions operate on all base renderer. Giving a specific base renderer will modify the node only for the given renderer. + * these functions operate on the general property list. + * Giving a specific base renderer will modify the property list of the given renderer. */ class MITKQTWIDGETS_EXPORT RenderWindowLayerController { public: RenderWindowLayerController(); /** * @brief Set the data storage on which to work. */ void SetDataStorage(DataStorage::Pointer dataStorage); - /** - * @brief Set the controlled base renderer. - */ - void SetControlledRenderer(RenderWindowLayerUtilities::RendererVector controlledRenderer); // wrapper functions to modify the layer order / visibility of render window data /** - * @brief Set the given node as the base node of the given renderer. - * - * @param dataNode The data node whose layer is to be modified. - * @param renderer Pointer to the renderer instance for which the data node property should be modified. - * If it is a nullptr (default) all controlled renderer will be affected. - */ - void SetBaseDataNode(DataNode* dataNode, const BaseRenderer* renderer = nullptr); - /** * @brief Insert the given data node at the specified layer for the given renderer. * * @param dataNode The data node that should be inserted. * @param layer The layer value for the "layer" property of the data node (insertion level). "layer = RenderWindowLayerUtilities::TOP_LAYER_INDEX" (default) inserts the given data node at the top of the node stack (topmost layer). * @param renderer Pointer to the renderer instance for which the data node should be inserted. * If it is a nullptr (default) all controlled renderer will be affected. * - * @post After a successful call, the "fixedLayer" and "visibility" property will be true and the "layer" property will be set correctly. + * @post After a successful call, the "visibility" property will be true and the "layer" property will be set correctly. */ void InsertLayerNode(DataNode* dataNode, int layer = RenderWindowLayerUtilities::TOP_LAYER_INDEX, const BaseRenderer* renderer = nullptr); /** - * @brief Remove the given data node for the given renderer. - * - * @param dataNode The data node that should be removed. - * @param renderer Pointer to the renderer instance for which the data node should be removed. - * If it is a nullptr (default) all controlled renderer will be affected. - * - * @post After a successful call, the "fixedLayer" and "visibility" property will be false and the "layer" property will be deleted. - */ - void RemoveLayerNode(DataNode* dataNode, const BaseRenderer* renderer = nullptr); - /** * @brief Move the data node to the given layer. This will change only the "layer" property. * * @param dataNode The data node that should be moved. - * @param newLayer + * @param layer The layer to which the data node should be moved. * @param renderer Pointer to the renderer instance for which the data node should be moved. - * If it is a nullptr (default) all controlled renderer will be affected. + * If it is a nullptr (default) all controlled renderer will be affected and the + * common property list will be modified. */ - bool MoveNodeToPosition(DataNode* dataNode, int newLayer, const BaseRenderer* renderer = nullptr); + bool MoveNodeToPosition(DataNode* dataNode, int layer, const BaseRenderer* renderer = nullptr); /** * @brief Set the node in the given renderer as the topmost layer. This will change only the "layer" property. * * @param dataNode The data node that should be moved. * @param renderer Pointer to the renderer instance for which the data node should be moved. * If it is a nullptr (default) all controlled renderer will be affected. */ bool MoveNodeToFront(DataNode* dataNode, const BaseRenderer* renderer = nullptr); /** * @brief Set the node in the given renderer as the lowermost layer. This will change only the "layer" property. * * @param dataNode The data node that should be moved. * @param renderer Pointer to the renderer instance for which the data node should be moved. * If it is a nullptr (default) all controlled renderer will be affected. */ bool MoveNodeToBack(DataNode* dataNode, const BaseRenderer* renderer = nullptr); /** * @brief Move the node in the given renderer one layer down. This will change only the "layer" property. * * @param dataNode The data node that should be moved. * @param renderer Pointer to the renderer instance for which the data node should be moved. * If it is a nullptr (default) all controlled renderer will be affected. */ bool MoveNodeUp(DataNode* dataNode, const BaseRenderer* renderer = nullptr); /** * @brief Move the node in the given renderer one layer up. This will change only the "layer" property. * * @param dataNode The data node that should be moved. * @param renderer Pointer to the renderer instance for which the data node should be moved. * If it is a nullptr (default) all controlled renderer will be affected. */ bool MoveNodeDown(DataNode* dataNode, const BaseRenderer* renderer = nullptr); - /** - * @brief Set the visibility of the given data node for the given renderer. - * - * @param visibility Boolean to set the "visible" property of the given data node. - * @param dataNode The data node that should be moved. - * @param renderer Pointer to the renderer instance for which the data node should be modified. - * If it is a nullptr (default) all controlled renderer will be affected. - * - * @post After a successful call , the "visibility" property will be set to the "visibility" value. - */ - void SetVisibilityOfDataNode(bool visibility, DataNode* dataNode, const BaseRenderer* renderer = nullptr); - /** - * @brief Hide the given data node by setting the "visible" property of the data node for - * all controlled renderer to false. - * Later setting the "visible" property of the data node for a certain renderer will overwrite - * the same property of the common renderer. - * - * @param dataNode The data node that should be hid. - * - * @post After a successful call , the "visibility" property will be set to the false. - */ - void HideDataNodeInAllRenderer(const DataNode* dataNode); - /** - * @brief Reset the given render window: - * If "onlyVisibility = true": set all data nodes for the given render window to invisible, except for the base node. - * If "onlyVisibility = false": remove all data nodes from the render window, except for the base node. - * - * @param onlyVisibility Boolean to define the reset mode. - * @param renderer Pointer to the renderer instance for which the data node should be reset. - * If it is a nullptr (default) all controlled renderer will be affected. - * - * @post After a successful call , the "visibility" property will be set to the "false" value (except for the base node). - * If "onlyVisibility = false": additionally the "fixedLayer" property will be false and the "layer" property will be deleted. - */ - void ResetRenderer(bool onlyVisibility = true, const BaseRenderer* renderer = nullptr); private: - void InsertLayerNodeInternal(DataNode* dataNode, int layer, const BaseRenderer* renderer = nullptr); - DataStorage::Pointer m_DataStorage; - RenderWindowLayerUtilities::RendererVector m_ControlledRenderer; }; } // namespace mitk #endif diff --git a/Modules/QtWidgets/include/mitkRenderWindowLayerUtilities.h b/Modules/QtWidgets/include/mitkRenderWindowLayerUtilities.h index 6c81c3b451..e953e9355c 100644 --- a/Modules/QtWidgets/include/mitkRenderWindowLayerUtilities.h +++ b/Modules/QtWidgets/include/mitkRenderWindowLayerUtilities.h @@ -1,77 +1,67 @@ /*============================================================================ 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 mitkRenderWindowLayerUtilities_h #define mitkRenderWindowLayerUtilities_h // qt widgets module #include "MitkQtWidgetsExports.h" // mitk core #include #include #include #include /** * @brief Render window layer helper functions to retrieve the currently valid layer stack */ namespace mitk { namespace RenderWindowLayerUtilities { typedef std::vector RendererVector; typedef std::map> LayerStack; - /** - * The base data node of a renderer is supposed to be on layer 0 (zero), which should be the lowest layer in a render window. - */ - const int BASE_LAYER_INDEX = 0; /** * The top layer index, denoting that no valid (positive) layer index is given and therefore the index should be resolved into the topmost layer index. */ const int TOP_LAYER_INDEX = -1; /** * @brief Return the stack of layers of the given renderer as std::map, which guarantees ordering of the layers. - * Stacked layers are only included if they have their "fixedLayer" property set to true and their "layer" property set. + * Stacked layers are only included if they have their "layer" property set. * * If "renderer" = nullptr: a layer stack won't be created and an empty "LayerStack" will be returned. - * If "withBaseNode" = true: include the base node in the layer stack, if existing. - * If "withBaseNode" = false: exclude the base node from the layer stack. * * @param dataStorage Pointer to a data storage instance whose data nodes should be checked and possibly be included. * @param renderer Pointer to the renderer instance for which the layer stack should be generated. - * @param withBaseNode Boolean to decide whether the base node should be included in or excluded from the layer stack. - */ - MITKQTWIDGETS_EXPORT LayerStack GetLayerStack(const DataStorage* dataStorage, const BaseRenderer* renderer, bool withBaseNode); - /** - * @brief Helper function to get a node predicate that can be used to filter render window specific data nodes. - * The data nodes must have set a 'fixed layer' property for the given renderer. - * - * @param renderer Pointer to the renderer instance for which the 'fixed layer' should be true. - * @return The node predicate to filter 'fixed layer' data nodes. */ - MITKQTWIDGETS_EXPORT NodePredicateBase::Pointer GetRenderWindowPredicate(const BaseRenderer* renderer); + MITKQTWIDGETS_EXPORT LayerStack GetLayerStack(const DataStorage* dataStorage, const BaseRenderer* renderer); /** * @brief Set renderer-specific properties to mark a data node as 'managed by the specific renderer'. - * In order for a renderer to manage a data node, the 'fixedLayer' property has to be set for the given renderer. - * Additionally, the 'visible' and the 'layer' property are set and allow to individually render a set of nodes - * with a specific renderer. + * In order for a renderer to manage a data node, the 'visible' and the 'layer' property are set and + * allow to individually render a set of nodes with a specific renderer. * The last two mentioned properties are set so that they initially have the same value as the corresponding * global property. */ - MITKQTWIDGETS_EXPORT void SetRenderWindowProperties(mitk::DataNode* dataNode, const BaseRenderer* renderer); + MITKQTWIDGETS_EXPORT void SetRenderWindowProperties(DataNode* dataNode, const BaseRenderer* renderer); + + MITKQTWIDGETS_EXPORT void DeleteRenderWindowProperties(DataNode* dataNode, const BaseRenderer* renderer); + + MITKQTWIDGETS_EXPORT void TransferRenderWindowProperties(DataNode* dataNode, const BaseRenderer* newRenderer, const BaseRenderer* oldRenderer); + + } // namespace RenderWindowLayerUtilities } // namespace mitk #endif diff --git a/Modules/QtWidgets/resource/Qmitk.qrc b/Modules/QtWidgets/resource/Qmitk.qrc index f38e7de208..773e1502be 100644 --- a/Modules/QtWidgets/resource/Qmitk.qrc +++ b/Modules/QtWidgets/resource/Qmitk.qrc @@ -1,29 +1,33 @@ Binaerbilder_48.png Images_48.png PointSet_48.png Segmentation_48.png Surface_48.png mm_pointer.png mm_scroll.png mm_zoom.png mm_contrast.png mm_pan.png LabelSetImage_48.png mwLayout.png mwSynchronized.png mwDesynchronized.png mwMITK.png mwPACS.png star-solid.svg history-solid.svg tree_inspector.svg list-solid.svg favorite_add.svg favorite_remove.svg hourglass-half-solid.svg times.svg reset.svg + lock.svg + unlock.svg + invisible.svg + visible.svg diff --git a/Modules/SegmentationUI/resources/invisible.svg b/Modules/QtWidgets/resource/invisible.svg similarity index 100% rename from Modules/SegmentationUI/resources/invisible.svg rename to Modules/QtWidgets/resource/invisible.svg diff --git a/Modules/SegmentationUI/resources/lock.svg b/Modules/QtWidgets/resource/lock.svg similarity index 100% rename from Modules/SegmentationUI/resources/lock.svg rename to Modules/QtWidgets/resource/lock.svg diff --git a/Modules/SegmentationUI/resources/unlock.svg b/Modules/QtWidgets/resource/unlock.svg similarity index 100% rename from Modules/SegmentationUI/resources/unlock.svg rename to Modules/QtWidgets/resource/unlock.svg diff --git a/Modules/SegmentationUI/resources/visible.svg b/Modules/QtWidgets/resource/visible.svg similarity index 100% rename from Modules/SegmentationUI/resources/visible.svg rename to Modules/QtWidgets/resource/visible.svg diff --git a/Modules/QtWidgets/src/QmitkAbstractNodeSelectionWidget.cpp b/Modules/QtWidgets/src/QmitkAbstractNodeSelectionWidget.cpp index 1b3d6c9e98..acc64021c6 100644 --- a/Modules/QtWidgets/src/QmitkAbstractNodeSelectionWidget.cpp +++ b/Modules/QtWidgets/src/QmitkAbstractNodeSelectionWidget.cpp @@ -1,426 +1,426 @@ /*============================================================================ 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 "QmitkAbstractNodeSelectionWidget.h" #include "QmitkModelViewSelectionConnector.h" QmitkAbstractNodeSelectionWidget::QmitkAbstractNodeSelectionWidget(QWidget* parent) : QWidget(parent) , m_InvalidInfo("Error. Select data.") , m_EmptyInfo("Empty. Make a selection.") , m_PopUpTitel("Select a data node") , m_PopUpHint("") , m_IsOptional(false) , m_SelectOnlyVisibleNodes(true) , m_DataStorageDeletedTag(0) , m_LastEmissionAllowance(true) , m_RecursionGuard(false) { } QmitkAbstractNodeSelectionWidget::~QmitkAbstractNodeSelectionWidget() { auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNotNull()) { // remove Listener for the data storage itself dataStorage->RemoveObserver(m_DataStorageDeletedTag); // remove "add node listener" from data storage dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeAddedToStorage)); // remove "remove node listener" from data storage dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeRemovedFromStorage)); } for (auto& node : m_CurrentInternalSelection) { this->RemoveNodeObserver(node); } } QmitkAbstractNodeSelectionWidget::NodeList QmitkAbstractNodeSelectionWidget::GetSelectedNodes() const { return this->CompileEmitSelection(); } QmitkAbstractNodeSelectionWidget::ConstNodeStdVector QmitkAbstractNodeSelectionWidget::GetSelectedNodesStdVector() const { auto result = this->GetSelectedNodes(); return ConstNodeStdVector(result.begin(), result.end()); } void QmitkAbstractNodeSelectionWidget::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage == dataStorage) { return; } auto oldStorage = m_DataStorage.Lock(); if (oldStorage.IsNotNull()) { // remove Listener for the data storage itself oldStorage->RemoveObserver(m_DataStorageDeletedTag); // remove "add node listener" from old data storage oldStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeAddedToStorage)); // remove "remove node listener" from old data storage oldStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeRemovedFromStorage)); } m_DataStorage = dataStorage; auto newStorage = m_DataStorage.Lock(); if (newStorage.IsNotNull()) { // add Listener for the data storage itself auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkAbstractNodeSelectionWidget::SetDataStorageDeleted); m_DataStorageDeletedTag = newStorage->AddObserver(itk::DeleteEvent(), command); // add "add node listener" for new data storage newStorage->AddNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeAddedToStorage)); // add remove node listener for new data storage newStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeRemovedFromStorage)); } this->OnDataStorageChanged(); this->HandleChangeOfInternalSelection({}); } void QmitkAbstractNodeSelectionWidget::SetNodePredicate(const mitk::NodePredicateBase* nodePredicate) { if (m_NodePredicate != nodePredicate) { m_NodePredicate = nodePredicate; this->OnNodePredicateChanged(); NodeList newInternalNodes; for (auto& node : m_CurrentInternalSelection) { if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node)) { newInternalNodes.append(node); } } if (!m_SelectOnlyVisibleNodes) { for (auto& node : m_CurrentExternalSelection) { if (!newInternalNodes.contains(node) && (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node))) { newInternalNodes.append(node); } } } this->HandleChangeOfInternalSelection(newInternalNodes); } } void QmitkAbstractNodeSelectionWidget::HandleChangeOfInternalSelection(NodeList newInternalSelection) { if (!EqualNodeSelections(m_CurrentInternalSelection, newInternalSelection)) { this->ReviseSelectionChanged(m_CurrentInternalSelection, newInternalSelection); this->SetCurrentInternalSelection(newInternalSelection); this->OnInternalSelectionChanged(); auto newEmission = this->CompileEmitSelection(); this->EmitSelection(newEmission); this->UpdateInfo(); } } void QmitkAbstractNodeSelectionWidget::SetCurrentSelection(NodeList selectedNodes) { if (!m_RecursionGuard) { m_CurrentExternalSelection = selectedNodes; auto dataStorage = m_DataStorage.Lock(); NodeList newInternalSelection; for (const auto &node : qAsConst(selectedNodes)) { if (dataStorage.IsNotNull() && dataStorage->Exists(node) && (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node))) { newInternalSelection.append(node); } } this->HandleChangeOfInternalSelection(newInternalSelection); } } const mitk::NodePredicateBase* QmitkAbstractNodeSelectionWidget::GetNodePredicate() const { return m_NodePredicate; } QString QmitkAbstractNodeSelectionWidget::GetInvalidInfo() const { return m_InvalidInfo; } QString QmitkAbstractNodeSelectionWidget::GetEmptyInfo() const { return m_EmptyInfo; } QString QmitkAbstractNodeSelectionWidget::GetPopUpTitel() const { return m_PopUpTitel; } QString QmitkAbstractNodeSelectionWidget::GetPopUpHint() const { return m_PopUpHint; } bool QmitkAbstractNodeSelectionWidget::GetSelectionIsOptional() const { return m_IsOptional; } bool QmitkAbstractNodeSelectionWidget::GetSelectOnlyVisibleNodes() const { return m_SelectOnlyVisibleNodes; } void QmitkAbstractNodeSelectionWidget::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) { if (m_SelectOnlyVisibleNodes != selectOnlyVisibleNodes) { m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes; auto newEmission = this->CompileEmitSelection(); this->EmitSelection(newEmission); } } void QmitkAbstractNodeSelectionWidget::SetInvalidInfo(QString info) { m_InvalidInfo = info; this->UpdateInfo(); } void QmitkAbstractNodeSelectionWidget::SetEmptyInfo(QString info) { m_EmptyInfo = info; this->UpdateInfo(); } void QmitkAbstractNodeSelectionWidget::SetPopUpTitel(QString info) { m_PopUpTitel = info; } void QmitkAbstractNodeSelectionWidget::SetPopUpHint(QString info) { m_PopUpHint = info; } void QmitkAbstractNodeSelectionWidget::SetSelectionIsOptional(bool isOptional) { m_IsOptional = isOptional; this->UpdateInfo(); } void QmitkAbstractNodeSelectionWidget::SetDataStorageDeleted() { this->OnDataStorageChanged(); this->HandleChangeOfInternalSelection({}); } void QmitkAbstractNodeSelectionWidget::ReviseSelectionChanged(const NodeList& /*oldInternalSelection*/, NodeList& /*newInternalSelection*/) { } bool QmitkAbstractNodeSelectionWidget::AllowEmissionOfSelection(const NodeList& /*emissionCandidates*/) const { return true; } void QmitkAbstractNodeSelectionWidget::EmitSelection(const NodeList& emissionCandidates) { m_LastEmissionAllowance = this->AllowEmissionOfSelection(emissionCandidates); if (m_LastEmissionAllowance && !EqualNodeSelections(m_LastEmission, emissionCandidates)) { m_RecursionGuard = true; emit CurrentSelectionChanged(emissionCandidates); m_RecursionGuard = false; m_LastEmission = emissionCandidates; } } void QmitkAbstractNodeSelectionWidget::SetCurrentInternalSelection(NodeList selectedNodes) { for (auto& node : m_CurrentInternalSelection) { this->RemoveNodeObserver(node); } m_CurrentInternalSelection = selectedNodes; for (auto& node : m_CurrentInternalSelection) { this->AddNodeObserver(node); } } const QmitkAbstractNodeSelectionWidget::NodeList& QmitkAbstractNodeSelectionWidget::GetCurrentInternalSelection() const { return m_CurrentInternalSelection; } const QmitkAbstractNodeSelectionWidget::NodeList& QmitkAbstractNodeSelectionWidget::GetCurrentExternalSelection() const { return m_CurrentExternalSelection; } void QmitkAbstractNodeSelectionWidget::OnNodePredicateChanged() { } void QmitkAbstractNodeSelectionWidget::OnDataStorageChanged() { } void QmitkAbstractNodeSelectionWidget::OnInternalSelectionChanged() { } void QmitkAbstractNodeSelectionWidget::NodeAddedToStorage(const mitk::DataNode* node) { this->OnNodeAddedToStorage(node); } void QmitkAbstractNodeSelectionWidget::OnNodeAddedToStorage(const mitk::DataNode* /*node*/) { } void QmitkAbstractNodeSelectionWidget::NodeRemovedFromStorage(const mitk::DataNode* node) { this->OnNodeRemovedFromStorage(node); this->RemoveNodeFromSelection(node); } void QmitkAbstractNodeSelectionWidget::OnNodeRemovedFromStorage(const mitk::DataNode* /*node*/) { } +void QmitkAbstractNodeSelectionWidget::OnNodeModified(const itk::Object* caller, const itk::EventObject& event) +{ + if (itk::ModifiedEvent().CheckEvent(&event)) + { + auto node = dynamic_cast(caller); + + if (node) + { + if (m_NodePredicate.IsNotNull() && !m_NodePredicate->CheckNode(node)) + { + this->RemoveNodeFromSelection(node); + } + else + { + auto oldAllowance = m_LastEmissionAllowance; + auto newEmission = this->CompileEmitSelection(); + auto nonConstNode = const_cast(node); + if (newEmission.contains(nonConstNode) && (oldAllowance != this->AllowEmissionOfSelection(newEmission))) + { + this->EmitSelection(newEmission); + this->UpdateInfo(); + } + } + } + } +} + QmitkAbstractNodeSelectionWidget::NodeList QmitkAbstractNodeSelectionWidget::CompileEmitSelection() const { NodeList result = m_CurrentInternalSelection; if (!m_SelectOnlyVisibleNodes) { for (const auto &node : m_CurrentExternalSelection) { if (!result.contains(node) && m_NodePredicate.IsNotNull() && !m_NodePredicate->CheckNode(node)) { result.append(node); } } } return result; } void QmitkAbstractNodeSelectionWidget::RemoveNodeFromSelection(const mitk::DataNode* node) { auto newSelection = m_CurrentInternalSelection; auto finding = std::find(std::begin(newSelection), std::end(newSelection), node); if (finding != std::end(newSelection)) { newSelection.erase(finding); this->HandleChangeOfInternalSelection(newSelection); } } -void QmitkAbstractNodeSelectionWidget::OnNodeModified(const itk::Object * caller, const itk::EventObject & event) -{ - if (itk::ModifiedEvent().CheckEvent(&event)) - { - auto node = dynamic_cast(caller); - - if (node) - { - if (m_NodePredicate.IsNotNull() && !m_NodePredicate->CheckNode(node)) - { - this->RemoveNodeFromSelection(node); - } - else - { - auto oldAllowance = m_LastEmissionAllowance; - auto newEmission = this->CompileEmitSelection(); - auto nonConstNode = const_cast(node); - if (newEmission.contains(nonConstNode) && (oldAllowance != this->AllowEmissionOfSelection(newEmission))) - { - this->EmitSelection(newEmission); - this->UpdateInfo(); - } - } - } - } -} - void QmitkAbstractNodeSelectionWidget::AddNodeObserver(mitk::DataNode* node) { if (node) { auto modifiedCommand = itk::MemberCommand::New(); modifiedCommand->SetCallbackFunction(this, &QmitkAbstractNodeSelectionWidget::OnNodeModified); auto nodeModifiedObserverTag = node->AddObserver(itk::ModifiedEvent(), modifiedCommand); m_NodeObserverTags.insert(std::make_pair(node, nodeModifiedObserverTag)); } } void QmitkAbstractNodeSelectionWidget::RemoveNodeObserver(mitk::DataNode* node) { if (node) { auto finding = m_NodeObserverTags.find(node); if (finding != std::end(m_NodeObserverTags)) { node->RemoveObserver(finding->second); } else { MITK_ERROR << "Selection widget is in a wrong state. A node should be removed from the internal selection but seems to have no observer. Node:" << node; } m_NodeObserverTags.erase(node); } } diff --git a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp index d3efda2364..a4f43d3581 100644 --- a/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkMxNMultiWidget.cpp @@ -1,392 +1,448 @@ /*============================================================================ 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 "QmitkMxNMultiWidget.h" -#include "QmitkRenderWindowWidget.h" // mitk core #include #include #include +#include +#include +#include + +// mitk qt widget +#include +#include // qt #include #include QmitkMxNMultiWidget::QmitkMxNMultiWidget(QWidget* parent, Qt::WindowFlags f/* = 0*/, const QString& multiWidgetName/* = "mxnmulti"*/) : QmitkAbstractMultiWidget(parent, f, multiWidgetName) , m_TimeNavigationController(nullptr) + , m_SynchronizedWidgetConnector(std::make_unique()) , m_CrosshairVisibility(false) { m_TimeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); } QmitkMxNMultiWidget::~QmitkMxNMultiWidget() { auto allRenderWindows = this->GetRenderWindows(); for (auto& renderWindow : allRenderWindows) { m_TimeNavigationController->Disconnect(renderWindow->GetSliceNavigationController()); } } void QmitkMxNMultiWidget::InitializeMultiWidget() { SetLayout(1, 1); SetDisplayActionEventHandler(std::make_unique()); auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } + + this->SetInitialSelection(); } void QmitkMxNMultiWidget::Synchronize(bool synchronized) { if (synchronized) { SetDisplayActionEventHandler(std::make_unique()); } else { SetDisplayActionEventHandler(std::make_unique()); } auto displayActionEventHandler = GetDisplayActionEventHandler(); if (nullptr != displayActionEventHandler) { displayActionEventHandler->InitActions(); } } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const QString& widgetName) const { if ("axial" == widgetName || "sagittal" == widgetName || "coronal" == widgetName || "3d" == widgetName) { return GetActiveRenderWindowWidget()->GetRenderWindow(); } return QmitkAbstractMultiWidget::GetRenderWindow(widgetName); } QmitkRenderWindow* QmitkMxNMultiWidget::GetRenderWindow(const mitk::AnatomicalPlane& /*orientation*/) const { // currently no mapping between plane orientation and render windows // simply return the currently active render window return GetActiveRenderWindowWidget()->GetRenderWindow(); } void QmitkMxNMultiWidget::SetActiveRenderWindowWidget(RenderWindowWidgetPointer activeRenderWindowWidget) { auto currentActiveRenderWindowWidget = GetActiveRenderWindowWidget(); if (currentActiveRenderWindowWidget == activeRenderWindowWidget) { return; } // reset the decoration color of the previously active render window widget if (nullptr != currentActiveRenderWindowWidget) { auto decorationColor = currentActiveRenderWindowWidget->GetDecorationColor(); QColor hexColor(decorationColor[0] * 255, decorationColor[1] * 255, decorationColor[2] * 255); currentActiveRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid " + hexColor.name(QColor::HexRgb) + "; }"); } // set the new decoration color of the currently active render window widget if (nullptr != activeRenderWindowWidget) { activeRenderWindowWidget->setStyleSheet("QmitkRenderWindowWidget { border: 2px solid #FF6464; }"); } QmitkAbstractMultiWidget::SetActiveRenderWindowWidget(activeRenderWindowWidget); } void QmitkMxNMultiWidget::InitializeViews(const mitk::TimeGeometry* geometry, bool resetCamera) { auto* renderingManager = mitk::RenderingManager::GetInstance(); mitk::Point3D currentPosition = mitk::Point3D(); unsigned int imageTimeStep = 0; if (!resetCamera) { // store the current position to set it again later, if the camera should not be reset currentPosition = this->GetSelectedPosition(""); // store the current time step to set it again later, if the camera should not be reset const auto currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); if (geometry->IsValidTimePoint(currentTimePoint)) { imageTimeStep = geometry->TimePointToTimeStep(currentTimePoint); } } // initialize active render window renderingManager->InitializeView( this->GetActiveRenderWindowWidget()->GetRenderWindow()->GetVtkRenderWindow(), geometry, resetCamera); if (!resetCamera) { this->SetSelectedPosition(currentPosition, ""); renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); } } void QmitkMxNMultiWidget::SetInteractionReferenceGeometry(const mitk::TimeGeometry* referenceGeometry) { // Set the interaction reference referenceGeometry for all render windows. auto allRenderWindows = this->GetRenderWindows(); for (auto& renderWindow : allRenderWindows) { auto* baseRenderer = mitk::BaseRenderer::GetInstance(renderWindow->GetRenderWindow()); baseRenderer->SetInteractionReferenceGeometry(referenceGeometry); } } bool QmitkMxNMultiWidget::HasCoupledRenderWindows() const { return false; } void QmitkMxNMultiWidget::SetSelectedPosition(const mitk::Point3D& newPosition, const QString& widgetName) { RenderWindowWidgetPointer renderWindowWidget; if (widgetName.isNull() || widgetName.isEmpty()) { renderWindowWidget = GetActiveRenderWindowWidget(); } else { renderWindowWidget = GetRenderWindowWidget(widgetName); } if (nullptr != renderWindowWidget) { renderWindowWidget->GetSliceNavigationController()->SelectSliceByPoint(newPosition); return; } MITK_ERROR << "Position can not be set for an unknown render window widget."; } const mitk::Point3D QmitkMxNMultiWidget::GetSelectedPosition(const QString& widgetName) const { RenderWindowWidgetPointer renderWindowWidget; if (widgetName.isNull() || widgetName.isEmpty()) { renderWindowWidget = GetActiveRenderWindowWidget(); } else { renderWindowWidget = GetRenderWindowWidget(widgetName); } if (nullptr != renderWindowWidget) { return renderWindowWidget->GetCrosshairPosition(); } MITK_ERROR << "Crosshair position can not be retrieved."; return mitk::Point3D(0.0); } void QmitkMxNMultiWidget::SetCrosshairVisibility(bool visible) { // get the specific render window that sent the signal QmitkRenderWindow* renderWindow = qobject_cast(sender()); if (nullptr == renderWindow) { return; } auto renderWindowWidget = this->GetRenderWindowWidget(renderWindow); renderWindowWidget->SetCrosshairVisibility(visible); } bool QmitkMxNMultiWidget::GetCrosshairVisibility() const { // get the specific render window that sent the signal QmitkRenderWindow* renderWindow = qobject_cast(sender()); if (nullptr == renderWindow) { return false; } auto renderWindowWidget = this->GetRenderWindowWidget(renderWindow); return renderWindowWidget->GetCrosshairVisibility(); } void QmitkMxNMultiWidget::SetCrosshairGap(unsigned int gapSize) { auto renderWindowWidgets = this->GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { renderWindowWidget.second->SetCrosshairGap(gapSize); } } void QmitkMxNMultiWidget::ResetCrosshair() { auto dataStorage = GetDataStorage(); if (nullptr == dataStorage) { return; } // get the specific render window that sent the signal QmitkRenderWindow* renderWindow = qobject_cast(sender()); if (nullptr == renderWindow) { return; } mitk::RenderingManager::GetInstance()->InitializeViewByBoundingObjects(renderWindow->GetRenderWindow(), dataStorage); SetWidgetPlaneMode(mitk::InteractionSchemeSwitcher::MITKStandard); } void QmitkMxNMultiWidget::SetWidgetPlaneMode(int userMode) { MITK_DEBUG << "Changing crosshair mode to " << userMode; switch (userMode) { case 0: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKStandard); break; case 1: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationUncoupled); break; case 2: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKRotationCoupled); break; case 3: SetInteractionScheme(mitk::InteractionSchemeSwitcher::MITKSwivel); break; } } mitk::SliceNavigationController* QmitkMxNMultiWidget::GetTimeNavigationController() { return m_TimeNavigationController; } void QmitkMxNMultiWidget::AddPlanesToDataStorage() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { renderWindowWidget.second->AddPlanesToDataStorage(); } } void QmitkMxNMultiWidget::RemovePlanesFromDataStorage() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); for (const auto& renderWindowWidget : renderWindowWidgets) { renderWindowWidget.second->RemovePlanesFromDataStorage(); } } ////////////////////////////////////////////////////////////////////////// // PUBLIC SLOTS // MOUSE EVENTS ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::wheelEvent(QWheelEvent* e) { emit WheelMoved(e); } void QmitkMxNMultiWidget::mousePressEvent(QMouseEvent*) { // nothing here, but necessary for mouse interactions (.xml-configuration files) } void QmitkMxNMultiWidget::moveEvent(QMoveEvent* e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the overlays as the MultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } void QmitkMxNMultiWidget::RemoveRenderWindowWidget() { auto renderWindowWidgets = this->GetRenderWindowWidgets(); auto iterator = renderWindowWidgets.find(this->GetNameFromIndex(this->GetNumberOfRenderWindowWidgets() - 1)); if (iterator == renderWindowWidgets.end()) { return; } // disconnect each signal of this render window widget RenderWindowWidgetPointer renderWindowWidgetToRemove = iterator->second; m_TimeNavigationController->Disconnect(renderWindowWidgetToRemove->GetSliceNavigationController()); QmitkAbstractMultiWidget::RemoveRenderWindowWidget(); } ////////////////////////////////////////////////////////////////////////// // PRIVATE ////////////////////////////////////////////////////////////////////////// void QmitkMxNMultiWidget::SetLayoutImpl() { int requiredRenderWindowWidgets = GetRowCount() * GetColumnCount(); int existingRenderWindowWidgets = GetRenderWindowWidgets().size(); int difference = requiredRenderWindowWidgets - existingRenderWindowWidgets; while (0 < difference) { // more render window widgets needed CreateRenderWindowWidget(); --difference; } while (0 > difference) { // less render window widgets needed RemoveRenderWindowWidget(); ++difference; } auto firstRenderWindowWidget = GetFirstRenderWindowWidget(); if (nullptr != firstRenderWindowWidget) { SetActiveRenderWindowWidget(firstRenderWindowWidget); } GetMultiWidgetLayoutManager()->SetLayoutDesign(QmitkMultiWidgetLayoutManager::LayoutDesign::DEFAULT); } void QmitkMxNMultiWidget::CreateRenderWindowWidget() { // create the render window widget and connect signal / slot QString renderWindowWidgetName = GetNameFromIndex(GetNumberOfRenderWindowWidgets()); - RenderWindowWidgetPointer renderWindowWidget = std::make_shared(this, renderWindowWidgetName, GetDataStorage(), true); + RenderWindowWidgetPointer renderWindowWidget = std::make_shared(this, renderWindowWidgetName, GetDataStorage()); renderWindowWidget->SetCornerAnnotationText(renderWindowWidgetName.toStdString()); AddRenderWindowWidget(renderWindowWidgetName, renderWindowWidget); auto renderWindow = renderWindowWidget->GetRenderWindow(); + + QmitkRenderWindowUtilityWidget* utilityWidget = new QmitkRenderWindowUtilityWidget(this, renderWindow, GetDataStorage()); + renderWindowWidget->AddUtilityWidget(utilityWidget); + + connect(utilityWidget, &QmitkRenderWindowUtilityWidget::SynchronizationToggled, + this, &QmitkMxNMultiWidget::ToggleSynchronization); + + // needs to be done after 'QmitkRenderWindowUtilityWidget::ToggleSynchronization' has been connected + // initially synchronize the node selection widget + utilityWidget->ToggleSynchronization(true); + auto layoutManager = GetMultiWidgetLayoutManager(); connect(renderWindow, &QmitkRenderWindow::LayoutDesignChanged, layoutManager, &QmitkMultiWidgetLayoutManager::SetLayoutDesign); connect(renderWindow, &QmitkRenderWindow::ResetView, this, &QmitkMxNMultiWidget::ResetCrosshair); connect(renderWindow, &QmitkRenderWindow::CrosshairVisibilityChanged, this, &QmitkMxNMultiWidget::SetCrosshairVisibility); connect(renderWindow, &QmitkRenderWindow::CrosshairRotationModeChanged, this, &QmitkMxNMultiWidget::SetWidgetPlaneMode); // connect time navigation controller to react on referenceGeometry time events with the render window's slice naviation controller m_TimeNavigationController->ConnectGeometryTimeEvent(renderWindow->GetSliceNavigationController()); // reverse connection between the render window's slice navigation controller and the time navigation controller renderWindow->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController); } + +void QmitkMxNMultiWidget::SetInitialSelection() +{ + auto dataStorage = this->GetDataStorage(); + if (nullptr == dataStorage) + { + return; + } + + mitk::NodePredicateAnd::Pointer noHelperObjects = mitk::NodePredicateAnd::New(); + noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); + noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object"))); + auto allNodes = dataStorage->GetSubset(noHelperObjects); + QmitkSynchronizedNodeSelectionWidget::NodeList currentSelection; + for (auto& node : *allNodes) + { + currentSelection.append(node); + } + + m_SynchronizedWidgetConnector->ChangeSelection(currentSelection); +} + +void QmitkMxNMultiWidget::ToggleSynchronization(QmitkSynchronizedNodeSelectionWidget* synchronizedWidget) +{ + bool synchronized = synchronizedWidget->IsSynchronized(); + + if (synchronized) + { + m_SynchronizedWidgetConnector->ConnectWidget(synchronizedWidget); + m_SynchronizedWidgetConnector->SynchronizeWidget(synchronizedWidget); + } + else + { + m_SynchronizedWidgetConnector->DisconnectWidget(synchronizedWidget); + } +} diff --git a/Modules/QtWidgets/src/QmitkRenderWindowContextDataStorageInspector.cpp b/Modules/QtWidgets/src/QmitkRenderWindowContextDataStorageInspector.cpp index 664f99a2f6..cff14a7f17 100644 --- a/Modules/QtWidgets/src/QmitkRenderWindowContextDataStorageInspector.cpp +++ b/Modules/QtWidgets/src/QmitkRenderWindowContextDataStorageInspector.cpp @@ -1,144 +1,137 @@ /*============================================================================ 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. ============================================================================*/ // render window manager UI module #include "QmitkRenderWindowContextDataStorageInspector.h" #include #include #include #include #include // qt #include #include QmitkRenderWindowContextDataStorageInspector::QmitkRenderWindowContextDataStorageInspector( QWidget* parent /* =nullptr */, mitk::BaseRenderer* renderer /* = nullptr */) : QmitkAbstractDataStorageInspector(parent) { m_Controls.setupUi(this); mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer{ renderer }; m_RenderWindowLayerController = std::make_unique(); - m_RenderWindowLayerController->SetControlledRenderer(controlledRenderer); m_StorageModel = std::make_unique(this); m_StorageModel->SetControlledRenderer(controlledRenderer); m_Controls.renderWindowTreeView->setModel(m_StorageModel.get()); m_Controls.renderWindowTreeView->setHeaderHidden(true); m_Controls.renderWindowTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers); m_Controls.renderWindowTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_Controls.renderWindowTreeView->setSelectionMode(QAbstractItemView::ExtendedSelection); m_Controls.renderWindowTreeView->setAlternatingRowColors(true); m_Controls.renderWindowTreeView->setDragEnabled(true); m_Controls.renderWindowTreeView->setDropIndicatorShown(true); m_Controls.renderWindowTreeView->setAcceptDrops(true); m_Controls.renderWindowTreeView->setContextMenuPolicy(Qt::CustomContextMenu); connect(m_Controls.renderWindowTreeView, &QTreeView::customContextMenuRequested, this, &QmitkRenderWindowContextDataStorageInspector::OnContextMenuRequested); SetUpConnections(); if (nullptr == renderer) { return; } m_StorageModel->SetCurrentRenderer(renderer); } QAbstractItemView* QmitkRenderWindowContextDataStorageInspector::GetView() { return m_Controls.renderWindowTreeView; } const QAbstractItemView* QmitkRenderWindowContextDataStorageInspector::GetView() const { return m_Controls.renderWindowTreeView; } void QmitkRenderWindowContextDataStorageInspector::SetSelectionMode(SelectionMode mode) { m_Controls.renderWindowTreeView->setSelectionMode(mode); } QmitkRenderWindowContextDataStorageInspector::SelectionMode QmitkRenderWindowContextDataStorageInspector::GetSelectionMode() const { return m_Controls.renderWindowTreeView->selectionMode(); } QItemSelectionModel* QmitkRenderWindowContextDataStorageInspector::GetDataNodeSelectionModel() const { return m_Controls.renderWindowTreeView->selectionModel(); } void QmitkRenderWindowContextDataStorageInspector::Initialize() { auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNull()) return; m_StorageModel->SetDataStorage(dataStorage); mitk::NodePredicateAnd::Pointer noHelperObjects = mitk::NodePredicateAnd::New(); noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object"))); m_StorageModel->SetNodePredicate(noHelperObjects); m_RenderWindowLayerController->SetDataStorage(dataStorage); m_Connector->SetView(m_Controls.renderWindowTreeView); } void QmitkRenderWindowContextDataStorageInspector::SetUpConnections() { connect(m_StorageModel.get(), &QAbstractItemModel::rowsInserted, this, &QmitkRenderWindowContextDataStorageInspector::ModelRowsInserted); } void QmitkRenderWindowContextDataStorageInspector::ModelRowsInserted(const QModelIndex& parent, int /*start*/, int /*end*/) { m_Controls.renderWindowTreeView->setExpanded(parent, true); } -void QmitkRenderWindowContextDataStorageInspector::ResetRenderer() -{ - m_RenderWindowLayerController->ResetRenderer(true, m_StorageModel->GetCurrentRenderer()); - m_Controls.renderWindowTreeView->clearSelection(); -} - void QmitkRenderWindowContextDataStorageInspector::OnContextMenuRequested(const QPoint& pos) { QMenu contextMenu; contextMenu.addAction(tr("Reinit with node"), this, &QmitkRenderWindowContextDataStorageInspector::OnReinit); contextMenu.addAction(tr("Reset to node geometry"), this, &QmitkRenderWindowContextDataStorageInspector::OnReset); contextMenu.exec(this->mapToGlobal(pos)); } void QmitkRenderWindowContextDataStorageInspector::OnReinit() { auto nodes = this->GetSelectedNodes(); emit ReinitAction(nodes); } void QmitkRenderWindowContextDataStorageInspector::OnReset() { auto nodes = this->GetSelectedNodes(); emit ResetAction(nodes); } diff --git a/Modules/QtWidgets/src/QmitkRenderWindowDataNodeTableModel.cpp b/Modules/QtWidgets/src/QmitkRenderWindowDataNodeTableModel.cpp new file mode 100644 index 0000000000..a3c9062704 --- /dev/null +++ b/Modules/QtWidgets/src/QmitkRenderWindowDataNodeTableModel.cpp @@ -0,0 +1,376 @@ +/*============================================================================ + +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 "QmitkRenderWindowDataNodeTableModel.h" + +// mitk core +#include + +// qt widgets module +#include +#include +#include +#include +#include + +#include +#include + +QmitkRenderWindowDataNodeTableModel::QmitkRenderWindowDataNodeTableModel(QObject* parent /*= nullptr*/) + : QAbstractItemModel(parent) +{ + m_RenderWindowLayerController = std::make_unique(); + + m_VisibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg")); + m_InvisibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg")); + m_ArrowIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/reset.svg")); + m_TimesIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/times.svg")); +} + +void QmitkRenderWindowDataNodeTableModel::UpdateModelData() +{ + auto baseRenderer = m_BaseRenderer.Lock(); + + auto greaterThan = [&baseRenderer](const mitk::DataNode* dataNodeLeft, const mitk::DataNode* dataNodeRight) + { + int layerLeft = -1; + int layerRight = -1; + + bool layerLeftFound = dataNodeLeft->GetIntProperty("layer", layerLeft, baseRenderer); + bool layerRightFound = dataNodeRight->GetIntProperty("layer", layerRight, baseRenderer); + + if (layerLeftFound && layerRightFound) + { + return layerLeft > layerRight; + } + + return true; + }; + + // sort node selection + beginResetModel(); + std::sort(m_CurrentSelection.begin(), m_CurrentSelection.end(), greaterThan); + endResetModel(); + + emit ModelUpdated(); +} + +void QmitkRenderWindowDataNodeTableModel::SetDataStorage(mitk::DataStorage* dataStorage) +{ + m_RenderWindowLayerController->SetDataStorage(dataStorage); +} + +void QmitkRenderWindowDataNodeTableModel::SetCurrentRenderer(mitk::BaseRenderer* baseRenderer) +{ + if (m_BaseRenderer == baseRenderer) + { + // resetting the same base renderer does nothing + return; + } + + m_BaseRenderer = baseRenderer; + + // update the model, since a new base renderer could have set the relevant node-properties differently + this->UpdateModelData(); +} + +mitk::BaseRenderer::Pointer QmitkRenderWindowDataNodeTableModel::GetCurrentRenderer() const +{ + return m_BaseRenderer.Lock(); +} + +void QmitkRenderWindowDataNodeTableModel::SetCurrentSelection(NodeList selectedNodes) +{ + m_CurrentSelection = selectedNodes; + + // update the model: sort the current internal selection + this->UpdateModelData(); +} + +QmitkRenderWindowDataNodeTableModel::NodeList QmitkRenderWindowDataNodeTableModel::GetCurrentSelection() const +{ + return m_CurrentSelection; +} + +QModelIndex QmitkRenderWindowDataNodeTableModel::index(int row, int column, const QModelIndex& parent /*= QModelIndex()*/) const +{ + bool hasIndex = this->hasIndex(row, column, parent); + if (hasIndex) + { + return this->createIndex(row, column); + } + + return QModelIndex(); +} + +QModelIndex QmitkRenderWindowDataNodeTableModel::parent(const QModelIndex& /*child*/) const +{ + return QModelIndex(); +} + +int QmitkRenderWindowDataNodeTableModel::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const +{ + if (parent.isValid()) + { + return 0; + } + + return static_cast(m_CurrentSelection.size()); +} + +int QmitkRenderWindowDataNodeTableModel::columnCount(const QModelIndex& parent /*= QModelIndex()*/) const +{ + if (parent.isValid()) + { + return 0; + } + + return 4; +} + +Qt::ItemFlags QmitkRenderWindowDataNodeTableModel::flags(const QModelIndex &index) const +{ + if (this != index.model()) + { + return Qt::NoItemFlags; + } + + if (index.isValid()) + { + if (index.column() == 0) + { + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable | + Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + } + + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; + } + else + { + return Qt::ItemIsDropEnabled; + } +} + +QVariant QmitkRenderWindowDataNodeTableModel::data(const QModelIndex& index, int role) const +{ + if (!index.isValid() || this != index.model()) + { + return QVariant(); + } + + if (index.row() < 0 || index.row() >= static_cast(m_CurrentSelection.size())) + { + return QVariant(); + } + + mitk::DataNode* dataNode = m_CurrentSelection.at(index.row()); + + if (role == QmitkDataNodeRole) + { + return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); + } + + if (role == QmitkDataNodeRawPointerRole) + { + return QVariant::fromValue(dataNode); + } + + if (index.column() == 0) // data node information column + { + QString nodeName = QString::fromStdString(dataNode->GetName()); + if (nodeName.isEmpty()) + { + nodeName = "unnamed"; + } + + if (role == Qt::DisplayRole || role == Qt::EditRole) + { + return nodeName; + } + + if (role == Qt::ToolTipRole) + { + return QVariant("Name of the data node."); + } + + if (role == Qt::DecorationRole) + { + QmitkNodeDescriptor* nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode); + return QVariant(nodeDescriptor->GetIcon(dataNode)); + } + } + + if (index.column() == 1) // node visibility column + { + if (role == Qt::DecorationRole) + { + auto baseRenderer = m_BaseRenderer.Lock(); + bool visibility = false; + dataNode->GetVisibility(visibility, baseRenderer); + + return visibility ? QVariant(m_VisibleIcon) : QVariant(m_InvisibleIcon); + } + + if (role == Qt::EditRole) + { + auto baseRenderer = m_BaseRenderer.Lock(); + bool visibility = false; + dataNode->GetVisibility(visibility, baseRenderer); + + return QVariant(visibility); + } + } + + if (index.column() == 2) // reset geometry column + { + if (role == Qt::DecorationRole) + { + return QVariant(m_ArrowIcon); + } + } + + if (index.column() == 3) // remove node column + { + if (role == Qt::DecorationRole) + { + return QVariant(m_TimesIcon); + } + } + + return QVariant(); +} + +bool QmitkRenderWindowDataNodeTableModel::setData(const QModelIndex& index, const QVariant& value, int role) +{ + if (!index.isValid() || this != index.model()) + { + return false; + } + + if (index.row() < 0 || index.row() >= static_cast(m_CurrentSelection.size())) + { + return false; + } + + mitk::DataNode* dataNode = m_CurrentSelection.at(index.row()); + + if (index.column() == 0) // data node information column + { + if (role == Qt::EditRole && !value.toString().isEmpty()) + { + dataNode->SetName(value.toString().toStdString()); + emit dataChanged(index, index); + return true; + } + } + + if (index.column() == 1) // data node visibility column + { + if (role == Qt::EditRole) + { + auto baseRenderer = m_BaseRenderer.Lock(); + bool visibility = value.toBool(); + dataNode->SetVisibility(visibility, baseRenderer); + + if (baseRenderer.IsNotNull()) + { + // Explicitly request an update since a renderer-specific property change does not mark the node as modified. + // see https://phabricator.mitk.org/T22322 + mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); + } + + emit dataChanged(index, index); + return true; + } + } + + return false; +} + +Qt::DropActions QmitkRenderWindowDataNodeTableModel::supportedDropActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +Qt::DropActions QmitkRenderWindowDataNodeTableModel::supportedDragActions() const +{ + return Qt::CopyAction | Qt::MoveAction; +} + +QStringList QmitkRenderWindowDataNodeTableModel::mimeTypes() const +{ + QStringList types = QAbstractItemModel::mimeTypes(); + types << QmitkMimeTypes::DataNodePtrs; + return types; +} + +QMimeData* QmitkRenderWindowDataNodeTableModel::mimeData(const QModelIndexList& indexes) const +{ + QMimeData* mimeData = new QMimeData(); + QByteArray encodedData; + + QDataStream stream(&encodedData, QIODevice::WriteOnly); + + for (const auto& index : indexes) + { + if (index.isValid()) + { + auto dataNode = data(index, QmitkDataNodeRawPointerRole).value(); + stream << reinterpret_cast(dataNode); + } + } + + mimeData->setData(QmitkMimeTypes::DataNodePtrs, encodedData); + return mimeData; +} + +bool QmitkRenderWindowDataNodeTableModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex& parent) +{ + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNull()) + { + return false; + } + + if (action == Qt::IgnoreAction) + { + return true; + } + + if (!data->hasFormat(QmitkMimeTypes::DataNodePtrs)) + { + return false; + } + + if (parent.isValid()) + { + int layer = -1; + auto dataNode = this->data(parent, QmitkDataNodeRawPointerRole).value(); + + if (nullptr != dataNode) + { + dataNode->GetIntProperty("layer", layer, baseRenderer); + } + + auto dataNodeList = QmitkMimeTypes::ToDataNodePtrList(data); + for (const auto& dataNode : qAsConst(dataNodeList)) + { + m_RenderWindowLayerController->MoveNodeToPosition(dataNode, layer, baseRenderer); + } + + this->UpdateModelData(); + + return true; + } + + return false; +} diff --git a/Modules/QtWidgets/src/QmitkRenderWindowDataStorageTreeModel.cpp b/Modules/QtWidgets/src/QmitkRenderWindowDataStorageTreeModel.cpp index 3d761af13c..cebc96e009 100644 --- a/Modules/QtWidgets/src/QmitkRenderWindowDataStorageTreeModel.cpp +++ b/Modules/QtWidgets/src/QmitkRenderWindowDataStorageTreeModel.cpp @@ -1,628 +1,606 @@ /*============================================================================ 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. ============================================================================*/ // render window manager UI module #include "QmitkRenderWindowDataStorageTreeModel.h" #include - -// mitk core -#include - // qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" #include "QmitkMimeTypes.h" #include "QmitkNodeDescriptorManager.h" QmitkRenderWindowDataStorageTreeModel::QmitkRenderWindowDataStorageTreeModel(QObject* parent /*= nullptr*/) : QmitkAbstractDataStorageModel(parent) , m_Root(nullptr) { m_RenderWindowLayerController = std::make_unique(); ResetTree(); } void QmitkRenderWindowDataStorageTreeModel::DataStorageChanged() { m_RenderWindowLayerController->SetDataStorage(m_DataStorage.Lock()); ResetTree(); UpdateModelData(); } void QmitkRenderWindowDataStorageTreeModel::NodePredicateChanged() { ResetTree(); UpdateModelData(); } void QmitkRenderWindowDataStorageTreeModel::NodeAdded(const mitk::DataNode* node) { - for (const auto renderer : m_ControlledRenderer) - { - // add the node to each render window - mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(const_cast(node), renderer); - } - auto baseRenderer = m_BaseRenderer.Lock(); if (baseRenderer.IsNull()) { return; } - mitk::NodePredicateBase::Pointer renderWindowPredicate = mitk::RenderWindowLayerUtilities::GetRenderWindowPredicate(baseRenderer); - mitk::NodePredicateAnd::Pointer combinedNodePredicate = mitk::NodePredicateAnd::New(); - combinedNodePredicate->AddPredicate(renderWindowPredicate); if (m_NodePredicate.IsNotNull()) { - combinedNodePredicate->AddPredicate(m_NodePredicate); - } - if (combinedNodePredicate->CheckNode(node)) - { - AddNodeInternal(node, baseRenderer); + if (m_NodePredicate->CheckNode(node)) + { + AddNodeInternal(node, baseRenderer); + } } } void QmitkRenderWindowDataStorageTreeModel::NodeChanged(const mitk::DataNode* node) { auto item = m_Root->Find(node); if (nullptr != item) { auto parentItem = item->GetParent(); // as the root node should not be removed one should always have a parent item if (nullptr == parentItem) { return; } auto index = createIndex(item->GetIndex(), 0, item); emit dataChanged(index, index); } } void QmitkRenderWindowDataStorageTreeModel::NodeRemoved(const mitk::DataNode* node) { RemoveNodeInternal(node); } QModelIndex QmitkRenderWindowDataStorageTreeModel::index(int row, int column, const QModelIndex& parent) const { auto item = GetItemByIndex(parent); if (nullptr != item) { item = item->GetChild(row); } if (nullptr == item) { return QModelIndex(); } return createIndex(row, column, item); } QModelIndex QmitkRenderWindowDataStorageTreeModel::parent(const QModelIndex& parent) const { auto item = GetItemByIndex(parent); if (nullptr != item) { item = item->GetParent(); } if(nullptr == item) { return QModelIndex(); } if (item == m_Root) { return QModelIndex(); } return createIndex(item->GetIndex(), 0, item); } int QmitkRenderWindowDataStorageTreeModel::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const { auto item = GetItemByIndex(parent); if (nullptr == item) { return 0; } return item->GetChildCount(); } int QmitkRenderWindowDataStorageTreeModel::columnCount(const QModelIndex&/* parent = QModelIndex()*/) const { if (0 == m_Root->GetChildCount()) { // no items stored, no need to display columns return 0; } return 1; } QVariant QmitkRenderWindowDataStorageTreeModel::data(const QModelIndex& index, int role) const { auto baseRenderer = m_BaseRenderer.Lock(); if (baseRenderer.IsNull()) { return QVariant(); } if (!index.isValid() || this != index.model()) { return QVariant(); } auto item = GetItemByIndex(index); if (nullptr == item) { return QVariant(); } auto dataNode = item->GetDataNode(); if (nullptr == dataNode) { return QVariant(); } if (Qt::CheckStateRole == role) { bool visibility = false; dataNode->GetVisibility(visibility, baseRenderer); if (visibility) { return Qt::Checked; } else { return Qt::Unchecked; } } else if (Qt::DisplayRole == role) { return QVariant(QString::fromStdString(dataNode->GetName())); } else if (Qt::ToolTipRole == role) { return QVariant("Name of the data node."); } else if (Qt::DecorationRole == role) { QmitkNodeDescriptor* nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode); return nodeDescriptor->GetIcon(dataNode); } else if (Qt::UserRole == role || QmitkDataNodeRawPointerRole == role) { // user role always returns a reference to the data node, // which can be used to modify the data node in the data storage return QVariant::fromValue(dataNode); } else if (QmitkDataNodeRole == role) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } return QVariant(); } bool QmitkRenderWindowDataStorageTreeModel::setData(const QModelIndex& index, const QVariant& value, int role /*= Qt::EditRole*/) { auto baseRenderer = m_BaseRenderer.Lock(); if (baseRenderer.IsNull()) { return false; } if (!index.isValid() || this != index.model()) { return false; } auto item = GetItemByIndex(index); if (nullptr == item) { return false; } auto dataNode = item->GetDataNode(); if (nullptr == dataNode) { return false; } if (Qt::EditRole == role && !value.toString().isEmpty()) { dataNode->SetName(value.toString().toStdString().c_str()); emit dataChanged(index, index); return true; } if (Qt::CheckStateRole == role) { Qt::CheckState newCheckState = static_cast(value.toInt()); bool isVisible = newCheckState; dataNode->SetVisibility(isVisible, baseRenderer); emit dataChanged(index, index); mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); return true; } return false; } Qt::ItemFlags QmitkRenderWindowDataStorageTreeModel::flags(const QModelIndex& index) const { if (this != index.model()) { return Qt::NoItemFlags; } if (!index.isValid()) { return Qt::ItemIsDropEnabled; } auto item = GetItemByIndex(index); if (nullptr == item) { return Qt::NoItemFlags; } const auto dataNode = item->GetDataNode(); if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(dataNode)) { return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } return Qt::NoItemFlags; } Qt::DropActions QmitkRenderWindowDataStorageTreeModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions QmitkRenderWindowDataStorageTreeModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } QStringList QmitkRenderWindowDataStorageTreeModel::mimeTypes() const { QStringList types = QAbstractItemModel::mimeTypes(); types << QmitkMimeTypes::DataNodePtrs; return types; } QMimeData* QmitkRenderWindowDataStorageTreeModel::mimeData(const QModelIndexList& indexes) const { QMimeData* mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const auto& index : indexes) { if (index.isValid()) { auto dataNode = data(index, QmitkDataNodeRawPointerRole).value(); stream << reinterpret_cast(dataNode); } } mimeData->setData(QmitkMimeTypes::DataNodePtrs, encodedData); return mimeData; } bool QmitkRenderWindowDataStorageTreeModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex& parent) { auto baseRenderer = m_BaseRenderer.Lock(); if (baseRenderer.IsNull()) { return false; } if (action == Qt::IgnoreAction) { return true; } if (!data->hasFormat(QmitkMimeTypes::DataNodePtrs)) { return false; } if (!parent.isValid()) { return false; } int layer = -1; auto dataNode = this->data(parent, QmitkDataNodeRawPointerRole).value(); if (nullptr != dataNode) { dataNode->GetIntProperty("layer", layer, baseRenderer); } auto dataNodeList = QmitkMimeTypes::ToDataNodePtrList(data); for (const auto& dataNode : qAsConst(dataNodeList)) { m_RenderWindowLayerController->MoveNodeToPosition(dataNode, layer, baseRenderer); } ResetTree(); UpdateModelData(); AdjustLayerProperty(); return true; } void QmitkRenderWindowDataStorageTreeModel::SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer) { - m_RenderWindowLayerController->SetControlledRenderer(controlledRenderer); - m_ControlledRenderer = controlledRenderer; - ResetTree(); auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNull()) { return; } for (const auto& renderer : controlledRenderer) { if (nullptr == renderer) { continue; } auto allDataNodes = dataStorage->GetAll(); for (const auto& dataNode : *allDataNodes) { // add the node to each render window mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(dataNode, renderer); } } } void QmitkRenderWindowDataStorageTreeModel::SetCurrentRenderer(mitk::BaseRenderer* baseRenderer) { if (m_BaseRenderer == baseRenderer) { return; } // base renderer changed // reset tree to build a new renderer-specific item hierarchy m_BaseRenderer = baseRenderer; ResetTree(); UpdateModelData(); } mitk::BaseRenderer::Pointer QmitkRenderWindowDataStorageTreeModel::GetCurrentRenderer() const { return m_BaseRenderer.Lock(); } void QmitkRenderWindowDataStorageTreeModel::ResetTree() { beginResetModel(); if (nullptr != m_Root) { m_Root->Delete(); } mitk::DataNode::Pointer rootDataNode = mitk::DataNode::New(); rootDataNode->SetName("Data Storage"); m_Root = new QmitkDataStorageTreeModelInternalItem(rootDataNode); endResetModel(); } void QmitkRenderWindowDataStorageTreeModel::UpdateModelData() { auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNotNull()) { auto baseRenderer = m_BaseRenderer.Lock(); if (baseRenderer.IsNotNull()) { auto allDataNodes = dataStorage->GetAll(); for (const auto& dataNode : *allDataNodes) { // add the node to each render window mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(dataNode, baseRenderer); } - mitk::NodePredicateBase::Pointer renderWindowPredicate = mitk::RenderWindowLayerUtilities::GetRenderWindowPredicate(baseRenderer); - mitk::NodePredicateAnd::Pointer combinedNodePredicate = mitk::NodePredicateAnd::New(); - combinedNodePredicate->AddPredicate(renderWindowPredicate); if (m_NodePredicate.IsNotNull()) { - combinedNodePredicate->AddPredicate(m_NodePredicate); - } - - auto filteredDataNodes = dataStorage->GetSubset(combinedNodePredicate); - for (const auto& dataNode : *filteredDataNodes) - { - AddNodeInternal(dataNode, baseRenderer); + auto filteredDataNodes = dataStorage->GetSubset(m_NodePredicate); + for (const auto& dataNode : *filteredDataNodes) + { + AddNodeInternal(dataNode, baseRenderer); + } } } } } void QmitkRenderWindowDataStorageTreeModel::AdjustLayerProperty() { auto baseRenderer = m_BaseRenderer.Lock(); if (baseRenderer.IsNull()) { return; } std::vector treeAsVector; TreeToVector(m_Root, treeAsVector); int i = treeAsVector.size() - 1; for (auto it = treeAsVector.begin(); it != treeAsVector.end(); ++it) { auto dataNode = (*it)->GetDataNode(); dataNode->SetIntProperty("layer", i, baseRenderer); --i; } } void QmitkRenderWindowDataStorageTreeModel::TreeToVector(QmitkDataStorageTreeModelInternalItem* parent, std::vector& treeAsVector) const { QmitkDataStorageTreeModelInternalItem* item; for (int i = 0; i < parent->GetChildCount(); ++i) { item = parent->GetChild(i); TreeToVector(item, treeAsVector); treeAsVector.push_back(item); } } void QmitkRenderWindowDataStorageTreeModel::AddNodeInternal(const mitk::DataNode* dataNode, const mitk::BaseRenderer* renderer) { if (nullptr == dataNode || m_DataStorage.IsExpired() || nullptr != m_Root->Find(dataNode)) { return; } // find out if we have a root node auto parentItem = m_Root; QModelIndex index; auto parentDataNode = GetParentNode(dataNode); if (nullptr != parentDataNode) // no top level data node { parentItem = m_Root->Find(parentDataNode); if (nullptr == parentItem) { // parent node not contained in the tree; add it NodeAdded(parentDataNode); parentItem = m_Root->Find(parentDataNode); if (nullptr == parentItem) { // could not find and add the parent tree; abort return; } } // get the index of this parent with the help of the grand parent index = createIndex(parentItem->GetIndex(), 0, parentItem); } int firstRowWithASiblingBelow = 0; int nodeLayer = -1; dataNode->GetIntProperty("layer", nodeLayer, renderer); for (const auto& siblingItem : parentItem->GetChildren()) { int siblingLayer = -1; auto siblingNode = siblingItem->GetDataNode(); if (nullptr != siblingNode) { siblingNode->GetIntProperty("layer", siblingLayer, renderer); } if (nodeLayer > siblingLayer) { break; } ++firstRowWithASiblingBelow; } beginInsertRows(index, firstRowWithASiblingBelow, firstRowWithASiblingBelow); auto newNode = new QmitkDataStorageTreeModelInternalItem(const_cast(dataNode)); parentItem->InsertChild(newNode, firstRowWithASiblingBelow); endInsertRows(); } void QmitkRenderWindowDataStorageTreeModel::RemoveNodeInternal(const mitk::DataNode* dataNode) { if (nullptr == dataNode || nullptr == m_Root) { return; } auto item = m_Root->Find(dataNode); if (nullptr == item) { return; } auto parentItem = item->GetParent(); auto parentIndex = GetIndexByItem(parentItem); auto children = item->GetChildren(); beginRemoveRows(parentIndex, item->GetIndex(), item->GetIndex()); parentItem->RemoveChild(item); delete item; endRemoveRows(); if (!children.empty()) { // rebuild tree because children could not be at the top level ResetTree(); UpdateModelData(); } } mitk::DataNode* QmitkRenderWindowDataStorageTreeModel::GetParentNode(const mitk::DataNode* node) const { mitk::DataNode* dataNode = nullptr; auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNull()) { return dataNode; } auto sources = dataStorage->GetSources(node); if (sources->empty()) { return dataNode; } return sources->front(); } QmitkDataStorageTreeModelInternalItem* QmitkRenderWindowDataStorageTreeModel::GetItemByIndex(const QModelIndex& index) const { if (index.isValid()) { return static_cast(index.internalPointer()); } return m_Root; } QModelIndex QmitkRenderWindowDataStorageTreeModel::GetIndexByItem(QmitkDataStorageTreeModelInternalItem* item) const { if (item == m_Root) { return QModelIndex(); } return createIndex(item->GetIndex(), 0, item); } diff --git a/Modules/QtWidgets/src/QmitkRenderWindowUtilityWidget.cpp b/Modules/QtWidgets/src/QmitkRenderWindowUtilityWidget.cpp index 8fc6d892a7..50bedcae3a 100644 --- a/Modules/QtWidgets/src/QmitkRenderWindowUtilityWidget.cpp +++ b/Modules/QtWidgets/src/QmitkRenderWindowUtilityWidget.cpp @@ -1,95 +1,175 @@ /*============================================================================ 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 "QmitkRenderWindowUtilityWidget.h" #include +// mitk core +#include +#include +#include +#include + +// mitk qt widgets +#include +#include + +// itk +#include + QmitkRenderWindowUtilityWidget::QmitkRenderWindowUtilityWidget( QWidget* parent/* = nullptr */, QmitkRenderWindow* renderWindow/* = nullptr */, mitk::DataStorage* dataStorage/* = nullptr */) - : m_Layout(nullptr) - , m_MenuBar(nullptr) - , m_RenderWindow(renderWindow) - , m_DataStorage(dataStorage) - , m_RenderWindowInspector(nullptr) + : m_NodeSelectionWidget(nullptr) , m_SliceNavigationWidget(nullptr) , m_StepperAdapter(nullptr) , m_ViewDirectionSelector(nullptr) { - m_Layout = new QHBoxLayout(this); - m_Layout->setMargin(0); - - auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow()); - auto* sliceNavigationController = m_RenderWindow->GetSliceNavigationController(); - m_RenderWindowInspector = new QmitkRenderWindowContextDataStorageInspector(parent, baseRenderer); - m_RenderWindowInspector->SetDataStorage(m_DataStorage); - m_RenderWindowInspector->setObjectName(QStringLiteral("m_RenderWindowManipulatorWidget")); - connect(m_RenderWindowInspector, &QmitkRenderWindowContextDataStorageInspector::ReinitAction, - this, &QmitkRenderWindowUtilityWidget::ReinitAction); - connect(m_RenderWindowInspector, &QmitkRenderWindowContextDataStorageInspector::ResetAction, - this, &QmitkRenderWindowUtilityWidget::ResetAction); - - m_MenuBar = new QMenuBar(this); - m_MenuBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); - auto menu = m_MenuBar->addMenu("Data"); - QWidgetAction* newAct = new QWidgetAction(menu); - newAct->setDefaultWidget(m_RenderWindowInspector); - menu->addAction(newAct); - m_Layout->addWidget(m_MenuBar); + this->setParent(parent); + auto layout = new QHBoxLayout(this); + layout->setMargin(0); + + mitk::NodePredicateAnd::Pointer noHelperObjects = mitk::NodePredicateAnd::New(); + noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); + noHelperObjects->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object"))); + + m_BaseRenderer = mitk::BaseRenderer::GetInstance(renderWindow->GetVtkRenderWindow()); + m_NodeSelectionWidget = new QmitkSynchronizedNodeSelectionWidget(parent); + m_NodeSelectionWidget->SetBaseRenderer(m_BaseRenderer); + m_NodeSelectionWidget->SetDataStorage(dataStorage); + m_NodeSelectionWidget->SetNodePredicate(noHelperObjects); + + auto menuBar = new QMenuBar(this); + menuBar->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + auto dataMenu = menuBar->addMenu("Data"); + QWidgetAction* dataAction = new QWidgetAction(dataMenu); + dataAction->setDefaultWidget(m_NodeSelectionWidget); + dataMenu->addAction(dataAction); + layout->addWidget(menuBar); + + auto* synchPushButton = new QPushButton(this); + auto* synchIcon = new QIcon(); + auto synchronizeSvg = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg")); + auto desynchronizeSvg = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg")); + synchIcon->addPixmap(synchronizeSvg.pixmap(64), QIcon::Normal, QIcon::On); + synchIcon->addPixmap(desynchronizeSvg.pixmap(64), QIcon::Normal, QIcon::Off); + synchPushButton->setIcon(*synchIcon); + synchPushButton->setToolTip("Synchronize / desynchronize data management"); + synchPushButton->setCheckable(true); + synchPushButton->setChecked(true); + connect(synchPushButton, &QPushButton::clicked, + this, &QmitkRenderWindowUtilityWidget::ToggleSynchronization); + layout->addWidget(synchPushButton); + + auto* sliceNavigationController = m_BaseRenderer->GetSliceNavigationController(); m_SliceNavigationWidget = new QmitkSliceNavigationWidget(this); m_StepperAdapter = new QmitkStepperAdapter(m_SliceNavigationWidget, sliceNavigationController->GetSlice()); - m_Layout->addWidget(m_SliceNavigationWidget); + layout->addWidget(m_SliceNavigationWidget); - mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer{ baseRenderer }; + mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer{ m_BaseRenderer }; m_RenderWindowViewDirectionController = std::make_unique(); m_RenderWindowViewDirectionController->SetControlledRenderer(controlledRenderer); m_RenderWindowViewDirectionController->SetDataStorage(dataStorage); m_ViewDirectionSelector = new QComboBox(this); QStringList viewDirections{ "axial", "coronal", "sagittal"}; m_ViewDirectionSelector->insertItems(0, viewDirections); connect(m_ViewDirectionSelector, &QComboBox::currentTextChanged, this, &QmitkRenderWindowUtilityWidget::ChangeViewDirection); auto viewDirection = sliceNavigationController->GetDefaultViewDirection(); switch (viewDirection) { case mitk::AnatomicalPlane::Axial: m_ViewDirectionSelector->setCurrentIndex(0); break; case mitk::AnatomicalPlane::Coronal: m_ViewDirectionSelector->setCurrentIndex(1); break; case mitk::AnatomicalPlane::Sagittal: m_ViewDirectionSelector->setCurrentIndex(2); break; default: break; } - m_Layout->addWidget(m_ViewDirectionSelector); + + layout->addWidget(m_ViewDirectionSelector); + + // finally add observer, after all relevant objects have been created / initialized + sliceNavigationController->ConnectGeometrySendEvent(this); } QmitkRenderWindowUtilityWidget::~QmitkRenderWindowUtilityWidget() { } -void QmitkRenderWindowUtilityWidget::SetInvertedSliceNavigation(bool inverted) +void QmitkRenderWindowUtilityWidget::ToggleSynchronization(bool synchronized) { - m_SliceNavigationWidget->SetInverseDirection(inverted); + m_NodeSelectionWidget->SetSynchronized(synchronized); + emit SynchronizationToggled(m_NodeSelectionWidget); +} + +void QmitkRenderWindowUtilityWidget::SetGeometry(const itk::EventObject& event) +{ + if (!mitk::SliceNavigationController::GeometrySendEvent(nullptr, 0).CheckEvent(&event)) + { + return; + } + + const auto* sliceNavigationController = m_BaseRenderer->GetSliceNavigationController(); + auto viewDirection = sliceNavigationController->GetViewDirection(); + unsigned int axis = 0; + switch (viewDirection) + { + case mitk::AnatomicalPlane::Original: + return; + case mitk::AnatomicalPlane::Axial: + { + axis = 2; + break; + } + case mitk::AnatomicalPlane::Coronal: + { + axis = 1; + break; + } + case mitk::AnatomicalPlane::Sagittal: + { + axis = 0; + break; + } + } + + const auto* inputTimeGeometry = sliceNavigationController->GetInputWorldTimeGeometry(); + const mitk::BaseGeometry* rendererGeometry = m_BaseRenderer->GetCurrentWorldGeometry(); + + mitk::TimeStepType timeStep = sliceNavigationController->GetTime()->GetPos(); + mitk::BaseGeometry::ConstPointer geometry = inputTimeGeometry->GetGeometryForTimeStep(timeStep); + + mitk::AffineTransform3D::MatrixType matrix = geometry->GetIndexToWorldTransform()->GetMatrix(); + matrix.GetVnlMatrix().normalize_columns(); + mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); + + int dominantAxis = itk::Function::Max3(inverseMatrix[0][axis], inverseMatrix[1][axis], inverseMatrix[2][axis]); + + bool referenceGeometryAxisInverted = inverseMatrix[dominantAxis][axis] < 0; + bool rendererZAxisInverted = rendererGeometry->GetAxisVector(2)[axis] < 0; + + m_SliceNavigationWidget->SetInverseDirection(referenceGeometryAxisInverted != rendererZAxisInverted); } void QmitkRenderWindowUtilityWidget::ChangeViewDirection(const QString& viewDirection) { m_RenderWindowViewDirectionController->SetViewDirectionOfRenderer(viewDirection.toStdString()); } diff --git a/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp b/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp index fa26a2dc61..20df5b5f9d 100644 --- a/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp +++ b/Modules/QtWidgets/src/QmitkRenderWindowWidget.cpp @@ -1,439 +1,319 @@ /*============================================================================ 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 "QmitkRenderWindowWidget.h" -#include -#include -#include - -// itk -#include - // vtk #include #include QmitkRenderWindowWidget::QmitkRenderWindowWidget(QWidget* parent/* = nullptr*/, const QString& widgetName/* = ""*/, - mitk::DataStorage* dataStorage/* = nullptr*/, - bool windowControls/* = false */) + mitk::DataStorage* dataStorage/* = nullptr*/) : QFrame(parent) , m_WidgetName(widgetName) , m_DataStorage(dataStorage) , m_RenderWindow(nullptr) , m_CrosshairManager(nullptr) - , m_UtilityWidget(nullptr) - , m_WindowControls(windowControls) { this->InitializeGUI(); } QmitkRenderWindowWidget::~QmitkRenderWindowWidget() { auto sliceNavigationController = this->GetSliceNavigationController(); if (nullptr != sliceNavigationController) { sliceNavigationController->SetCrosshairEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkRenderWindowWidget::SetCrosshairPosition)); } } void QmitkRenderWindowWidget::SetDataStorage(mitk::DataStorage* dataStorage) { if (dataStorage == m_DataStorage) { return; } m_DataStorage = dataStorage; if (nullptr != m_RenderWindow) { mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow())->SetDataStorage(dataStorage); } m_CrosshairManager->SetDataStorage(m_DataStorage); } mitk::SliceNavigationController* QmitkRenderWindowWidget::GetSliceNavigationController() const { return m_RenderWindow->GetSliceNavigationController(); } void QmitkRenderWindowWidget::RequestUpdate() { mitk::RenderingManager::GetInstance()->RequestUpdate(m_RenderWindow->renderWindow()); } void QmitkRenderWindowWidget::ForceImmediateUpdate() { mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(m_RenderWindow->renderWindow()); } +void QmitkRenderWindowWidget::AddUtilityWidget(QWidget* utilityWidget) +{ + m_Layout->insertWidget(0, utilityWidget); +} + void QmitkRenderWindowWidget::SetGradientBackgroundColors(const mitk::Color& upper, const mitk::Color& lower) { vtkRenderer* vtkRenderer = m_RenderWindow->GetRenderer()->GetVtkRenderer(); if (nullptr == vtkRenderer) { return; } m_GradientBackgroundColors.first = upper; m_GradientBackgroundColors.second = lower; vtkRenderer->SetBackground(lower[0], lower[1], lower[2]); vtkRenderer->SetBackground2(upper[0], upper[1], upper[2]); ShowGradientBackground(true); } void QmitkRenderWindowWidget::ShowGradientBackground(bool show) { m_RenderWindow->GetRenderer()->GetVtkRenderer()->SetGradientBackground(show); } bool QmitkRenderWindowWidget::IsGradientBackgroundOn() const { return m_RenderWindow->GetRenderer()->GetVtkRenderer()->GetGradientBackground(); } void QmitkRenderWindowWidget::SetDecorationColor(const mitk::Color& color) { m_DecorationColor = color; m_CornerAnnotation->GetTextProperty()->SetColor(m_DecorationColor[0], m_DecorationColor[1], m_DecorationColor[2]); QColor hexColor(m_DecorationColor[0] * 255, m_DecorationColor[1] * 255, m_DecorationColor[2] * 255); setStyleSheet("QmitkRenderWindowWidget { border: 2px solid " + hexColor.name(QColor::HexRgb) + "; }"); } void QmitkRenderWindowWidget::ShowColoredRectangle(bool show) { if (show) { setFrameStyle(QFrame::Box | QFrame::Plain); } else { setFrameStyle(NoFrame); } } bool QmitkRenderWindowWidget::IsColoredRectangleVisible() const { return frameStyle() > 0; } void QmitkRenderWindowWidget::ShowCornerAnnotation(bool show) { m_CornerAnnotation->SetVisibility(show); } bool QmitkRenderWindowWidget::IsCornerAnnotationVisible() const { return m_CornerAnnotation->GetVisibility() > 0; } void QmitkRenderWindowWidget::SetCornerAnnotationText(const std::string& cornerAnnotation) { m_CornerAnnotation->SetText(0, cornerAnnotation.c_str()); } std::string QmitkRenderWindowWidget::GetCornerAnnotationText() const { return std::string(m_CornerAnnotation->GetText(0)); } bool QmitkRenderWindowWidget::IsRenderWindowMenuActivated() const { return m_RenderWindow->GetActivateMenuWidgetFlag(); } void QmitkRenderWindowWidget::SetCrosshairVisibility(bool visible) { m_CrosshairManager->SetCrosshairVisibility(visible); this->RequestUpdate(); } bool QmitkRenderWindowWidget::GetCrosshairVisibility() { return m_CrosshairManager->GetCrosshairVisibility(); } void QmitkRenderWindowWidget::SetCrosshairGap(unsigned int gapSize) { m_CrosshairManager->SetCrosshairGap(gapSize); } void QmitkRenderWindowWidget::AddPlanesToDataStorage() { m_CrosshairManager->AddPlanesToDataStorage(); } void QmitkRenderWindowWidget::RemovePlanesFromDataStorage() { m_CrosshairManager->RemovePlanesFromDataStorage(); } void QmitkRenderWindowWidget::InitializeGUI() { m_Layout = new QVBoxLayout(this); m_Layout->setMargin(0); setLayout(m_Layout); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setContentsMargins(0, 0, 0, 0); if (nullptr == m_DataStorage) { return; } mitk::RenderingManager::GetInstance()->SetDataStorage(m_DataStorage); // create render window for this render window widget m_RenderWindow = new QmitkRenderWindow(this, m_WidgetName, nullptr); m_RenderWindow->SetLayoutIndex(mitk::AnatomicalPlane::Sagittal); connect(m_RenderWindow, &QmitkRenderWindow::ResetGeometry, this, &QmitkRenderWindowWidget::OnResetGeometry); auto sliceNavigationController = this->GetSliceNavigationController(); sliceNavigationController->SetDefaultViewDirection(mitk::AnatomicalPlane::Sagittal); - if (m_WindowControls) - { - m_UtilityWidget = new QmitkRenderWindowUtilityWidget(this, m_RenderWindow, m_DataStorage); - m_Layout->addWidget(m_UtilityWidget); - connect(m_UtilityWidget, &QmitkRenderWindowUtilityWidget::ReinitAction, - this, &QmitkRenderWindowWidget::OnReinitAction); - connect(m_UtilityWidget, &QmitkRenderWindowUtilityWidget::ResetAction, - this, &QmitkRenderWindowWidget::OnResetAction); - } - m_Layout->addWidget(m_RenderWindow); // set colors and corner annotation InitializeDecorations(); // use crosshair manager m_CrosshairManager = mitk::CrosshairManager::New(m_DataStorage, m_RenderWindow->GetRenderer()); sliceNavigationController->SetCrosshairEvent.AddListener( mitk::MessageDelegate1( this, &QmitkRenderWindowWidget::SetCrosshairPosition)); // finally add observer, after all relevant objects have been created / initialized sliceNavigationController->ConnectGeometrySendEvent(this); sliceNavigationController->ConnectGeometrySliceEvent(this); mitk::TimeGeometry::ConstPointer timeGeometry = m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()); mitk::RenderingManager::GetInstance()->InitializeView(m_RenderWindow->GetVtkRenderWindow(), timeGeometry); } void QmitkRenderWindowWidget::InitializeDecorations() { vtkRenderer* vtkRenderer = m_RenderWindow->GetRenderer()->GetVtkRenderer(); if (nullptr == vtkRenderer) { return; } // initialize background color gradients float black[3] = { 0.0f, 0.0f, 0.0f }; SetGradientBackgroundColors(black, black); // initialize annotation text and decoration color setFrameStyle(QFrame::Box | QFrame::Plain); m_CornerAnnotation = vtkSmartPointer::New(); m_CornerAnnotation->SetText(0, "Sagittal"); m_CornerAnnotation->SetMaximumFontSize(12); if (0 == vtkRenderer->HasViewProp(m_CornerAnnotation)) { vtkRenderer->AddViewProp(m_CornerAnnotation); } float white[3] = { 1.0f, 1.0f, 1.0f }; SetDecorationColor(mitk::Color(white)); } void QmitkRenderWindowWidget::SetCrosshairPosition(const mitk::Point3D& newPosition) { m_CrosshairManager->SetCrosshairPosition(newPosition); this->RequestUpdate(); } mitk::Point3D QmitkRenderWindowWidget::GetCrosshairPosition() const { return m_CrosshairManager->GetCrosshairPosition(); } void QmitkRenderWindowWidget::SetGeometry(const itk::EventObject& event) { if (!mitk::SliceNavigationController::GeometrySendEvent(nullptr, 0).CheckEvent(&event)) { return; } auto sliceNavigationController = this->GetSliceNavigationController(); const auto* inputTimeGeometry = sliceNavigationController->GetInputWorldTimeGeometry(); m_CrosshairManager->ComputeOrientedTimeGeometries(inputTimeGeometry); - - if (m_WindowControls) - { - this->ComputeInvertedSliceNavigation(); - } } void QmitkRenderWindowWidget::SetGeometrySlice(const itk::EventObject& event) { if (!mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0).CheckEvent(&event)) { return; } auto sliceNavigationController = this->GetSliceNavigationController(); m_CrosshairManager->UpdateSlice(sliceNavigationController); } -void QmitkRenderWindowWidget::ComputeInvertedSliceNavigation() -{ - auto sliceNavigationController = this->GetSliceNavigationController(); - auto viewDirection = sliceNavigationController->GetViewDirection(); - unsigned int axis = 0; - switch (viewDirection) - { - case mitk::AnatomicalPlane::Original: - return; - case mitk::AnatomicalPlane::Axial: - { - axis = 2; - break; - } - case mitk::AnatomicalPlane::Coronal: - { - axis = 1; - break; - } - case mitk::AnatomicalPlane::Sagittal: - { - axis = 0; - break; - } - } - - const auto* inputTimeGeometry = sliceNavigationController->GetInputWorldTimeGeometry(); - const mitk::BaseGeometry* rendererGeometry = m_RenderWindow->GetRenderer()->GetCurrentWorldGeometry(); - - // todo: check timepoint / timestep - mitk::TimeStepType timeStep = sliceNavigationController->GetTime()->GetPos(); - mitk::BaseGeometry::ConstPointer geometry = inputTimeGeometry->GetGeometryForTimeStep(timeStep); - - mitk::AffineTransform3D::MatrixType matrix = geometry->GetIndexToWorldTransform()->GetMatrix(); - matrix.GetVnlMatrix().normalize_columns(); - mitk::AffineTransform3D::MatrixType::InternalMatrixType inverseMatrix = matrix.GetInverse(); - - int dominantAxis = itk::Function::Max3(inverseMatrix[0][axis], inverseMatrix[1][axis], inverseMatrix[2][axis]); - - bool referenceGeometryAxisInverted = inverseMatrix[dominantAxis][axis] < 0; - bool rendererZAxisInverted = rendererGeometry->GetAxisVector(2)[axis] < 0; - - m_UtilityWidget->SetInvertedSliceNavigation(referenceGeometryAxisInverted != rendererZAxisInverted); -} - -void QmitkRenderWindowWidget::OnReinitAction(QList selectedNodes) -{ - if (selectedNodes.empty()) - { - return; - } - - auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow()); - auto boundingBoxPredicate = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false), baseRenderer)); - mitk::DataStorage::SetOfObjects::Pointer nodes = mitk::DataStorage::SetOfObjects::New(); - for (const auto& dataNode : selectedNodes) - { - if (boundingBoxPredicate->CheckNode(dataNode)) - { - nodes->InsertElement(nodes->Size(), dataNode); - } - } - - if (nodes->empty()) - { - return; - } - - if (1 == nodes->Size()) - { - auto selectedImage = dynamic_cast(nodes->ElementAt(0)->GetData()); - - if (nullptr != selectedImage) - { - mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), selectedImage->GetTimeGeometry()); - return; - } - } - - auto boundingGeometry = m_DataStorage->ComputeBoundingGeometry3D(nodes, "visible", baseRenderer); - mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), boundingGeometry); -} - -void QmitkRenderWindowWidget::OnResetAction(QList selectedNodes) -{ - if (selectedNodes.empty()) - { - return; - } - - auto selectedImage = dynamic_cast(selectedNodes.front()->GetData()); - if (nullptr == selectedImage) - { - return; - } - - const mitk::TimeGeometry* referenceGeometry = selectedImage->GetTimeGeometry(); - this->ResetGeometry(referenceGeometry); -} - void QmitkRenderWindowWidget::OnResetGeometry() { auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->GetRenderWindow()); const auto* interactionReferenceGeometry = baseRenderer->GetInteractionReferenceGeometry(); this->ResetGeometry(interactionReferenceGeometry); m_RenderWindow->ShowOverlayMessage(false); } void QmitkRenderWindowWidget::ResetGeometry(const mitk::TimeGeometry* referenceGeometry) { if (nullptr == referenceGeometry) { return; } mitk::TimeStepType imageTimeStep = 0; // store the current position to set it again later, if the camera should not be reset mitk::Point3D currentPosition = this->GetCrosshairPosition(); // store the current time step to set it again later, if the camera should not be reset auto* renderingManager = mitk::RenderingManager::GetInstance(); const auto currentTimePoint = renderingManager->GetTimeNavigationController()->GetSelectedTimePoint(); if (referenceGeometry->IsValidTimePoint(currentTimePoint)) { imageTimeStep = referenceGeometry->TimePointToTimeStep(currentTimePoint); } auto* baseRenderer = mitk::BaseRenderer::GetInstance(m_RenderWindow->renderWindow()); renderingManager->InitializeView(baseRenderer->GetRenderWindow(), referenceGeometry, false); // reset position and time step this->GetSliceNavigationController()->SelectSliceByPoint(currentPosition); renderingManager->GetTimeNavigationController()->GetTime()->SetPos(imageTimeStep); } diff --git a/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.cpp b/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.cpp new file mode 100644 index 0000000000..da730d116e --- /dev/null +++ b/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.cpp @@ -0,0 +1,663 @@ +/*============================================================================ + +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. + +============================================================================*/ + +// mitk qt widgets module +#include +#include +#include +#include + +// mitk core module +#include +#include +#include +#include + +QmitkSynchronizedNodeSelectionWidget::QmitkSynchronizedNodeSelectionWidget(QWidget* parent) + : QmitkAbstractNodeSelectionWidget(parent) +{ + m_Controls.setupUi(this); + + m_StorageModel = std::make_unique(this); + + m_Controls.tableView->setModel(m_StorageModel.get()); + m_Controls.tableView->horizontalHeader()->setVisible(false); + m_Controls.tableView->verticalHeader()->setVisible(false); + m_Controls.tableView->setSelectionMode(QAbstractItemView::SingleSelection); + m_Controls.tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + m_Controls.tableView->setContextMenuPolicy(Qt::CustomContextMenu); + + this->SetUpConnections(); + this->Initialize(); +} + +QmitkSynchronizedNodeSelectionWidget::~QmitkSynchronizedNodeSelectionWidget() +{ + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNull()) + { + return; + } + + auto dataStorage = m_DataStorage.Lock(); + if (dataStorage.IsNull()) + { + return; + } + + bool isSynchronized = this->IsSynchronized(); + if (!isSynchronized) + { + // If the model is not synchronizes, + // we know that renderer-specific properties exist for all nodes. + // These properties need to be removed from the nodes. + auto allNodes = dataStorage->GetAll(); + for (auto& node : *allNodes) + { + // Delete the relevant renderer-specific properties for the node using the current base renderer. + mitk::RenderWindowLayerUtilities::DeleteRenderWindowProperties(node, baseRenderer); + } + } +} + +void QmitkSynchronizedNodeSelectionWidget::SetBaseRenderer(mitk::BaseRenderer* baseRenderer) +{ + if (m_BaseRenderer == baseRenderer) + { + // no need to do something + return; + } + + if (nullptr == baseRenderer) + { + return; + } + + auto oldBaseRenderer = m_BaseRenderer.Lock(); + m_BaseRenderer = baseRenderer; + + auto dataStorage = m_DataStorage.Lock(); + if (dataStorage.IsNull()) + { + return; + } + + bool isSynchronized = this->IsSynchronized(); + if (isSynchronized) + { + // If the model is synchronized, + // all nodes use global / default properties. + // No renderer-specific property lists should exist + // so there is no need to transfer any property values. + } + else + { + // If the model is not synchronized, + // we know that renderer-specific properties exist for all nodes. + // These properties need to be removed from the nodes and + // we need to transfer their values to new renderer-specific properties. + auto allNodes = dataStorage->GetAll(); + for (auto& node : *allNodes) + { + // Set the relevant renderer-specific properties for the node using the new base renderer. + // By transferring the values from the old property list, + // the same property-state is kept when switching to another base renderer. + mitk::RenderWindowLayerUtilities::TransferRenderWindowProperties(node, baseRenderer, oldBaseRenderer); + // Delete the relevant renderer-specific properties for the node using the old base renderer. + mitk::RenderWindowLayerUtilities::DeleteRenderWindowProperties(node, oldBaseRenderer); + } + } + + this->Initialize(); +} + +void QmitkSynchronizedNodeSelectionWidget::SetSelectAll(bool selectAll) +{ + if (selectAll == m_Controls.selectionModeCheckBox->isChecked()) + { + // no need to do something + return; + } + + m_Controls.selectionModeCheckBox->setChecked(selectAll); +} + +bool QmitkSynchronizedNodeSelectionWidget::GetSelectAll() const +{ + return m_Controls.selectionModeCheckBox->isChecked(); +} + +void QmitkSynchronizedNodeSelectionWidget::SetSynchronized(bool synchronize) +{ + if (synchronize == this->IsSynchronized()) + { + // no need to do something + return; + } + + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNull()) + { + return; + } + + auto dataStorage = m_DataStorage.Lock(); + if (dataStorage.IsNull()) + { + return; + } + + if (synchronize) + { + // set the base renderer of the model to nullptr, such that global properties are used + m_StorageModel->SetCurrentRenderer(nullptr); + + // If the model is synchronized, + // we know that the model was not synchronized before. + // That means that all nodes use renderer-specific properties, + // but now all nodes need global properties. + // Thus we need to remove the renderer-specific properties of all nodes of the + // datastorage. + auto allNodes = dataStorage->GetAll(); + for (auto& node : *allNodes) + { + // Delete the relevant renderer-specific properties for the node using the current base renderer. + mitk::RenderWindowLayerUtilities::DeleteRenderWindowProperties(node, baseRenderer); + } + } + else + { + // set the base renderer of the model to current base renderer, such that renderer-specific properties are used + m_StorageModel->SetCurrentRenderer(baseRenderer); + + // If the model is not synchronized anymore, + // we know that the model was synchronized before. + // That means that all nodes use global / default properties, + // but now all nodes need renderer-specific properties. + // Thus we need to modify the renderer-specific properties of all nodes of the + // datastorage: + // - hide those nodes, which are not part of the newly selected nodes. + // - keep the property values of those nodes, which are part of the new selection AND + // have been selected before + auto currentNodeSelection = this->GetCurrentInternalSelection(); + auto allNodes = dataStorage->GetAll(); + for (auto& node : *allNodes) + { + // check if the node is part of the current selection + auto finding = std::find(std::begin(currentNodeSelection), std::end(currentNodeSelection), node); + if (finding != std::end(currentNodeSelection)) // node found / part of the current selection + { + // Set the relevant renderer-specific properties for the node using the curent base renderer. + // By transferring the values from the global / default property list, + // the same property-state is kept when switching to non-synchronized mode. + mitk::RenderWindowLayerUtilities::TransferRenderWindowProperties(node, baseRenderer, nullptr); + } + else + { + // If the node is not part of the selection, unset the relevant renderer-specific properties. + // This will unset the "visible" and "layer" property for the renderer-specific property list and + // hide the node for this renderer. + // ATTENTION: This is required, since the synchronized property needs to be overwritten + // to make sure that the visibility is correctly set for the specific base renderer. + this->DeselectNode(node); + } + } + } + + // Since the synchronization might lead to a different node order depending on the layer properties, the render window + // needs to be updated. + // Explicitly request an update since a renderer-specific property change does not mark the node as modified. + // see https://phabricator.mitk.org/T22322 + mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); +} + +bool QmitkSynchronizedNodeSelectionWidget::IsSynchronized() const +{ + return m_StorageModel->GetCurrentRenderer().IsNull(); +} + +void QmitkSynchronizedNodeSelectionWidget::OnModelUpdated() +{ + m_Controls.tableView->resizeRowsToContents(); + m_Controls.tableView->resizeColumnsToContents(); +} + +void QmitkSynchronizedNodeSelectionWidget::OnSelectionModeChanged(bool selectAll) +{ + emit SelectionModeChanged(selectAll); + + if (selectAll) + { + auto dataStorage = m_DataStorage.Lock(); + if (dataStorage.IsNull()) + { + return; + } + + auto allNodes = m_NodePredicate ? dataStorage->GetSubset(m_NodePredicate) : dataStorage->GetAll(); + NodeList currentSelection; + for (auto& node : *allNodes) + { + currentSelection.append(node); + } + + this->HandleChangeOfInternalSelection(currentSelection); + } +} + +void QmitkSynchronizedNodeSelectionWidget::OnEditSelection() +{ + QmitkNodeSelectionDialog* dialog = new QmitkNodeSelectionDialog(this); + + dialog->SetDataStorage(m_DataStorage.Lock()); + dialog->SetNodePredicate(m_NodePredicate); + dialog->SetCurrentSelection(m_StorageModel->GetCurrentSelection()); + dialog->SetSelectionMode(QAbstractItemView::MultiSelection); + + m_Controls.changeSelectionButton->setChecked(true); + if (dialog->exec()) + { + m_Controls.selectionModeCheckBox->setChecked(false); + emit SelectionModeChanged(false); + + auto selectedNodes = dialog->GetSelectedNodes(); + this->HandleChangeOfInternalSelection(selectedNodes); + } + + m_Controls.changeSelectionButton->setChecked(false); + + delete dialog; +} + +void QmitkSynchronizedNodeSelectionWidget::OnTableClicked(const QModelIndex& index) +{ + if (!index.isValid() || m_StorageModel.get() != index.model()) + { + return; + } + + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNull()) + { + return; + } + + QVariant dataNodeVariant = index.data(QmitkDataNodeRole); + auto dataNode = dataNodeVariant.value(); + + if (index.column() == 1) // node visibility column + { + bool visibiliy = index.data(Qt::EditRole).toBool(); + m_StorageModel->setData(index, QVariant(!visibiliy), Qt::EditRole); + return; + } + + if (index.column() == 2) // reinit node column + { + this->ReinitNode(dataNode); + return; + } + + if (index.column() == 3) // remove node column + { + this->RemoveFromInternalSelection(dataNode); + return; + } +} + +void QmitkSynchronizedNodeSelectionWidget::SetUpConnections() +{ + connect(m_StorageModel.get(), &QmitkRenderWindowDataNodeTableModel::ModelUpdated, + this, &QmitkSynchronizedNodeSelectionWidget::OnModelUpdated); + + connect(m_Controls.selectionModeCheckBox, &QCheckBox::clicked, + this, &QmitkSynchronizedNodeSelectionWidget::OnSelectionModeChanged); + connect(m_Controls.changeSelectionButton, &QPushButton::clicked, + this, &QmitkSynchronizedNodeSelectionWidget::OnEditSelection); + + connect(m_Controls.tableView, &QTableView::clicked, + this, &QmitkSynchronizedNodeSelectionWidget::OnTableClicked); +} + +void QmitkSynchronizedNodeSelectionWidget::Initialize() +{ + auto baseRenderer = m_BaseRenderer.Lock(); + auto dataStorage = m_DataStorage.Lock(); + m_StorageModel->SetDataStorage(dataStorage); + m_StorageModel->SetCurrentRenderer(baseRenderer); + + if (baseRenderer.IsNull() || dataStorage.IsNull()) + { + m_Controls.selectionModeCheckBox->setEnabled(false); + m_Controls.changeSelectionButton->setEnabled(false); + // reset the model if no data storage is defined + m_StorageModel->removeRows(0, m_StorageModel->rowCount()); + return; + } + + // Use the new data storage / node predicate to correctly set the list of + // currently selected data nodes for the model. + // If a new data storage or node predicate has been defined, + // we switch to the "selectAll" mode and synchronize the selection for simplicity. + // enable UI + m_Controls.selectionModeCheckBox->setEnabled(true); + m_Controls.changeSelectionButton->setEnabled(true); + + m_Controls.selectionModeCheckBox->setChecked(true); + + // set the base renderer of the model to nullptr, such that global properties are used (synchronized mode) + m_StorageModel->SetCurrentRenderer(nullptr); +} + +void QmitkSynchronizedNodeSelectionWidget::UpdateInfo() +{ + +} + +void QmitkSynchronizedNodeSelectionWidget::OnDataStorageChanged() +{ + this->Initialize(); +} + +void QmitkSynchronizedNodeSelectionWidget::OnNodePredicateChanged() +{ + this->Initialize(); +} + +void QmitkSynchronizedNodeSelectionWidget::ReviseSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection) +{ + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNull()) + { + return; + } + + bool isSynchronized = this->IsSynchronized(); + if (isSynchronized) + { + this->ReviseSynchronizedSelectionChanged(oldInternalSelection, newInternalSelection); + } + else + { + this->ReviseDesynchronizedSelectionChanged(oldInternalSelection, newInternalSelection); + } + + // Since a new selection might have a different rendering tree the render windows + // need to be updated. + // Explicitly request an update since a renderer-specific property change does not mark the node as modified. + // see https://phabricator.mitk.org/T22322 + mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); +} + +void QmitkSynchronizedNodeSelectionWidget::OnInternalSelectionChanged() +{ + m_StorageModel->SetCurrentSelection(this->GetCurrentInternalSelection()); +} + +bool QmitkSynchronizedNodeSelectionWidget::AllowEmissionOfSelection(const NodeList& /*emissionCandidates*/) const +{ + return this->IsSynchronized(); +} + +void QmitkSynchronizedNodeSelectionWidget::OnNodeAddedToStorage(const mitk::DataNode* node) +{ + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNull()) + { + return; + } + + // The selection mode determines if we want to show all nodes from the data storage + // or use a local selected list of nodes. + // We need to hide each new incoming data node, if we use a local selection, + // since we do not want to show / select newly added nodes immediately. + // We need to add the incoming node to our selection, if the selection mode check box + // is checked. + if (m_Controls.selectionModeCheckBox->isChecked()) + { + if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node)) + { + auto currentSelection = this->GetCurrentInternalSelection(); + // Check if the nodes is already part of the internal selection. + // That can happen if another render window already added the new node and sent out the new, updated + // selection to be synchronized. + auto finding = std::find(std::begin(currentSelection), std::end(currentSelection), node); + if (finding != std::end(currentSelection)) // node found + { + // node already part of the selection + return; + } + + currentSelection.append(const_cast(node)); + this->HandleChangeOfInternalSelection(currentSelection); + } + + // For helper / hidden nodes: + // If the node predicate does not match, show the node but do not add + // it to the current selection. + return; + } + + // If the model is in "local-selection" state (selectionModeCheckBox unchecked), + // the node needs to be hid. + // Here it depends on the synchronization-state which properties need + // to be modified. + if (this->IsSynchronized()) + { + // If the node will not be part of the new selection, hide the node. + const_cast(node)->SetVisibility(false); + } + else + { + // If the model is not synchronized, all nodes use renderer-specific properties. + // Thus we need to modify the renderer-specific properties of a new node: + // - hide the node, which is not part of the selection + this->DeselectNode(const_cast(node)); + } +} + +void QmitkSynchronizedNodeSelectionWidget::OnNodeModified(const itk::Object* caller, const itk::EventObject& event) +{ + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNull()) + { + return; + } + + if (!itk::ModifiedEvent().CheckEvent(&event)) + { + return; + } + + auto node = dynamic_cast(caller); + + if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node)) + { + auto currentSelection = this->GetCurrentInternalSelection(); + // check if the node to be modified is part of the current selection + auto finding = std::find(std::begin(currentSelection), std::end(currentSelection), node); + if (finding == std::end(currentSelection)) // node not found + { + // node not part of the selection + return; + } + + // We know that the node is relevant, but we don't know if the node modification was relevant + // for the rendering. We just request an update here. + // Explicitly request an update since a renderer-specific property change does not mark the node as modified. + // see https://phabricator.mitk.org/T22322 + mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); + m_StorageModel->UpdateModelData(); + } +} + +void QmitkSynchronizedNodeSelectionWidget::ReviseSynchronizedSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection) +{ + // If the model is synchronized, all nodes use global / default properties. + // Thus we need to modify the global properties of the selection: + // - a) show those nodes, which are part of the new selection AND have not been + // selected before + // - b) keep the property values of those nodes, which are part of the new selection AND + // have been selected before + // - c) hide those nodes, which are part of the old selection AND + // have not been newly selected + for (auto& node : newInternalSelection) + { + // check if the node is part of the old selection + auto finding = std::find(std::begin(oldInternalSelection), std::end(oldInternalSelection), node); + if (finding == std::end(oldInternalSelection)) // node not found + { + // If the node is part of the new selection and was not already part of the old selection, + // set the relevant renderer-specific properties. + // This will set the "visible" property for the global / default property list + // and show the node for this renderer. + node->SetVisibility(true); // item a) + } + // else: item b): node that was already selected before does not need to be modified + } + + for (auto& node : oldInternalSelection) + { + // check if the node is part of the new selection + auto finding = std::find(std::begin(newInternalSelection), std::end(newInternalSelection), node); + if (finding == std::end(newInternalSelection)) // node not found + { + // If the node is not part of the new selection, hide the node. + node->SetVisibility(false); // item c) + } + // else: item b): node that was already selected before does not need to be modified + } +} + +void QmitkSynchronizedNodeSelectionWidget::ReviseDesynchronizedSelectionChanged(const NodeList& oldInternalSelection, NodeList& newInternalSelection) +{ + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNull()) + { + return; + } + + // If the model is not synchronized, all nodes need renderer-specific properties. + // Thus we need to modify the renderer-specific properties of the selection: + // - a) set the renderer-specific properties of those nodes, which are part of the new selection AND + // have not been selected before (see 'SelectNode') + // - b) show those nodes, which are part of the new selection AND have not been + // selected before + // - c) keep the property values of those nodes, which are part of the new selection AND + // have been selected before + // - d) hide those nodes, which are part of the old selection AND + // have not been newly selected + // - e) set the renderer-specific properties of those nodes, which are part of the old selection AND + // have not been newly selected, to denote which nodes are selected + for (auto& node : newInternalSelection) + { + // check if the node is part of the old selection + auto finding = std::find(std::begin(oldInternalSelection), std::end(oldInternalSelection), node); + if (finding == std::end(oldInternalSelection)) // node not found + { + // If the node is part of the new selection and was not already part of the old selection, + // set the relevant renderer-specific properties. + // This will set the "visible" and "layer" property for the renderer-specific property list + // such that the global / default property list values are overwritten + mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(node, baseRenderer); // item a) + // Explicitly set the visibility to true for selected nodes to show them in the render window. + node->SetVisibility(true, baseRenderer); // item b) + } + // else: item c): node that was already selected before does not need to be modified + } + + for (auto& node : oldInternalSelection) + { + // check if the node is part of the new selection + auto finding = std::find(std::begin(newInternalSelection), std::end(newInternalSelection), node); + if (finding == std::end(newInternalSelection)) // node not found + { + // If the node is not part of the new selection, unset the relevant renderer-specific properties. + // This will unset the "visible" and "layer" property for the renderer-specific property list and + // hide the node for this renderer. + // ATTENTION: This is required, since the synchronized global property needs to be overwritten + // to make sure that the visibility is correctly set for the specific base renderer. + this->DeselectNode(node); // item d) and e) + } + // else: item c): node that was already selected before does not need to be modified + } +} + +void QmitkSynchronizedNodeSelectionWidget::ReinitNode(const mitk::DataNode* dataNode) +{ + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNull()) + { + return; + } + + auto selectedImage = dynamic_cast(dataNode->GetData()); + if (nullptr == selectedImage) + { + return; + } + + auto boundingBoxPredicate = mitk::NodePredicateNot::New( + mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false), baseRenderer)); + if (!boundingBoxPredicate->CheckNode(dataNode)) + { + return; + } + + mitk::RenderingManager::GetInstance()->InitializeView(baseRenderer->GetRenderWindow(), selectedImage->GetTimeGeometry()); +} + +void QmitkSynchronizedNodeSelectionWidget::RemoveFromInternalSelection(mitk::DataNode* dataNode) +{ + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNull()) + { + return; + } + + if (this->IsSynchronized()) + { + // If the model is synchronized, all nodes use global / default properties. + // Thus we need to modify the global property of the node. + // Explicitly set the visibility to false for unselected nodes to hide them in the render window. + dataNode->SetVisibility(false); + } + + m_Controls.selectionModeCheckBox->setChecked(false); + emit SelectionModeChanged(false); + + this->RemoveNodeFromSelection(dataNode); +} + +void QmitkSynchronizedNodeSelectionWidget::DeselectNode(mitk::DataNode* dataNode) +{ + auto baseRenderer = m_BaseRenderer.Lock(); + if (baseRenderer.IsNull()) + { + return; + } + + if (nullptr == dataNode) + { + return; + } + + if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(dataNode)) + { + // If the node should not be part of the selection, set the relevant renderer-specific properties. + // This will set the "visible" and "layer" property for the renderer-specific property list, + // such that the global / default property list values are overwritten. + mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(dataNode, baseRenderer); + // Explicitly set the visibility to false for the node to hide them in the render window. + dataNode->SetVisibility(false, baseRenderer); + } +} diff --git a/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.ui b/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.ui new file mode 100644 index 0000000000..f1def382b3 --- /dev/null +++ b/Modules/QtWidgets/src/QmitkSynchronizedNodeSelectionWidget.ui @@ -0,0 +1,66 @@ + + + QmitkSynchronizedNodeSelectionWidget + + + + 0 + 0 + 350 + 300 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::InternalMove + + + + + + + Select all nodes + + + true + + + + + + + Change selection + + + true + + + + + + + + diff --git a/Modules/QtWidgets/src/QmitkSynchronizedWidgetConnector.cpp b/Modules/QtWidgets/src/QmitkSynchronizedWidgetConnector.cpp new file mode 100644 index 0000000000..8091ac673f --- /dev/null +++ b/Modules/QtWidgets/src/QmitkSynchronizedWidgetConnector.cpp @@ -0,0 +1,101 @@ +/*============================================================================ + +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. + +============================================================================*/ + +// mitk gui qt common plugin +#include "QmitkSynchronizedWidgetConnector.h" + +bool NodeListsEqual(const QmitkSynchronizedWidgetConnector::NodeList& selection1, const QmitkSynchronizedWidgetConnector::NodeList& selection2) +{ + if (selection1.size() != selection2.size()) + { + return false; + } + + // lambda to compare node pointer inside both lists + auto lambda = [](mitk::DataNode::Pointer lhs, mitk::DataNode::Pointer rhs) + { + return lhs == rhs; + }; + + return std::is_permutation(selection1.begin(), selection1.end(), selection2.begin(), selection2.end(), lambda); +} + +QmitkSynchronizedWidgetConnector::QmitkSynchronizedWidgetConnector() + : m_SelectAll(true) +{ + +} + +void QmitkSynchronizedWidgetConnector::ConnectWidget(const QmitkSynchronizedNodeSelectionWidget* nodeSelectionWidget) const +{ + connect(nodeSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, + this, &QmitkSynchronizedWidgetConnector::ChangeSelection); + + connect(this, &QmitkSynchronizedWidgetConnector::NodeSelectionChanged, + nodeSelectionWidget, &QmitkAbstractNodeSelectionWidget::SetCurrentSelection); + + connect(nodeSelectionWidget, &QmitkSynchronizedNodeSelectionWidget::SelectionModeChanged, + this, &QmitkSynchronizedWidgetConnector::ChangeSelectionMode); + + connect(this, &QmitkSynchronizedWidgetConnector::SelectionModeChanged, + nodeSelectionWidget, &QmitkSynchronizedNodeSelectionWidget::SetSelectAll); +} + +void QmitkSynchronizedWidgetConnector::DisconnectWidget(const QmitkSynchronizedNodeSelectionWidget* nodeSelectionWidget) const +{ + disconnect(nodeSelectionWidget, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, + this, &QmitkSynchronizedWidgetConnector::ChangeSelection); + + disconnect(this, &QmitkSynchronizedWidgetConnector::NodeSelectionChanged, + nodeSelectionWidget, &QmitkAbstractNodeSelectionWidget::SetCurrentSelection); + + disconnect(nodeSelectionWidget, &QmitkSynchronizedNodeSelectionWidget::SelectionModeChanged, + this, &QmitkSynchronizedWidgetConnector::ChangeSelectionMode); + + disconnect(this, &QmitkSynchronizedWidgetConnector::SelectionModeChanged, + nodeSelectionWidget, &QmitkSynchronizedNodeSelectionWidget::SetSelectAll); +} + +void QmitkSynchronizedWidgetConnector::SynchronizeWidget(QmitkSynchronizedNodeSelectionWidget* nodeSelectionWidget) const +{ + // widget is newly synchronized / connected so an initial setup needs to be made + nodeSelectionWidget->SetCurrentSelection(m_InternalSelection); + nodeSelectionWidget->SetSelectAll(m_SelectAll); +} + +QmitkSynchronizedWidgetConnector::NodeList QmitkSynchronizedWidgetConnector::GetNodeSelection() const +{ + return m_InternalSelection; +} + +bool QmitkSynchronizedWidgetConnector::GetSelectionMode() const +{ + return m_SelectAll; +} + +void QmitkSynchronizedWidgetConnector::ChangeSelection(NodeList nodes) +{ + if (!NodeListsEqual(m_InternalSelection, nodes)) + { + m_InternalSelection = nodes; + emit NodeSelectionChanged(m_InternalSelection); + } +} + +void QmitkSynchronizedWidgetConnector::ChangeSelectionMode(bool selectAll) +{ + if (m_SelectAll!= selectAll) + { + m_SelectAll = selectAll; + emit SelectionModeChanged(m_SelectAll); + } +} diff --git a/Modules/QtWidgets/src/mitkRenderWindowLayerController.cpp b/Modules/QtWidgets/src/mitkRenderWindowLayerController.cpp index 37cbe9cde5..4b8a8c91e3 100644 --- a/Modules/QtWidgets/src/mitkRenderWindowLayerController.cpp +++ b/Modules/QtWidgets/src/mitkRenderWindowLayerController.cpp @@ -1,563 +1,318 @@ /*============================================================================ 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. ============================================================================*/ // render window manager module #include "mitkRenderWindowLayerController.h" mitk::RenderWindowLayerController::RenderWindowLayerController() : m_DataStorage(nullptr) { // nothing here } void mitk::RenderWindowLayerController::SetDataStorage(DataStorage::Pointer dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage m_DataStorage = dataStorage; } } -void mitk::RenderWindowLayerController::SetControlledRenderer(RenderWindowLayerUtilities::RendererVector controlledRenderer) -{ - if (m_ControlledRenderer != controlledRenderer) - { - // set the new set of controlled renderer - m_ControlledRenderer = controlledRenderer; - } -} - -void mitk::RenderWindowLayerController::SetBaseDataNode(DataNode* dataNode, const BaseRenderer* renderer /*= nullptr*/) -{ - if (nullptr == dataNode) - { - return; - } - - if (nullptr == renderer) - { - // set the data node as base data node in all controlled renderer - for (const auto& renderer : m_ControlledRenderer) - { - if (nullptr != renderer) - { - SetBaseDataNode(dataNode, renderer); - } - } - } - else - { - // get the layer stack with the base data node of the current renderer - RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, true); - if (!stackedLayers.empty()) - { - // see if base layer exists - RenderWindowLayerUtilities::LayerStack::iterator layerStackIterator = stackedLayers.find(RenderWindowLayerUtilities::BASE_LAYER_INDEX); - if (layerStackIterator != stackedLayers.end()) - { - // remove the current base data node from the current renderer - layerStackIterator->second->GetPropertyList(renderer)->DeleteProperty("layer"); - layerStackIterator->second->SetBoolProperty("fixedLayer", false, renderer); - layerStackIterator->second->SetVisibility(false, renderer); - } - } - - // "RenderWindowLayerUtilities::BASE_LAYER_INDEX" indicates the base data node --> set as new background - dataNode->SetIntProperty("layer", RenderWindowLayerUtilities::BASE_LAYER_INDEX, renderer); - dataNode->SetBoolProperty("fixedLayer", true, renderer); - dataNode->SetVisibility(true, renderer); - dataNode->Modified(); - mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); - } -} - void mitk::RenderWindowLayerController::InsertLayerNode(DataNode* dataNode, int layer /*= RenderWindowLayerUtilities::TOP_LAYER_INDEX*/, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return; } - if (nullptr == renderer) - { - // insert data node in all controlled renderer - for (const auto& renderer : m_ControlledRenderer) - { - if (nullptr != renderer) - { - InsertLayerNode(dataNode, layer, renderer); - } - } - } - else - { - if (RenderWindowLayerUtilities::BASE_LAYER_INDEX == layer) - { - // "RenderWindowLayerUtilities::BASE_LAYER_INDEX" indicates the base data node --> set as new background (overwrite current base node if needed) - SetBaseDataNode(dataNode, renderer); - } - else - { - InsertLayerNodeInternal(dataNode, layer, renderer); - } - } -} - -void mitk::RenderWindowLayerController::InsertLayerNodeInternal(DataNode* dataNode, int newLayer, const BaseRenderer* renderer /*= nullptr*/) -{ RenderWindowLayerUtilities::SetRenderWindowProperties(dataNode, renderer); - // get the layer stack without the base node of the current renderer - RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, false); + // get the layer stack of the current renderer + RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer); if (stackedLayers.empty()) { // no layer stack for the current renderer - if (RenderWindowLayerUtilities::TOP_LAYER_INDEX == newLayer) + if (RenderWindowLayerUtilities::TOP_LAYER_INDEX == layer) { - // set given layer as first layer above base layer (= 1) - newLayer = 1; - // alternatively: no layer stack for the current renderer -> insert as background node - //SetBaseDataNode(dataNode, renderer); + // set given layer as first layer + layer = 0; } } else { - if (RenderWindowLayerUtilities::TOP_LAYER_INDEX == newLayer) + if (RenderWindowLayerUtilities::TOP_LAYER_INDEX == layer) { // get the first value (highest int-key -> topmost layer) // + 1 indicates inserting the node above the topmost layer - newLayer = stackedLayers.begin()->first + 1; + layer = stackedLayers.begin()->first + 1; } else { - MoveNodeToPosition(dataNode, newLayer, renderer); + MoveNodeToPosition(dataNode, layer, renderer); return; } } // update data storage (the "data node model") - dataNode->SetIntProperty("layer", newLayer, renderer); + dataNode->SetIntProperty("layer", layer, renderer); dataNode->Modified(); - mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); -} - -void mitk::RenderWindowLayerController::RemoveLayerNode(DataNode* dataNode, const BaseRenderer* renderer /*= nullptr*/) -{ - if (nullptr == dataNode) - { - return; - } - if (nullptr == renderer) { - // remove data node from all controlled renderer - for (const auto& renderer : m_ControlledRenderer) - { - if (nullptr != renderer) - { - RemoveLayerNode(dataNode, renderer); - } - } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } else { - // "remove" node from the renderer list - dataNode->GetPropertyList(renderer)->DeleteProperty("layer"); - dataNode->SetBoolProperty("fixedLayer", false, renderer); - dataNode->SetVisibility(false, renderer); - dataNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); } } bool mitk::RenderWindowLayerController::MoveNodeToPosition(DataNode* dataNode, int newLayer, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return false; } - if (nullptr == renderer) + // get the layer stack of the current renderer + RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer); + if (!stackedLayers.empty()) { - // move data node to position in all controlled renderer - for (const auto& renderer : m_ControlledRenderer) + // get the current layer value of the given data node + int currentLayer; + bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); + if (wasFound && currentLayer != newLayer) { - if (nullptr != renderer) + // move the given data node to the specified layer + dataNode->SetIntProperty("layer", newLayer, renderer); + + int upperBound; + int lowerBound; + int step; + if (currentLayer < newLayer) { - MoveNodeToPosition(dataNode, newLayer, renderer); - // we don't store/need the returned boolean value - return false; + // move node up + upperBound = newLayer + 1; + lowerBound = currentLayer + 1; + step = -1; // move all other nodes one step down } - } - } - else - { - // get the layer stack without the base node of the current renderer - RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, false); - if (!stackedLayers.empty()) - { - // get the current layer value of the given data node - int currentLayer; - bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); - if (wasFound && currentLayer != newLayer) + else { - // move the given data node to the specified layer - dataNode->SetIntProperty("layer", newLayer, renderer); - - int upperBound; - int lowerBound; - int step; - if (currentLayer < newLayer) - { - // move node up - upperBound = newLayer + 1; - lowerBound = currentLayer + 1; - step = -1; // move all other nodes one step down - } - else - { - upperBound = currentLayer; - lowerBound = newLayer; - step = 1; // move all other nodes one step up - } + upperBound = currentLayer; + lowerBound = newLayer; + step = 1; // move all other nodes one step up + } - // move all other data nodes between the upper and the lower bound - for (auto& layer : stackedLayers) + // move all other data nodes between the upper and the lower bound + for (auto& layer : stackedLayers) + { + if (layer.second != dataNode && layer.first < upperBound && layer.first >= lowerBound) { - if (layer.second != dataNode && layer.first < upperBound && layer.first >= lowerBound) - { - layer.second->SetIntProperty("layer", layer.first + step, renderer); - } - // else: current data node is the selected data node or - // was previously already above the selected data node or - // was previously already below the new layer position + layer.second->SetIntProperty("layer", layer.first + step, renderer); } - dataNode->Modified(); + // else: current data node is the selected data node or + // was previously already above the selected data node or + // was previously already below the new layer position + } + dataNode->Modified(); + if (nullptr == renderer) + { + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } + else + { mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); - return true; } - // else: data node has no layer information or is already at the specified position + + return true; } - // else: do not work with empty layer stack + // else: data node has no layer information or is already at the specified position } + // else: do not work with empty layer stack return false; } bool mitk::RenderWindowLayerController::MoveNodeToFront(DataNode* dataNode, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return false; } - if (nullptr == renderer) + // get the layer stack of the current renderer + RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer); + if (!stackedLayers.empty()) { - // move data node to front in all controlled renderer - for (const auto& renderer : m_ControlledRenderer) + // get the first value (highest int-key -> topmost layer) + int topmostLayer = stackedLayers.begin()->first; + // get the current layer value of the given data node + int currentLayer; + bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); + if (wasFound && currentLayer < topmostLayer) { - if (nullptr != renderer) + // move the current data node above the current topmost layer + dataNode->SetIntProperty("layer", topmostLayer+1, renderer); + dataNode->Modified(); + if (nullptr == renderer) { - MoveNodeToFront(dataNode, renderer); - // we don't store/need the returned boolean value - return false; + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } - } - } - else - { - // get the layer stack without the base node of the current renderer - RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, false); - if (!stackedLayers.empty()) - { - // get the first value (highest int-key -> topmost layer) - int topmostLayer = stackedLayers.begin()->first; - // get the current layer value of the given data node - int currentLayer; - bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); - if (wasFound && currentLayer < topmostLayer) + else { - // move the current data node above the current topmost layer - dataNode->SetIntProperty("layer", topmostLayer+1, renderer); - dataNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); - return true; } - // else: data node has no layer information or is already the topmost layer node + return true; } - // else: do not work with empty layer stack + // else: data node has no layer information or is already the topmost layer node } + // else: do not work with empty layer stack return false; } bool mitk::RenderWindowLayerController::MoveNodeToBack(DataNode* dataNode, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return false; } - if (nullptr == renderer) - { - // move data node to back in all controlled renderer - for (const auto& renderer : m_ControlledRenderer) - { - if (nullptr != renderer) - { - MoveNodeToBack(dataNode, renderer); - // we don't store/need the returned boolean value - return false; - } - } - } - else + // get the layer stack of the current renderer + RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer); + if (!stackedLayers.empty()) { - // get the layer stack without the base node of the current renderer - RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, false); - if (!stackedLayers.empty()) + // get the last value (lowest int-key) + int lowermostLayer = stackedLayers.rbegin()->first; + // get the current layer value of the given data node + int currentLayer; + bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); + if (wasFound && currentLayer > lowermostLayer) { - // get the last value (lowest int-key) - // cannot be the base layer as the base node was excluded by the 'GetLayerStack'-function - int lowermostLayer = stackedLayers.rbegin()->first; - // get the current layer value of the given data node - int currentLayer; - bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); - if (wasFound && currentLayer > lowermostLayer) + // move the current data node to the current lowermost layer + dataNode->SetIntProperty("layer", lowermostLayer, renderer); + // move all other data nodes one layer up + for (auto& layer : stackedLayers) { - // move the current data node to the current lowermost layer - dataNode->SetIntProperty("layer", lowermostLayer, renderer); - // move all other data nodes one layer up - for (auto& layer : stackedLayers) + if (layer.second != dataNode && layer.first < currentLayer) { - if (layer.second != dataNode && layer.first < currentLayer) - { - layer.second->SetIntProperty("layer", layer.first + 1, renderer); - } - // else: current data node is the selected data node or - // was previously already above the selected data node + layer.second->SetIntProperty("layer", layer.first + 1, renderer); } - dataNode->Modified(); + // else: current data node is the selected data node or + // was previously already above the selected data node + } + dataNode->Modified(); + if (nullptr == renderer) + { + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + + } + else + { mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); - return true; } - // else: data node has no layer information or is already the lowermost layer node + return true; } - // else: do not work with empty layer stack + // else: data node has no layer information or is already the lowermost layer node } + // else: do not work with empty layer stack return false; } bool mitk::RenderWindowLayerController::MoveNodeUp(DataNode* dataNode, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return false; } - if (nullptr == renderer) - { - // move data node down in all controlled renderer - for (const auto& renderer : m_ControlledRenderer) - { - if (nullptr != renderer) - { - MoveNodeUp(dataNode, renderer); - // we don't store/need the returned boolean value - return false; - } - } - } - else + // get the layer stack of the current renderer + RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer); + if (!stackedLayers.empty()) { - // get the layer stack without the base node of the current renderer - RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, false); - if (!stackedLayers.empty()) + // get the current layer value of the given data node + int currentLayer; + bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); + if (wasFound) { - // get the current layer value of the given data node - int currentLayer; - bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); - if (wasFound) + // get the current layer in the map of stacked layers + RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIterator = stackedLayers.find(currentLayer); + if (layerStackIterator != stackedLayers.end() && layerStackIterator != stackedLayers.begin()) { - // get the current layer in the map of stacked layers - RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIterator = stackedLayers.find(currentLayer); - if (layerStackIterator != stackedLayers.end() && layerStackIterator != stackedLayers.begin()) + // found the element in the map, at different position than 'begin' -> + // current node is not on the topmost layer and therefore can be moved one layer up + // swap the layers of the dataNode and the dataNode on the next higher layer (previous map element) + RenderWindowLayerUtilities::LayerStack::const_iterator prevLayerStackIterator = std::prev(layerStackIterator); + dataNode->SetIntProperty("layer", prevLayerStackIterator->first, renderer); + prevLayerStackIterator->second->SetIntProperty("layer", currentLayer, renderer); + dataNode->Modified(); + if (nullptr == renderer) + { + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + + } + else { - // found the element in the map, at different position than 'begin' -> - // current node is not on the topmost layer and therefore can be moved one layer up - // swap the layers of the dataNode and the dataNode on the next higher layer (previous map element) - RenderWindowLayerUtilities::LayerStack::const_iterator prevLayerStackIterator = std::prev(layerStackIterator); - dataNode->SetIntProperty("layer", prevLayerStackIterator->first, renderer); - prevLayerStackIterator->second->SetIntProperty("layer", currentLayer, renderer); - dataNode->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); - return true; } - // else: layer stack does not contain a layer with the 'currentLayer'data node or - // layer is already the topmost layer node + return true; } - // else: data node has no layer information + // else: layer stack does not contain a layer with the 'currentLayer'data node or + // layer is already the topmost layer node } - // else: do not work with empty layer stack + // else: data node has no layer information } + // else: do not work with empty layer stack return false; } bool mitk::RenderWindowLayerController::MoveNodeDown(DataNode* dataNode, const BaseRenderer* renderer /*= nullptr*/) { if (nullptr == dataNode) { return false; } - if (nullptr == renderer) - { - // move data node up in all controlled renderer - for (const auto& renderer : m_ControlledRenderer) - { - if (nullptr != renderer) - { - MoveNodeDown(dataNode, renderer); - // we don't store/need the returned boolean value - return false; - } - } - } - else + // get the layer stack of the current renderer + RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer); + if (!stackedLayers.empty()) { - // get the layer stack without the base node of the current renderer - RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, false); - if (!stackedLayers.empty()) + // get the current layer value of the given data node + int currentLayer; + bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); + if (wasFound) { - // get the current layer value of the given data node - int currentLayer; - bool wasFound = dataNode->GetIntProperty("layer", currentLayer, renderer); - if (wasFound) + // get the current layer in the map of stacked layers + RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIterator = stackedLayers.find(currentLayer); + if (layerStackIterator != stackedLayers.end()) { - // get the current layer in the map of stacked layers - RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIterator = stackedLayers.find(currentLayer); - if (layerStackIterator != stackedLayers.end()) + // found the element in the map ... + RenderWindowLayerUtilities::LayerStack::const_iterator nextLayerStackIterator = std::next(layerStackIterator); + if (nextLayerStackIterator != stackedLayers.end()) { - // found the element in the map ... - RenderWindowLayerUtilities::LayerStack::const_iterator nextLayerStackIterator = std::next(layerStackIterator); - if (nextLayerStackIterator != stackedLayers.end()) - { - // ... and found a successor -> - // current node is not on the lowermost layer and therefore can be moved one layer down - // swap the layers of the dataNode and the dataNode on the next lower layer (next map element) - dataNode->SetIntProperty("layer", nextLayerStackIterator->first, renderer); - nextLayerStackIterator->second->SetIntProperty("layer", currentLayer, renderer); - dataNode->Modified(); - mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); - return true; - } - // else: data node is already the lowermost layer node + // ... and found a successor -> + // current node is not on the lowermost layer and therefore can be moved one layer down + // swap the layers of the dataNode and the dataNode on the next lower layer (next map element) + dataNode->SetIntProperty("layer", nextLayerStackIterator->first, renderer); + nextLayerStackIterator->second->SetIntProperty("layer", currentLayer, renderer); + dataNode->Modified(); + mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); + return true; } - // else: layer stack does not contain a layer with the 'currentLayer' + // else: data node is already the lowermost layer node } - // else: data node has no layer information + // else: layer stack does not contain a layer with the 'currentLayer' } - // else: do not work with empty layer stack + // else: data node has no layer information } + // else: do not work with empty layer stack return false; } - -void mitk::RenderWindowLayerController::SetVisibilityOfDataNode(bool visibility, DataNode* dataNode, const BaseRenderer* renderer /*=nullptr*/) -{ - if (nullptr == dataNode) - { - return; - } - - if (nullptr == renderer) - { - // set visibility of data node in all controlled renderer - for (const auto& renderer : m_ControlledRenderer) - { - if (nullptr != renderer) - { - SetVisibilityOfDataNode(visibility, dataNode, renderer); - } - } - } - else - { - dataNode->SetVisibility(visibility, renderer); - dataNode->Modified(); - mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); - } -} - -void mitk::RenderWindowLayerController::HideDataNodeInAllRenderer(const DataNode* dataNode) -{ - if (nullptr == dataNode) - { - return; - } - - for (const auto& renderer : m_ControlledRenderer) - { - if (nullptr != renderer) - { - dataNode->GetPropertyList(renderer)->SetBoolProperty("visible", false); - } - } - - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); -} - -void mitk::RenderWindowLayerController::ResetRenderer(bool onlyVisibility /*= true*/, const BaseRenderer* renderer /*= nullptr*/) -{ - if (nullptr == renderer) - { - // reset all controlled renderer - for (const auto& renderer : m_ControlledRenderer) - { - if (nullptr != renderer) - { - ResetRenderer(onlyVisibility, renderer); - } - } - } - else - { - // get the layer stack with the base node of the current renderer - RenderWindowLayerUtilities::LayerStack stackedLayers = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage, renderer, true); - if (!stackedLayers.empty()) - { - for (const auto& layer : stackedLayers) - { - int layerLevel; - layer.second->GetIntProperty("layer", layerLevel, renderer); - if (RenderWindowLayerUtilities::BASE_LAYER_INDEX == layerLevel) - { - // set base data node visibility to true - layer.second->SetVisibility(true, renderer); - } - else - { - // set visibility of all other data nodes to false - layer.second->SetVisibility(false, renderer); - - // modify layer node - if (!onlyVisibility) - { - // clear mode: additionally remove layer node from current renderer - layer.second->GetPropertyList(renderer)->DeleteProperty("layer"); - layer.second->SetBoolProperty("fixedLayer", false, renderer); - } - } - layer.second->Modified(); - } - - mitk::RenderingManager::GetInstance()->RequestUpdate(renderer->GetRenderWindow()); - } - } -} diff --git a/Modules/QtWidgets/src/mitkRenderWindowLayerUtilities.cpp b/Modules/QtWidgets/src/mitkRenderWindowLayerUtilities.cpp index e2e8c375ba..50e8866c3f 100644 --- a/Modules/QtWidgets/src/mitkRenderWindowLayerUtilities.cpp +++ b/Modules/QtWidgets/src/mitkRenderWindowLayerUtilities.cpp @@ -1,82 +1,125 @@ /*============================================================================ 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. ============================================================================*/ // render window manager module #include "mitkRenderWindowLayerUtilities.h" // mitk core #include -mitk::RenderWindowLayerUtilities::LayerStack mitk::RenderWindowLayerUtilities::GetLayerStack(const DataStorage* dataStorage, const BaseRenderer* renderer, bool withBaseNode) +mitk::RenderWindowLayerUtilities::LayerStack mitk::RenderWindowLayerUtilities::GetLayerStack(const DataStorage* dataStorage, const BaseRenderer* renderer) { LayerStack stackedLayers; - if (nullptr == dataStorage || nullptr == renderer) + if (nullptr == dataStorage) { - // no nodes to stack or no renderer selected + // no nodes to stack return stackedLayers; } int layer = -1; - NodePredicateBase::Pointer fixedLayerPredicate = GetRenderWindowPredicate(renderer); - DataStorage::SetOfObjects::ConstPointer filteredDataNodes = dataStorage->GetSubset(fixedLayerPredicate); - for (DataStorage::SetOfObjects::ConstIterator it = filteredDataNodes->Begin(); it != filteredDataNodes->End(); ++it) + + auto allDataNodes = dataStorage->GetAll(); + for (DataStorage::SetOfObjects::ConstIterator it = allDataNodes->Begin(); it != allDataNodes->End(); ++it) { DataNode::Pointer dataNode = it->Value(); if (dataNode.IsNull()) { continue; } bool layerFound = dataNode->GetIntProperty("layer", layer, renderer); if (layerFound) { - if (BASE_LAYER_INDEX != layer|| withBaseNode) - { - // data node is not on the base layer or the base layer should be included anyway - stackedLayers.insert(std::make_pair(layer, dataNode)); - } + stackedLayers.insert(std::make_pair(layer, dataNode)); } } return stackedLayers; } -mitk::NodePredicateBase::Pointer mitk::RenderWindowLayerUtilities::GetRenderWindowPredicate(const BaseRenderer* renderer) -{ - NodePredicateBase::Pointer fixedLayerPredicate = - NodePredicateProperty::New("fixedLayer", BoolProperty::New(true), renderer); - - return fixedLayerPredicate; -} - void mitk::RenderWindowLayerUtilities::SetRenderWindowProperties(mitk::DataNode* dataNode, const BaseRenderer* renderer) { - dataNode->SetBoolProperty("fixedLayer", true, renderer); // use visibility of existing renderer or common renderer // common renderer is used if renderer-specific property does not exist bool visible = false; bool visibilityProperty = dataNode->GetVisibility(visible, renderer); if (true == visibilityProperty) { // found a visibility property dataNode->SetVisibility(visible, renderer); } // use layer of existing renderer or common renderer // common renderer is used if renderer-specific property does not exist int layer = -1; bool layerProperty = dataNode->GetIntProperty("layer", layer, renderer); if (true == layerProperty) { // found a layer property dataNode->SetIntProperty("layer", layer, renderer); } } + +void mitk::RenderWindowLayerUtilities::DeleteRenderWindowProperties(mitk::DataNode* dataNode, const BaseRenderer* renderer) +{ + if (nullptr == renderer) + { + MITK_ERROR << "Cannot remove general properties. Please provide a specific base renderer."; + return; + } + + try + { + dataNode->RemoveProperty("visible", renderer->GetName()); + } + catch (mitk::Exception& e) + { + MITK_DEBUG << "Exception caught: " << e.GetDescription() << " Nothing to remove."; + } + + try + { + dataNode->RemoveProperty("layer", renderer->GetName()); + } + catch (mitk::Exception& e) + { + MITK_ERROR << "Exception caught: " << e.GetDescription() << " Nothing to remove."; + } +} + +void mitk::RenderWindowLayerUtilities::TransferRenderWindowProperties(DataNode* dataNode, const BaseRenderer* newRenderer, const BaseRenderer* oldRenderer) +{ + if (nullptr == newRenderer) + { + MITK_ERROR << "Cannot transfer properties. Please provide a specific base renderer for the new renderer-specific properties."; + return; + } + + // use visibility of existing renderer or common renderer + // common renderer is used if renderer-specific property does not exist + bool visible = false; + bool visibilityProperty = dataNode->GetVisibility(visible, oldRenderer); + if (true == visibilityProperty) + { + // found a visibility property + dataNode->SetVisibility(visible, newRenderer); + } + + // use layer of existing renderer or common renderer + // common renderer is used if renderer-specific property does not exist + int layer = -1; + bool layerProperty = dataNode->GetIntProperty("layer", layer, oldRenderer); + if (true == layerProperty) + { + // found a layer property + dataNode->SetIntProperty("layer", layer, newRenderer); + } +} diff --git a/Modules/QtWidgets/src/mitkRenderWindowViewDirectionController.cpp b/Modules/QtWidgets/src/mitkRenderWindowViewDirectionController.cpp index 7410b0ec9d..21a70620e6 100644 --- a/Modules/QtWidgets/src/mitkRenderWindowViewDirectionController.cpp +++ b/Modules/QtWidgets/src/mitkRenderWindowViewDirectionController.cpp @@ -1,137 +1,134 @@ /*============================================================================ 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 "mitkRenderWindowViewDirectionController.h" // mitk core #include #include #include mitk::RenderWindowViewDirectionController::RenderWindowViewDirectionController() : m_DataStorage(nullptr) { // nothing here } void mitk::RenderWindowViewDirectionController::SetDataStorage(DataStorage::Pointer dataStorage) { if (m_DataStorage != dataStorage) { // set the new data storage m_DataStorage = dataStorage; } } void mitk::RenderWindowViewDirectionController::SetControlledRenderer(RenderWindowLayerUtilities::RendererVector controlledRenderer) { if (m_ControlledRenderer != controlledRenderer) { // set the new set of controlled renderer m_ControlledRenderer = controlledRenderer; } } void mitk::RenderWindowViewDirectionController::SetViewDirectionOfRenderer(const std::string& viewDirection, BaseRenderer* renderer/* =nullptr*/) { if (nullptr == renderer) { // set visibility of data node in all controlled renderer for (auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { SetViewDirectionOfRenderer(viewDirection, renderer); } } } else { mitk::SliceNavigationController* sliceNavigationController = renderer->GetSliceNavigationController(); if ("axial" == viewDirection) { sliceNavigationController->SetDefaultViewDirection(AnatomicalPlane::Axial); } else if ("coronal" == viewDirection) { sliceNavigationController->SetDefaultViewDirection(AnatomicalPlane::Coronal); } else if ("sagittal" == viewDirection) { sliceNavigationController->SetDefaultViewDirection(AnatomicalPlane::Sagittal); } if ("3D" == viewDirection) { renderer->SetMapperID(mitk::BaseRenderer::Standard3D); } else { renderer->SetMapperID(mitk::BaseRenderer::Standard2D); } // initialize the views to the bounding geometry InitializeViewByBoundingObjects(renderer); } } void mitk::RenderWindowViewDirectionController::SetViewDirectionOfRenderer(AnatomicalPlane viewDirection , BaseRenderer* renderer/* =nullptr*/) { if (nullptr == renderer) { // set visibility of data node in all controlled renderer for (auto& renderer : m_ControlledRenderer) { if (nullptr != renderer) { SetViewDirectionOfRenderer(viewDirection, renderer); } } } else { mitk::SliceNavigationController* sliceNavigationController = renderer->GetSliceNavigationController(); sliceNavigationController->SetDefaultViewDirection(viewDirection); // initialize the views to the bounding geometry InitializeViewByBoundingObjects(renderer); } } void mitk::RenderWindowViewDirectionController::InitializeViewByBoundingObjects(const BaseRenderer* renderer) { if (nullptr == m_DataStorage || nullptr == renderer) { return; } // get all nodes that have not set "includeInBoundingBox" to false mitk::NodePredicateProperty::Pointer includeInBoundingBox = mitk::NodePredicateProperty::New("includeInBoundingBox", mitk::BoolProperty::New(false)); mitk::NodePredicateNot::Pointer notIncludeInBoundingBox = mitk::NodePredicateNot::New(includeInBoundingBox); // get all non-helper objects mitk::NodePredicateProperty::Pointer helperObject = mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer notAHelperObject = mitk::NodePredicateNot::New(helperObject); - // get all nodes that have a fixed layer for the given renderer - mitk::NodePredicateProperty::Pointer fixedLayer = mitk::NodePredicateProperty::New("fixedLayer", mitk::BoolProperty::New(true), renderer); - // combine node predicates - mitk::NodePredicateAnd::Pointer combinedNodePredicate = mitk::NodePredicateAnd::New(notIncludeInBoundingBox, notAHelperObject, fixedLayer); + mitk::NodePredicateAnd::Pointer combinedNodePredicate = mitk::NodePredicateAnd::New(notIncludeInBoundingBox, notAHelperObject); mitk::DataStorage::SetOfObjects::ConstPointer filteredDataNodes = m_DataStorage->GetSubset(combinedNodePredicate); // calculate bounding geometry of these nodes auto bounds = m_DataStorage->ComputeBoundingGeometry3D(filteredDataNodes, "visible", renderer); // initialize the views to the bounding geometry mitk::RenderingManager::GetInstance()->InitializeView(renderer->GetRenderWindow(), bounds); } diff --git a/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageInspector.h b/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageInspector.h index 201f8cf854..ee454680c7 100644 --- a/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageInspector.h +++ b/Modules/RenderWindowManagerUI/include/QmitkRenderWindowDataStorageInspector.h @@ -1,92 +1,88 @@ /*============================================================================ 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 QmitkRenderWindowDataStorageInspector_h #define QmitkRenderWindowDataStorageInspector_h // render window manager UI module #include "MitkRenderWindowManagerUIExports.h" #include "ui_QmitkRenderWindowDataStorageInspector.h" // render window manager module #include #include #include // qt widgets module #include /** * The 'QmitkRenderWindowDataStorageInspector' offers a GUI to manipulate the base renderer / render windows of the MITK workbench. * * In order to use this widget, a (e.g.) plugin has to set the controlled renderer, which will be forwarded to * a render window view direction controller. */ class MITKRENDERWINDOWMANAGERUI_EXPORT QmitkRenderWindowDataStorageInspector : public QmitkAbstractDataStorageInspector { Q_OBJECT public: QmitkRenderWindowDataStorageInspector(QWidget* parent = nullptr); // override from 'QmitkAbstractDataStorageInspector' /** * @brief See 'QmitkAbstractDataStorageInspector' */ QAbstractItemView* GetView() override; /** * @brief See 'QmitkAbstractDataStorageInspector' */ const QAbstractItemView* GetView() const override; /** * @brief See 'QmitkAbstractDataStorageInspector' */ void SetSelectionMode(SelectionMode mode) override; /** * @brief See 'QmitkAbstractDataStorageInspector' */ SelectionMode GetSelectionMode() const override; /** * @brief Set the controlled base renderer. */ void SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer); /** * @brief Set the currently selected render window. * * @param renderWindowId the text inside the combo box */ void SetActiveRenderWindow(const QString& renderWindowId); private Q_SLOTS: void ModelRowsInserted(const QModelIndex& parent, int start, int end); - void SetAsBaseLayer(); - - void ResetRenderer(); - void ChangeViewDirection(const QString& viewDirection); private: void Initialize() override; void SetUpConnections(); Ui::QmitkRenderWindowDataStorageInspector m_Controls; std::unique_ptr m_StorageModel; std::unique_ptr m_RenderWindowLayerController; std::unique_ptr m_RenderWindowViewDirectionController; }; #endif diff --git a/Modules/RenderWindowManagerUI/src/QmitkDataStorageLayerStackModel.cpp b/Modules/RenderWindowManagerUI/src/QmitkDataStorageLayerStackModel.cpp index 59232e7a5a..441c29e276 100644 --- a/Modules/RenderWindowManagerUI/src/QmitkDataStorageLayerStackModel.cpp +++ b/Modules/RenderWindowManagerUI/src/QmitkDataStorageLayerStackModel.cpp @@ -1,234 +1,234 @@ /*============================================================================ 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 // qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" QmitkDataStorageLayerStackModel::QmitkDataStorageLayerStackModel(QObject* parent/* = nullptr*/) : QmitkAbstractDataStorageModel(parent) { // nothing here } void QmitkDataStorageLayerStackModel::DataStorageChanged() { UpdateModelData(); } void QmitkDataStorageLayerStackModel::NodePredicateChanged() { UpdateModelData(); } void QmitkDataStorageLayerStackModel::NodeAdded(const mitk::DataNode* /*node*/) { // nothing here; layers (nodes) are only added after button click } void QmitkDataStorageLayerStackModel::NodeChanged(const mitk::DataNode* /*node*/) { UpdateModelData(); } void QmitkDataStorageLayerStackModel::NodeRemoved(const mitk::DataNode* /*node*/) { UpdateModelData(); } void QmitkDataStorageLayerStackModel::SetCurrentRenderer(const std::string& renderWindowName) { if (!m_DataStorage.IsExpired()) { m_BaseRenderer = mitk::BaseRenderer::GetByName(renderWindowName); UpdateModelData(); } } mitk::BaseRenderer* QmitkDataStorageLayerStackModel::GetCurrentRenderer() const { return m_BaseRenderer.Lock(); } QModelIndex QmitkDataStorageLayerStackModel::index(int row, int column, const QModelIndex& parent) const { bool hasIndex = this->hasIndex(row, column, parent); if (hasIndex) { return this->createIndex(row, column); } return QModelIndex(); } QModelIndex QmitkDataStorageLayerStackModel::parent(const QModelIndex& /*child*/) const { return QModelIndex(); } Qt::ItemFlags QmitkDataStorageLayerStackModel::flags(const QModelIndex& index) const { Qt::ItemFlags flags = Qt::NoItemFlags; if (index.isValid() && index.model() == this) { flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable; if (0 == index.column()) { flags |= Qt::ItemIsUserCheckable; } } return flags; } QVariant QmitkDataStorageLayerStackModel::headerData(int section, Qt::Orientation orientation, int role) const { if (Qt::Horizontal == orientation && Qt::DisplayRole == role) { if (0 == section) { return QVariant("Visibility"); } else if (1 == section) { return QVariant("Data node"); } } return QVariant(); } int QmitkDataStorageLayerStackModel::rowCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return static_cast(m_TempLayerStack.size()); } int QmitkDataStorageLayerStackModel::columnCount(const QModelIndex& parent/* = QModelIndex()*/) const { if (parent.isValid()) { return 0; } return 2; } QVariant QmitkDataStorageLayerStackModel::data(const QModelIndex& index, int role) const { if (!index.isValid() || index.model() != this) { return QVariant(); } if ((index.row()) < 0 || index.row() >= static_cast(m_TempLayerStack.size())) { return QVariant(); } RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIt = m_TempLayerStack.begin(); std::advance(layerStackIt, index.row()); mitk::DataNode* dataNode = layerStackIt->second; if (Qt::CheckStateRole == role && 0 == index.column()) { bool visibility = false; dataNode->GetVisibility(visibility, m_BaseRenderer.Lock()); if (visibility) { return Qt::Checked; } else { return Qt::Unchecked; } } else if (Qt::DisplayRole == role && 1 == index.column()) { return QVariant(QString::fromStdString(dataNode->GetName())); } else if (Qt::ToolTipRole == role) { if (0 == index.column()) { return QVariant("Show/hide data node."); } else if (1 == index.column()) { return QVariant("Name of the data node."); } } else if (QmitkDataNodeRole == role) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } else if (QmitkDataNodeRawPointerRole == role) { return QVariant::fromValue(dataNode); } return QVariant(); } bool QmitkDataStorageLayerStackModel::setData(const QModelIndex &index, const QVariant &value, int role /*= Qt::EditRole*/) { if (!index.isValid() || index.model() != this) { return false; } if ((index.row()) < 0 || index.row() >= static_cast(m_TempLayerStack.size())) { return false; } auto baseRenderer = m_BaseRenderer.Lock(); if (baseRenderer.IsNotNull()) { RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIt = m_TempLayerStack.begin(); std::advance(layerStackIt, index.row()); mitk::DataNode* dataNode = layerStackIt->second; if (Qt::CheckStateRole == role) { Qt::CheckState newCheckState = static_cast(value.toInt()); if (Qt::PartiallyChecked == newCheckState || Qt::Checked == newCheckState) { dataNode->SetVisibility(true, baseRenderer); } else { dataNode->SetVisibility(false, baseRenderer); } emit dataChanged(index, index); mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); return true; } } return false; } void QmitkDataStorageLayerStackModel::UpdateModelData() { // update the model, so that the table will be filled with the nodes according to the current // data storage and base renderer beginResetModel(); // get the current layer stack of the given base renderer - m_TempLayerStack = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage.Lock(), m_BaseRenderer.Lock(), true); + m_TempLayerStack = RenderWindowLayerUtilities::GetLayerStack(m_DataStorage.Lock(), m_BaseRenderer.Lock()); endResetModel(); } diff --git a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.cpp b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.cpp index 8094567bc2..f489241706 100644 --- a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.cpp +++ b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.cpp @@ -1,167 +1,142 @@ /*============================================================================ 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. ============================================================================*/ // render window manager UI module #include "QmitkRenderWindowDataStorageInspector.h" #include "QmitkCustomVariants.h" // mitk core #include // qt #include QmitkRenderWindowDataStorageInspector::QmitkRenderWindowDataStorageInspector(QWidget* parent /*=nullptr*/) : QmitkAbstractDataStorageInspector(parent) { m_Controls.setupUi(this); // initialize the render window layer controller and the render window view direction controller m_RenderWindowLayerController = std::make_unique(); m_RenderWindowViewDirectionController = std::make_unique(); m_StorageModel = std::make_unique(this); m_Controls.renderWindowTreeView->setModel(m_StorageModel.get()); m_Controls.renderWindowTreeView->setHeaderHidden(true); m_Controls.renderWindowTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers); m_Controls.renderWindowTreeView->setSelectionBehavior(QAbstractItemView::SelectRows); m_Controls.renderWindowTreeView->setSelectionMode(QAbstractItemView::ExtendedSelection); m_Controls.renderWindowTreeView->setAlternatingRowColors(true); m_Controls.renderWindowTreeView->setDragEnabled(true); m_Controls.renderWindowTreeView->setDropIndicatorShown(true); m_Controls.renderWindowTreeView->setAcceptDrops(true); m_Controls.renderWindowTreeView->setContextMenuPolicy(Qt::CustomContextMenu); SetUpConnections(); } QAbstractItemView* QmitkRenderWindowDataStorageInspector::GetView() { return m_Controls.renderWindowTreeView; } const QAbstractItemView* QmitkRenderWindowDataStorageInspector::GetView() const { return m_Controls.renderWindowTreeView; } void QmitkRenderWindowDataStorageInspector::SetSelectionMode(SelectionMode mode) { m_Controls.renderWindowTreeView->setSelectionMode(mode); } QmitkRenderWindowDataStorageInspector::SelectionMode QmitkRenderWindowDataStorageInspector::GetSelectionMode() const { return m_Controls.renderWindowTreeView->selectionMode(); } void QmitkRenderWindowDataStorageInspector::Initialize() { auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNull()) return; m_StorageModel->SetDataStorage(dataStorage); m_StorageModel->SetNodePredicate(m_NodePredicate); m_RenderWindowLayerController->SetDataStorage(dataStorage); m_RenderWindowViewDirectionController->SetDataStorage(dataStorage); m_Connector->SetView(m_Controls.renderWindowTreeView); } void QmitkRenderWindowDataStorageInspector::SetUpConnections() { connect(m_StorageModel.get(), &QAbstractItemModel::rowsInserted, this, &QmitkRenderWindowDataStorageInspector::ModelRowsInserted); - connect(m_Controls.pushButtonSetAsBaseLayer, &QPushButton::clicked, this, &QmitkRenderWindowDataStorageInspector::SetAsBaseLayer); - connect(m_Controls.pushButtonResetRenderer, &QPushButton::clicked, this, &QmitkRenderWindowDataStorageInspector::ResetRenderer); - QSignalMapper* changeViewDirectionSignalMapper = new QSignalMapper(this); changeViewDirectionSignalMapper->setMapping(m_Controls.radioButtonAxial, QString("axial")); changeViewDirectionSignalMapper->setMapping(m_Controls.radioButtonCoronal, QString("coronal")); changeViewDirectionSignalMapper->setMapping(m_Controls.radioButtonSagittal, QString("sagittal")); changeViewDirectionSignalMapper->setMapping(m_Controls.radioButton3D, QString("3D")); connect(changeViewDirectionSignalMapper, static_cast(&QSignalMapper::mapped), this, &QmitkRenderWindowDataStorageInspector::ChangeViewDirection); - connect(m_Controls.radioButtonAxial, &QPushButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); - connect(m_Controls.radioButtonCoronal, &QPushButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); - connect(m_Controls.radioButtonSagittal, &QPushButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); - connect(m_Controls.radioButton3D, &QPushButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); + connect(m_Controls.radioButtonAxial, &QRadioButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); + connect(m_Controls.radioButtonCoronal, &QRadioButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); + connect(m_Controls.radioButtonSagittal, &QRadioButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); + connect(m_Controls.radioButton3D, &QRadioButton::clicked, changeViewDirectionSignalMapper, static_cast(&QSignalMapper::map)); } void QmitkRenderWindowDataStorageInspector::SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer) { m_StorageModel->SetControlledRenderer(controlledRenderer); - m_RenderWindowLayerController->SetControlledRenderer(controlledRenderer); m_RenderWindowViewDirectionController->SetControlledRenderer(controlledRenderer); } void QmitkRenderWindowDataStorageInspector::SetActiveRenderWindow(const QString& renderWindowId) { mitk::BaseRenderer* selectedRenderer = mitk::BaseRenderer::GetByName(renderWindowId.toStdString()); if (nullptr == selectedRenderer) { return; } m_StorageModel->SetCurrentRenderer(selectedRenderer); mitk::AnatomicalPlane viewDirection = selectedRenderer->GetSliceNavigationController()->GetDefaultViewDirection(); switch (viewDirection) { case mitk::AnatomicalPlane::Axial: m_Controls.radioButtonAxial->setChecked(true); break; case mitk::AnatomicalPlane::Coronal: m_Controls.radioButtonCoronal->setChecked(true); break; case mitk::AnatomicalPlane::Sagittal: m_Controls.radioButtonSagittal->setChecked(true); break; default: break; } } void QmitkRenderWindowDataStorageInspector::ModelRowsInserted(const QModelIndex& parent, int /*start*/, int /*end*/) { m_Controls.renderWindowTreeView->setExpanded(parent, true); } -void QmitkRenderWindowDataStorageInspector::SetAsBaseLayer() -{ - QModelIndex selectedIndex = m_Controls.renderWindowTreeView->currentIndex(); - if (selectedIndex.isValid()) - { - QVariant qvariantDataNode = m_StorageModel->data(selectedIndex, Qt::UserRole); - if (qvariantDataNode.canConvert()) - { - mitk::DataNode* dataNode = qvariantDataNode.value(); - m_RenderWindowLayerController->SetBaseDataNode(dataNode, m_StorageModel->GetCurrentRenderer()); - m_Controls.renderWindowTreeView->clearSelection(); - } - } -} - -void QmitkRenderWindowDataStorageInspector::ResetRenderer() -{ - m_RenderWindowLayerController->ResetRenderer(true, m_StorageModel->GetCurrentRenderer()); - m_Controls.renderWindowTreeView->clearSelection(); -} - void QmitkRenderWindowDataStorageInspector::ChangeViewDirection(const QString& viewDirection) { m_RenderWindowViewDirectionController->SetViewDirectionOfRenderer(viewDirection.toStdString(), m_StorageModel->GetCurrentRenderer()); } diff --git a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.ui b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.ui index 785e20c50c..0ad31773e2 100644 --- a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.ui +++ b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageInspector.ui @@ -1,92 +1,78 @@ QmitkRenderWindowDataStorageInspector 0 0 340 246 0 0 0 0 Render window manager Render window overview - - - - Reset render window - - - - - - - Set as base layer - - - - + Axial true - + Coronal - + Sagittal - + 3D diff --git a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageListModel.cpp b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageListModel.cpp index 9b6face241..efba98a992 100644 --- a/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageListModel.cpp +++ b/Modules/RenderWindowManagerUI/src/QmitkRenderWindowDataStorageListModel.cpp @@ -1,351 +1,350 @@ /*============================================================================ 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. ============================================================================*/ // render window manager UI module #include "QmitkRenderWindowDataStorageListModel.h" // qt widgets module #include "QmitkCustomVariants.h" #include "QmitkEnums.h" #include "QmitkMimeTypes.h" #include "QmitkNodeDescriptorManager.h" QmitkRenderWindowDataStorageListModel::QmitkRenderWindowDataStorageListModel(QObject* parent /*= nullptr*/) : QmitkAbstractDataStorageModel(parent) { m_RenderWindowLayerController = std::make_unique(); } void QmitkRenderWindowDataStorageListModel::DataStorageChanged() { m_RenderWindowLayerController->SetDataStorage(m_DataStorage.Lock()); UpdateModelData(); } void QmitkRenderWindowDataStorageListModel::NodePredicateChanged() { UpdateModelData(); } void QmitkRenderWindowDataStorageListModel::NodeAdded(const mitk::DataNode* node) { // add a node to each render window specific list (or to a global list initially) AddDataNodeToAllRenderer(const_cast(node)); UpdateModelData(); } void QmitkRenderWindowDataStorageListModel::NodeChanged(const mitk::DataNode* /*node*/) { // nothing here, since the "'NodeChanged'-event is currently sent far too often } void QmitkRenderWindowDataStorageListModel::NodeRemoved(const mitk::DataNode* /*node*/) { // update model data to create a new list without the removed data node UpdateModelData(); } QModelIndex QmitkRenderWindowDataStorageListModel::index(int row, int column, const QModelIndex& parent) const { bool hasIndex = this->hasIndex(row, column, parent); if (hasIndex) { return this->createIndex(row, column); } return QModelIndex(); } QModelIndex QmitkRenderWindowDataStorageListModel::parent(const QModelIndex& /*child*/) const { return QModelIndex(); } int QmitkRenderWindowDataStorageListModel::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const { if (parent.isValid()) { return 0; } return static_cast(m_LayerStack.size()); } int QmitkRenderWindowDataStorageListModel::columnCount(const QModelIndex& parent /*= QModelIndex()*/) const { if (parent.isValid()) { return 0; } return 1; } QVariant QmitkRenderWindowDataStorageListModel::data(const QModelIndex& index, int role) const { auto baseRenderer = m_BaseRenderer.Lock(); if (baseRenderer.IsNull()) { return QVariant(); } if (!index.isValid() || this != index.model()) { return QVariant(); } if (index.row() < 0 || index.row() >= static_cast(m_LayerStack.size())) { return QVariant(); } mitk::RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIt = m_LayerStack.begin(); std::advance(layerStackIt, index.row()); mitk::DataNode* dataNode = layerStackIt->second; if (Qt::CheckStateRole == role) { bool visibility = false; dataNode->GetVisibility(visibility, baseRenderer); if (visibility) { return Qt::Checked; } else { return Qt::Unchecked; } } else if (Qt::DisplayRole == role) { return QVariant(QString::fromStdString(dataNode->GetName())); } else if (Qt::ToolTipRole == role) { return QVariant("Name of the data node."); } else if (Qt::DecorationRole == role) { QmitkNodeDescriptor* nodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(dataNode); return nodeDescriptor->GetIcon(dataNode); } else if (Qt::UserRole == role || QmitkDataNodeRawPointerRole == role) { // user role always returns a reference to the data node, // which can be used to modify the data node in the data storage return QVariant::fromValue(dataNode); } else if (QmitkDataNodeRole == role) { return QVariant::fromValue(mitk::DataNode::Pointer(dataNode)); } return QVariant(); } bool QmitkRenderWindowDataStorageListModel::setData(const QModelIndex& index, const QVariant& value, int role /*= Qt::EditRole*/) { auto baseRenderer = m_BaseRenderer.Lock(); if (baseRenderer.IsNull()) { return false; } if (!index.isValid() || this != index.model()) { return false; } if (index.row() < 0 || index.row() >= static_cast(m_LayerStack.size())) { return false; } mitk::RenderWindowLayerUtilities::LayerStack::const_iterator layerStackIt = m_LayerStack.begin(); std::advance(layerStackIt, index.row()); mitk::DataNode* dataNode = layerStackIt->second; if (Qt::CheckStateRole == role) { Qt::CheckState newCheckState = static_cast(value.toInt()); bool isVisible = newCheckState; dataNode->SetVisibility(isVisible, baseRenderer); emit dataChanged(index, index); mitk::RenderingManager::GetInstance()->RequestUpdate(baseRenderer->GetRenderWindow()); return true; } return false; } Qt::ItemFlags QmitkRenderWindowDataStorageListModel::flags(const QModelIndex &index) const { if (this != index.model()) { return Qt::NoItemFlags; } if (!index.isValid()) { return Qt::ItemIsDropEnabled; } return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; } Qt::DropActions QmitkRenderWindowDataStorageListModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } Qt::DropActions QmitkRenderWindowDataStorageListModel::supportedDragActions() const { return Qt::CopyAction | Qt::MoveAction; } QStringList QmitkRenderWindowDataStorageListModel::mimeTypes() const { QStringList types = QAbstractItemModel::mimeTypes(); types << QmitkMimeTypes::DataNodePtrs; return types; } QMimeData* QmitkRenderWindowDataStorageListModel::mimeData(const QModelIndexList& indexes) const { QMimeData* mimeData = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); for (const auto& index : indexes) { if (index.isValid()) { auto dataNode = data(index, QmitkDataNodeRawPointerRole).value(); stream << reinterpret_cast(dataNode); } } mimeData->setData(QmitkMimeTypes::DataNodePtrs, encodedData); return mimeData; } bool QmitkRenderWindowDataStorageListModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int /*row*/, int column, const QModelIndex& parent) { auto baseRenderer = m_BaseRenderer.Lock(); if (baseRenderer.IsNull()) { return false; } if (action == Qt::IgnoreAction) { return true; } if (!data->hasFormat(QmitkMimeTypes::DataNodePtrs)) { return false; } if (column > 0) { return false; } if (parent.isValid()) { int layer = -1; auto dataNode = this->data(parent, QmitkDataNodeRawPointerRole).value(); if (nullptr != dataNode) { dataNode->GetIntProperty("layer", layer, baseRenderer); } auto dataNodeList = QmitkMimeTypes::ToDataNodePtrList(data); for (const auto& dataNode : qAsConst(dataNodeList)) { m_RenderWindowLayerController->MoveNodeToPosition(dataNode, layer, baseRenderer); } UpdateModelData(); return true; } return false; } -void QmitkRenderWindowDataStorageListModel::SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector controlledRenderer) +void QmitkRenderWindowDataStorageListModel::SetControlledRenderer(mitk::RenderWindowLayerUtilities::RendererVector /*controlledRenderer*/) { - m_RenderWindowLayerController->SetControlledRenderer(controlledRenderer); auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNotNull()) { mitk::DataStorage::SetOfObjects::ConstPointer allDataNodes = dataStorage->GetAll(); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allDataNodes->Begin(); it != allDataNodes->End(); ++it) { mitk::DataNode::Pointer dataNode = it->Value(); if (dataNode.IsNull()) { continue; } AddDataNodeToAllRenderer(dataNode); } } } void QmitkRenderWindowDataStorageListModel::SetCurrentRenderer(mitk::BaseRenderer* baseRenderer) { if (m_BaseRenderer == baseRenderer) { return; } m_BaseRenderer = baseRenderer; if (!m_BaseRenderer.IsExpired()) { UpdateModelData(); } } mitk::BaseRenderer::Pointer QmitkRenderWindowDataStorageListModel::GetCurrentRenderer() const { return m_BaseRenderer.Lock(); } void QmitkRenderWindowDataStorageListModel::AddDataNodeToAllRenderer(mitk::DataNode* dataNode) { m_RenderWindowLayerController->InsertLayerNode(dataNode); } void QmitkRenderWindowDataStorageListModel::UpdateModelData() { auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNotNull()) { auto baseRenderer = m_BaseRenderer.Lock(); if (baseRenderer.IsNotNull()) { // update the model, so that it will be filled with the nodes of the new data storage beginResetModel(); // get the current layer stack of the given base renderer - m_LayerStack = mitk::RenderWindowLayerUtilities::GetLayerStack(dataStorage, baseRenderer, true); + m_LayerStack = mitk::RenderWindowLayerUtilities::GetLayerStack(dataStorage, baseRenderer); endResetModel(); } } } diff --git a/Modules/SegmentationUI/resources/SegmentationUI.qrc b/Modules/SegmentationUI/resources/SegmentationUI.qrc index 02157a1290..c3ac5072c8 100644 --- a/Modules/SegmentationUI/resources/SegmentationUI.qrc +++ b/Modules/SegmentationUI/resources/SegmentationUI.qrc @@ -1,33 +1,29 @@ BooleanDifference_48x48.png BooleanIntersection_48x48.png BooleanUnion_48x48.png BooleanLabelA_32x32.png BooleanLabelB_32x32.png Dilate_48x48.png Erode_48x48.png Closing_48x48.png Opening_48x48.png FillHoles_48x48.png DeleteLayer_48x48.png PreviousLayer_48x48.png NextLayer_48x48.png AddLayer_48x48.png LockExterior_48x48.png UnlockExterior_48x48.png NewLabel_48x48.png NewSegmentation_48x48.png - visible.svg - invisible.svg - lock.svg - unlock.svg MergeLabels.png RemoveLabel.png EraseLabel.png CreateSurface.png CreateMask.png RandomColor.png RenameLabel.png diff --git a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.cpp b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.cpp index 3156db0e06..73ac1edd38 100644 --- a/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.cpp +++ b/Plugins/org.mitk.gui.qt.semanticrelations/src/internal/QmitkDataSetOpenInAction.cpp @@ -1,160 +1,156 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // semantic relations plugin #include "QmitkDataSetOpenInAction.h" // mitk core #include #include // render window manager module #include #include // semantic relations module #include "mitkNodePredicates.h" // mitk gui qt application plugin #include // qt #include #include namespace OpenInAction { void Run(mitk::DataStorage::Pointer dataStorage, mitk::DataNode::Pointer imageNode, mitk::BaseRenderer* renderer /*= nullptr*/) { if (dataStorage.IsNull()) { return; } if (imageNode.IsNull()) { return; } auto renderWindowLayerController = std::make_unique(); renderWindowLayerController->SetDataStorage(dataStorage); - // get currently fixed layer nodes of the specified renderer + // get current layer stack of the specified renderer // remove them from the specified renderer - auto layerStack = mitk::RenderWindowLayerUtilities::GetLayerStack(dataStorage, renderer, true); - for (auto& fixedLayer : layerStack) + auto layerStack = mitk::RenderWindowLayerUtilities::GetLayerStack(dataStorage, renderer); + for (auto& layerNode : layerStack) { // hide all nodes of the specified renderer - fixedLayer.second->SetVisibility(false, renderer); - fixedLayer.second->Modified(); + layerNode.second->SetVisibility(false, renderer); + layerNode.second->Modified(); } - // add the selected data node to the specified renderer - // only needed in case the render window manager plugin is not used - imageNode->SetBoolProperty("fixedLayer", true, renderer); - // make is visible + // make the selected data node visible imageNode->SetVisibility(true, renderer); imageNode->Modified(); // move node to front, which also request a render update renderWindowLayerController->MoveNodeToFront(imageNode, renderer); QList visibleNodes; visibleNodes.push_back(imageNode); // get all corresponding segmentations of this node mitk::DataStorage::SetOfObjects::ConstPointer segmentationNodes = dataStorage->GetDerivations(imageNode, mitk::NodePredicates::GetSegmentationPredicate(), false); for (auto it = segmentationNodes->Begin(); it != segmentationNodes->End(); ++it) { auto segmentation = it->Value(); - segmentation->SetBoolProperty("fixedLayer", true, renderer); segmentation->SetVisibility(true, renderer); // move node to front, which also request a render update renderWindowLayerController->MoveNodeToFront(segmentation, renderer); visibleNodes.push_back(segmentation); } ReinitAction::Run(berry::IWorkbenchPartSite::Pointer(), dataStorage, visibleNodes, renderer); } } QmitkDataSetOpenInAction::QmitkDataSetOpenInAction(QWidget* parent, berry::IWorkbenchPartSite::Pointer workbenchPartSite) : QmitkDataNodeOpenInAction(parent, workbenchPartSite) { setText(tr("Open in")); InitializeAction(); } QmitkDataSetOpenInAction::QmitkDataSetOpenInAction(QWidget* parent, berry::IWorkbenchPartSite* workbenchPartSite) : QmitkDataNodeOpenInAction(parent, berry::IWorkbenchPartSite::Pointer(workbenchPartSite)) { setText(tr("Open in")); InitializeAction(); } void QmitkDataSetOpenInAction::InitializeAction() { setCheckable(true); setMenu(new QMenu); connect(menu(), &QMenu::aboutToShow, this, &QmitkDataSetOpenInAction::OnMenuAboutToShow); SetControlledRenderer(); } void QmitkDataSetOpenInAction::OnMenuAboutToShow() { menu()->clear(); QAction* action; QStringList rendererNames; for (const auto& renderer : m_ControlledRenderer) { rendererNames.append(renderer->GetName()); } rendererNames.sort(); for (const auto& rendererName : rendererNames) { action = menu()->addAction(rendererName); connect(action, &QAction::triggered, this, &QmitkDataSetOpenInAction::OnActionTriggered); } } void QmitkDataSetOpenInAction::OnActionTriggered(bool /*checked*/) { auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNull()) { return; } auto dataNode = GetSelectedNode(); if (dataNode.IsNull()) { return; } QAction* senderAction = qobject_cast(QObject::sender()); if (nullptr == senderAction) { return; } std::string selectedRenderer = senderAction->text().toStdString(); mitk::BaseRenderer* renderer = mitk::BaseRenderer::GetByName(selectedRenderer); if (nullptr == renderer) { return; } OpenInAction::Run(dataStorage, dataNode, renderer); }