diff --git a/Documentation/Doxygen/DeveloperManual/Starting/GettingToKnow/Tutorial/Step05.dox b/Documentation/Doxygen/DeveloperManual/Starting/GettingToKnow/Tutorial/Step05.dox index c17e7c0f44..4f22b054e3 100644 --- a/Documentation/Doxygen/DeveloperManual/Starting/GettingToKnow/Tutorial/Step05.dox +++ b/Documentation/Doxygen/DeveloperManual/Starting/GettingToKnow/Tutorial/Step05.dox @@ -1,34 +1,38 @@ /** - + \page Step05Page MITK Tutorial - Step 5: Interactively add points - + In addition to Step 4 where 3 views were created on the data, we now want to interactively add points. - A node containing a PointSet as data is added to the data tree and a PointSetInteractor is associated with the node, which handles the interaction. + A node containing a PointSet as data is added to the data tree and a PointSetDataInteractor is associated with the node, which handles the interaction. The @em interaction @em pattern is defined in a state-machine, stored in an external XML file. Thus, we need to load a state-machine. - The interaction patterns defines the @em events, on which the interactor reacts (e.g., which mouse buttons are used to set a point), the @em transition to the next state (e.g., the initial - may be "empty point set") and associated @a actions (e.g., add a point at the position where the mouse-click occured). + A state machine describes interaction pattern with different states (states beeing something like "a point is selected") and transitions to these states (e.g. "select a point"). + These transitions are associated with actions. In this way it is possible to model complex interaction schemes. + By what these transitions and actions are triggered is described in a configuration file. It maps user events to identifiers that are used in the state machine patterns. + In this way the user interaction can be changed by simply loading a different configuration file for a state machine, and the user may add points now with a right click instead of + left click + SHIFT, as in our case. + + Therefore after loading the state machine pattern the PointSetDataInteractor is also given a event configuration file. + More information about interaction in MITK can be found \ref InteractionPage "here". - - In order to add a point the shift key has to be pressed simultaneously to highlight the point with the mouse. - - \li \ref Step5.cpp "Step5.cpp"\n + + In order to add a point the shift key has to be pressed while left clicking in a render window. + You can also move points or remove them (left click while pressing ALT). + + \li \ref Step5.cpp "Step5.cpp"\n Contains the code for this step. - - \image html step5_result.png - + \image html step5_result.png \dontinclude Step5.cpp - A PointSet and a node for it have to be created to be able to interactively adding points: - + \skipline mitk::PointSet - \until "pointsetinteractor" - + \until interactor->SetDataNode(pointSetNode) + \ref Step04Page "[Previous step]" \ref Step06Page "[Next step]" \ref TutorialPage "[Main tutorial page]" - + */ - + diff --git a/Documentation/Doxygen/DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox b/Documentation/Doxygen/DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox index 64963e686e..7e2c77ce9e 100644 --- a/Documentation/Doxygen/DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox +++ b/Documentation/Doxygen/DeveloperManual/Starting/GettingToKnow/Tutorial/Step10.dox @@ -1,438 +1,475 @@ /** \page Step10Page MITK Tutorial - Step 10: Adding new Interaction \tableofcontents -\section HowToUseDataInteractor How to use an existing DataInteractor +\section HowToUseDataInteractor Step 01 - How to use an existing DataInteractor in your Module/Plugin/... MITK provides finished DataInteractors for a variety of tasks, they can be found in Code/Core/Interactors. They can be used with state machine patterns and config files located under Resources/Interactions. - A mitk::DataInteractor consists of four parts. The class describing the functionality and two XML files; one describes the state machine pattern, that is the workflow of an interaction and the second describes the user events which trigger an action. Lastly every mitk::DataInteractor works on a mitk::DataNode in which it stores and manipulates data. To use a mitk::DataInteractor these parts have to be brought together. +** TODO add code of mitk::PointSetDataInteractor Plugin .. + This code demonstrates the use of an existing mitk::DataInteractor exemplary for the mitk::PointSetDataInteractor: -First we need a mitk::DataNode that is added to the mitk::DataStorage. +First we need a mitk::DataNode in which the PointSets is stored. It has to be added to the mitk::DataStorage. \code mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); GetDataStorage()->Add(dataNode.GetPointer()); \endcode -Then we create an instance of a mitk::PointSetDataInteractor and load a statemachine pattern as well as a configuration +Then we create an instance of a mitk::PointSetDataInteractor and load a predefined statemachine pattern as well as a configuration for it: \code m_CurrentInteractor = mitk::PointSetDataInteractor::New(); m_CurrentInteractor->LoadStateMachine("PointSet.xml"); m_CurrentInteractor->SetEventConfig("PointSetConfig.xml"); \endcode Lastly the mitk::DataNode is added to the mitk::DataInteractor \code m_CurrentInteractor->SetDataNode(dataNode); \endcode now the mitk::DataInteractor is ready for usage. + + +\section HowToModifyInteraction Step 02 - How to modify the behaviour of a DataInteractor + +The behavior of a mitk::DataInteractor is determined by two aspects. One, the state machine pattern which describes the flow/order of actions +that are performed. Secondly the configuration which determines which user interaction triggers the actions. + + +\subsection ModifyDisplayInteractorBehavior How to modify the display interactor behavior + +Sometimes it may be desirable to change the behaviour of the mitk::DisplayInteractor which controls zooming, panning and scrolling, e.g. when a +Tool is activated that reacts to the same events. Changing the behavior of the DisplayInteractor (or possibly any other EventHandler) can be achieved from anywhere +in the code by requesting the InteractionEventObserver and assigning an alternative configuration to it, as demonstrated in this example: + +\code + +std::ifstream* configStream = new std::ifstream( #path to alternative configuration file# ); +mitk::EventConfig newConfig(configStream); + +// Requesting all registered EventObservers +std::list listEventObserver = GetModuleContext()->GetServiceReferences(); + +for (std::list::iterator it = listEventObserver.begin(); it != listEventObserver.end(); ++it) +{ + DisplayInteractor* displayInteractor = dynamic_cast(GetModuleContext()->GetService(*it)); + // filtering: only adjust the DisplayInteractor + if (displayInteractor != NULL) + { + displayInteractor->SetEventConfig(newConfig); + } +} + +\endcode + + + \section SectionImplementationDataInteractor How to implement a new DataInteractor This second part of the tutorial step goes beyond the activation of an interactor, that modifies data by user interaction) as shown above. It shows what needs to be implemented to add a new way of interaction within your MITK application. Please see \ref DataInteractionPage as an introduction to the MITK interaction mechanism, you may also want to read \ref DataInteractionTechnicalPage. This tutorial is structured as follows: The first section deals with config files, describing all the parameters of events and how to use them in a configuration file. In the second section the basics are described that are needed to write a state machine pattern. The last section deals with brining configuration, state machine pattern and code together and gives an exemplary implementation of a mitk::DataInteractor. \section ConfigFileDescriptionSection How to create a Config-File \subsection EventDescriptionSection Event Description Events are described by their parameters. Each event type has its own set of parameters that can be set in the configuration file. If a parameter is omitted it is set to its default value. All possible parameters are listed and described below. Event parameters are also described in the documentation of the event class itself. Mandatory for each event description is the event class and the event variant. The parameters of an event are set by attribute tags. \note Refer to \ref EventClassSection for the meaning of event class. \b Mouse \b Buttons \n mitk::InteractionEvent::MouseButtons represent the buttons. They can be used for two attributes. First the EventButton attribute which describes the button that triggered the event, this always is a single button. Secondly the ButtonState attribute that describes which buttons were pressed at the moment the event has been generated. For example assume the right mouse button and the middle mouse button are already pressed, now the left mouse button is pressed too and generates a second event, this would be described as follows: \code \endcode Note: Technically the LeftMouseButton is also pressed and should be listed in the ButtonState, but this is taken care of by the mitk::EventFactory . Key Events \n mitk::InteractionKeyEvent represents a pressed key, which key is pressed is provided with the Key attribute like this \code \endcode or \code \endcode \note Key Events do not require an explicit configuration, for all key events there exists a predefined event variant with the name 'Std' + value, that is key a is named 'StdA'. The names for special keys are listed here: \dontinclude mitkInteractionEvent.h \skipline // Special Keys \until // End special keys Modifier Keys \n mitk::InteractionEvent::ModifierKeys represent the combination of pressed modifier keys, several modifier keys pressed at the same time are denoted by listing them all separated by commas. \code \endcode \b ScrollDirection \n This attribute is unique to the mitk::MouseWheelEvent and describes the direction in which the mouse wheel is rotated. In the event description actual only the direction is provided, but the event is generated with the actual value, and this value can be retrieved from the object. \code \endcode \subsection ExamplesSection Examples Examples for key events: \code \endcode Examples for MousePress events: \code \endcode There exists a standard configuration file for the most common events called GlobalConfig.xml that can be used to as a default and can be extended by a specific definition. \subsection ParameterDescriptionSection Parameter Description It is also possible to store parameters in the config file. Those are stored using the param-tag, like this: \code \endcode Within the application these properties can then be access via a mitk::PropertyList like this: \code // sm - state machine loaded with config file example2 mitk::PropertyList::Pointer properties = GetAttributes(); std::string prop1; properties->GetStringProperty("property1",prop1); \endcode \section HowToStateMachine HowTo Write a State Machine A state machine pattern is described in a XML file. \subsection StateSection States States are described using the state-tag. Each state has to have a name. Exactly one state has to be a start state in each state machine to indicate the state in which the state machine is set when it is constructed. So a valid, but rather useless state machine would like like this: \code \endcode Optionally a state can be assigned a special mode that influences the event distribution. These modes are GRAB_INPUT , PREFER_INPUT and REGULAR (where REGULAR is default and does not need to be indicated). See \ref DataInteractionTechnicalPage_DispatcherEventDistSection for a description of these modes. Use the special modes only when necessary as they prevent other DataInteractors to receive events. \code \endcode \subsection TransitionSection Transitions Transitions are part of a state and describe all possible state switches, and are therefore important for modeling an interaction scheme. Transitions consist a part that describes the event which triggers the transition (event class and event variant) and a target which is state to which the state machine switches after executing a transition. An event class describes the event type (see mitk::InteractionEvent for the different classes) and the event variant is a specification thereof and the exact description is taken from a config file. Together they determine which event can trigger this transition. For example this state machine will switch from state A to state B when the StdMousePressPrimaryButton event (left mouse button is pressed) occurs. \subsubsection EventClassSection Event Class The event class description supports the polymorphism of the event classes. Therefore state machine patters should be written in the most general ways possible. So for a given class hierarchy like this: \dot digraph { node [shape=record, fontname=Helvetica, fontsize=10]; a [ label="{InteractionPositionEvent}"]; b [ label="{MousePressEvent}" ]; c [ label="MouseReleaseEvent" ]; d [ label="TouchEvent", style=dotted ]; a -> b; a -> c; a -> d; } \enddot in the state machine pattern the mitk::InteractionPositionEvent can be declared as event class to restrict to the events which hold a position information. The actual implementation is then given in the configuration file. In this case it allows to define events of the classes mitk::InteractionPositionEvent itself, or mitk::MousePressEvent, mitk::MouseReleaseEvent, mitk::TouchEvent. This has the advantage that the patterns remain the same no matter what input devices are used, and the state machine patterns can be configured for newly added event classes as long as they match the class hierarchy (this ensures they hold the necessary properties). \code \endcode \subsection ActionSection Actions Actions can be added to transitions and represent functions in the mitk::DataInteractor that are executed on taking a transition. The following simple state machine will listen for left mouse clicks and execute two actions (and actually never stop). \code \endcode In order to tell the mitk::DataInteractor which function to execute these actions are made known to the mitk::DataInteractor using the CONNECT_FUNCTION macro. This example assumes that there exists an ExampleInteractor which inherits from mitkDataInteractor. This class implements the functions AddPoint and CountClicks. The actions are introduced by implementing the virtual method ConnectActionsAndFunctions(): \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("countClicks", CountClicks); } \endcode \section ReferenceToIncludeFiles Integration of the pattern and configuration files See \ref IncludeFiles for a description. \section HowToDataInteractor Implementation of a new mitk::DataInteractor DataInteractors are to inherit from mitk::DataInteractor. Their functionality is implemented in functions that follow this interface: \code bool SomeFunctionality(StateMachineAction* , InteractionEvent*); \endcode Your functions are connected with actions by implementing the function ConnectActionsAndFunctions(), e.g. \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("enoughPoints", EnoughPoints); } \endcode Now all that is left is to write a state machine pattern and a config file as is described in the tutorials. \subsection ExampleInternalEvent PointSetDataInteractor To provide a useful example the mitk::PointSetDataInteractor is annotated with comments that describe the important parts for an implementation of a mitk::DataInteractor. This step assumes knowledge of the Interaction concept described in \ref DataInteractionPage and some background of the implementation which is described in \ref DataInteractionPageTechnical. Please refer to these pages before proceeding. DataInteractor are to inherit from mitk::DataInteractor. Their functionality is implemented in functions that follow this interface: \code bool SomeFunctionality(StateMachineAction* , InteractionEvent*); \endcode Your functions are connected with actions by implementing the function ConnectActionsAndFunctions(), e.g. \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("addPoint", AddPoint); CONNECT_FUNCTION("enoughPoints", EnoughPoints); } \endcode Now all that is left it to write a state machine pattern and a config file as is described in the tutorials. \subsection ExampleInternalEvent Example Interactor using InternalEvent A useful tool in creating DataInteractors is mitk::InternalEvent which allows the mitk::DataInteractor to send signals on its own. The following will describe how to build a mitk::DataInteractor that allows to add points until a certain number of points is reached. The number of accepted points is provided in the config file as a parameter. So we start by writing a state machine pattern that add points until it receives an mitk::InternalEvent telling it, that enough points have been added. \code <--! dead state, nothing happens any more, once we reached this --> \endcode In our config file we set the number of maximal points to 10, and define AddPointClick as a right mouse click with the ctrl button pressed. \code \endcode The implementation is described in the following. \see Step10.h \see Step10.cpp \dontinclude Step10.h Implementation of protected functions: \skipline protected: \until virtual void ConfigurationChanged(); ConnectActionsAndFunctions - Is inherited from mitk::InteractionStateMachine, here action strings from the xml are connected with functions in the mitk::DataInteractor (as seen above). In our example this looks like this: \dontinclude Step10.cpp \skipline void mitk::ExampleInteractor::ConnectActionsAndFunctions() \until } ConfigurationChanged - Is called whenever a new configuration file is loaded (by the mitk::InteractionEventHandler super class), this function allows to implement initialization code that depends on configuration values. In our example we want to set the limit of allowed points: \dontinclude Step10.cpp \skipline void mitk::ExampleInteractor::ConfigurationChang \until } Next the actual functionality of the DataInteractor is implemented, by providing one function per action, following this prototype: \code bool FUNCTION_NAME (StateMachineAction* , InteractionEvent*); \endcode \dontinclude Step10.h \skipline private: \until bool EnoughPoints(StateMac Each function has to return a boolean value. True if the action has been executed, and false if the action has not been executed. \dontinclude Step10.cpp \skipline bool mitk::ExampleInteractor::AddPoint(StateM \until //- Here we see an internal event used to signal that the point set reached the maximal number of allowed points. The event is created and added to the Dispatchers event queue. \dontinclude Step10.cpp \skipline // create internal \until positionEvent->GetSender( \note Internal events do not need any mapping to event variants. Their signal name is equivalent with the event variant. There are also two documented classes implementing a mitk::DataInteractor and a mitk::InteractionEventObserver which can be looked at for further understanding: \see mitk::PointSetDataInteractor \see mitk::DisplayInteractor Have fun with creating your own interaction and please think about contributing it to MITK! If you meet any difficulties during this step, don't hesitate to ask on the MITK mailing list mitk-users@lists.sourceforge.net! People there are kind and will try to help you. \ref Step09Page "[Previous step]" \ref TutorialPage "[Main tutorial page]" */ diff --git a/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/InteractionMigration.dox b/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/InteractionMigration.dox index f52a7ff02c..a00e4c7e93 100644 --- a/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/InteractionMigration.dox +++ b/Documentation/Doxygen/DeveloperManual/Toolkit/ModuleManuals/InteractionMigration.dox @@ -1,178 +1,152 @@ /** \page InteractionMigration Migration Guide to new Interaction Concept \tableofcontents \section GeneralChanges General Changes \subsection StatemachineDefinitions Previously the statemachine pattern along with the event description has been stored in a single file (StateMachine.xml). Now the pattern and the events are separated from each other. The statemachine pattern describes the workflow and the configuration file describes which specific event triggers an action in the workflow. Every pattern is now put into a single file (for inclusion refer to \ref IncludeFiles ). The pattern description has to be completely rewritten, but is pretty straight forward based on the old one, how to do this is explained here \ref HowToStateMachine . Here an example is shown for a simple state containing a transition and a parameter. \code \endcode Example snippet (old) \code \endcode Example snippet (new) Changes:
  • ID is dropped, states are referenced now by their name
  • transition now longer have names
  • NEXT_STATE_ID becomes target
  • action ids become names, these names are used in the interactor to connect an action with a function
  • parameters are stored in the configuration file
  • EVENT_ID is replaced by event_class and event_variant (see below)
Event_class describes the class of the event (see \ref EventClassSection ) - the event_variant provides a name that is used in the configuration file to give an explicit description of the event (or the globalConfig.xml is loaded as configuration file and standard values are used). Configuration files for state machines are described here: \ref ConfigFileDescriptionSection . \subsection InteractorStruture Structure of an Interactor Actions are now directly connected to functions in the DataInteractors, so the classic switch statement becomes obsolete: \code bool mitk::SomeInteractor::ExecuteAction(Action* action, mitk::StateEvent const* stateEvent) { switch (action->GetActionId()) { case Action1: ... case Action2: ... } } \endcode changes to \code void mitk::ExampleInteractor::ConnectActionsAndFunctions() { CONNECT_FUNCTION("stringOfActionFromXmlFile", Action1); CONNECT_FUNCTION("stringOfActionFromXmlFile2", Action2); ... } bool Action1(StateMachineAction* , InteractionEvent*) { ... } ... \endcode where each action is implemented as a function. See \ref HowToDataInteractor . \subsection GuardStates Guard States Formerly there where so called guard states, which cause a transition only if certain conditions are met. These states have been removed, as a replacement serve the InternalEvents which can be triggered from within a data interactor. An example showing how to use them is given in \ref ExampleInternalEvent . \subsection IncludeFiles Register statemachine patterns and configuration w/o the build system There are different ways to load a statemachine pattern or configuration objects from files. If you are working in a module you can use the resources to easily load patterns and configurations. To use this place your XML file in the 'Resources/Interactions' folder of the respective module, and add the file path to the corresponding files.cmake in the resource section like this: \code set(RESOURCE_FILES Interactions/dummyStatemachine.xml ) \endcode Loading the statemachine pattern can then be done by simply calling \code #include "mitkModule.h" #include Module* module = GetModuleContext()->GetModule(); mitk::PointSetDataInteractor::m_CurrentInteractor = mitk::PointSetDataInteractor::New(); m_CurrentInteractor->LoadStateMachine("dummyStatemachine.xml", module); \endcode here module is optional, if none is provided the core module is assumed. The third possibility is to build up a configuration by vector of mitk::PropertyLists, where each item describes an event configuration. In the following example a configuration is build which defines two event variants using basic properties, for a full list of usable properties see mitkInteractionEventConst.h \code #include "mitkPropertyList.h" #include "mitkEventConfig.h" // First event mitk::PropertyList::Pointer propertyList1 = mitk::PropertyList::New(); // Setting the EventClass property to 'MousePressEvent' propertyList1->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventClass.c_str(), "MousePressEvent"); // Setting the Event variant value to 'MousePressEventVariantÄ propertyList1->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventVariant.c_str(), "MousePressEventVariant"); // set control and alt buttons as modifiers propertyList1->SetStringProperty("Modifiers","CTRL,ALT"); // Second event mitk::PropertyList::Pointer propertyList2 = mitk::PropertyList::New(); propertyList2->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventClass.c_str(), "MouseReleaseEvent"); propertyList2->SetStringProperty(mitk::InteractionEventConst::xmlParameterEventVariant.c_str(), "MouseReleaseEventVariant"); propertyList2->SetStringProperty("Modifiers","SHIFT"); // putting both descriptions in a vector std::vector* configDescription = new std::vector(); configDescription->push_back(propertyList1); configDescription->push_back(propertyList2); // create the config object mitk::EventConfig newConfig(configDescription); \endcode -\section ModifyDisplayInteractorBehavior How to modify the display interactor behavior - -Sometimes it may be desirable to change the behaviour of the mitk::DisplayInteractor which controls zooming, panning and scrolling, e.g. when a -Tool is activated that reacts to the same events. Changing the behavior of the DisplayInteractor (or possibly any other EventHandler) can be achieved from anywhere -in the code by requesting the InteractionEventObserver and assigning an alternative configuration to it, as demonstrated in this example: - -\code - -std::ifstream* configStream = new std::ifstream( #path to alternative configuration file# ); -mitk::EventConfig newConfig(configStream); - -// Requesting all registered EventObservers -std::list listEventObserver = GetModuleContext()->GetServiceReferences(); - -for (std::list::iterator it = listEventObserver.begin(); it != listEventObserver.end(); ++it) -{ - DisplayInteractor* displayInteractor = dynamic_cast(GetModuleContext()->GetService(*it)); - // filtering: only adjust the DisplayInteractor - if (displayInteractor != NULL) - { - displayInteractor->SetEventConfig(newConfig); - } -} - -\endcode - */ diff --git a/Examples/Tutorial/Step5/Step5.cpp b/Examples/Tutorial/Step5/Step5.cpp index 494fdc05e1..9d0fd14e36 100644 --- a/Examples/Tutorial/Step5/Step5.cpp +++ b/Examples/Tutorial/Step5/Step5.cpp @@ -1,203 +1,219 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkRegisterClasses.h" #include "QmitkRenderWindow.h" #include "QmitkSliceWidget.h" #include "mitkDataNodeFactory.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkStandaloneDataStorage.h" -#include "mitkGlobalInteraction.h" #include "mitkPointSet.h" -#include "mitkPointSetInteractor.h" +// NEW INCLUDE +#include "mitkPointSetDataInteractor.h" #include #include #include //##Documentation //## @brief Interactively add points //## //## As in Step4, load one or more data sets (many image, //## surface and other formats) and create 3 views on the data. //## Additionally, we want to interactively add points. A node containing -//## a PointSet as data is added to the data tree and a PointSetInteractor +//## a PointSet as data is added to the data tree and a PointSetDataInteractor //## is associated with the node, which handles the interaction. The //## @em interaction @em pattern is defined in a state-machine, stored in an //## external XML file. Thus, we need to load a state-machine //## The interaction patterns defines the @em events, //## on which the interactor reacts (e.g., which mouse buttons are used to //## set a point), the @em transition to the next state (e.g., the initial //## may be "empty point set") and associated @a actions (e.g., add a point //## at the position where the mouse-click occured). int main(int argc, char* argv[]) { QApplication qtapplication( argc, argv ); if(argc<2) { fprintf( stderr, "Usage: %s [filename1] [filename2] ...\n\n", itksys::SystemTools::GetFilenameName(argv[0]).c_str() ); return 1; } // Register Qmitk-dependent global instances QmitkRegisterClasses(); //************************************************************************* // Part I: Basic initialization //************************************************************************* // Create a DataStorage mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); //************************************************************************* // Part II: Create some data by reading files //************************************************************************* int i; for(i=1; iSetFileName(filename); nodeReader->Update(); //********************************************************************* // Part III: Put the data into the datastorage //********************************************************************* // Since the DataNodeFactory directly creates a node, // use the iterator to add the read node to the tree mitk::DataNode::Pointer node = nodeReader->GetOutput(); ds->Add(node); } catch(...) { fprintf( stderr, "Could not open file %s \n\n", filename ); exit(2); } } - // ******************************************************* - // ****************** START OF NEW PART ****************** - // ******************************************************* - - //************************************************************************* - // Part VI: For allowing to interactively add points ... - //************************************************************************* - - // Create PointSet and a node for it - mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); - mitk::DataNode::Pointer pointSetNode = mitk::DataNode::New(); - pointSetNode->SetData(pointSet); - - // Add the node to the tree - ds->Add(pointSetNode); - - // Create PointSetInteractor, associate to pointSetNode and add as - // interactor to GlobalInteraction - mitk::GlobalInteraction::GetInstance()->AddInteractor( - mitk::PointSetInteractor::New("pointsetinteractor", pointSetNode) - ); - - // ******************************************************* - // ******************* END OF NEW PART ******************* - // ******************************************************* - //************************************************************************* // Part V: Create windows and pass the tree to it //************************************************************************* // Create toplevel widget with horizontal layout QWidget toplevelWidget; QHBoxLayout layout; layout.setSpacing(2); layout.setMargin(0); toplevelWidget.setLayout(&layout); //************************************************************************* // Part Va: 3D view //************************************************************************* // Create a renderwindow QmitkRenderWindow renderWindow(&toplevelWidget); layout.addWidget(&renderWindow); // Tell the renderwindow which (part of) the tree to render + renderWindow.GetRenderer()->SetDataStorage(ds); // Use it as a 3D view renderWindow.GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard3D); //************************************************************************* // Part Vb: 2D view for slicing axially //************************************************************************* // Create QmitkSliceWidget, which is based on the class // QmitkRenderWindow, but additionally provides sliders QmitkSliceWidget view2(&toplevelWidget); layout.addWidget(&view2); // Tell the QmitkSliceWidget which (part of) the tree to render. // By default, it slices the data axially view2.SetDataStorage(ds); mitk::DataStorage::SetOfObjects::ConstPointer rs = ds->GetAll(); view2.SetData(rs->Begin(), mitk::SliceNavigationController::Axial); // We want to see the position of the slice in 2D and the // slice itself in 3D: add it to the tree! ds->Add(view2.GetRenderer()->GetCurrentWorldGeometry2DNode()); //************************************************************************* // Part Vc: 2D view for slicing sagitally //************************************************************************* // Create QmitkSliceWidget, which is based on the class // QmitkRenderWindow, but additionally provides sliders QmitkSliceWidget view3(&toplevelWidget); layout.addWidget(&view3); // Tell the QmitkSliceWidget which (part of) the tree to render // and to slice sagitall view3.SetDataStorage(ds); view3.SetData(rs->Begin(), mitk::SliceNavigationController::Sagittal); // We want to see the position of the slice in 2D and the // slice itself in 3D: add it to the tree! ds->Add(view3.GetRenderer()->GetCurrentWorldGeometry2DNode()); + // ******************************************************* + // ****************** START OF NEW PART ****************** + // ******************************************************* + + //************************************************************************* + // Part VI: For allowing to interactively add points ... + //************************************************************************* + + + // ATTENTION: It is very important that the renderer already know their DataStorage, + // because registerig DataInteractors with the render windows is done automatically + // and only works if the BaseRenderer and the DataStorage know each other. + + // Create PointSet and a node for it + mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); + mitk::DataNode::Pointer pointSetNode = mitk::DataNode::New(); + // Store the point set in the DataNode + pointSetNode->SetData(pointSet); + + // Add the node to the tree + ds->Add(pointSetNode); + + // Create PointSetDataInteractor + mitk::PointSetDataInteractor::Pointer interactor = mitk::PointSetDataInteractor::New(); + // Set the StateMachine pattern that describes the flow of the interactions + interactor->LoadStateMachine("PointSet.xml"); + // Set the configuration file, which describes the user interactions that trigger actions + // in this file SHIFT + LeftClick triggers add Point, but by modifying this file, + // it could as well be changes to any other user interaction. + interactor->SetEventConfig("PointSetConfig.xml"); + + // Assign the pointSetNode to the interactor, + // alternatively one could also add the DataInteractor to the pointSetNode using the SetDataInteractor() method. + interactor->SetDataNode(pointSetNode); + + // ******************************************************* + // ******************* END OF NEW PART ******************* + // ******************************************************* + + + //************************************************************************* //Part VII: Qt-specific initialization //************************************************************************* toplevelWidget.show(); // For testing #include "QtTesting.h" if(strcmp(argv[argc-1], "-testing")!=0) return qtapplication.exec(); else return QtTesting(); } /** \example Step5.cpp */ diff --git a/Examples/Tutorial/Step6/Step6.cpp b/Examples/Tutorial/Step6/Step6.cpp index 815b4b286f..0e8c17ea86 100644 --- a/Examples/Tutorial/Step6/Step6.cpp +++ b/Examples/Tutorial/Step6/Step6.cpp @@ -1,247 +1,251 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "Step6.h" #include "QmitkRenderWindow.h" #include "QmitkSliceWidget.h" #include "mitkDataNodeFactory.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" -#include "mitkGlobalInteraction.h" #include "mitkPointSet.h" -#include "mitkPointSetInteractor.h" +#include "mitkPointSetDataInteractor.h" #include "mitkImageAccessByItk.h" #include "mitkRenderingManager.h" #include #include #include #include #include #include //##Documentation //## @brief Start region-grower at interactively added points Step6::Step6(int argc, char* argv[], QWidget *parent) : QWidget(parent) { // load data as in the previous steps; a reference to the first loaded // image is kept in the member m_FirstImage and used as input for the // region growing Load(argc, argv); } void Step6::Initialize() { // setup the widgets as in the previous steps, but with an additional // QVBox for a button to start the segmentation this->SetupWidgets(); // Create controlsParent widget with horizontal layout QWidget *controlsParent = new QWidget(this); this->layout()->addWidget(controlsParent); QHBoxLayout* hlayout = new QHBoxLayout(controlsParent); hlayout->setSpacing(2); QLabel *labelThresholdMin = new QLabel("Lower Threshold:", controlsParent); hlayout->addWidget(labelThresholdMin); m_LineEditThresholdMin = new QLineEdit("-1000", controlsParent); hlayout->addWidget(m_LineEditThresholdMin); QLabel *labelThresholdMax = new QLabel("Upper Threshold:", controlsParent); hlayout->addWidget(labelThresholdMax); m_LineEditThresholdMax = new QLineEdit("-400", controlsParent); hlayout->addWidget(m_LineEditThresholdMax); // create button to start the segmentation and connect its clicked() // signal to method StartRegionGrowing QPushButton* startButton = new QPushButton("start region growing", controlsParent); hlayout->addWidget(startButton); connect(startButton, SIGNAL(clicked()), this, SLOT(StartRegionGrowing())); if (m_FirstImage.IsNull()) startButton->setEnabled(false); // as in Step5, create PointSet (now as a member m_Seeds) and // associate a interactor to it + m_Seeds = mitk::PointSet::New(); mitk::DataNode::Pointer pointSetNode = mitk::DataNode::New(); pointSetNode->SetData(m_Seeds); pointSetNode->SetProperty("layer", mitk::IntProperty::New(2)); m_DataStorage->Add(pointSetNode); - mitk::GlobalInteraction::GetInstance()->AddInteractor( - mitk::PointSetInteractor::New("pointsetinteractor", pointSetNode)); + + // Create PointSetDataInteractor + mitk::PointSetDataInteractor::Pointer interactor = mitk::PointSetDataInteractor::New(); + interactor->LoadStateMachine("PointSet.xml"); + interactor->SetEventConfig("PointSetConfig.xml"); + interactor->SetDataNode(pointSetNode); } int Step6::GetThresholdMin() { return m_LineEditThresholdMin->text().toInt(); } int Step6::GetThresholdMax() { return m_LineEditThresholdMax->text().toInt(); } void Step6::StartRegionGrowing() { AccessByItk_1(m_FirstImage, RegionGrowing, this); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void Step6::Load(int argc, char* argv[]) { //************************************************************************* // Part I: Basic initialization //************************************************************************* m_DataStorage = mitk::StandaloneDataStorage::New(); //************************************************************************* // Part II: Create some data by reading files //************************************************************************* int i; for (i = 1; i < argc; ++i) { // For testing if (strcmp(argv[i], "-testing") == 0) continue; // Create a DataNodeFactory to read a data format supported // by the DataNodeFactory (many image formats, surface formats, etc.) mitk::DataNodeFactory::Pointer nodeReader = mitk::DataNodeFactory::New(); const char * filename = argv[i]; try { nodeReader->SetFileName(filename); nodeReader->Update(); //********************************************************************* // Part III: Put the data into the datastorage //********************************************************************* // Since the DataNodeFactory directly creates a node, // use the iterator to add the read node to the tree mitk::DataNode::Pointer node = nodeReader->GetOutput(); m_DataStorage->Add(node); mitk::Image::Pointer image = dynamic_cast (node->GetData()); if ((m_FirstImage.IsNull()) && (image.IsNotNull())) m_FirstImage = image; } catch (...) { fprintf(stderr, "Could not open file %s \n\n", filename); exit(2); } } } void Step6::SetupWidgets() { //************************************************************************* // Part I: Create windows and pass the datastorage to it //************************************************************************* // Create toplevel widget with vertical layout QVBoxLayout* vlayout = new QVBoxLayout(this); vlayout->setMargin(0); vlayout->setSpacing(2); // Create viewParent widget with horizontal layout QWidget* viewParent = new QWidget(this); vlayout->addWidget(viewParent); QHBoxLayout* hlayout = new QHBoxLayout(viewParent); hlayout->setMargin(0); hlayout->setSpacing(2); //************************************************************************* // Part Ia: 3D view //************************************************************************* // Create a renderwindow QmitkRenderWindow* renderWindow = new QmitkRenderWindow(viewParent); hlayout->addWidget(renderWindow); // Tell the renderwindow which (part of) the tree to render renderWindow->GetRenderer()->SetDataStorage(m_DataStorage); // Use it as a 3D view renderWindow->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard3D); //************************************************************************* // Part Ib: 2D view for slicing axially //************************************************************************* // Create QmitkSliceWidget, which is based on the class // QmitkRenderWindow, but additionally provides sliders QmitkSliceWidget *view2 = new QmitkSliceWidget(viewParent); hlayout->addWidget(view2); // Tell the QmitkSliceWidget which (part of) the tree to render. // By default, it slices the data axially view2->SetDataStorage(m_DataStorage); mitk::DataStorage::SetOfObjects::ConstPointer rs = m_DataStorage->GetAll(); view2->SetData(rs->Begin(), mitk::SliceNavigationController::Axial); // We want to see the position of the slice in 2D and the // slice itself in 3D: add it to the tree! m_DataStorage->Add(view2->GetRenderer()->GetCurrentWorldGeometry2DNode()); //************************************************************************* // Part Ic: 2D view for slicing sagitally //************************************************************************* // Create QmitkSliceWidget, which is based on the class // QmitkRenderWindow, but additionally provides sliders QmitkSliceWidget *view3 = new QmitkSliceWidget(viewParent); hlayout->addWidget(view3); // Tell the QmitkSliceWidget which (part of) the tree to render // and to slice sagitally view3->SetDataStorage(m_DataStorage); view3->SetData(rs->Begin(), mitk::SliceNavigationController::Sagittal); // We want to see the position of the slice in 2D and the // slice itself in 3D: add it to the tree! m_DataStorage->Add(view3->GetRenderer()->GetCurrentWorldGeometry2DNode()); //************************************************************************* // Part II: handle updates: To avoid unnecessary updates, we have to //************************************************************************* // define when to update. The RenderingManager serves this purpose, and // each RenderWindow has to be registered to it. /*mitk::RenderingManager *renderingManager = mitk::RenderingManager::GetInstance(); renderingManager->AddRenderWindow( renderWindow ); renderingManager->AddRenderWindow( view2->GetRenderWindow() ); renderingManager->AddRenderWindow( view3->GetRenderWindow() );*/ } /** \example Step6.cpp */