diff --git a/CMakeExternals/Boost.cmake b/CMakeExternals/Boost.cmake index 44c95ebc35..3678c66616 100644 --- a/CMakeExternals/Boost.cmake +++ b/CMakeExternals/Boost.cmake @@ -1,199 +1,202 @@ #----------------------------------------------------------------------------- # Boost #----------------------------------------------------------------------------- if(MITK_USE_Boost) # Sanity checks if(DEFINED BOOST_ROOT AND NOT EXISTS ${BOOST_ROOT}) message(FATAL_ERROR "BOOST_ROOT variable is defined but corresponds to non-existing directory") endif() string(REPLACE "^^" ";" MITK_USE_Boost_LIBRARIES "${MITK_USE_Boost_LIBRARIES}") set(proj Boost) set(proj_DEPENDENCIES ) set(Boost_DEPENDS ${proj}) if(NOT DEFINED BOOST_ROOT AND NOT MITK_USE_SYSTEM_Boost) set(_boost_version 1_56) set(_boost_install_include_dir include/boost) if(WIN32) set(_boost_install_include_dir include/boost-${_boost_version}/boost) endif() set(_boost_libs ) set(_with_boost_libs ) set(_install_lib_dir ) # Set the boost root to the libraries install directory set(BOOST_ROOT "${ep_prefix}") if(MITK_USE_Boost_LIBRARIES) string(REPLACE ";" "," _boost_libs "${MITK_USE_Boost_LIBRARIES}") foreach(_boost_lib ${MITK_USE_Boost_LIBRARIES}) list(APPEND _with_boost_libs ${_with_boost_libs} --with-${_boost_lib}) endforeach() endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) set(_boost_address_model "address-model=64") else() set(_boost_address_model "address-model=32") endif() if(WIN32) set(_shell_extension .bat) set(_boost_layout) if(MSVC) if(MSVC_VERSION EQUAL 1600) set(_boost_with_toolset "vc10") set(_boost_toolset "msvc-10.0") elseif(MSVC_VERSION EQUAL 1700) set(_boost_with_toolset "vc11") set(_boost_toolset "msvc-11.0") elseif(MSVC_VERSION EQUAL 1800) set(_boost_with_toolset "vc12") set(_boost_toolset "msvc-12.0") endif() endif() set(_install_lib_dir "--libdir=/bin") set(WIN32_CMAKE_SCRIPT ${ep_prefix}/src/${proj}-cmake/MoveBoostLibsToLibDirForWindows.cmake) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMakeExternals/MoveBoostLibsToLibDirForWindows.cmake.in ${WIN32_CMAKE_SCRIPT} @ONLY) set(_windows_move_libs_cmd COMMAND ${CMAKE_COMMAND} -P ${WIN32_CMAKE_SCRIPT}) else() set(_shell_extension .sh) set(_boost_layout "--layout=tagged") endif() if(UNIX AND NOT APPLE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(_boost_with_toolset "gcc") elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") set(_boost_with_toolset "clang") else() message(FATAL_ERROR "Compiler '${CMAKE_CXX_COMPILER_ID}' not supported. Use GNU or Clang instead.") endif() string(REGEX MATCH "^[0-9]+\\.[0-9]+" _compiler_version "${CMAKE_CXX_COMPILER_VERSION}") set(_boost_toolset "${_boost_with_toolset}-${_compiler_version}") endif() if(_boost_toolset) set(_boost_toolset "--toolset=${_boost_toolset}") endif() set (APPLE_SYSROOT_FLAG) if(APPLE) set(APPLE_CMAKE_SCRIPT ${ep_prefix}/src/${proj}-cmake/ChangeBoostLibsInstallNameForMac.cmake) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CMakeExternals/ChangeBoostLibsInstallNameForMac.cmake.in ${APPLE_CMAKE_SCRIPT} @ONLY) set(_macos_change_install_name_cmd COMMAND ${CMAKE_COMMAND} -P ${APPLE_CMAKE_SCRIPT}) # Set OSX_SYSROOT if (NOT ${CMAKE_OSX_SYSROOT} STREQUAL "") set (APPLE_SYSROOT_FLAG --sysroot=${CMAKE_OSX_SYSROOT}) endif() endif() set(_boost_variant "$<$:debug>$<$:release>") set(_boost_link shared) if(NOT BUILD_SHARED_LIBS) set(_boost_link static) endif() set(_boost_cxxflags ) if(CMAKE_CXX_FLAGS OR MITK_CXX11_FLAG) set(_boost_cxxflags "cxxflags=${MITK_CXX11_FLAG} ${CMAKE_CXX_FLAGS}") endif() set(_boost_linkflags ) if(BUILD_SHARED_LIBS AND _install_rpath_linkflag) set(_boost_linkflags "linkflags=${_install_rpath_linkflag}") endif() set(_build_cmd "/b2" ${APPLE_SYSROOT_FLAG} ${_boost_toolset} ${_boost_layout} "--prefix=" ${_install_lib_dir} ${_with_boost_libs} # Use the option below to view the shell commands (for debugging) #-d+4 variant=${_boost_variant} link=${_boost_link} ${_boost_cxxflags} ${_boost_linkflags} ${_boost_address_model} threading=multi runtime-link=shared + # Some distributions site config breaks boost build + # For example on Gentoo: http://stackoverflow.com/questions/23013433/how-to-install-modular-boost + --ignore-site-config -q ) if(MITK_USE_Boost_LIBRARIES) set(_boost_build_cmd BUILD_COMMAND ${_build_cmd}) set(_install_cmd ${_build_cmd} install ${_macos_change_install_name_cmd} ${_windows_move_libs_cmd}) else() set(_boost_build_cmd BUILD_COMMAND ${CMAKE_COMMAND} -E echo "no binary libraries") set(_install_cmd ${CMAKE_COMMAND} -E echo "copying Boost header..." COMMAND ${CMAKE_COMMAND} -E copy_directory "/boost" "/${_boost_install_include_dir}") endif() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/boost_${_boost_version}_0.tar.bz2 URL_MD5 a744cf167b05d72335f27c88115f211d # We use in-source builds for Boost BINARY_DIR ${ep_prefix}/src/${proj} CONFIGURE_COMMAND "/bootstrap${_shell_extension}" --with-toolset=${_boost_with_toolset} --with-libraries=${_boost_libs} "--prefix=" ${_boost_build_cmd} INSTALL_COMMAND ${_install_cmd} DEPENDS ${proj_DEPENDENCIES} ) ExternalProject_Get_Property(${proj} install_dir) if(WIN32) set(BOOST_LIBRARYDIR "${install_dir}/lib") endif() # Manual install commands (for a MITK super-build install) # until the Boost CMake system is used. # We just copy the include directory install(DIRECTORY "${install_dir}/${_boost_install_include_dir}" DESTINATION "include" COMPONENT dev ) if(MITK_USE_Boost_LIBRARIES) # Copy the boost libraries file(GLOB _boost_libs "${install_dir}/lib/libboost*.so*" "${install_dir}/lib/libboost*.dylib") install(FILES ${_boost_libs} DESTINATION "lib" COMPONENT runtime) file(GLOB _boost_libs "${install_dir}/bin/libboost*.dll") install(FILES ${_boost_libs} DESTINATION "bin" COMPONENT runtime) file(GLOB _boost_libs "${install_dir}/lib/libboost*.lib" "${install_dir}/lib/libboost*.a") install(FILES ${_boost_libs} DESTINATION "lib" COMPONENT dev) endif() else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/CMakeExternals/Vigra.cmake b/CMakeExternals/Vigra.cmake index 3fd5a82954..2b47454640 100644 --- a/CMakeExternals/Vigra.cmake +++ b/CMakeExternals/Vigra.cmake @@ -1,56 +1,64 @@ #----------------------------------------------------------------------------- # VIGRA #----------------------------------------------------------------------------- if(MITK_USE_Vigra) # Sanity checks if(DEFINED Vigra_DIR AND NOT EXISTS ${Vigra_DIR}) message(FATAL_ERROR "Vigra_DIR variable is defined but corresponds to non-existing directory") endif() if(NOT ${MITK_USE_HDF5}) message(FATAL_ERROR "HDF5 is required for Vigra. Please enable it.") endif() set(proj Vigra) set(proj_DEPENDENCIES HDF5) set(Vigra_DEPENDS ${proj}) + # If a mac ports installation is present some imaging-io-libraries may interfere with the vigra build. + # Hence, we exclude them here. + set(mac_additional_cmake_args) + if(APPLE) + set(mac_additional_cmake_args -DJPEG_INCLUDE_DIR= -DJPEG_LIBRARY= -DTIFF_INCLUDE_DIR= -DTIFF_LIBRARY= -DPNG_LIBRARY_RELEASE= -DPNG_PNG_INCLUDE_DIR= ) + endif() + if(NOT DEFINED Vigra_DIR) set(additional_cmake_args ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() ExternalProject_Add(${proj} URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/vigra-1.10.0-src.tar.gz URL_MD5 4f963f0be4fcb8b06271c2aa40baa9be PATCH_COMMAND ${PATCH_COMMAND} -N -p1 -i ${CMAKE_CURRENT_LIST_DIR}/Vigra.patch CMAKE_GENERATOR ${gen} CMAKE_ARGS ${ep_common_args} ${additional_cmake_args} + ${mac_additional_cmake_args} -DAUTOEXEC_TESTS:BOOL=OFF -DWITH_VIGRANUMPY:BOOL=OFF -DHDF5_DIR:PATH=${HDF5_DIR} -DCMAKE_INSTALL_RPATH_USE_LINK_PATH:BOOL=TRUE -DCMAKE_INSTALL_PREFIX:PATH= CMAKE_CACHE_ARGS ${ep_common_cache_args} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} DEPENDS ${proj_DEPENDENCIES} ) set(Vigra_DIR ${ep_prefix}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif(MITK_USE_Vigra) diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/TestsGeneral.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/TestsGeneral.dox index 4e0992f1a5..e1481463fb 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/TestsGeneral.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/TestsGeneral.dox @@ -1,93 +1,104 @@ /** \page GeneralTests General: Tests in MITK -Two types of standardizes automated tests are provided by MITK. These types are unit tests and rendering tests . This section will describe unit testing in MITK, while more information on rendering tests can be found in the section \ref RenderingTests. +\tableofcontents --# \ref GeneralTestsSection1 "Basic Information on Unit Testing" --# \ref GeneralTestsSection2 "Adding a Unit Test to MITK" - -# \ref GeneralTestsSection2_1 "Structure your test method" --# \ref GeneralTestsSection3 "Run a Unit Test with MITK" --# \ref GeneralTestsSection4 "MITK Testing Macros" +\section GeneralTestsIntroduction Testing in MITK -\section GeneralTestsSection1 Basic Information on Unit Testing +The basic idea about unit testing is to write a test for every unit (e.g. function) of your software, which automatically checks if the code still returns the expected result for a defined input. The goal is to isolate units as much as possible, in order to be efficient with fixing errors. -The basic idea about unit testing is to write a test for every class (unit) of your software. The test should then run all methods of the tested class and check if the results are correct. Thus, the testing environment for MITK allows for simple implementation of corresponding test methods for MITK classes. +If this is performed on a low level, this is called unit testing. On higher levels, where various circumstances (such as configuration files, internet connection, hardware, or even input/output) may influence the outcome of the test, this is called integration testing. -The build system of MITK generates a test driver which includes all tests that have been added to the project. Alternativly you can run MITK tests by using the program ctest. This is the way all MITK tests run on the continous dart clients of MITK. The results of these runs can be found at http://cdash.mitk.org. +MITK uses CppUnit, which is a framework for efficient and fast test writing, to support developers in test coding. Furthermore MITK offers currently three types of tests: classic unit tests, rendering tests and interaction tests. Although our unit tests often involve data input (or output), we still consider them to be unit tests. -The following sections will explain how to write your own tests with MITK and how to run them. The last section will describe the testing macros of MITK in detail. +\warning Most of our units tests have been written before adopting CppUnit and have not yet been updated. Therefore we do not recommend to look at a random test for an example of coding a test. -\section GeneralTestsSection2 Adding a Unit Test to MITK +This pages gives a general introduction to our test framework. Rendering tests are explained in more detail in the section \ref RenderingTests and interaction tests the section \ref InteractionTestingHowTo. The following sections will explain how to write your own tests with MITK and how to run them. -To add a test, you simply need to create a new file "mitkMyTest.cpp" in the folder "Testing" of the software module to which you want to add the test. The file must contain such a method: \code int mitkMyTest(int argc, char* argv[]) \endcode This method is automatically called by the test driver. A header file to this cpp file is not needed. An example for a simple test method is shown next. +\section GeneralTestsAdding Adding a test to your module +Generally, functional code you want to test using unit tests will be located in a module. For an overview of the directory structure see \ref NewModulePageCreateFolder and how to add the files comprising your test compare \ref NewModulePageCreateTestingEnvironment + +\subsection GeneralTestsAddingFramework Test and test class/suite + +CppUnit and MITK distinguish between the term test class and test, which is important to understand. A test is simply a call of a single function and a comparison against a reference. + +The ideal way to write a test is: \code -int mitkMyTest(int argc, char* argv[]) -{ - MITK_TEST_BEGIN("MyTest"); - MITK_TEST_CONDITION_REQUIRED(true,"Here we test our condition"); - MITK_TEST_END(); -} + result = myGreatFunction(myParameter); + ASSERT_EQUAL(result, reference); \endcode -Additionaly you've to add the test file to the files.cmake of your testing directory. In the files.cmake you'll find a command "SET(MODULE_TESTS [...])", this is where you've to add the filename of your test. This will add your test to the test driver. A possible files.cmake where a test have already been added is shown next. +which is just 2 lines of code at the best. Sometimes objects and data may be initialized in a test, but usually this should be done in the setUp() method (see below). If possible, avoid multiple assertions in order to be efficient in error localization and for better readability. For instance, if you instantiate an object and test if its data is not NULL, this is already a complete test and you do not want to repeat this comparison in other tests. + +All tests check different things and just one at a time. Remember: The goal is to have isolated unit tests and avoid large, confusing tests which are hard to maintain. +\note The reference should never be computed and ideally be a distinct and well defined (often hard coded) value. You don't care where you reference comes from, you just want to ensure that your test actually results in the reference value. + +A test class (also called suite) is a set of tests and often associated to a single functional class in MITK. A test class refers to a TestSuite in CppUnit. These terms are easily confused, so we suggest to use the term test suite for a collection of tests (which is often in a single file), and the term "test" for a single test (usually many per file). + +In order to create a test class you need to create a new class deriving from mitk::TestFixture. While a purist approach would be to create a new mitk::TestFixture for each method of your class (resulting in as many test source files as your class has methods), we usually follow a more pragmatic approach within MITK. In most cases we suggest having one test source file per class. If your class source file is called mitkGreatClass.cpp we suggest the name mitkGreatClassTest.cpp for your test source file. For very complex and long classes splitting this into several tests may be advisable. Additionally, rendering tests and interaction tests should always get their own test class/suite to mark them as obvious integration tests, because the goal is to isolate unit tests from integration tests. The dashboard for continuous integration will automatically show the results of a test suite and summarize the output. If you run your test manually (e.g. with ctest -VV), you will see the detailed output of all tests of the suite. + +In order to use CppUnit via MITK you will need to include the corresponding files and register your test: \code -SET(MODULE_TESTS - mitkMyTest.cpp - [...] -) + #include + #include + … + MITK_TEST_SUITE_REGISTRATION(mitkImageEqual) \endcode -Finally you only have to run cmake one your project and then compile the test driver. Don't forget to turn "BUILD_TESTING" on, when running cmake. -\subsection GeneralTestsSection2_1 Structure your test method +\subsection GeneralTestsAddingHow How to structure your test -When implementing more complex test methods you might want to structure them, e.g. by using sub methods. This is also possible. You can create a test class and add static methods which can then be called in your main test method. This is a simple way to keep a clear structure in your test file. An example for such a structured test file is shown next. +As mentioned before, all test suites derive from mitk::TestFixture. A suggested name for your test suite would be TestSuite. +You then create a suite and register your tests. A suggested naming convention for test methods is __. We suggest not to add the word test, because that is most likely the only thing the reader knows anyway about your test, as he is reading a test suite. + +An example: \code -//first: your test class with static methods, if it comes before the main test method -// like shown here, you still don't need a header and you can keep your code as -// simple as possible -class mitkMyTestClass -{ -public: - - static void TestInstantiation() + class mitkImageEqualTestSuite : public mitk::TestFixture { - //do your instantiation test here + CPPUNIT_TEST_SUITE(mitkImageEqualTestSuite); + MITK_TEST(Equal_CloneAndOriginal_ReturnsTrue); + MITK_TEST(Equal_InputIsNull_ReturnsFalse); + MITK_TEST(Equal_DifferentImageGeometry_ReturnsFalse); + MITK_TEST(Equal_DifferentPixelTypes_ReturnsFalse); + … + CPPUNIT_TEST_SUITE_END(); + … } - - static void TestMethod1() - { - //do a test of method 1 here - } -};//do not forget the semicolon at this place! - -//secondly: your main test method -int mitkMyTest(int argc, char* argv[]) -{ - MITK_TEST_BEGIN("MyTest"); - mitkMyTestClass.TestInstantiation(); - mitkMyTestClass.TestMethod1(); - MITK_TEST_END(); -} \endcode -\section GeneralTestsSection3 Run a Unit Test with MITK +You also may provide a setUp() and a tearDown() function. These will be called before/after each test and should be used to make sure that each test works independently and on freshly initialized members and memory to maximize isolation. That way you avoid only testing whether a function works after another function has already been called. See CppUnit documentation for more information. For an example test suite including tests see \ref GeneralTestsExample + +\section GeneralTestsRunning Running your test suite + +The build system of MITK generates a test driver which includes all test suites that have been added to the project. Alternatively you can run MITK test suites by using the program ctest. This is the way all MITK tests run on the continuous dart clients of MITK. The results of these runs can be found at http://cdash.mitk.org. + +If you use the test driver, you only need to start the executable. If you start it without parameters, it will then give you an overview of all tests which are included in this test driver and you can choose one by typing a number. Alternatively, you can give your test driver the name of your test suite as parameter. If you want to use ctest instead of the test driver you need to start a command line, go to the binary directory of MITK and call ctest. To avoid errors, check if your path variable contains all relevant paths to start MITK. -After building and compiling MITK, there are two ways to run your test. Firstly, you can run your test using the executable test driver. Secondly, you can use the external program ctest. +\section GeneralTestsParameterInput Adding parameters to your test -If you use the test driver, you only need to start the executable. If you start it without parameters, it will then give you an overview of all tests which are included in this test driver and you can choose one by typing a number. +If possible, the setUp() method of the test suite should provide all necessary inputs for the respective tests. MITK provides several helper classes to generate synthetic test data, such as the mitk::ImageGenerator. If you have to load data from the hard disc for your test, you can use the method GetTestDataFilePath(string fileName). For an example of loading data from the MITK_DATA_DIR check the mitkIOUtilTestSuite. -\note This way you can not set additional input, such as images. +\section GeneralTestsPredefinedAssertions Predefined assertions -Alternatively you can give your test driver the name of your test as parameter. Then this test will be started directly. You are also allowed to give more parameters which are then given to the main test method as parameters (argv[]). +MITK and CppUnit offer predefined assertions, i.e. helper methods which will help to compare your data against a certain reference. All basic types are covered by CppUnit assertions, such as CPPUNIT_ASSERT. For examples, please check the CppUnit documentation. MITK further offers comparison tools for floating point numbers, vectors, images, surfaces and point sets. A complete list of assertion macros is given in \ref MITKTestingAPI. -If you want to use ctest instead of the test driver you need to start a command line, go to the binary directory of MITK and call ctest. To avoid errors, check if your path variable contains all relevant paths to start MITK. +An example to compare images: +\code + MITK_ASSERT_EQUAL(image, reference, "Checks if image is equal to a reference"); +\endcode -\section GeneralTestsSection4 MITK Testing Macros +By default, the method uses an mitk::eps for floating point comparison, but this can be adapted. It can be necessary to write your own assertion for your own data meeting your special requirements. Recommended examples are all equal test suites for basic data types (mitkImageEqualTest, mitkSurfaceEqualTest and mitkPointSetEqualTest). -MITK offers testing macros to simplify testing, e.g. to test your testing conditions. These macros can be found in the header mitkTestingMacros.h . +\section GeneralTestsExample An example +\include Examples/FirstSteps/NewModule/test/mitkExampleDataStructureTest.cpp +\section GeneralTestsFurtherInfo Further information +More examples can be found in the corresponding bugsquashing presentation. */ + +\section GeneralTestsDeprecatedOldTestingStyle Deprecated macros + +All tests with MITK_TEST_BEGIN(); and MITK_TEST_END(); use the deprecated old MITK testing style. If possible, they should be replaced with the new CppUnit style as explained before. Most of these test classes contain very unfortunate examples and should not be regarded as reference. diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/FirstSteps/FirstSteps.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/FirstSteps/FirstSteps.dox index 7b01d0b3e1..cb3f5cd61f 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/FirstSteps/FirstSteps.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/FirstSteps/FirstSteps.dox @@ -1,15 +1,16 @@ /** \page FirstSteps First steps in Development This section will show you how to extend MITK for your own project.
  • \subpage NewPluginPage
  • -
  • \subpage NewModulePage
  • +
  • \subpage NewModulePage
  • +
  • \subpage NewDataTypePage
  • \subpage NewViewPage
  • \subpage CMAKE_FAQ
  • \subpage StatemachineEditor
  • \subpage MiniAppCommandLineToolHowToPage
*/ diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/FirstSteps/NewDataType.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/FirstSteps/NewDataType.dox new file mode 100644 index 0000000000..e30e8f3e7d --- /dev/null +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/FirstSteps/NewDataType.dox @@ -0,0 +1,52 @@ +/** + +\page NewDataTypePage How to create a new custom data type + +\tableofcontents + + +\section NewDataTypePagePrerequisites Prerequisites and further reading + +We will use some concepts during this tutorial which we assume you are aware of, as well as taking advantage of infrastructure which needs to be set up. The following is a list of prerequisites which should be present to effectively use this tutorial. + +
    +
  • An understanding of the general \ref DataManagementPage
  • +
  • An understanding about the \ref ModularizationPage "modularization of MITK"
  • +
  • We also assume you have created your own module "NewModule" ( \ref NewModulePage )
  • +
+ +Some concepts will only be briefly touched upon in this tutorial, for a more concise presentation of these concepts please refer to the following further reading. + +
    +
  • A general understanding of how the \ref ReaderWriterPage concept works
  • +
  • If you want to know more about the mitk rendering see \ref QVTKRendering
  • +
+ +\section NewDataTypePageCreatingANewDataType Creating a new data type + +A new data type needs to derive from mitk::BaseData in order to be handled by the mitk::DataStorage via mitk::DataNode. An example of a very simple data type is provided in the example module. This type encapsulates a string. + +\include mitkExampleDataStructure.h + +Overloading mitk::Equal to work with your data type will enable you to write simpler, standardized tests. + +\section NewDataTypePageAddingReadersAndWriters Adding readers and writers + +In order for your data type to be read from and written to disk, you need to implement readers and writers. In order for your readers/writers to be registered and available even if your module has not been loaded (usually after just starting the application), it is advisable to implement them separately in a autoload module. The default location for this is "YourModuleName/autoload/IO". + +More information regarding implementing IO and MimeTypes is available at \ref ReaderWriterPage. An example MimeType is implemented for the example data structure. + +\include mitkExampleIOMimeTypes.h + +\note{ You do not need to create your own class to manage your MimeTypes. Instead they can be defined within the Reader/Writer. } + +\section NewDataTypePageAddingReadersAndWriters Adding a mapper + +If your data type needs a special way to render its data for the user, you need to implement a new mapper. More information can be found at \ref QVTKRendering. + +

+If you meet any difficulties during this How-To, 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. +If you notice that there is an error, or that the code has changed so the way shown here is no longer correct, please open a bug report a http://bugs.mitk.org .

+ +*/ diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/FirstSteps/NewModule.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/FirstSteps/NewModule.dox index 20c324fd0a..609cdd831d 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/FirstSteps/NewModule.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/FirstSteps/NewModule.dox @@ -1,82 +1,122 @@ /** \page NewModulePage How to create a new MITK Module -\section NewModulePageCreateFolder 1) Create a Folder for your Module +\section NewModulePageCreateFolder Create a Folder for your Module First, create a folder for your module within /Modules e.g. 'NewModule'. You need to add the new Folder to the CMakeLists.txt in the Module directory as well as well. -Open /Modules/CMakeLists.txt, it should be pretty clear how to add the Module, just insert it into the set(module_dirs) section. +Open /Modules/CMakeLists.txt, insert it into the set(module_dirs) section. \code set(module_dirs ... NewModule ) \endcode -Inside the folder create a new folder called "Testing", which will later contain the module tests. -Also create subfolders for you sourceFiles, for example "NewModuleFilters" and "NewModuleSourceFiles". +A simple example module is provided in the MITK/Examples/FirstSteps/NewModule directory, it includes a new data type +(more information at \ref NewDataTypePage) and adds a MiniApp for that data type (more information at \ref MiniAppCommandLineToolHowToPage). -\section NewModulePageCreateCMakeLists 2) Create CMakeLists.txt - -Within your module create the following file named CMakeLists.txt with the following content: +Within the module we recommend using a Unix like directory structure. This helps others finding their way around your code. +Depending on your use case you might not need every directory. \code -MITK_CREATE_MODULE(NewModule #<-- module name - SUBPROJECTS - INCLUDE_DIRS NewModuleFilters NewModuleServices #<-- sub-folders of module - INTERNAL_INCLUDE_DIRS ${INCLUDE_DIRS_INTERNAL} - DEPENDS Mitk #<-- modules on which your module depends on -) - -ADD_SUBDIRECTORY(Testing) #<-- Directory for tests +NewModule/ + autoload/ + cmdapps/ + doc/ + include/ + resource/ + src/ + test/ \endcode -Choose a fitting module name. This name should only contain Letters (both upper- and lowercase), no numbers, no underscores etc. -This name will be used to qualify your Module within the MITK Framework, so make sure it is unique. -Typically, the name will be the same as name of the Folder the Module resides in. +Subsequently a quick description of what each directory contains. -It is good practice to create subfolders in your module to structure your classes. -Make sure to include these folders in the List of subfolders, or CMake will not find the internal Files. +\subsection NewModulePageCreateFolderAutoload autoload -In the DEPENDS section, you can enter the modules that your module requires to function. -You will not be able to use classes from modules that are not listed here. +This directory should not directly contain files. Instead it contains further directories each of which is its own module. -\section NewModulePageCreatefilesdotcmake 3) Create files.cmake +These modules provide functionality which should be available at application start, but will not be included by other modules. +Examples would be a module registering readers/writers or providing an interface for specific hardware. -Next, create a new file and name it files.cmake, containing the following: +For an example of an autoload module take a look at NewModule/autoload/IO. -\code -SET(CPP_FILES - NewModuleFilters/File1.cpp - NewModuleFilters/File2.cpp +\subsection NewModulePageCreateFolderCmdApps cmdapps - NewModuleServices/Filter1.cpp -) -\endcode +This directory contains all cmdapps (command line tools) related to your module. For more information see \ref MiniAppCommandLineToolHowToPage. -Add each .cpp file you create to this file. -Also, only add you .cpp files here, not the header files! +\subsection NewModulePageCreateFolderDoc doc -\section NewModulePageCreateTEstingEnvironment 4) Set up the Test environment +This directory contains the documentation for your module. -We also need to set up a testing environment where you can add your tests. -Inside your "Testing" Folder, create a new files.cmake containing the following: +\subsection NewModulePageCreateFolderInclude include -\code -SET(MODULE_TESTS - mitkNewModuleTest.cpp -) -\endcode +This directory contains all header files which might be included by other modules. -Also, create a new CMakeLists.text: +\subsection NewModulePageCreateFolderResource resource -\code -MITK_CREATE_MODULE_TESTS() -\endcode +This directory contains resources needed by your module, such as xmls, images or similar. + +\subsection NewModulePageCreateFolderSrc src + +This directory contains source and header files which should not be included by other modules. Further subdivision can help keeping it organized. +(e.g. src/DataManagement src/Algorithms) + +\subsection NewModulePageCreateFolderTest test + +This directory contains all tests for your module. + + +\section NewModulePageCreateCMakeLists Create CMakeLists.txt + +Within your module create a CMakeLists.txt using the MITK_CREATE_MODULE macro: + +An example: + +\include Examples/FirstSteps/NewModule/CMakeLists.txt + +If you do not choose a module name, one will be generated based on the folder name (In this case the resulting name will be MitkNewModule). +This name has to be unique across the entire project in order to avoid collisions. It should only contain Letters (both upper- and lowercase), +no numbers, no underscores etc. + +An example of an autoload module that sets its own name is: + +\include Examples/FirstSteps/NewModule/autoload/IO/CMakeLists.txt + +the resulting name is MitkNewModuleIO. + +\note For more information about the parameters of the new module macro see mitkFunctionCreateModule.cmake + +\section NewModulePageCreatefilesdotcmake Create files.cmake + +Next, create a new file and name it files.cmake, containing the files of your module. + +An example: + +\include Examples/FirstSteps/NewModule/autoload/IO/files.cmake + +If you do not add a source file here it will not be compiled, unless it is included elsewhere. + +\section NewModulePageCreateTestingEnvironment Set up the Test environment + +Providing tests for your code is a very good way to save yourself a lot of debugging time and ensure +consistency. An example of a small test environment is provided in the NewModule example. + +Again you need a CMakeLists.txt, e.g.: + +\include Examples/FirstSteps/NewModule/test/CMakeLists.txt + +a files.cmake, e.g.: + +\include Examples/FirstSteps/NewModule/test/files.cmake + +and an actual test, e.g.: + +\include Examples/FirstSteps/NewModule/test/mitkExampleDataStructureTest.cpp -That's it! Enjoy your new module! After following these steps, it should look something like this: +For more information regarding tests please refer to \ref AboutTestingPage. -\imageMacro{NewModule.png,"Your shiny new module!",6} +That's it! Enjoy your new module! */ \ No newline at end of file diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Tutorial.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Tutorial.dox index c556bd47af..ee7517c3d1 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Tutorial.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Tutorial.dox @@ -1,46 +1,48 @@ /** \page TutorialPage The MITK Tutorial If you have set up and compiled MITK already and want to know more about developing with MITK you might want to read the \ref TutorialSection. If you want help setting up MITK and creating your own project using MITK you should take a look at \ref HowToNewProject. \section TutorialFirstSteps First steps in MITK If you are absolutely new to MITK you might want to read up on setting up MITK to use in your development. The following pages will help you in this task.
  • \subpage HowToNewProject "A comprehensive guide to setting up your own MITK project"
  • \ref BuildInstructionsPage "Building MITK"
  • \ref CMAKE_FAQ
\section TutorialSection Tutorial chapters This tutorial will give you an introduction to developing with MITK. We will start with configuring MITK to compile the tutorial, continue to show how to display and do basic interaction with images, and finally show how to build a plug-in and add new interactions. The source code of the examples can be found in Examples/Tutorial/ \n Two data files are used to execute the example code. \li Pic3D.nrrd \n This file contains an image and can be downloaded from http://mitk.org/download/tutorial-data/Pic3D.nrrd . \li lungs.vtk \n This file contains a surface and can be downloaded from http://mitk.org/download/tutorial-data/lungs.vtk . \li \subpage Step00Page "Step 0: Getting started" \li \subpage Step01Page "Step 1: Displaying an image" \li \subpage Step02Page "Step 2: Load one or more data sets" \li \subpage Step03Page "Step 3: Create 3D view" \li \subpage Step04Page "Step 4: Use several views to explore data" \li \subpage Step05Page "Step 5: Interactively add points" \li \subpage Step06Page "Step 6: Use an interactive region-grower" \li \subpage Step07Page "Step 7: Convert result of region growing into a surface" \li \subpage Step08Page "Step 8: Use QmitkStdMultiWidget as widget" \li \subpage Step09Page "Step 9: A plug-in" \li \subpage Step10Page "Step 10: How to use Interactor and how to implement new ones" +More advanced How-Tos can be found at \ref FirstSteps . + Enjoy MITK! */ diff --git a/Documentation/Doxygen/3-DeveloperManual/images/NewModule.png b/Documentation/Doxygen/3-DeveloperManual/images/NewModule.png deleted file mode 100644 index c379c6649a..0000000000 Binary files a/Documentation/Doxygen/3-DeveloperManual/images/NewModule.png and /dev/null differ diff --git a/Examples/CMakeLists.txt b/Examples/CMakeLists.txt index 960535e42b..5f55f7d253 100644 --- a/Examples/CMakeLists.txt +++ b/Examples/CMakeLists.txt @@ -1,63 +1,64 @@ set(MITK_DEFAULT_SUBPROJECTS MITK-Examples) #----------------------------------------------------------------------------- # Set-up example plugins #----------------------------------------------------------------------------- if(MITK_USE_BLUEBERRY) # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_example_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk OUTPUT_VARIABLE ${varname}) endmacro() set(MITK_EXAMPLE_PLUGIN_TARGETS ) foreach(mitk_example_plugin ${MITK_EXAMPLE_PLUGINS}) ctkFunctionExtractOptionNameAndValue(${mitk_example_plugin} plugin_name plugin_value) string(REPLACE "." "_" _plugin_target ${plugin_name}) list(APPEND MITK_EXAMPLE_PLUGIN_TARGETS ${_plugin_target}) mark_as_advanced(${${_plugin_target}_option_name}) endforeach() endif() #----------------------------------------------------------------------------- # Add example executables #----------------------------------------------------------------------------- set(MITK_DIR ${PROJECT_BINARY_DIR}) set(MITK_EXPORTS_FILE_INCLUDED 1) set(_example_dirs Dump MbiLog QtFreeRender Tutorial + FirstSteps # Overlays# depends on MitkQtWidgetsExt.. ) # some examples depend on Qt 5 if (MITK_USE_Qt5) list(APPEND _example_dirs QuickRender ) elseif (MITK_USE_Qt4) list(APPEND _example_dirs QtAppExample DumpDir ) endif() if(MITK_USE_BLUEBERRY) list(APPEND _example_dirs BlueBerryExampleLauncher ) endif() foreach(_example_dir ${_example_dirs}) add_subdirectory(${_example_dir}) endforeach() diff --git a/Examples/FirstSteps/CMakeLists.txt b/Examples/FirstSteps/CMakeLists.txt new file mode 100644 index 0000000000..1f6579406d --- /dev/null +++ b/Examples/FirstSteps/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(NewModule) \ No newline at end of file diff --git a/Examples/FirstSteps/NewModule/CMakeLists.txt b/Examples/FirstSteps/NewModule/CMakeLists.txt new file mode 100644 index 0000000000..ddad55247e --- /dev/null +++ b/Examples/FirstSteps/NewModule/CMakeLists.txt @@ -0,0 +1,12 @@ +MITK_CREATE_MODULE( + INCLUDE_DIRS + PUBLIC ${MITK_BINARY_DIR} + PRIVATE src/DataManagement + DEPENDS PUBLIC MitkCore + WARNINGS_AS_ERRORS + SUBPROJECTS MITK-Examples +) + +add_subdirectory(autoload/IO) +add_subdirectory(cmdapps) +add_subdirectory(test) diff --git a/Examples/FirstSteps/NewModule/autoload/IO/CMakeLists.txt b/Examples/FirstSteps/NewModule/autoload/IO/CMakeLists.txt new file mode 100644 index 0000000000..d5ca58f7ce --- /dev/null +++ b/Examples/FirstSteps/NewModule/autoload/IO/CMakeLists.txt @@ -0,0 +1,10 @@ +MITK_CREATE_MODULE( NewModuleIO + INCLUDE_DIRS + PRIVATE src/IO + DEPENDS PUBLIC MitkNewModule MitkSceneSerialization + PACKAGE_DEPENDS + PRIVATE tinyxml + AUTOLOAD_WITH MitkCore + WARNINGS_AS_ERRORS + SUBPROJECTS MITK-Examples +) \ No newline at end of file diff --git a/Examples/FirstSteps/NewModule/autoload/IO/files.cmake b/Examples/FirstSteps/NewModule/autoload/IO/files.cmake new file mode 100644 index 0000000000..6fdfa62128 --- /dev/null +++ b/Examples/FirstSteps/NewModule/autoload/IO/files.cmake @@ -0,0 +1,12 @@ +set(CPP_FILES + mitkExampleDataStructureReaderService.cpp + mitkExampleDataStructureReaderService.h + mitkExampleDataStructureSerializer.cpp + mitkExampleDataStructureSerializer.h + mitkExampleDataStructureWriterService.cpp + mitkExampleDataStructureWriterService.h + mitkExampleIOMimeTypes.cpp + mitkExampleIOMimeTypes.h + mitkNewModuleIOActivator.cpp +) + diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.cpp b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.cpp new file mode 100644 index 0000000000..e910d119ca --- /dev/null +++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.cpp @@ -0,0 +1,99 @@ +/*=================================================================== + +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 "mitkExampleDataStructureReaderService.h" + +// mitk includes +#include "mitkGeometry3D.h" +#include +#include "mitkExampleIOMimeTypes.h" + +// itk includes +#include "itksys/SystemTools.hxx" + +namespace mitk +{ + + ExampleDataStructureReaderService::ExampleDataStructureReaderService(const ExampleDataStructureReaderService& other) + : mitk::AbstractFileReader(other) + { + } + + ExampleDataStructureReaderService::ExampleDataStructureReaderService() + : mitk::AbstractFileReader( CustomMimeType( mitk::ExampleIOMimeTypes::EXAMPLE_MIMETYPE() ), "Default reader for the example data structure" ) + { + m_ServiceReg = this->RegisterService(); + } + + ExampleDataStructureReaderService::~ExampleDataStructureReaderService() + { + } + + std::vector > ExampleDataStructureReaderService::Read() + { + std::vector > result; + std::string location = GetInputLocation(); + + std::string ext = itksys::SystemTools::GetFilenameLastExtension(location); + ext = itksys::SystemTools::LowerCase(ext); + + if ( location == "") + { + MITK_ERROR << "No file name specified."; + } + try + { + std::ifstream file(location); + std::string content(""); + std::string line(""); + if (file.is_open()) + { + while (getline(file, line)) + { + content += line; + content += "\n"; + } + } + else + { + mitkThrow() << "Could not open file " << this->GetInputLocation() << " for reading."; + } + + mitk::ExampleDataStructure::Pointer outputData = mitk::ExampleDataStructure::New(); + outputData->SetData(content); + result.push_back(outputData.GetPointer()); + MITK_INFO << "Example file read"; + } + + catch (mitk::Exception e) + { + MITK_ERROR << e.GetDescription(); + } + catch(...) + { + MITK_ERROR << "Unknown error occurred while trying to read file."; + } + + return result; + } + + +} //namespace MITK + +mitk::ExampleDataStructureReaderService* mitk::ExampleDataStructureReaderService::Clone() const +{ + return new ExampleDataStructureReaderService(*this); +} diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.h b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.h new file mode 100644 index 0000000000..563387da58 --- /dev/null +++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.h @@ -0,0 +1,53 @@ +/*=================================================================== + +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. + +===================================================================*/ + +#ifndef __mitkExampleDataStructureReaderService_h +#define __mitkExampleDataStructureReaderService_h + +#include "mitkCommon.h" +#include +#include +#include +#include "mitkExampleDataStructure.h" + +namespace mitk +{ + + /** \brief The reader service for the MITK example data type + */ + + class ExampleDataStructureReaderService : public mitk::AbstractFileReader + { + public: + + typedef mitk::ExampleDataStructure OutputType; + + ExampleDataStructureReaderService(const ExampleDataStructureReaderService& other); + ExampleDataStructureReaderService(); + virtual ~ExampleDataStructureReaderService(); + + using AbstractFileReader::Read; + virtual std::vector > Read() override; + + private: + ExampleDataStructureReaderService* Clone() const override; + + us::ServiceRegistration m_ServiceReg; + }; + +} //namespace MITK + +#endif // __mitkExampleDataStructureReaderService_h diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureSerializer.cpp b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureSerializer.cpp new file mode 100644 index 0000000000..3853425192 --- /dev/null +++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureSerializer.cpp @@ -0,0 +1,71 @@ +/*=================================================================== + +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 "mitkExampleDataStructureSerializer.h" +#include "mitkExampleDataStructure.h" +#include "mitkIOUtil.h" + +#include + + +MITK_REGISTER_SERIALIZER(ExampleDataStructureSerializer) + + +mitk::ExampleDataStructureSerializer::ExampleDataStructureSerializer() +{ +} + + +mitk::ExampleDataStructureSerializer::~ExampleDataStructureSerializer() +{ +} + + +std::string mitk::ExampleDataStructureSerializer::Serialize() +{ + const ExampleDataStructure* exData = dynamic_cast( m_Data.GetPointer() ); + if (exData == NULL) + { + MITK_ERROR << " Object at " << (const void*) this->m_Data + << " is not an mitk::ExampleDataStructure. Cannot serialize as ExampleDataStructure."; + return ""; + } + + std::string filename( this->GetUniqueFilenameInWorkingDirectory() ); + filename += "_"; + filename += m_FilenameHint; + filename += ".txt"; + + std::string fullname(m_WorkingDirectory); + fullname += "/"; + fullname += itksys::SystemTools::ConvertToOutputPath(filename.c_str()); + + try + { + mitk::IOUtil::Save(exData, fullname); + } + catch (std::exception& e) + { + MITK_ERROR << " Error serializing object at " << (const void*) this->m_Data + << " to " + << fullname + << ": " + << e.what(); + return ""; + } + return filename; +} + diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureSerializer.h b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureSerializer.h new file mode 100644 index 0000000000..1e76b67071 --- /dev/null +++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureSerializer.h @@ -0,0 +1,39 @@ +/*=================================================================== + +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. + +===================================================================*/ + +#ifndef mitkExampleDataStructureSerializer_h_included +#define mitkExampleDataStructureSerializer_h_included + +#include "mitkBaseDataSerializer.h" + +namespace mitk +{ +/** + \brief Serializes mitk::ExampleDataStructure for mitk::SceneIO +*/ +class ExampleDataStructureSerializer : public BaseDataSerializer +{ + public: + mitkClassMacro( ExampleDataStructureSerializer, BaseDataSerializer ); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + virtual std::string Serialize() override; + protected: + ExampleDataStructureSerializer(); + virtual ~ExampleDataStructureSerializer(); +}; +} // namespace +#endif diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.cpp b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.cpp new file mode 100644 index 0000000000..a42c23fd86 --- /dev/null +++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.cpp @@ -0,0 +1,95 @@ +/*=================================================================== + +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 "mitkExampleDataStructureWriterService.h" + +// itk includes +#include "itksys/SystemTools.hxx" + +// mitk includes +#include "mitkExampleIOMimeTypes.h" + +mitk::ExampleDataStructureWriterService::ExampleDataStructureWriterService() + : AbstractFileWriter(mitk::ExampleDataStructure::GetStaticNameOfClass(), CustomMimeType(mitk::ExampleIOMimeTypes::EXAMPLE_MIMETYPE()), "Default writer for the example data structure") +{ + RegisterService(); +} + +mitk::ExampleDataStructureWriterService::ExampleDataStructureWriterService(const mitk::ExampleDataStructureWriterService& other) + : AbstractFileWriter(other) +{ +} + + +mitk::ExampleDataStructureWriterService::~ExampleDataStructureWriterService() +{} + +mitk::ExampleDataStructureWriterService* mitk::ExampleDataStructureWriterService::Clone() const +{ + return new ExampleDataStructureWriterService(*this); +} + +void mitk::ExampleDataStructureWriterService::Write() +{ + MITK_INFO << "Writing ExampleDataStructure"; + InputType::ConstPointer input = dynamic_cast(this->GetInput()); + if (input.IsNull() ) + { + MITK_ERROR <<"Sorry, input to ExampleDataStructureWriterService is NULL!"; + return; + } + if ( this->GetOutputLocation().empty() ) + { + MITK_ERROR << "Sorry, filename has not been set!" ; + return ; + } + + std::string ext = itksys::SystemTools::GetFilenameLastExtension(this->GetOutputLocation()); + ext = itksys::SystemTools::LowerCase(ext); + + // default extension is .txt + if(ext == "") + { + ext = ".txt"; + this->SetOutputLocation(this->GetOutputLocation() + ext); + } + + try + { + std::ofstream file(this->GetOutputLocation()); + + if ( file.is_open() ) + { + file << input->GetData(); + } + else + { + mitkThrow() << "Could not open file " << this->GetOutputLocation() << " for writing."; + } + + MITK_INFO << "Example Data Structure has been written"; + } + + catch (mitk::Exception e) + { + MITK_ERROR << e.GetDescription(); + } + catch (...) + { + MITK_ERROR << "Unknown error occurred while trying to write file."; + } + +} diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.h b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.h new file mode 100644 index 0000000000..1e571c2b21 --- /dev/null +++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureWriterService.h @@ -0,0 +1,51 @@ +/*=================================================================== + +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. + +===================================================================*/ + +#ifndef __mitkExampleDataStructureWriterService_h +#define __mitkExampleDataStructureWriterService_h + +#include +#include + +namespace mitk +{ + + /** + * Writes example data strucutres to a file + * @ingroup Process + */ + class ExampleDataStructureWriterService : public mitk::AbstractFileWriter + { + public: + typedef mitk::ExampleDataStructure InputType; + + ExampleDataStructureWriterService(); + virtual ~ExampleDataStructureWriterService(); + + using AbstractFileWriter::Write; + virtual void Write() override; + + protected: + + ExampleDataStructureWriterService(const ExampleDataStructureWriterService& other); + virtual mitk::ExampleDataStructureWriterService* Clone() const override; + + }; + + +} // end of namespace mitk + +#endif //__mitkExampleDataStructureWriterService_h diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleIOMimeTypes.cpp b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleIOMimeTypes.cpp new file mode 100644 index 0000000000..7ac4701984 --- /dev/null +++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleIOMimeTypes.cpp @@ -0,0 +1,116 @@ +/*=================================================================== + +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 "mitkExampleIOMimeTypes.h" +#include "mitkIOMimeTypes.h" +#include +#include + +namespace mitk +{ + +std::vector ExampleIOMimeTypes::Get() +{ + std::vector mimeTypes; + + // order matters here (descending rank for mime types) + + mimeTypes.push_back(EXAMPLE_MIMETYPE().Clone()); + mimeTypes.push_back(EXAMPLE_TWO_MIMETYPE().Clone()); + + return mimeTypes; +} + +// Mime Types + +ExampleIOMimeTypes::ExampleDataStructureMimeType::ExampleDataStructureMimeType() + : CustomMimeType(EXAMPLE_MIMETYPE_NAME()) +{ + std::string category = "Example and Tutorial Data"; + this->SetCategory(category); + this->SetComment("Example data structure containing just a string"); + + this->AddExtension("example"); + this->AddExtension("txt"); +} + +bool ExampleIOMimeTypes::ExampleDataStructureMimeType::AppliesTo(const std::string &path) const +{ + bool canRead( CustomMimeType::AppliesTo(path) ); + + // fix for bug 18572 + // Currently this function is called for writing as well as reading, in that case + // the image information can of course not be read + // This is a bug, this function should only be called for reading. + if( ! itksys::SystemTools::FileExists( path.c_str() ) ) + { + return canRead; + } + //end fix for bug 18572 + + std::string ext = this->GetExtension( path ); + ext = itksys::SystemTools::LowerCase( ext ); + + if( ext == ".txt" ) + { + // we could test something here + if( false ) + { + MITK_WARN << "Can not read .txt file as ExampleDataStructure"; + canRead = false; + } + } + + return canRead; +} + +ExampleIOMimeTypes::ExampleDataStructureMimeType* ExampleIOMimeTypes::ExampleDataStructureMimeType::Clone() const +{ + return new ExampleDataStructureMimeType(*this); +} + + +ExampleIOMimeTypes::ExampleDataStructureMimeType ExampleIOMimeTypes::EXAMPLE_MIMETYPE() +{ + return ExampleDataStructureMimeType(); +} + +CustomMimeType ExampleIOMimeTypes::EXAMPLE_TWO_MIMETYPE() +{ + CustomMimeType mimeType(EXAMPLE_TWO_MIMETYPE_NAME()); + std::string category = "Example and Tutorial Data"; + mimeType.SetComment("Simpler Example Data MimeType"); + mimeType.SetCategory(category); + mimeType.AddExtension("example2"); + return mimeType; +} + +// Names +std::string ExampleIOMimeTypes::EXAMPLE_MIMETYPE_NAME() +{ + // create a unique and sensible name for this mime type + static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".examples.example-data-structure"; + return name; +} + +std::string ExampleIOMimeTypes::EXAMPLE_TWO_MIMETYPE_NAME() +{ + // create a unique and sensible name for this mime type + static std::string name = IOMimeTypes::DEFAULT_BASE_NAME() + ".examples.example-data-structure-the-second"; + return name; +} + +} diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleIOMimeTypes.h b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleIOMimeTypes.h new file mode 100644 index 0000000000..f24c89bd33 --- /dev/null +++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleIOMimeTypes.h @@ -0,0 +1,58 @@ +/*=================================================================== + +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. + +===================================================================*/ + +#ifndef MITKEXAMPLEIOMIMETYPES_H +#define MITKEXAMPLEIOMIMETYPES_H + +#include "mitkCustomMimeType.h" + +#include + +namespace mitk { + +class ExampleIOMimeTypes +{ +public: + + // Deriving your own MimeType will probably be overkill in most situations. + class ExampleDataStructureMimeType : public CustomMimeType + { + public: + ExampleDataStructureMimeType(); + virtual bool AppliesTo(const std::string &path) const override; + virtual ExampleDataStructureMimeType* Clone() const override; + }; + + static ExampleDataStructureMimeType EXAMPLE_MIMETYPE(); + static std::string EXAMPLE_MIMETYPE_NAME(); + + // Simpler method of creating a new MimeType + static CustomMimeType EXAMPLE_TWO_MIMETYPE(); + static std::string EXAMPLE_TWO_MIMETYPE_NAME(); + + // Get all example Mime Types + static std::vector Get(); + +private: + + // purposely not implemented + ExampleIOMimeTypes(); + ExampleIOMimeTypes(const ExampleIOMimeTypes&); +}; + +} + +#endif // MITKEXAMPLEIOMIMETYPES_H diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkNewModuleIOActivator.cpp b/Examples/FirstSteps/NewModule/autoload/IO/mitkNewModuleIOActivator.cpp new file mode 100644 index 0000000000..f8093aeaa2 --- /dev/null +++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkNewModuleIOActivator.cpp @@ -0,0 +1,77 @@ +/*=================================================================== + +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 +#include + +#include + +#include + +#include "mitkExampleIOMimeTypes.h" + +namespace mitk +{ + /** + \brief Registers services for example module. + */ + class NewModuleIOActivator : public us::ModuleActivator + { + public: + + void Load(us::ModuleContext* context) override + { + // We can register our read/write services with a custom service ranking + // services with a higher ranking are prioritized, default us 0 + us::ServiceProperties props; + props[ us::ServiceConstants::SERVICE_RANKING() ] = 10; + + m_MimeTypes = mitk::ExampleIOMimeTypes::Get(); + for (std::vector::const_iterator mimeTypeIter = m_MimeTypes.begin(), + iterEnd = m_MimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) + { + context->RegisterService(*mimeTypeIter, props); + } + + m_ExampleDataStructureReaderService = new ExampleDataStructureReaderService(); + + m_ExampleDataStructureWriterService = new ExampleDataStructureWriterService(); + } + + void Unload(us::ModuleContext*) override + { + for (unsigned int loop(0); loop < m_MimeTypes.size(); ++loop) + { + delete m_MimeTypes.at(loop); + } + + delete m_ExampleDataStructureReaderService; + + delete m_ExampleDataStructureWriterService; + + } + + private: + + ExampleDataStructureReaderService * m_ExampleDataStructureReaderService; + + ExampleDataStructureWriterService * m_ExampleDataStructureWriterService; + + std::vector m_MimeTypes; + + }; +} + +US_EXPORT_MODULE_ACTIVATOR(mitk::NewModuleIOActivator) diff --git a/Modules/DiffusionImaging/MiniApps/CMakeLists.txt b/Examples/FirstSteps/NewModule/cmdapps/CMakeLists.txt old mode 100755 new mode 100644 similarity index 56% copy from Modules/DiffusionImaging/MiniApps/CMakeLists.txt copy to Examples/FirstSteps/NewModule/cmdapps/CMakeLists.txt index 84dd09cf5b..b6219118e7 --- a/Modules/DiffusionImaging/MiniApps/CMakeLists.txt +++ b/Examples/FirstSteps/NewModule/cmdapps/CMakeLists.txt @@ -1,128 +1,76 @@ -option(BUILD_DiffusionMiniApps "Build commandline tools for diffusion" OFF) +option(BUILD_NewModuleMiniApps "Build commandline tools for the example module" OFF) -if(BUILD_DiffusionMiniApps OR MITK_BUILD_ALL_APPS) - - - find_package(OpenMP) - if(NOT OPENMP_FOUND) - message("OpenMP is not available.") - endif() - if(OPENMP_FOUND) - message(STATUS "Found OpenMP.") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - endif() +if(BUILD_NewModuleMiniApps) # needed include directories include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) - - # list of diffusion miniapps + # list of miniapps # if an app requires additional dependencies # they are added after a "^^" and separated by "_" - set( diffusionminiapps - DwiDenoising^^ - ImageResampler^^ - NetworkCreation^^MitkFiberTracking_MitkConnectomics - NetworkStatistics^^MitkConnectomics - ExportShImage^^ - Fiberfox^^MitkFiberTracking - MultishellMethods^^MitkFiberTracking - PeaksAngularError^^MitkFiberTracking - PeakExtraction^^MitkFiberTracking - FiberExtraction^^MitkFiberTracking - FiberProcessing^^MitkFiberTracking - FiberDirectionExtraction^^MitkFiberTracking - LocalDirectionalFiberPlausibility^^MitkFiberTracking - StreamlineTracking^^MitkFiberTracking - GibbsTracking^^MitkFiberTracking - CopyGeometry^^ - DiffusionIndices^^ - TractometerMetrics^^MitkFiberTracking - QballReconstruction^^ - Registration^^ - FileFormatConverter^^MitkFiberTracking - TensorReconstruction^^ - TensorDerivedMapsExtraction^^ - DICOMLoader^^ - DFTraining^^MitkFiberTracking - DFTracking^^MitkFiberTracking + set( miniapps + ExampleToUpperCaseMiniApp^^ ) - foreach(diffusionminiapp ${diffusionminiapps}) + foreach(miniapp ${miniapps}) # extract mini app name and dependencies - string(REPLACE "^^" "\\;" miniapp_info ${diffusionminiapp}) + string(REPLACE "^^" "\\;" miniapp_info ${miniapp}) set(miniapp_info_list ${miniapp_info}) list(GET miniapp_info_list 0 appname) list(GET miniapp_info_list 1 raw_dependencies) string(REPLACE "_" "\\;" dependencies "${raw_dependencies}") set(dependencies_list ${dependencies}) mitk_create_executable(${appname} - DEPENDS MitkCore MitkDiffusionCore ${dependencies_list} + DEPENDS MitkNewModule MitkCommandLine ${dependencies_list} PACKAGE_DEPENDS ITK - CPP_FILES ${appname}.cpp mitkCommandLineParser.cpp + CPP_FILES ${appname}.cpp ) if(EXECUTABLE_IS_ENABLED) # On Linux, create a shell script to start a relocatable application if(UNIX AND NOT APPLE) install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${EXECUTABLE_TARGET}.sh) endif() get_target_property(_is_bundle ${EXECUTABLE_TARGET} MACOSX_BUNDLE) if(APPLE) if(_is_bundle) set(_target_locations ${EXECUTABLE_TARGET}.app) set(${_target_locations}_qt_plugins_install_dir ${EXECUTABLE_TARGET}.app/Contents/MacOS) set(_bundle_dest_dir ${EXECUTABLE_TARGET}.app/Contents/MacOS) set(_qt_plugins_for_current_bundle ${EXECUTABLE_TARGET}.app/Contents/MacOS) set(_qt_conf_install_dirs ${EXECUTABLE_TARGET}.app/Contents/Resources) install(TARGETS ${EXECUTABLE_TARGET} BUNDLE DESTINATION . ) else() if(NOT MACOSX_BUNDLE_NAMES) set(_qt_conf_install_dirs bin) set(_target_locations bin/${EXECUTABLE_TARGET}) set(${_target_locations}_qt_plugins_install_dir bin) install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) else() foreach(bundle_name ${MACOSX_BUNDLE_NAMES}) list(APPEND _qt_conf_install_dirs ${bundle_name}.app/Contents/Resources) set(_current_target_location ${bundle_name}.app/Contents/MacOS/${EXECUTABLE_TARGET}) list(APPEND _target_locations ${_current_target_location}) set(${_current_target_location}_qt_plugins_install_dir ${bundle_name}.app/Contents/MacOS) message( " set(${_current_target_location}_qt_plugins_install_dir ${bundle_name}.app/Contents/MacOS) ") install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION ${bundle_name}.app/Contents/MacOS/) endforeach() endif() endif() else() set(_target_locations bin/${EXECUTABLE_TARGET}${CMAKE_EXECUTABLE_SUFFIX}) set(${_target_locations}_qt_plugins_install_dir bin) set(_qt_conf_install_dirs bin) install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) endif() endif() endforeach() - # This mini app does not depend on mitkDiffusionImaging at all - mitk_create_executable(Dicom2Nrrd - DEPENDS MitkCore - CPP_FILES Dicom2Nrrd.cpp mitkCommandLineParser.cpp - ) - - # On Linux, create a shell script to start a relocatable application - if(UNIX AND NOT APPLE) - install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${EXECUTABLE_TARGET}.sh) - endif() - - if(EXECUTABLE_IS_ENABLED) - MITK_INSTALL_TARGETS(EXECUTABLES ${EXECUTABLE_TARGET}) - endif() - endif() diff --git a/Examples/FirstSteps/NewModule/cmdapps/ExampleToUpperCaseMiniApp.cpp b/Examples/FirstSteps/NewModule/cmdapps/ExampleToUpperCaseMiniApp.cpp new file mode 100644 index 0000000000..09fd150ecf --- /dev/null +++ b/Examples/FirstSteps/NewModule/cmdapps/ExampleToUpperCaseMiniApp.cpp @@ -0,0 +1,108 @@ +/*=================================================================== + +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. + +===================================================================*/ + +// std includes +#include +#include + +// CTK includes +#include "mitkCommandLineParser.h" + +// MITK includes +#include "mitkExampleDataStructure.h" +#include + +int main( int argc, char* argv[] ) +{ + mitkCommandLineParser parser; + + parser.setTitle( "To Upper Case" ); + parser.setCategory( "MITK-Examples" ); + parser.setDescription( "" ); + parser.setContributor( "MBI" ); + + parser.setArgumentPrefix( "--", "-" ); + parser.addArgument( + "input", "i", mitkCommandLineParser::InputFile, "Input file", "input file (.txt/.example)", us::Any(), false ); + parser.addArgument( "output", + "o", + mitkCommandLineParser::OutputFile, + "Output file", + "where to save the output (.txt/.example)", + us::Any(), + false ); + + parser.addArgument( + "verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose output" ); + + map parsedArgs = parser.parseArguments( argc, argv ); + if ( parsedArgs.size() == 0 ) + return EXIT_FAILURE; + + // default values + bool verbose( false ); + + // parse command line arguments + std::string inFilename = us::any_cast( parsedArgs[ "input" ] ); + std::string outFileName = us::any_cast( parsedArgs[ "output" ] ); + + // if optional flag is present + if ( parsedArgs.count( "verbose" ) ) + verbose = us::any_cast( parsedArgs[ "verbose" ] ); + + try + { + // load file + std::vector inVector = mitk::IOUtil::Load( inFilename ); + if ( inVector.empty() ) + { + std::string errorMessage = "File at " + inFilename + " could not be read. Aborting."; + MITK_ERROR << errorMessage; + return EXIT_FAILURE; + } + mitk::BaseData* inBaseData = inVector.at( 0 ); + mitk::ExampleDataStructure* inExample = dynamic_cast( inBaseData ); + + // do creation + mitk::ExampleDataStructure::Pointer outExample = mitk::ExampleDataStructure::New(); + std::string data = inExample->GetData(); + std::transform( data.begin(), data.end(), data.begin(), ::toupper ); + outExample->SetData( data ); + + std::cout << "searching writer"; + + mitk::IOUtil::SaveBaseData( outExample.GetPointer(), outFileName ); + + return EXIT_SUCCESS; + } + catch ( itk::ExceptionObject e ) + { + std::cout << e; + return EXIT_FAILURE; + } + catch ( std::exception e ) + { + std::cout << e.what(); + return EXIT_FAILURE; + } + catch ( ... ) + { + std::cout << "ERROR!?!"; + return EXIT_FAILURE; + } + std::cout << "DONE"; + return EXIT_SUCCESS; +} diff --git a/Examples/FirstSteps/NewModule/doc/Readme.txt b/Examples/FirstSteps/NewModule/doc/Readme.txt new file mode 100644 index 0000000000..0a9980df7e --- /dev/null +++ b/Examples/FirstSteps/NewModule/doc/Readme.txt @@ -0,0 +1 @@ +This directory should contain documentation for your module. \ No newline at end of file diff --git a/Examples/FirstSteps/NewModule/files.cmake b/Examples/FirstSteps/NewModule/files.cmake new file mode 100644 index 0000000000..7bf639d033 --- /dev/null +++ b/Examples/FirstSteps/NewModule/files.cmake @@ -0,0 +1,6 @@ +file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") + +set(CPP_FILES + # DataStructures + DataManagement/mitkExampleDataStructure.cpp +) diff --git a/Examples/FirstSteps/NewModule/include/mitkExampleDataStructure.h b/Examples/FirstSteps/NewModule/include/mitkExampleDataStructure.h new file mode 100644 index 0000000000..aee319fe34 --- /dev/null +++ b/Examples/FirstSteps/NewModule/include/mitkExampleDataStructure.h @@ -0,0 +1,87 @@ + +/*=================================================================== + +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. + +===================================================================*/ + +#ifndef _MITK_ExampleDataStructure_H +#define _MITK_ExampleDataStructure_H + +#include + +#include "mitkBaseData.h" + +namespace mitk +{ +/** +* \brief Example Data Structure +* +* This class is an example of deriving your own data structure based on mitk::BaseData . +*/ +class MITKNEWMODULE_EXPORT ExampleDataStructure : public BaseData +{ +public: + // virtual methods that need to be implemented + virtual void UpdateOutputInformation() override; + virtual void SetRequestedRegionToLargestPossibleRegion() override; + virtual bool RequestedRegionIsOutsideOfTheBufferedRegion() override; + virtual bool VerifyRequestedRegion() override; + virtual void SetRequestedRegion( const itk::DataObject* ) override; + + // Macros + mitkClassMacro( ExampleDataStructure, BaseData ); + itkFactorylessNewMacro( Self ) itkCloneMacro( Self ) + + ////////////////// Interface /////////////////// + + // Get macros + itkGetMacro( Data, std::string ); + itkGetConstMacro( Data, std::string ); + + // Set macros + itkSetMacro( Data, std::string ); + + /** + * \brief Append a string to the data string + * + * Takes a string that is appended to the data string. + * + * \param input string to be appended + */ + void AppendAString( const std::string input ); + +protected: + ExampleDataStructure(); + virtual ~ExampleDataStructure(); + + // this string is the data stored in this example data structure + std::string m_Data; + +private: +}; + +/** +* \brief Returns true if the example data structures are considered equal. +* +* They are considered equal if their string is equal +*/ + +MITKNEWMODULE_EXPORT bool Equal( mitk::ExampleDataStructure* leftHandSide, + mitk::ExampleDataStructure* rightHandSide, + mitk::ScalarType eps, + bool verbose ); + +} // namespace mitk + +#endif /* _MITK_ExampleDataStructure_H */ diff --git a/Examples/FirstSteps/NewModule/resource/Readme.txt b/Examples/FirstSteps/NewModule/resource/Readme.txt new file mode 100644 index 0000000000..2643a3523e --- /dev/null +++ b/Examples/FirstSteps/NewModule/resource/Readme.txt @@ -0,0 +1 @@ +This directory should contain resources, such as images, presets, xmls or similar. \ No newline at end of file diff --git a/Examples/FirstSteps/NewModule/src/DataManagement/mitkExampleDataStructure.cpp b/Examples/FirstSteps/NewModule/src/DataManagement/mitkExampleDataStructure.cpp new file mode 100644 index 0000000000..6f53ec6456 --- /dev/null +++ b/Examples/FirstSteps/NewModule/src/DataManagement/mitkExampleDataStructure.cpp @@ -0,0 +1,98 @@ + +/*=================================================================== + +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 "mitkExampleDataStructure.h" +#include "mitkGeometry3D.h" + +// implementation of virtual methods + +void mitk::ExampleDataStructure::UpdateOutputInformation() +{ + +} + +void mitk::ExampleDataStructure::SetRequestedRegionToLargestPossibleRegion() +{ + +} + +bool mitk::ExampleDataStructure::RequestedRegionIsOutsideOfTheBufferedRegion() +{ + return false; +} + +bool mitk::ExampleDataStructure::VerifyRequestedRegion() +{ + return true; +} + +void mitk::ExampleDataStructure::SetRequestedRegion(const itk::DataObject *) +{ + +} + +/* Constructor and Destructor */ +mitk::ExampleDataStructure::ExampleDataStructure() +: m_Data("Initialized") +{ + this->SetGeometry(mitk::Geometry3D::New()); +} + +mitk::ExampleDataStructure::~ExampleDataStructure() +{ +} + +void mitk::ExampleDataStructure::AppendAString(const std::string input) +{ + m_Data.append( input ); +} + + +bool mitk::Equal( mitk::ExampleDataStructure* leftHandSide, mitk::ExampleDataStructure* rightHandSide, mitk::ScalarType /*eps*/, bool verbose ) +{ + bool noDifferenceFound = true; + + if( rightHandSide == NULL ) + { + if(verbose) + { + MITK_INFO << "[Equal( ExampleDataStructure*, ExampleDataStructure* )] rightHandSide NULL."; + } + return false; + } + + if( leftHandSide == NULL ) + { + if(verbose) + { + MITK_INFO << "[Equal( ExampleDataStructure*, ExampleDataStructure* )] leftHandSide NULL."; + } + return false; + } + + if (!(leftHandSide->GetData() == rightHandSide->GetData()) ) + { + if(verbose) + { + MITK_INFO << "[Equal( ExampleDataStructure*, ExampleDataStructure* )] Data not equal. "; + MITK_INFO << leftHandSide->GetData() << " != " << rightHandSide->GetData(); + } + noDifferenceFound = false; + } + + return noDifferenceFound; +} diff --git a/Examples/FirstSteps/NewModule/test/CMakeLists.txt b/Examples/FirstSteps/NewModule/test/CMakeLists.txt new file mode 100644 index 0000000000..153cd81e2e --- /dev/null +++ b/Examples/FirstSteps/NewModule/test/CMakeLists.txt @@ -0,0 +1 @@ +MITK_CREATE_MODULE_TESTS() diff --git a/Examples/FirstSteps/NewModule/test/files.cmake b/Examples/FirstSteps/NewModule/test/files.cmake new file mode 100644 index 0000000000..f365da9ce1 --- /dev/null +++ b/Examples/FirstSteps/NewModule/test/files.cmake @@ -0,0 +1,7 @@ +set(MODULE_TESTS + mitkExampleDataStructureReaderWriterTest.cpp + mitkExampleDataStructureTest.cpp +) + +SET(MODULE_CUSTOM_TESTS +) diff --git a/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureReaderWriterTest.cpp b/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureReaderWriterTest.cpp new file mode 100644 index 0000000000..0a44cc952f --- /dev/null +++ b/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureReaderWriterTest.cpp @@ -0,0 +1,74 @@ +/*=================================================================== + +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. + +===================================================================*/ + +// Testing +#include "mitkTestingMacros.h" +#include "mitkTestFixture.h" + +// std includes +#include + +// MITK includes +#include "mitkExampleDataStructure.h" +#include "mitkIOUtil.h" + +// VTK includes +#include + +class mitkExampleDataStructureReaderWriterTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkExampleDataStructureReaderWriterTestSuite); + + // Test saving/loading + MITK_TEST(ReadWrite_ExampleData_SavedAndLoadedDataEqualToExample); + CPPUNIT_TEST_SUITE_END(); + +private: + + mitk::ExampleDataStructure::Pointer m_Data; + std::string m_DefaultDataString; + +public: + + /** + * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members for a new test case. (If the members are not used in a test, the method does not need to be called). + */ + void setUp() override + { + m_DefaultDataString = "This is the example data content\nAnd a second line\n"; + m_Data = mitk::ExampleDataStructure::New(); + m_Data->SetData(m_DefaultDataString); + } + + void tearDown() override + { + m_DefaultDataString = ""; + m_Data = nullptr; + } + + void ReadWrite_ExampleData_SavedAndLoadedDataEqualToExample() + { + std::string path = mitk::IOUtil::GetTempPath() + "ExampleDataOutput.txt"; + mitk::IOUtil::SaveBaseData(m_Data, path); + mitk::ExampleDataStructure::Pointer loadedData = dynamic_cast(mitk::IOUtil::LoadBaseData(path).GetPointer()); + + itksys::SystemTools::RemoveFile(path); + + CPPUNIT_ASSERT_MESSAGE("Comparing created and loaded example data.", mitk::Equal(m_Data, loadedData, mitk::eps, true)); + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkExampleDataStructureReaderWriter) diff --git a/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureTest.cpp b/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureTest.cpp new file mode 100644 index 0000000000..e94b0ce60b --- /dev/null +++ b/Examples/FirstSteps/NewModule/test/mitkExampleDataStructureTest.cpp @@ -0,0 +1,71 @@ +/*=================================================================== + +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. + +===================================================================*/ + +// Testing +#include "mitkTestingMacros.h" +#include "mitkTestFixture.h" + +// std includes +#include + +// MITK includes +#include "mitkExampleDataStructure.h" + +// VTK includes +#include + +class mitkExampleDataStructureTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkExampleDataStructureTestSuite); + + // Test the append method + MITK_TEST(Append_ExampleString_AddsExampleStringToData); + CPPUNIT_TEST_SUITE_END(); + +private: + + mitk::ExampleDataStructure::Pointer m_Data; + std::string m_DefaultDataString; + +public: + + /** + * @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members for a new test case. (If the members are not used in a test, the method does not need to be called). + */ + void setUp() override + { + m_DefaultDataString = "This is the example data content\nAnd a second line\n"; + m_Data = mitk::ExampleDataStructure::New(); + m_Data->SetData(m_DefaultDataString); + } + + void tearDown() override + { + m_DefaultDataString = ""; + m_Data = nullptr; + } + + void Append_ExampleString_AddsExampleStringToData() + { + std::string appendedString = "And a third line\n"; + std::string wholeString = m_DefaultDataString + appendedString; + m_Data->AppendAString(appendedString); + + CPPUNIT_ASSERT_MESSAGE("Checking whether string was correctly appended.", m_Data->GetData() == wholeString); + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkExampleDataStructure) diff --git a/Modules/AppUtil/src/mitkBaseApplication.cpp b/Modules/AppUtil/src/mitkBaseApplication.cpp index 096469db1b..eb2a4288c0 100644 --- a/Modules/AppUtil/src/mitkBaseApplication.cpp +++ b/Modules/AppUtil/src/mitkBaseApplication.cpp @@ -1,787 +1,787 @@ /*=================================================================== 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 "mitkBaseApplication.h" #include "mitkLogMacros.h" #include "mitkProvisioningInfo.h" #include "QmitkSafeApplication.h" #include "QmitkSingleApplication.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { QString BaseApplication::ARG_NEWINSTANCE = "BlueBerry.newInstance"; QString BaseApplication::ARG_CLEAN = "BlueBerry.clean"; QString BaseApplication::ARG_APPLICATION = "BlueBerry.application"; QString BaseApplication::ARG_PRODUCT = "BlueBerry.product"; QString BaseApplication::ARG_HOME = "BlueBerry.home"; QString BaseApplication::ARG_STORAGE_DIR = "BlueBerry.storageDir"; QString BaseApplication::ARG_PLUGIN_CACHE = "BlueBerry.plugin_cache_dir"; QString BaseApplication::ARG_PLUGIN_DIRS = "BlueBerry.plugin_dirs"; QString BaseApplication::ARG_FORCE_PLUGIN_INSTALL = "BlueBerry.forcePlugins"; QString BaseApplication::ARG_PRELOAD_LIBRARY = "BlueBerry.preloadLibrary"; QString BaseApplication::ARG_PROVISIONING = "BlueBerry.provisioning"; QString BaseApplication::ARG_DEBUG = "BlueBerry.debug"; QString BaseApplication::ARG_CONSOLELOG = "BlueBerry.consoleLog"; QString BaseApplication::ARG_TESTPLUGIN = "BlueBerry.testplugin"; QString BaseApplication::ARG_TESTAPPLICATION = "BlueBerry.testapplication"; QString BaseApplication::ARG_NO_REGISTRY_CACHE = "BlueBerry.noRegistryCache"; QString BaseApplication::ARG_NO_LAZY_REGISTRY_CACHE_LOADING = "BlueBerry.noLazyRegistryCacheLoading"; QString BaseApplication::ARG_REGISTRY_MULTI_LANGUAGE = "BlueBerry.registryMultiLanguage"; QString BaseApplication::ARG_XARGS = "xargs"; QString BaseApplication::PROP_NEWINSTANCE = BaseApplication::ARG_NEWINSTANCE; QString BaseApplication::PROP_FORCE_PLUGIN_INSTALL = BaseApplication::ARG_FORCE_PLUGIN_INSTALL; QString BaseApplication::PROP_NO_REGISTRY_CACHE = BaseApplication::ARG_NO_REGISTRY_CACHE; QString BaseApplication::PROP_NO_LAZY_REGISTRY_CACHE_LOADING = BaseApplication::ARG_NO_LAZY_REGISTRY_CACHE_LOADING; QString BaseApplication::PROP_REGISTRY_MULTI_LANGUAGE = BaseApplication::ARG_REGISTRY_MULTI_LANGUAGE; QString BaseApplication::PROP_PRODUCT = "blueberry.product"; QString BaseApplication::PROP_APPLICATION = "blueberry.application"; QString BaseApplication::PROP_TESTPLUGIN = "BlueBerry.testplugin"; QString BaseApplication::PROP_TESTAPPLICATION = "BlueBerry.testapplication"; struct BaseApplication::Impl { ctkProperties m_FWProps; QScopedPointer m_QApp; int m_Argc; char** m_Argv; QString m_AppName; QString m_OrgaName; QString m_OrgaDomain; bool m_SingleMode; bool m_SafeMode; QStringList m_PreloadLibs; QString m_ProvFile; Impl(int argc, char** argv) : m_Argc(argc) , m_Argv(argv) , m_SingleMode(false) , m_SafeMode(true) { #ifdef Q_OS_MAC /* * This is a workaround for bug 19080: * On Mac OS X the prosess serial number is passed as an commandline argument (-psn_) * if the application is started via the.app bundle. * This option is unknown, which causes a Poco exception. * Since this is done by the system we have to manually remove the argument here. */ int newArgc = m_Argc-1; char** newArgs = new char*[newArgc]; bool argFound (false); for (int i = 0; i < m_Argc; ++i) { if (QString::fromAscii(m_Argv[i]).contains("-psn")) { argFound = true; } else { newArgs[i] = m_Argv[i]; } } if (argFound) { m_Argc = newArgc; m_Argv = newArgs; } #endif } QVariant getProperty(const QString& property) const { auto iter = m_FWProps.find(property); return iter == m_FWProps.end() ? QVariant() : iter.value(); } void handleBooleanOption(const std::string& name, const std::string& /*value*/) { QString fwKey = QString::fromStdString(name); // translate some keys to proper framework properties if (fwKey == ARG_CONSOLELOG) { fwKey = ctkPluginFrameworkLauncher::PROP_CONSOLE_LOG; } // For all other options we use the command line option name as the // framework property key. m_FWProps[fwKey] = true; } void handlePreloadLibraryOption(const std::string& /*name*/, const std::string& value) { m_PreloadLibs.push_back(QString::fromStdString(value)); } void handleClean(const std::string& /*name*/, const std::string& /*value*/) { m_FWProps[ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN] = ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT; } void initializeCTKPluginFrameworkProperties(Poco::Util::LayeredConfiguration& configuration) { // add all configuration key / value pairs as framework properties Poco::Util::LayeredConfiguration::Keys keys; Poco::Util::LayeredConfiguration::Keys keyStack; configuration.keys(keyStack); std::vector keyChain; while(!keyStack.empty()) { std::string currSubKey = keyStack.back(); if (!keyChain.empty() && keyChain.back() == currSubKey) { keyChain.pop_back(); keyStack.pop_back(); continue; } Poco::Util::LayeredConfiguration::Keys subKeys; configuration.keys(currSubKey, subKeys); if (subKeys.empty()) { keyStack.pop_back(); std::string finalKey; for (auto k = keyChain.begin(); k != keyChain.end(); ++k) { finalKey += *k + "."; } finalKey += currSubKey; keys.push_back(finalKey); } else { keyChain.push_back(currSubKey); for (auto s : subKeys) { keyStack.push_back(s); } } } for (auto key : keys) { QString qKey = QString::fromStdString(key); if (configuration.hasProperty(key)) { // ini and command line options overwrite already inserted keys m_FWProps[qKey] = QString::fromStdString(configuration.getString(key)); } } } void parseProvisioningFile(const QString& filePath) { // Skip parsing if the file path is empty if (filePath.isEmpty()) return; bool consoleLog = this->getProperty(ctkPluginFrameworkLauncher::PROP_CONSOLE_LOG).toBool(); // read initial plugins from a provisioning file QStringList pluginsToStart; QFileInfo provFile(filePath); if (provFile.exists()) { MITK_INFO(consoleLog) << "Using provisioning file: " << qPrintable(provFile.absoluteFilePath()); ProvisioningInfo provInfo(provFile.absoluteFilePath()); // it can still happen, that the encoding is not compatible with the fromUtf8 function ( i.e. when manipulating the LANG variable // in such case, the QStringList in provInfo is empty which we can easily check for if( provInfo.getPluginDirs().empty() ) { MITK_ERROR << "Cannot search for provisioning file, the retrieved directory list is empty.\n" << "This can occur if there are some special (non-ascii) characters in the install path."; } else { foreach(QString pluginPath, provInfo.getPluginDirs()) { ctkPluginFrameworkLauncher::addSearchPath(pluginPath); } //bool forcePluginOverwrite = this->getProperty(ARG_FORCE_PLUGIN_INSTALL).toBool(); QList pluginUrlsToStart = provInfo.getPluginsToStart(); for(auto url : pluginUrlsToStart) { pluginsToStart.push_back(url.toString()); } //foreach(QUrl pluginUrl, provInfo.getPluginsToInstall()) //{ // TODO for "uninstall", we need a proper configuration agent, e.g. a dedicated // plug-in for provisioning of the platform /* if (forcePluginOverwrite) { uninstallPugin(pluginUrl, context); } */ //try //{ //MITK_INFO(consoleLog) << "Installing CTK plug-in from: " << pluginUrl.toString().toStdString(); /* QSharedPointer plugin = context->installPlugin(pluginUrl); if (pluginsToStart.contains(pluginUrl)) { m_CTKPluginsToStart << plugin->getPluginId(); } */ /* } catch (const ctkPluginException& e) { QString errorMsg; QDebug dbg(&errorMsg); dbg << e.printStackTrace(); BERRY_ERROR << qPrintable(errorMsg); } */ //} } } else { MITK_INFO(consoleLog) << "No provisioning file set."; } if(!pluginsToStart.isEmpty()) { m_FWProps[ctkPluginFrameworkLauncher::PROP_PLUGINS] = pluginsToStart; // Use transient start with declared activation policy (this helps when // the provisioning file changes and some plug-ins should not be installed // in the application any more). ctkPlugin::StartOptions startOptions(ctkPlugin::START_TRANSIENT | ctkPlugin::START_ACTIVATION_POLICY); m_FWProps[ctkPluginFrameworkLauncher::PROP_PLUGINS_START_OPTIONS] = static_cast(startOptions); } } }; BaseApplication::BaseApplication(int argc, char** argv) : Application() , d(new Impl(argc, argv)) { } BaseApplication::~BaseApplication() { } void BaseApplication::printHelp(const std::string& /*name*/, const std::string& /*value*/) { Poco::Util::HelpFormatter help(this->options()); help.setAutoIndent(); help.setCommand(this->commandName()); help.format(std::cout); exit(EXIT_OK); } void BaseApplication::setApplicationName(const QString& name) { if (qApp) { qApp->setApplicationName(name); } d->m_AppName = name; } QString BaseApplication::getApplicationName() const { if (qApp) { return qApp->applicationName(); } return d->m_AppName; } void BaseApplication::setOrganizationName(const QString& name) { if (qApp) { qApp->setOrganizationName(name); } d->m_OrgaName = name; } QString BaseApplication::getOrganizationName() const { if (qApp) return qApp->organizationName(); return d->m_OrgaName; } void BaseApplication::setOrganizationDomain(const QString& domain) { if (qApp) { qApp->setOrganizationDomain(domain); } d->m_OrgaDomain = domain; } QString BaseApplication::getOrganizationDomain() const { if (qApp) return qApp->organizationDomain(); return d->m_OrgaDomain; } void BaseApplication::setSingleMode(bool singleMode) { if (qApp) return; d->m_SingleMode = singleMode; } bool BaseApplication::getSingleMode() const { return d->m_SingleMode; } void BaseApplication::setSafeMode(bool safeMode) { if (qApp && !d->m_QApp) return; d->m_SafeMode = safeMode; if (d->m_QApp) { if (getSingleMode()) { static_cast(d->m_QApp.data())->setSafeMode(safeMode); } else { static_cast(d->m_QApp.data())->setSafeMode(safeMode); } } } bool BaseApplication::getSafeMode() const { return d->m_SafeMode; } void BaseApplication::setPreloadLibraries(const QStringList& libraryBaseNames) { d->m_PreloadLibs = libraryBaseNames; } QStringList BaseApplication::getPreloadLibraries() const { return d->m_PreloadLibs; } void BaseApplication::setProvisioningFilePath(const QString& filePath) { d->m_ProvFile = filePath; } QString BaseApplication::getProvisioningFilePath() const { QString provFilePath = d->m_ProvFile; // A null QString means look up a default provisioning file if (provFilePath.isNull() && qApp) { QFileInfo appFilePath(QCoreApplication::applicationFilePath()); QDir basePath(QCoreApplication::applicationDirPath()); QString provFileName = appFilePath.baseName() + ".provisioning"; QFileInfo provFile(basePath.absoluteFilePath(provFileName)); #ifdef Q_OS_MAC /* * On Mac, if started from the build directory the .provisioning file is located at: * * but the executable path is: * * In this case we have to cdUp threetimes. * * During packaging however the MitkWorkbench.provisioning file is placed at the same * level like the executable, hence nothing has to be done. */ if (!provFile.exists()) { basePath.cdUp(); basePath.cdUp(); basePath.cdUp(); provFile = basePath.absoluteFilePath(provFileName); } #endif if (provFile.exists()) { provFilePath = provFile.absoluteFilePath(); } #ifdef CMAKE_INTDIR else { basePath.cdUp(); provFile.setFile(basePath.absoluteFilePath(provFileName)); if (provFile.exists()) { provFilePath = provFile.absoluteFilePath(); } } #endif } return provFilePath; } void BaseApplication::initializeQt() { if (qApp) return; // If previously parameters have been set we have to store them // to hand them through to the application QString appName = this->getApplicationName(); QString orgName = this->getOrganizationName(); QString orgDomain = this->getOrganizationDomain(); // Create a QCoreApplication instance this->getQApplication(); // provide parameters to QCoreApplication this->setApplicationName(appName); this->setOrganizationName(orgName); this->setOrganizationDomain(orgDomain); } void BaseApplication::initialize(Poco::Util::Application& self) { // 1. Call the super-class method Poco::Util::Application::initialize(self); // 2. Initialize the Qt framework (by creating a QCoreApplication) this->initializeQt(); // 3. Seed the random number generator, once at startup. QTime time = QTime::currentTime(); qsrand((uint)time.msec()); // 4. Load the "default" configuration, which involves parsing // an optional .ini file and parsing any // command line arguments this->loadConfiguration(); // 5. Add configuration data from the command line and the // optional .ini file as CTK plugin // framework properties. d->initializeCTKPluginFrameworkProperties(this->config()); // 6. Set the custom CTK Plugin Framework storage directory QString storageDir = this->getCTKFrameworkStorageDir(); if (!storageDir.isEmpty()) { d->m_FWProps[ctkPluginConstants::FRAMEWORK_STORAGE] = storageDir; } // 7. Set the library search paths and the pre-load library property this->initializeLibraryPaths(); QStringList preloadLibs = this->getPreloadLibraries(); if (!preloadLibs.isEmpty()) { - d->m_FWProps[ctkPluginConstants::FRAMEWORK_PRELOAD_LIBRARIES] = preloadLibs.join(QString(',')); + d->m_FWProps[ctkPluginConstants::FRAMEWORK_PRELOAD_LIBRARIES] = preloadLibs; } // 8. Initialize the CppMicroServices library. // The initializeCppMicroServices() method reuses the // FRAMEWORK_STORAGE property, so we call it after the // getCTKFrameworkStorageDir method. this->initializeCppMicroServices(); // 9. Parse the (optional) provisioning file and set the // correct framework properties. d->parseProvisioningFile(this->getProvisioningFilePath()); // Finally, set the CTK Plugin Framework properties ctkPluginFrameworkLauncher::setFrameworkProperties(d->m_FWProps); } void BaseApplication::uninitialize() { QSharedPointer pfw = this->getFramework(); if (pfw) { pfw->stop(); // wait 10 seconds for the CTK plugin framework to stop pfw->waitForStop(10000); } Poco::Util::Application::uninitialize(); } int BaseApplication::getArgc() const { return d->m_Argc; } char**BaseApplication::getArgv() const { return d->m_Argv; } QString BaseApplication::getCTKFrameworkStorageDir() const { QString storageDir; if (this->getSingleMode()) { // This function checks if an instance is already running // and either sends a message to it (containing the command // line arguments) or checks if a new instance was forced by // providing the BlueBerry.newInstance command line argument. // In the latter case, a path to a temporary directory for // the new application's storage directory is returned. storageDir = handleNewAppInstance(static_cast(d->m_QApp.data()), d->m_Argc, d->m_Argv, ARG_NEWINSTANCE); } if (storageDir.isEmpty()) { // This is a new instance and no other instance is already running. We specify // the storage directory here (this is the same code as in berryInternalPlatform.cpp // so that we can re-use the location for the persistent data location of the // the CppMicroServices library. // Append a hash value of the absolute path of the executable to the data location. // This allows to start the same application from different build or install trees. #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) storageDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/" + this->getOrganizationName() + "/" + this->getApplicationName() + '_'; #else storageDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation) + '_'; #endif storageDir += QString::number(qHash(QCoreApplication::applicationDirPath())) + "/"; } return storageDir; } void BaseApplication::initializeCppMicroServices() { QString storageDir = this->getProperty(ctkPluginConstants::FRAMEWORK_STORAGE).toString(); if (!storageDir.isEmpty()) { us::ModuleSettings::SetStoragePath((storageDir + QString("us") + QDir::separator()).toStdString()); } } QCoreApplication* BaseApplication::getQApplication() const { QCoreApplication* qCoreApp = qApp; if (!qCoreApp) { if (getSingleMode()) { qCoreApp = new QmitkSingleApplication(d->m_Argc, d->m_Argv, getSafeMode()); } else { auto safeApp = new QmitkSafeApplication(d->m_Argc, d->m_Argv); safeApp->setSafeMode(d->m_SafeMode); qCoreApp = safeApp; } d->m_QApp.reset(qCoreApp); } return qCoreApp; } void BaseApplication::initializeLibraryPaths() { QStringList suffixes; suffixes << "plugins"; #ifdef Q_OS_WINDOWS suffixes << "bin/plugins"; #ifdef CMAKE_INTDIR suffixes << "bin/" CMAKE_INTDIR "/plugins"; #endif #else suffixes << "lib/plugins"; #ifdef CMAKE_INTDIR suffixes << "lib/" CMAKE_INTDIR "/plugins"; #endif #endif #ifdef Q_OS_MAC suffixes << "../../plugins"; #endif // we add a couple of standard library search paths for plug-ins QDir appDir(QCoreApplication::applicationDirPath()); // walk one directory up and add bin and lib sub-dirs; this // might be redundant appDir.cdUp(); foreach(QString suffix, suffixes) { ctkPluginFrameworkLauncher::addSearchPath(appDir.absoluteFilePath(suffix)); } } int BaseApplication::main(const std::vector& /*args*/) { // Start the plugin framework and all installed plug-ins according with // their auto-start setting. return ctkPluginFrameworkLauncher::run().toInt(); } void BaseApplication::defineOptions(Poco::Util::OptionSet& options) { Poco::Util::Option helpOption("help", "h", "print this help text"); helpOption.callback(Poco::Util::OptionCallback(this, &BaseApplication::printHelp)); options.addOption(helpOption); Poco::Util::Option newInstanceOption(ARG_NEWINSTANCE.toStdString(), "", "forces a new instance of this application"); newInstanceOption.callback(Poco::Util::OptionCallback(d.data(), &Impl::handleBooleanOption)); options.addOption(newInstanceOption); Poco::Util::Option cleanOption(ARG_CLEAN.toStdString(), "", "cleans the plugin cache"); cleanOption.callback(Poco::Util::OptionCallback(d.data(), &Impl::handleClean)); options.addOption(cleanOption); Poco::Util::Option productOption(ARG_PRODUCT.toStdString(), "", "the id of the product to be launched"); productOption.argument("").binding(PROP_PRODUCT.toStdString()); options.addOption(productOption); Poco::Util::Option appOption(ARG_APPLICATION.toStdString(), "", "the id of the application extension to be executed"); appOption.argument("").binding(PROP_APPLICATION.toStdString()); options.addOption(appOption); Poco::Util::Option provOption(ARG_PROVISIONING.toStdString(), "", "the location of a provisioning file"); provOption.argument("").binding(ARG_PROVISIONING.toStdString()); options.addOption(provOption); Poco::Util::Option storageDirOption(ARG_STORAGE_DIR.toStdString(), "", "the location for storing persistent application data"); storageDirOption.argument("").binding(ctkPluginConstants::FRAMEWORK_STORAGE.toStdString()); options.addOption(storageDirOption); Poco::Util::Option consoleLogOption(ARG_CONSOLELOG.toStdString(), "", "log messages to the console"); consoleLogOption.callback(Poco::Util::OptionCallback(d.data(), &Impl::handleBooleanOption)); options.addOption(consoleLogOption); Poco::Util::Option debugOption(ARG_DEBUG.toStdString(), "", "enable debug mode"); debugOption.argument("", false).binding(ctkPluginFrameworkLauncher::PROP_DEBUG.toStdString()); options.addOption(debugOption); Poco::Util::Option forcePluginOption(ARG_FORCE_PLUGIN_INSTALL.toStdString(), "", "force installing plug-ins with same symbolic name"); forcePluginOption.callback(Poco::Util::OptionCallback(d.data(), &Impl::handleBooleanOption)); options.addOption(forcePluginOption); Poco::Util::Option preloadLibsOption(ARG_PRELOAD_LIBRARY.toStdString(), "", "preload a library"); preloadLibsOption.argument("").repeatable(true).callback(Poco::Util::OptionCallback(d.data(), &Impl::handlePreloadLibraryOption)); options.addOption(preloadLibsOption); Poco::Util::Option testPluginOption(ARG_TESTPLUGIN.toStdString(), "", "the plug-in to be tested"); testPluginOption.argument("").binding(PROP_TESTPLUGIN.toStdString()); options.addOption(testPluginOption); Poco::Util::Option testAppOption(ARG_TESTAPPLICATION.toStdString(), "", "the application to be tested"); testAppOption.argument("").binding(PROP_TESTAPPLICATION.toStdString()); options.addOption(testAppOption); Poco::Util::Option noRegistryCacheOption(ARG_NO_REGISTRY_CACHE.toStdString(), "", "do not use a cache for the registry"); noRegistryCacheOption.callback(Poco::Util::OptionCallback(d.data(), &Impl::handleBooleanOption)); options.addOption(noRegistryCacheOption); Poco::Util::Option noLazyRegistryCacheLoadingOption(ARG_NO_LAZY_REGISTRY_CACHE_LOADING.toStdString(), "", "do not use lazy cache loading for the registry"); noLazyRegistryCacheLoadingOption.callback(Poco::Util::OptionCallback(d.data(), &Impl::handleBooleanOption)); options.addOption(noLazyRegistryCacheLoadingOption); Poco::Util::Option registryMultiLanguageOption(ARG_REGISTRY_MULTI_LANGUAGE.toStdString(), "", "enable multi-language support for the registry"); registryMultiLanguageOption.callback(Poco::Util::OptionCallback(d.data(), &Impl::handleBooleanOption)); options.addOption(registryMultiLanguageOption); Poco::Util::Option xargsOption(ARG_XARGS.toStdString(), "", "Extended argument list"); xargsOption.argument("").binding(ARG_XARGS.toStdString()); options.addOption(xargsOption); Poco::Util::Application::defineOptions(options); } QSharedPointer BaseApplication::getFramework() const { return ctkPluginFrameworkLauncher::getPluginFramework(); } ctkPluginContext* BaseApplication::getFrameworkContext() const { QSharedPointer framework = getFramework(); if (framework) return framework->getPluginContext(); return nullptr; } QHash BaseApplication::getFrameworkProperties() const { return d->m_FWProps; } int BaseApplication::run() { this->init(d->m_Argc, d->m_Argv); return Application::run(); } void BaseApplication::setProperty(const QString& property, const QVariant& value) { d->m_FWProps[property] = value; } QVariant BaseApplication::getProperty(const QString& property) const { return d->getProperty(property); } } diff --git a/Modules/CommandLine/CMakeLists.txt b/Modules/CommandLine/CMakeLists.txt new file mode 100644 index 0000000000..d0ab40a745 --- /dev/null +++ b/Modules/CommandLine/CMakeLists.txt @@ -0,0 +1,7 @@ +MITK_CREATE_MODULE( + INCLUDE_DIRS + PUBLIC include/ + PRIVATE src/ + WARNINGS_AS_ERRORS + SUBPROJECTS MITK-Modules +) diff --git a/Modules/CommandLine/files.cmake b/Modules/CommandLine/files.cmake new file mode 100644 index 0000000000..18618b684a --- /dev/null +++ b/Modules/CommandLine/files.cmake @@ -0,0 +1,5 @@ +file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") + +set(CPP_FILES + mitkCommandLineParser.cpp +) diff --git a/Modules/CommandLine/include/mitkCommandLineParser.h b/Modules/CommandLine/include/mitkCommandLineParser.h new file mode 100644 index 0000000000..1419979e4a --- /dev/null +++ b/Modules/CommandLine/include/mitkCommandLineParser.h @@ -0,0 +1,382 @@ +/*=================================================================== + +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. + +===================================================================*/ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +#ifndef __mitkCommandLineParser_h +#define __mitkCommandLineParser_h + +#include +#include + +#include + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4251 ) +#endif + +/** + * + * The MITK command line parser, based on the CTK command line parser. + * + * Use this class to add information about the command line arguments + * your program understands and to easily parse them from a given list + * of strings. + * + * This parser provides the following features: + * + *
    + *
  • Add arguments by supplying a long name and/or a short name. + * Arguments are validated using a regular expression. They can have + * a default value and a help string.
  • + *
  • Deprecated arguments.
  • + *
  • Custom regular expressions for argument validation.
  • + *
  • Set different argument name prefixes for native platform look and feel.
  • + *
  • Create a help text for the command line arguments with support for + * grouping arguments.
  • + *
+ * + * The main difference between the MITK command line parser and the CTK command line + * parser is that the former does not depend on Qt. Apart from that an image type was + * added and XML output improved for automatic GUI generation. + * + * std::out is used for output to keep dependencies to a minimum. + */ + +using namespace std; + +class MITKCOMMANDLINE_EXPORT mitkCommandLineParser +{ +public: + enum Type + { + String = 0, + Bool = 1, + StringList = 2, + Int = 3, + Float = 4, + InputDirectory = 5, + InputFile = 6, + OutputDirectory = 7, + OutputFile = 8, + InputImage = 9 + }; + + typedef std::vector StringContainerType; + + mitkCommandLineParser(); + + ~mitkCommandLineParser(); + + /** + * Parse a given list of command line arguments. + * + * This method parses a list of string elements considering the known arguments + * added by calls to addArgument(). If any one of the argument + * values does not match the corresponding regular expression, + * ok is set to false and an empty map object is returned. + * + * The keys in the returned map object correspond to the long argument string, + * if it is not empty. Otherwise, the short argument string is used as key. The + * us::Any values can safely be converted to the type specified in the + * addArgument() method call. + * + * @param arguments A StringContainerType containing command line arguments. + * @param ok A pointer to a boolean variable. Will be set to true + * if all regular expressions matched, false otherwise. + * @return A map object mapping the long argument (if empty, the short one) + * to a us::Any containing the value. + */ + + map parseArguments( const StringContainerType& arguments, bool* ok = nullptr ); + + /** + * Convenient method allowing to parse a given list of command line arguments. + * @see parseArguments(const StringContainerType &, bool*) + */ + map parseArguments( int argc, char** argv, bool* ok = nullptr ); + + /** + * Returns a detailed error description if a call to parseArguments() + * failed. + * + * @return The error description, empty if no error occured. + * @see parseArguments(const StringContainerType&, bool*) + */ + string errorString() const; + + /** + * This method returns all unparsed arguments, i.e. all arguments + * for which no long or short name has been registered via a call + * to addArgument(). + * + * @see addArgument() + * + * @return A list containing unparsed arguments. + */ + const StringContainerType& unparsedArguments() const; + + /** + * Checks if the given argument has been added via a call + * to addArgument(). + * + * @see addArgument() + * + * @param argument The argument to be checked. + * @return true if the argument was added, false + * otherwise. + */ + bool argumentAdded( const string& argument ) const; + + /** + * Checks if the given argument has been parsed successfully by a previous + * call to parseArguments(). + * + * @param argument The argument to be checked. + * @return true if the argument was parsed, false + * otherwise. + */ + bool argumentParsed( const string& argument ) const; + + /** + * Adds a command line argument. An argument can have a long name + * (like --long-argument-name), a short name (like -l), or both. The type + * of the argument can be specified by using the type parameter. + * The following types are supported: + * + * + * + * + * + * + * + * + *
Type# of parametersDefault regular exprExample
us::Any::String1.*--test-string StringParameter
us::Any::Bool0does not apply--enable-something
us::Any::StringList-1.*--test-list string1 string2
us::Any::Int1-?[0-9]+--test-int -5
+ * + * The regular expressions are used to validate the parameters of command line + * arguments. You can restrict the valid set of parameters by calling + * setExactMatchRegularExpression() for your argument. + * + * Optionally, a help string and a default value can be provided for the argument. If + * the us::Any type of the default value does not match type, an + * exception is thrown. Arguments with default values are always returned by + * parseArguments(). + * + * You can also declare an argument deprecated, by setting deprecated + * to true. Alternatively you can add a deprecated argument by calling + * addDeprecatedArgument(). + * + * If the long or short argument has already been added, or if both are empty strings, + * the method call has no effect. + * + * @param longarg The long argument name. + * @param shortarg The short argument name. + * @param type The argument type (see the list above for supported types). + * @param argLabel The label of this argument, when auto generated interface is used. + * @param argHelp A help string describing the argument. + * @param defaultValue A default value for the argument. + * @param ignoreRest All arguments after the current one will be ignored. + * @param deprecated Declares the argument deprecated. + * + * @see setExactMatchRegularExpression() + * @see addDeprecatedArgument() + * @throws std::logic_error If the us::Any type of defaultValue + * does not match type, a std::logic_error is thrown. + */ + void addArgument( const string& longarg, + const string& shortarg, + Type type, + const string& argLabel, + const string& argHelp = string(), + const us::Any& defaultValue = us::Any(), + bool optional = true, + bool ignoreRest = false, + bool deprecated = false ); + + /** + * Adds a deprecated command line argument. If a deprecated argument is provided + * on the command line, argHelp is displayed in the console and + * processing continues with the next argument. + * + * Deprecated arguments are grouped separately at the end of the help text + * returned by helpText(). + * + * @param longarg The long argument name. + * @param shortarg The short argument name. + * @param argHelp A help string describing alternatives to the deprecated argument. + */ + void + addDeprecatedArgument( const string& longarg, const string& shortarg, const string& argLabel, const string& argHelp ); + + /** + * Sets a custom regular expression for validating argument parameters. The method + * errorString() can be used the get the last error description. + * + * @param argument The previously added long or short argument name. + * @param expression A regular expression which the arugment parameters must match. + * @param exactMatchFailedMessage An error message explaining why the parameter did + * not match. + * + * @return true if the argument was found and the regular expression was set, + * false otherwise. + * + * @see errorString() + */ + bool setExactMatchRegularExpression( const string& argument, + const string& expression, + const string& exactMatchFailedMessage ); + + /** + * The field width for the argument names without the help text. + * + * @return The argument names field width in the help text. + */ + std::string::size_type fieldWidth() const; + + /** + * Creates a help text containing properly formatted argument names and help strings + * provided by calls to addArgument(). The arguments can be grouped by + * using beginGroup() and endGroup(). + * + * @param charPad The padding character. + * @return The formatted help text. + */ + string helpText() const; + + /** + * Sets the argument prefix for long and short argument names. This can be used + * to create native command line arguments without changing the calls to + * addArgument(). For example on Unix-based systems, long argument + * names start with "--" and short names with "-", while on Windows argument names + * always start with "/". + * + * Note that all methods in mitkCommandLineParser which take an argument name + * expect the name as it was supplied to addArgument. + * + * Example usage: + * + * \code + * ctkCommandLineParser parser; + * parser.setArgumentPrefix("--", "-"); + * parser.addArgument("long-argument", "l", us::Any::String); + * StringContainerType args; + * args << "program name" << "--long-argument Hi"; + * parser.parseArguments(args); + * \endcode + * + * @param longPrefix The prefix for long argument names. + * @param shortPrefix The prefix for short argument names. + */ + void setArgumentPrefix( const string& longPrefix, const string& shortPrefix ); + + /** + * Begins a new group for documenting arguments. All newly added arguments via + * addArgument() will be put in the new group. You can close the + * current group by calling endGroup() or be opening a new group. + * + * Note that groups cannot be nested and all arguments which do not belong to + * a group will be listed at the top of the text created by helpText(). + * + * @param description The description of the group + */ + void beginGroup( const string& description ); + + /** + * Ends the current group. + * + * @see beginGroup(const string&) + */ + void endGroup(); + + /** + * Can be used to teach the parser to stop parsing the arguments and return False when + * an unknown argument is encountered. By default StrictMode is disabled. + * + * @see parseArguments(const StringContainerType &, bool*) + */ + void setStrictModeEnabled( bool strictMode ); + + /** + * Is used to generate an XML output for any commandline program. + */ + void generateXmlOutput(); + + /** + * Is used to set the title of the auto generated interface. + * + * @param title The title of the app. + */ + void setTitle( std::string title ); + /** + * Is used to set the contributor for the help view in the auto generated interface. + * + * @param contributor Contributor of the app. + */ + void setContributor( std::string contributor ); + /** + * Is used to categorize the apps in the commandline module. + * + * @param category The category of the app. + */ + void setCategory( std::string category ); + /** + * Is used as the help text in the auto generated interface. + * + * @param description A short description for the app. + */ + void setDescription( std::string description ); + /** + * Is used to group several Parameters in one groupbox in the auto generated interface. + * Default name is "Parameters", with the tooltip: "Groupbox containing parameters." + * + * To change the group of several arguments, call this method before the arguments are added. + * + * @param name The name of the groupbox. + * @param tooltip The tooltip of the groupbox. + */ + void changeParameterGroup( std::string name, std::string tooltip ); + +private: + class ctkInternal; + ctkInternal* Internal; + + string Title; + string Contributor; + string Category; + string Description; + string ParameterGroupName; + string ParameterGroupDescription; +}; + +#endif diff --git a/Modules/CommandLine/src/mitkCommandLineParser.cpp b/Modules/CommandLine/src/mitkCommandLineParser.cpp new file mode 100644 index 0000000000..a8447550bc --- /dev/null +++ b/Modules/CommandLine/src/mitkCommandLineParser.cpp @@ -0,0 +1,960 @@ +/*=================================================================== + +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. + +===================================================================*/ +/*========================================================================= + + Library: CTK + + Copyright (c) Kitware Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0.txt + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +=========================================================================*/ + +// STL includes +#include +#include + +// MITK includes +#include "mitkCommandLineParser.h" + +using namespace std; + +namespace +{ +// -------------------------------------------------------------------------- +class CommandLineParserArgumentDescription +{ +public: + CommandLineParserArgumentDescription( const string& longArg, + const string& longArgPrefix, + const string& shortArg, + const string& shortArgPrefix, + mitkCommandLineParser::Type type, + const string& argHelp, + const string& argLabel, + const us::Any& defaultValue, + bool ignoreRest, + bool deprecated, + bool optional, + string& argGroup, + string& groupDescription ) + : LongArg( longArg ) + , LongArgPrefix( longArgPrefix ) + , ShortArg( shortArg ) + , ShortArgPrefix( shortArgPrefix ) + , ArgHelp( argHelp ) + , ArgLabel( argLabel ) + , ArgGroup( argGroup ) + , ArgGroupDescription( groupDescription ) + , IgnoreRest( ignoreRest ) + , NumberOfParametersToProcess( 0 ) + , Deprecated( deprecated ) + , Optional( optional ) + , DefaultValue( defaultValue ) + , Value( type ) + , ValueType( type ) + { + Value = defaultValue; + + switch ( type ) + { + case mitkCommandLineParser::String: + { + NumberOfParametersToProcess = 1; + } + break; + case mitkCommandLineParser::Bool: + { + NumberOfParametersToProcess = 0; + } + break; + case mitkCommandLineParser::StringList: + { + NumberOfParametersToProcess = -1; + } + break; + case mitkCommandLineParser::Int: + { + NumberOfParametersToProcess = 1; + } + break; + case mitkCommandLineParser::Float: + { + NumberOfParametersToProcess = 1; + } + break; + + case mitkCommandLineParser::OutputDirectory: + case mitkCommandLineParser::InputDirectory: + { + NumberOfParametersToProcess = 1; + } + break; + + case mitkCommandLineParser::OutputFile: + case mitkCommandLineParser::InputFile: + { + NumberOfParametersToProcess = 1; + } + break; + case mitkCommandLineParser::InputImage: + { + NumberOfParametersToProcess = 1; + } + break; + + default: + std::cout << "Type not supported: " << static_cast( type ); + } + } + + ~CommandLineParserArgumentDescription() {} + bool addParameter( const string& value ); + + string helpText(); + + string LongArg; + string LongArgPrefix; + string ShortArg; + string ShortArgPrefix; + string ArgHelp; + string ArgLabel; + string ArgGroup; + string ArgGroupDescription; + bool IgnoreRest; + int NumberOfParametersToProcess; + bool Deprecated; + bool Optional; + + us::Any DefaultValue; + us::Any Value; + mitkCommandLineParser::Type ValueType; +}; + +// -------------------------------------------------------------------------- +bool CommandLineParserArgumentDescription::addParameter( const string& value ) +{ + switch ( ValueType ) + { + case mitkCommandLineParser::String: + { + Value = value; + } + break; + case mitkCommandLineParser::Bool: + { + if ( value.compare( "true" ) == 0 ) + Value = true; + else + Value = false; + } + break; + case mitkCommandLineParser::StringList: + { + try + { + mitkCommandLineParser::StringContainerType list = + us::any_cast( Value ); + list.push_back( value ); + Value = list; + } + catch ( ... ) + { + mitkCommandLineParser::StringContainerType list; + list.push_back( value ); + Value = list; + } + } + break; + case mitkCommandLineParser::Int: + { + stringstream ss( value ); + int i; + ss >> i; + Value = i; + } + break; + case mitkCommandLineParser::Float: + { + stringstream ss( value ); + float f; + ss >> f; + Value = f; + } + break; + + case mitkCommandLineParser::InputDirectory: + case mitkCommandLineParser::OutputDirectory: + { + Value = value; + } + break; + + case mitkCommandLineParser::InputFile: + case mitkCommandLineParser::InputImage: + case mitkCommandLineParser::OutputFile: + { + Value = value; + } + break; + + default: + return false; + } + + return true; +} + +// -------------------------------------------------------------------------- +string CommandLineParserArgumentDescription::helpText() +{ + string text; + + string shortAndLongArg; + if ( !this->ShortArg.empty() ) + { + shortAndLongArg = " "; + shortAndLongArg += this->ShortArgPrefix; + shortAndLongArg += this->ShortArg; + } + + if ( !this->LongArg.empty() ) + { + if ( this->ShortArg.empty() ) + shortAndLongArg.append( " " ); + else + shortAndLongArg.append( ", " ); + + shortAndLongArg += this->LongArgPrefix; + shortAndLongArg += this->LongArg; + } + + text = text + shortAndLongArg + ", " + this->ArgHelp; + + if ( this->Optional ) + text += " (optional)"; + + if ( !this->DefaultValue.Empty() ) + { + text = text + ", (default: " + this->DefaultValue.ToString() + ")"; + } + text += "\n"; + return text; +} +} + +// -------------------------------------------------------------------------- +// ctkCommandLineParser::ctkInternal class + +// -------------------------------------------------------------------------- +class mitkCommandLineParser::ctkInternal +{ +public: + ctkInternal() + : Debug( false ) + , FieldWidth( 0 ) + , StrictMode( false ) + { + } + + ~ctkInternal() {} + CommandLineParserArgumentDescription* argumentDescription( const string& argument ); + + vector ArgumentDescriptionList; + map ArgNameToArgumentDescriptionMap; + map> GroupToArgumentDescriptionListMap; + + StringContainerType UnparsedArguments; + StringContainerType ProcessedArguments; + string ErrorString; + bool Debug; + string::size_type FieldWidth; + string LongPrefix; + string ShortPrefix; + string CurrentGroup; + string DisableQSettingsLongArg; + string DisableQSettingsShortArg; + bool StrictMode; +}; + +// -------------------------------------------------------------------------- +// ctkCommandLineParser::ctkInternal methods + +// -------------------------------------------------------------------------- +CommandLineParserArgumentDescription* mitkCommandLineParser::ctkInternal::argumentDescription( const string& argument ) +{ + string unprefixedArg = argument; + + if ( !LongPrefix.empty() && argument.compare( 0, LongPrefix.size(), LongPrefix ) == 0 ) + { + // Case when (ShortPrefix + UnPrefixedArgument) matches LongPrefix + if ( argument == LongPrefix && !ShortPrefix.empty() && argument.compare( 0, ShortPrefix.size(), ShortPrefix ) == 0 ) + { + unprefixedArg = argument.substr( ShortPrefix.size(), argument.size() ); + } + else + { + unprefixedArg = argument.substr( LongPrefix.size(), argument.size() ); + } + } + else if ( !ShortPrefix.empty() && argument.compare( 0, ShortPrefix.size(), ShortPrefix ) == 0 ) + { + unprefixedArg = argument.substr( ShortPrefix.size(), argument.size() ); + } + else if ( !LongPrefix.empty() && !ShortPrefix.empty() ) + { + return nullptr; + } + + if ( ArgNameToArgumentDescriptionMap.count( unprefixedArg ) ) + { + return this->ArgNameToArgumentDescriptionMap[ unprefixedArg ]; + } + return nullptr; +} + +// -------------------------------------------------------------------------- +// ctkCommandLineParser methods + +// -------------------------------------------------------------------------- +mitkCommandLineParser::mitkCommandLineParser() +{ + this->Internal = new ctkInternal(); + this->Category = string(); + this->Title = string(); + this->Contributor = string(); + this->Description = string(); + this->ParameterGroupName = "Parameters"; + this->ParameterGroupDescription = "Parameters"; +} + +// -------------------------------------------------------------------------- +mitkCommandLineParser::~mitkCommandLineParser() +{ + delete this->Internal; +} + +// -------------------------------------------------------------------------- +map mitkCommandLineParser::parseArguments( const StringContainerType& arguments, bool* ok ) +{ + // Reset + this->Internal->UnparsedArguments.clear(); + this->Internal->ProcessedArguments.clear(); + this->Internal->ErrorString.clear(); + // foreach (CommandLineParserArgumentDescription* desc, this->Internal->ArgumentDescriptionList) + for ( unsigned int i = 0; i < Internal->ArgumentDescriptionList.size(); i++ ) + { + CommandLineParserArgumentDescription* desc = Internal->ArgumentDescriptionList.at( i ); + desc->Value = us::Any( desc->ValueType ); + if ( !desc->DefaultValue.Empty() ) + { + desc->Value = desc->DefaultValue; + } + } + bool error = false; + bool ignoreRest = false; + CommandLineParserArgumentDescription* currentArgDesc = nullptr; + vector parsedArgDescriptions; + for ( unsigned int i = 1; i < arguments.size(); ++i ) + { + string argument = arguments.at( i ); + + if ( this->Internal->Debug ) + { + std::cout << "Processing" << argument; + } + if ( !argument.compare( "--xml" ) || !argument.compare( "-xml" ) || !argument.compare( "--XML" ) || + !argument.compare( "-XML" ) ) + { + this->generateXmlOutput(); + return map(); + } + + // should argument be ignored ? + if ( ignoreRest ) + { + if ( this->Internal->Debug ) + { + std::cout << " Skipping: IgnoreRest flag was been set"; + } + this->Internal->UnparsedArguments.push_back( argument ); + continue; + } + + // Skip if the argument does not start with the defined prefix + if ( !( argument.compare( 0, Internal->LongPrefix.size(), Internal->LongPrefix ) == 0 || + argument.compare( 0, Internal->ShortPrefix.size(), Internal->ShortPrefix ) == 0 ) ) + { + if ( this->Internal->StrictMode ) + { + this->Internal->ErrorString = "Unknown argument "; + this->Internal->ErrorString += argument; + error = true; + break; + } + if ( this->Internal->Debug ) + { + std::cout << " Skipping: It does not start with the defined prefix"; + } + this->Internal->UnparsedArguments.push_back( argument ); + continue; + } + + // Skip if argument has already been parsed ... + bool alreadyProcessed = false; + for ( auto alreadyHandledArgument : Internal->ProcessedArguments ) + if ( argument.compare( alreadyHandledArgument ) == 0 ) + { + alreadyProcessed = true; + break; + } + + if ( alreadyProcessed ) + { + if ( this->Internal->StrictMode ) + { + this->Internal->ErrorString = "Argument "; + this->Internal->ErrorString += argument; + this->Internal->ErrorString += " already processed !"; + error = true; + break; + } + if ( this->Internal->Debug ) + { + std::cout << " Skipping: Already processed !"; + } + continue; + } + + // Retrieve corresponding argument description + currentArgDesc = this->Internal->argumentDescription( argument ); + + // Is there a corresponding argument description ? + if ( currentArgDesc ) + { + // If the argument is deprecated, print the help text but continue processing + if ( currentArgDesc->Deprecated ) + { + std::cout << "Deprecated argument " << argument << ": " << currentArgDesc->ArgHelp; + } + else + { + parsedArgDescriptions.push_back( currentArgDesc ); + } + + this->Internal->ProcessedArguments.push_back( currentArgDesc->ShortArg ); + this->Internal->ProcessedArguments.push_back( currentArgDesc->LongArg ); + int numberOfParametersToProcess = currentArgDesc->NumberOfParametersToProcess; + ignoreRest = currentArgDesc->IgnoreRest; + if ( this->Internal->Debug && ignoreRest ) + { + std::cout << " IgnoreRest flag is True"; + } + + // Is the number of parameters associated with the argument being processed known ? + if ( numberOfParametersToProcess == 0 ) + { + currentArgDesc->addParameter( "true" ); + } + else if ( numberOfParametersToProcess > 0 ) + { + string missingParameterError = "Argument %1 has %2 value(s) associated whereas exacly %3 are expected."; + for ( int j = 1; j <= numberOfParametersToProcess; ++j ) + { + if ( i + j >= arguments.size() ) + { + // this->Internal->ErrorString = + // missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess); + // if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; } + if ( ok ) + { + *ok = false; + } + return map(); + } + string parameter = arguments.at( i + j ); + if ( this->Internal->Debug ) + { + std::cout << " Processing parameter" << j << ", value:" << parameter; + } + if ( this->argumentAdded( parameter ) ) + { + // this->Internal->ErrorString = + // missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess); + // if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; } + if ( ok ) + { + *ok = false; + } + return map(); + } + if ( !currentArgDesc->addParameter( parameter ) ) + { + // this->Internal->ErrorString = string( + // "Value(s) associated with argument %1 are incorrect. %2"). + // arg(argument).arg(currentArgDesc->ExactMatchFailedMessage); + // if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; } + if ( ok ) + { + *ok = false; + } + return map(); + } + } + // Update main loop increment + i = i + numberOfParametersToProcess; + } + else if ( numberOfParametersToProcess == -1 ) + { + if ( this->Internal->Debug ) + { + std::cout << " Proccessing StringList ..."; + } + int j = 1; + while ( j + i < arguments.size() ) + { + if ( this->argumentAdded( arguments.at( j + i ) ) ) + { + if ( this->Internal->Debug ) + { + std::cout << " No more parameter for" << argument; + } + break; + } + string parameter = arguments.at( j + i ); + + if ( parameter.compare( 0, Internal->LongPrefix.size(), Internal->LongPrefix ) == 0 || + parameter.compare( 0, Internal->ShortPrefix.size(), Internal->ShortPrefix ) == 0 ) + { + j--; + break; + } + + if ( this->Internal->Debug ) + { + std::cout << " Processing parameter" << j << ", value:" << parameter; + } + if ( !currentArgDesc->addParameter( parameter ) ) + { + // this->Internal->ErrorString = string( + // "Value(s) associated with argument %1 are incorrect. %2"). + // arg(argument).arg(currentArgDesc->ExactMatchFailedMessage); + // if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; } + if ( ok ) + { + *ok = false; + } + return map(); + } + j++; + } + // Update main loop increment + i = i + j; + } + } + else + { + if ( this->Internal->StrictMode ) + { + this->Internal->ErrorString = "Unknown argument "; + this->Internal->ErrorString += argument; + error = true; + break; + } + if ( this->Internal->Debug ) + { + std::cout << " Skipping: Unknown argument"; + } + this->Internal->UnparsedArguments.push_back( argument ); + } + } + + if ( ok ) + { + *ok = !error; + } + + map parsedArguments; + + int obligatoryArgs = 0; + vector::iterator it; + for ( it = Internal->ArgumentDescriptionList.begin(); it != Internal->ArgumentDescriptionList.end(); ++it ) + { + CommandLineParserArgumentDescription* desc = *it; + + if ( !desc->Optional ) + obligatoryArgs++; + } + + int parsedObligatoryArgs = 0; + for ( it = parsedArgDescriptions.begin(); it != parsedArgDescriptions.end(); ++it ) + { + CommandLineParserArgumentDescription* desc = *it; + + string key; + if ( !desc->LongArg.empty() ) + { + key = desc->LongArg; + } + else + { + key = desc->ShortArg; + } + + if ( !desc->Optional ) + parsedObligatoryArgs++; + + std::pair elem; + elem.first = key; + elem.second = desc->Value; + parsedArguments.insert( elem ); + } + + if ( obligatoryArgs > parsedObligatoryArgs ) + { + parsedArguments.clear(); + cout << helpText(); + } + + return parsedArguments; +} + +// ------------------------------------------------------------------------- +map mitkCommandLineParser::parseArguments( int argc, char** argv, bool* ok ) +{ + StringContainerType arguments; + + // Create a StringContainerType of arguments + for ( int i = 0; i < argc; ++i ) + arguments.push_back( argv[ i ] ); + + return this->parseArguments( arguments, ok ); +} + +// ------------------------------------------------------------------------- +string mitkCommandLineParser::errorString() const +{ + return this->Internal->ErrorString; +} + +// ------------------------------------------------------------------------- +const mitkCommandLineParser::StringContainerType& mitkCommandLineParser::unparsedArguments() const +{ + return this->Internal->UnparsedArguments; +} + +// -------------------------------------------------------------------------- +void mitkCommandLineParser::addArgument( const string& longarg, + const string& shortarg, + Type type, + const string& argLabel, + const string& argHelp, + const us::Any& defaultValue, + bool optional, + bool ignoreRest, + bool deprecated ) +{ + if ( longarg.empty() && shortarg.empty() ) + { + return; + } + + /* Make sure it's not already added */ + bool added = ( this->Internal->ArgNameToArgumentDescriptionMap.count( longarg ) != 0 ); + if ( added ) + { + return; + } + + added = ( this->Internal->ArgNameToArgumentDescriptionMap.count( shortarg ) != 0 ); + if ( added ) + { + return; + } + + auto argDesc = new CommandLineParserArgumentDescription( longarg, + this->Internal->LongPrefix, + shortarg, + this->Internal->ShortPrefix, + type, + argHelp, + argLabel, + defaultValue, + ignoreRest, + deprecated, + optional, + ParameterGroupName, + ParameterGroupDescription ); + + std::string::size_type argWidth = 0; + if ( !longarg.empty() ) + { + this->Internal->ArgNameToArgumentDescriptionMap[ longarg ] = argDesc; + argWidth += longarg.size() + this->Internal->LongPrefix.size(); + } + if ( !shortarg.empty() ) + { + this->Internal->ArgNameToArgumentDescriptionMap[ shortarg ] = argDesc; + argWidth += shortarg.size() + this->Internal->ShortPrefix.size() + 2; + } + argWidth += 5; + + // Set the field width for the arguments + if ( argWidth > this->Internal->FieldWidth ) + { + this->Internal->FieldWidth = argWidth; + } + + this->Internal->ArgumentDescriptionList.push_back( argDesc ); + this->Internal->GroupToArgumentDescriptionListMap[ this->Internal->CurrentGroup ].push_back( argDesc ); +} + +// -------------------------------------------------------------------------- +void mitkCommandLineParser::addDeprecatedArgument( const string& longarg, + const string& shortarg, + const string& argLabel, + const string& argHelp ) +{ + addArgument( longarg, shortarg, StringList, argLabel, argHelp, us::Any(), false, true, false ); +} + +// -------------------------------------------------------------------------- +std::string::size_type mitkCommandLineParser::fieldWidth() const +{ + return this->Internal->FieldWidth; +} + +// -------------------------------------------------------------------------- +void mitkCommandLineParser::beginGroup( const string& description ) +{ + this->Internal->CurrentGroup = description; +} + +// -------------------------------------------------------------------------- +void mitkCommandLineParser::endGroup() +{ + this->Internal->CurrentGroup.clear(); +} + +// -------------------------------------------------------------------------- +string mitkCommandLineParser::helpText() const +{ + string text; + vector deprecatedArgs; + + // Loop over grouped argument descriptions + map>::iterator it; + for ( it = Internal->GroupToArgumentDescriptionListMap.begin(); + it != Internal->GroupToArgumentDescriptionListMap.end(); + ++it ) + { + if ( !( *it ).first.empty() ) + { + text = text + "\n" + ( *it ).first + "\n"; + } + + vector::iterator it2; + for ( it2 = ( *it ).second.begin(); it2 != ( *it ).second.end(); ++it2 ) + { + CommandLineParserArgumentDescription* argDesc = *it2; + if ( argDesc->Deprecated ) + { + deprecatedArgs.push_back( argDesc ); + } + else + { + text += argDesc->helpText(); + } + } + } + + if ( !deprecatedArgs.empty() ) + { + text += "\nDeprecated arguments:\n"; + vector::iterator it2; + for ( it2 = deprecatedArgs.begin(); it2 != deprecatedArgs.end(); ++it2 ) + { + CommandLineParserArgumentDescription* argDesc = *it2; + text += argDesc->helpText(); + } + } + + return text; +} + +// -------------------------------------------------------------------------- +bool mitkCommandLineParser::argumentAdded( const string& argument ) const +{ + return ( this->Internal->ArgNameToArgumentDescriptionMap.count( argument ) != 0 ); +} + +// -------------------------------------------------------------------------- +bool mitkCommandLineParser::argumentParsed( const string& argument ) const +{ + for ( unsigned int i = 0; i < Internal->ProcessedArguments.size(); i++ ) + if ( argument.compare( Internal->ProcessedArguments.at( i ) ) == 0 ) + return true; + return false; +} + +// -------------------------------------------------------------------------- +void mitkCommandLineParser::setArgumentPrefix( const string& longPrefix, const string& shortPrefix ) +{ + this->Internal->LongPrefix = longPrefix; + this->Internal->ShortPrefix = shortPrefix; +} + +// -------------------------------------------------------------------------- +void mitkCommandLineParser::setStrictModeEnabled( bool strictMode ) +{ + this->Internal->StrictMode = strictMode; +} + +void mitkCommandLineParser::generateXmlOutput() +{ + std::stringstream xml; + + xml << "" << endl; + xml << "" << Category << "" << endl; + xml << "" << Title << "" << endl; + xml << "" << Description << "" << endl; + xml << "" << Contributor << "" << endl; + xml << "" << endl; + + std::vector::iterator it; + + std::string lastParameterGroup = ""; + for ( it = this->Internal->ArgumentDescriptionList.begin(); it != this->Internal->ArgumentDescriptionList.end(); + it++ ) + { + std::string type; + switch ( ( *it )->ValueType ) + { + case mitkCommandLineParser::String: + type = "string"; + break; + + case mitkCommandLineParser::Bool: + type = "boolean"; + break; + + case mitkCommandLineParser::StringList: + type = "string-vector"; + break; + + case mitkCommandLineParser::Int: + type = "integer"; + break; + + case mitkCommandLineParser::Float: + type = "float"; + break; + + case mitkCommandLineParser::OutputDirectory: + case mitkCommandLineParser::InputDirectory: + type = "directory"; + break; + + case mitkCommandLineParser::InputImage: + type = "image"; + break; + + case mitkCommandLineParser::OutputFile: + case mitkCommandLineParser::InputFile: + type = "file"; + break; + } + + if ( lastParameterGroup.compare( ( *it )->ArgGroup ) ) + { + if ( it != this->Internal->ArgumentDescriptionList.begin() ) + { + xml << "" << endl; + xml << "" << endl; + } + xml << "" << endl; + xml << "" << ( *it )->ArgGroupDescription << "" << endl; + lastParameterGroup = ( *it )->ArgGroup; + } + + // Skip help item, as it's no use in GUI + if ( ( *it )->ShortArg == "h" ) + continue; + + xml << "<" << type << ">" << endl; + xml << "" << ( *it )->LongArg << "" << endl; + xml << "" << ( *it )->ArgHelp << "" << endl; + xml << "" << endl; + if ( !( *it )->DefaultValue.Empty() ) + xml << "" << ( *it )->DefaultValue.ToString() << "" << endl; + + xml << "" << ( *it )->LongArg << "" << endl; + xml << "" << ( *it )->ShortArg << "" << endl; + + if ( ( *it )->ValueType == mitkCommandLineParser::InputDirectory || + ( *it )->ValueType == mitkCommandLineParser::InputFile || + ( *it )->ValueType == mitkCommandLineParser::InputImage ) + { + xml << "input" << endl; + } + else if ( ( *it )->ValueType == mitkCommandLineParser::OutputDirectory || + ( *it )->ValueType == mitkCommandLineParser::OutputFile ) + { + xml << "output" << endl; + } + xml << "" << endl; + } + + xml << "" << endl; + xml << "" << endl; + + cout << xml.str(); +} + +void mitkCommandLineParser::setTitle( string title ) +{ + Title = title; +} +void mitkCommandLineParser::setContributor( string contributor ) +{ + Contributor = contributor; +} + +void mitkCommandLineParser::setCategory( string category ) +{ + Category = category; +} + +void mitkCommandLineParser::setDescription( string description ) +{ + Description = description; +} + +void mitkCommandLineParser::changeParameterGroup( string name, string tooltip ) +{ + ParameterGroupName = name; + ParameterGroupDescription = tooltip; +} diff --git a/Modules/Core/TestingHelper/include/mitkTestFixture.h b/Modules/Core/TestingHelper/include/mitkTestFixture.h index 6657749b10..2e9f5889eb 100644 --- a/Modules/Core/TestingHelper/include/mitkTestFixture.h +++ b/Modules/Core/TestingHelper/include/mitkTestFixture.h @@ -1,132 +1,132 @@ /*=================================================================== 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. ===================================================================*/ #ifndef MITKTESTFIXTURE_H #define MITKTESTFIXTURE_H #include #include #include #include #include namespace mitk { /** * \brief Test fixture for parameterized tests * * This class is a drop-in replacement for CppUnit::TextFixture and * enables test methods to access individual parameters. You can also * invoke one method multiple times with different parameters. * * - * The following simple example creates a single test withoud custom + * The following simple example creates a single test without custom * parameters: * * \code * class MySimpleTestSuite : public mitk::TestFixture * { * CPPUNIT_TEST_SUITE(MySimpleTestSuite); * MITK_TEST(FivePlusFiveTest); * CPPUNIT_TEST_SUITE_END(); * * public: * void FivePlusFiveTest() * { * CPPUNIT_ASSERT(5+5 == 10); * } * }; * MITK_TEST_SUITE_REGISTRATION(MySimpleTestSuite) * \endcode * * * The following example creates a test class containing only * one test method, but the associated test suite contains three tests, * using different parameters for each call of the same method. Use * the macro MITK_PARAMETERIZED_TEST_1 only if you know what you are * doing. If you are not sure, use MITK_TEST instead. * * \code * class MyTestSuite : public mitk::TestFixture * { * CPPUNIT_TEST_SUITE(MyTestSuite); * MITK_PARAMETERIZED_TEST_1(TestSomething, "One"); * MITK_PARAMETERIZED_TEST_1(TestSomething, "Two"); * MITK_PARAMETERIZED_TEST_1(TestSomething, "Three"); * CPPUNIT_TEST_SUITE_END(); * * public: * * void TestSomething() * { * std::vector parameter = GetTestParameter(); * CPPUNIT_ASSERT(parameter.size() == 1); * std::string testParam = parameter[0]; * * MITK_INFO << "Parameter: " << testParam; * } * }; * MITK_TEST_SUITE_REGISTRATION(MyTestSuite) * \endcode * * \sa MITK_PARAMETERIZED_TEST * \sa MITK_PARAMETERIZED_TEST_1 */ class TestFixture : public CppUnit::TestFixture { protected: /** * \brief Get parameters for this test fixture * * This method can be called in tests added via the MITK_PARAMETERIZED_TEST * macro or one of its variants. * * \return The list of \c std::string parameters passed to previous calls * of the MITK_PARAMETERIZED_TEST macro or one of its variants. * */ std::vector GetTestParameter() const { return m_Parameter; } /** * \brief Get the absolute path for test data. * * \param testData The realative path in the MITK test data repository. * * \return The absolute path for the test data. */ static std::string GetTestDataFilePath(const std::string& testData) { if (itksys::SystemTools::FileIsFullPath(testData.c_str())) return testData; return std::string(MITK_DATA_DIR) + "/" + testData; } private: template friend class TestCaller; std::vector m_Parameter; }; } #endif // MITKTESTFIXTURE_H diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index 2efe3c5dc2..0a3ef325a0 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,325 +1,326 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkCoreActivator.cpp mitkCoreObjectFactoryBase.cpp mitkCoreObjectFactory.cpp mitkCoreServices.cpp mitkException.cpp Algorithms/mitkBaseDataSource.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkFocusManager.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkSlicesRotator.cpp Controllers/mitkSlicesSwiveller.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkInteractorCameraController.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataStorage.cpp DataManagement/mitkDisplayGeometry.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGeometryTransformHolder.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLine.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMaterial.cpp DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModeOperation.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateSource.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkShaderProperty.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStateTransitionOperation.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkVector.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp Interactions/mitkAction.cpp Interactions/mitkAffineInteractor.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCoordinateSupplier.cpp Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkDisplayPositionEvent.cpp # Interactions/mitkDisplayVectorInteractorLevelWindow.cpp # legacy, prob even now unneeded # Interactions/mitkDisplayVectorInteractorScroll.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEvent.cpp Interactions/mitkEventDescription.cpp Interactions/mitkEventFactory.cpp Interactions/mitkEventMapper.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkGlobalInteraction.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkInteractionEventObserver.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractor.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkKeyEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMouseMovePointSetInteractor.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkMoveBaseDataInteractor.cpp Interactions/mitkNodeDepententPointSetInteractor.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkPointSetInteractor.cpp Interactions/mitkPositionEvent.cpp Interactions/mitkPositionTracker.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkState.cpp Interactions/mitkStateEvent.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateMachine.cpp Interactions/mitkStateMachineFactory.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkTransition.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkWheelEvent.cpp Interactions/mitkXML2EventParser.cpp IO/mitkAbstractFileIO.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkCustomMimeType.cpp IO/mitkDicomSeriesReader.cpp IO/mitkDicomSeriesReaderService.cpp IO/mitkDicomSR_GantryTiltInformation.cpp IO/mitkDicomSR_ImageBlockDescriptor.cpp IO/mitkDicomSR_LoadDICOMRGBPixel4D.cpp IO/mitkDicomSR_LoadDICOMRGBPixel.cpp IO/mitkDicomSR_LoadDICOMScalar4D.cpp IO/mitkDicomSR_LoadDICOMScalar.cpp IO/mitkDicomSR_SliceGroupingResult.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileReaderWriterBase.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp + IO/mitkGeometry3DToXML.cpp IO/mitkIFileIO.cpp IO/mitkIFileReader.cpp IO/mitkIFileWriter.cpp IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp IO/mitkIOConstants.cpp IO/mitkIOMimeTypes.cpp IO/mitkIOUtil.cpp IO/mitkItkImageIO.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkLegacyFileReaderService.cpp IO/mitkLegacyFileWriterService.cpp IO/mitkLog.cpp IO/mitkMimeType.cpp IO/mitkMimeTypeProvider.cpp IO/mitkOperation.cpp IO/mitkPixelType.cpp IO/mitkPointSetReaderService.cpp IO/mitkPointSetWriterService.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSurfaceStlIO.cpp IO/mitkSurfaceVtkIO.cpp IO/mitkSurfaceVtkLegacyIO.cpp IO/mitkSurfaceVtkXmlIO.cpp IO/mitkVtkLoggingAdapter.cpp Rendering/mitkAbstractOverlayLayouter.cpp Rendering/mitkBaseRenderer.cpp #Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module Rendering/mitkGradientBackground.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/mitkIShaderRepository.cpp Rendering/mitkManufacturerLogo.cpp Rendering/mitkMapper.cpp Rendering/mitkOverlay.cpp Rendering/mitkOverlayManager.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowFrame.cpp #Rendering/mitkSurfaceGLMapper2D.cpp Moved to deprecated LegacyGL Module Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkVtkOverlay2D.cpp Rendering/mitkVtkOverlay3D.cpp Rendering/mitkVtkOverlay.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigPACS.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITK.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml ) diff --git a/Modules/Core/src/DataManagement/mitkPointSet.cpp b/Modules/Core/src/DataManagement/mitkPointSet.cpp index e5b54f0a43..5009771f4d 100755 --- a/Modules/Core/src/DataManagement/mitkPointSet.cpp +++ b/Modules/Core/src/DataManagement/mitkPointSet.cpp @@ -1,920 +1,922 @@ /*=================================================================== 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 "mitkPointSet.h" #include "mitkPointOperation.h" #include "mitkInteractionConst.h" #include #include mitk::PointSet::PointSet() : m_CalculateBoundingBox(true) { this->InitializeEmpty(); } mitk::PointSet::PointSet(const PointSet &other) : BaseData(other) , m_PointSetSeries(other.GetPointSetSeriesSize()) , m_CalculateBoundingBox(true) { // Copy points for (std::size_t t = 0; t < m_PointSetSeries.size(); ++t) { m_PointSetSeries[t] = DataType::New(); DataType::Pointer otherPts = other.GetPointSet(t); for (PointsConstIterator i = other.Begin(t); i != other.End(t); ++i) { m_PointSetSeries[t]->SetPoint(i.Index(), i.Value()); PointDataType pointData; if (otherPts->GetPointData(i.Index(), &pointData)) { m_PointSetSeries[t]->SetPointData(i.Index(), pointData); } } } } mitk::PointSet::~PointSet() { this->ClearData(); } void mitk::PointSet::ClearData() { m_PointSetSeries.clear(); Superclass::ClearData(); } void mitk::PointSet::InitializeEmpty() { m_PointSetSeries.resize( 1 ); m_PointSetSeries[0] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[0]->SetPointData( pointData ); m_CalculateBoundingBox = false; Superclass::InitializeTimeGeometry(1); m_Initialized = true; m_EmptyPointsContainer = DataType::PointsContainer::New(); } bool mitk::PointSet::IsEmptyTimeStep(unsigned int t) const { return IsInitialized() && (GetSize(t) == 0); } void mitk::PointSet::Expand( unsigned int timeSteps ) { // Check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient pre-initialized // elements. // // NOTE: This method will never REDUCE the vector size; it should only // be used to make sure that the vector has enough elements to include the // specified time step. unsigned int oldSize = m_PointSetSeries.size(); if ( timeSteps > oldSize ) { Superclass::Expand( timeSteps ); m_PointSetSeries.resize( timeSteps ); for ( unsigned int i = oldSize; i < timeSteps; ++i ) { m_PointSetSeries[i] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[i]->SetPointData( pointData ); } //if the size changes, then compute the bounding box m_CalculateBoundingBox = true; this->InvokeEvent( PointSetExtendTimeRangeEvent() ); } } unsigned int mitk::PointSet::GetPointSetSeriesSize() const { return m_PointSetSeries.size(); } int mitk::PointSet::GetSize( unsigned int t ) const { if ( t < m_PointSetSeries.size() ) { return m_PointSetSeries[t]->GetNumberOfPoints(); } else { return 0; } } mitk::PointSet::DataType::Pointer mitk::PointSet::GetPointSet( int t ) const { if ( t < (int)m_PointSetSeries.size() ) { return m_PointSetSeries[t]; } else { return nullptr; } } mitk::PointSet::PointsIterator mitk::PointSet::Begin( int t ) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsConstIterator mitk::PointSet::Begin(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsIterator mitk::PointSet::End( int t ) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsConstIterator mitk::PointSet::End(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsIterator mitk::PointSet::GetMaxId( int t ) { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return m_EmptyPointsContainer->End(); } return this->Begin( t ) == this->End( t ) ? this->End( t ) : --End( t ); } int mitk::PointSet::SearchPoint( Point3D point, ScalarType distance, int t ) const { if ( t >= (int)m_PointSetSeries.size() ) { return -1; } // Out is the point which is checked to be the searched point PointType out; out.Fill( 0 ); PointType indexPoint; this->GetGeometry( t )->WorldToIndex(point, indexPoint); // Searching the first point in the Set, that is +- distance far away fro // the given point unsigned int i; PointsContainer::Iterator it, end; end = m_PointSetSeries[t]->GetPoints()->End(); int bestIndex = -1; distance = distance * distance; // To correct errors from converting index to world and world to index if (distance == 0.0) { distance = 0.000001; } ScalarType bestDist = distance; ScalarType dist, tmp; for ( it = m_PointSetSeries[t]->GetPoints()->Begin(), i = 0; it != end; ++it, ++i ) { bool ok = m_PointSetSeries[t]->GetPoints() ->GetElementIfIndexExists( it->Index(), &out ); if ( !ok ) { return -1; } else if ( indexPoint == out ) //if totally equal { return it->Index(); } //distance calculation tmp = out[0] - indexPoint[0]; dist = tmp * tmp; tmp = out[1] - indexPoint[1]; dist += tmp * tmp; tmp = out[2] - indexPoint[2]; dist += tmp * tmp; if ( dist < bestDist ) { bestIndex = it->Index(); bestDist = dist; } } return bestIndex; } mitk::PointSet::PointType mitk::PointSet::GetPoint( PointIdentifier id, int t ) const { PointType out; out.Fill(0); if ( (unsigned int) t >= m_PointSetSeries.size() ) { return out; } if ( m_PointSetSeries[t]->GetPoints()->IndexExists(id) ) { m_PointSetSeries[t]->GetPoint( id, &out ); this->GetGeometry(t)->IndexToWorld( out, out ); return out; } else { return out; } } bool mitk::PointSet ::GetPointIfExists( PointIdentifier id, PointType* point, int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return false; } if ( m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(id, point) ) { this->GetGeometry( t )->IndexToWorld( *point, *point ); return true; } else { return false; } } void mitk::PointSet::SetPoint( PointIdentifier id, PointType point, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::SetPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::InsertPoint( PointIdentifier id, PointType point, int t ) { this->InsertPoint(id, point, mitk::PTUNDEFINED, t); } void mitk::PointSet::InsertPoint( PointIdentifier id, PointType point, PointSpecificationType spec, int t ) { if ( (unsigned int) t < m_PointSetSeries.size() ) { mitk::Point3D indexPoint; mitk::BaseGeometry* tempGeometry = this->GetGeometry( t ); if (tempGeometry == nullptr) { MITK_INFO<< __FILE__ << ", l." << __LINE__ << ": GetGeometry of "<< t <<" returned NULL!" << std::endl; return; } tempGeometry->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->GetPoints()->InsertElement( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->GetPointData()->InsertElement(id, defaultPointData); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } } mitk::PointSet::PointIdentifier mitk::PointSet::InsertPoint( PointType point, int t ) { // Adapt the size of the data vector if necessary this->Expand( t+1 ); PointIdentifier id = 0; if ( m_PointSetSeries[t]->GetNumberOfPoints() > 0 ) { PointsIterator it = --End( t ); id = it.Index(); ++id; } mitk::Point3D indexPoint; this->GetGeometry( t )->WorldToIndex( point, indexPoint ); m_PointSetSeries[t]->SetPoint( id, indexPoint ); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData( id, defaultPointData ); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); return id; } bool mitk::PointSet::SwapPointPosition( PointIdentifier id, bool moveUpwards, int t ) { if(IndexExists(id, t) ) { PointType point = GetPoint(id,t); if(moveUpwards) {//up if(IndexExists(id-1,t)) { InsertPoint(id, GetPoint(id - 1, t), t); InsertPoint(id-1,point,t); this->Modified(); return true; } } else {//down if(IndexExists(id+1,t)) { InsertPoint(id, GetPoint(id + 1, t), t); InsertPoint(id+1,point,t); this->Modified(); return true; } } } return false; } bool mitk::PointSet::IndexExists( int position, int t ) const { if ( (unsigned int) t < m_PointSetSeries.size() ) { return m_PointSetSeries[t]->GetPoints()->IndexExists( position ); } else { return false; } } bool mitk::PointSet::GetSelectInfo( int position, int t ) const { if ( this->IndexExists( position, t ) ) { PointDataType pointData = { 0, false, PTUNDEFINED }; m_PointSetSeries[t]->GetPointData( position, &pointData ); return pointData.selected; } else { return false; } } void mitk::PointSet::SetSelectInfo( int position, bool selected, int t ) { if ( this->IndexExists( position, t ) ) { // timeStep to ms TimePointType timeInMS = this->GetTimeGeometry()->TimeStepToTimePoint( t ); // point Point3D point = this->GetPoint( position, t ); std::auto_ptr op; if (selected) { op.reset(new mitk::PointOperation(OpSELECTPOINT, timeInMS, point, position )); } else { op.reset(new mitk::PointOperation(OpDESELECTPOINT, timeInMS, point, position )); } this->ExecuteOperation( op.get() ); } } mitk::PointSpecificationType mitk::PointSet::GetSpecificationTypeInfo( int position, int t ) const { if ( this->IndexExists( position, t ) ) { PointDataType pointData = { 0, false, PTUNDEFINED }; m_PointSetSeries[t]->GetPointData( position, &pointData ); return pointData.pointSpec; } else { return PTUNDEFINED; } } int mitk::PointSet::GetNumberOfSelected( int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return 0; } int numberOfSelected = 0; PointDataIterator it; for ( it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++ ) { if (it->Value().selected == true) { ++numberOfSelected; } } return numberOfSelected; } int mitk::PointSet::SearchSelectedPoint( int t ) const { if ( (unsigned int) t >= m_PointSetSeries.size() ) { return -1; } PointDataIterator it; for ( it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++ ) { if ( it->Value().selected == true ) { return it->Index(); } } return -1; } void mitk::PointSet::ExecuteOperation( Operation* operation ) { int timeStep = -1; mitkCheckOperationTypeMacro(PointOperation, operation, pointOp); if ( pointOp ) { timeStep = this->GetTimeGeometry()->TimePointToTimeStep( pointOp->GetTimeInMS() ); } if ( timeStep < 0 ) { MITK_ERROR << "Time step (" << timeStep << ") outside of PointSet time bounds" << std::endl; return; } switch (operation->GetOperationType()) { case OpNOTHING: break; case OpINSERT://inserts the point at the given position and selects it. { int position = pointOp->GetIndex(); PointType pt; pt.CastFrom(pointOp->GetPoint()); + if(timeStep >= (int)this->GetTimeSteps()) this->Expand(timeStep+1); + //transfer from world to index coordinates mitk::BaseGeometry* geometry = this->GetGeometry( timeStep ); if (geometry == nullptr) { MITK_INFO<<"GetGeometry returned NULL!\n"; return; } geometry->WorldToIndex(pt, pt); m_PointSetSeries[timeStep]->GetPoints()->InsertElement(position, pt); PointDataType pointData = { static_cast(pointOp->GetIndex()), pointOp->GetSelected(), pointOp->GetPointType() }; m_PointSetSeries[timeStep]->GetPointData() ->InsertElement(position, pointData); this->Modified(); //boundingbox has to be computed m_CalculateBoundingBox = true; this->InvokeEvent( PointSetAddEvent() ); this->OnPointSetChange(); } break; case OpMOVE://moves the point given by index { PointType pt; pt.CastFrom(pointOp->GetPoint()); //transfer from world to index coordinates this->GetGeometry( timeStep )->WorldToIndex(pt, pt); // Copy new point into container m_PointSetSeries[timeStep]->SetPoint(pointOp->GetIndex(), pt); // Insert a default point data object to keep the containers in sync // (if no point data object exists yet) PointDataType pointData; if ( !m_PointSetSeries[timeStep]->GetPointData( pointOp->GetIndex(), &pointData ) ) { m_PointSetSeries[timeStep]->SetPointData( pointOp->GetIndex(), pointData ); } this->OnPointSetChange(); this->Modified(); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent( PointSetMoveEvent() ); } break; case OpREMOVE://removes the point at given by position { m_PointSetSeries[timeStep]->GetPoints()->DeleteIndex((unsigned)pointOp->GetIndex()); m_PointSetSeries[timeStep]->GetPointData()->DeleteIndex((unsigned)pointOp->GetIndex()); this->OnPointSetChange(); this->Modified(); //boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent( PointSetRemoveEvent() ); } break; case OpSELECTPOINT://select the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = true; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpDESELECTPOINT://unselect the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = false; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpSETPOINTTYPE: { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.pointSpec = pointOp->GetPointType(); m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpMOVEPOINTUP: // swap content of point with ID pointOp->GetIndex() with the point preceding it in the container // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that precedes this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); auto it = points.find(currentID); if (it == points.end()) // ID not found break; if (it == points.begin()) // we are at the first element, there is no previous element break; /* get and cache current point & pointdata and previous point & pointdata */ --it; PointIdentifier prevID = it->first; if (this->SwapPointContents(prevID, currentID, timeStep) == true) this->Modified(); } break; case OpMOVEPOINTDOWN: // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that succeeds this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); auto it = points.find(currentID); if (it == points.end()) // ID not found break; ++it; if (it == points.end()) // ID is already the last element, there is no succeeding element break; /* get and cache current point & pointdata and previous point & pointdata */ PointIdentifier nextID = it->first; if (this->SwapPointContents(nextID, currentID, timeStep) == true) this->Modified(); } break; default: itkWarningMacro("mitkPointSet could not understrand the operation. Please check!"); break; } //to tell the mappers, that the data is modified and has to be updated //only call modified if anything is done, so call in cases //this->Modified(); mitk::OperationEndEvent endevent(operation); ((const itk::Object*)this)->InvokeEvent(endevent); //*todo has to be done here, cause of update-pipeline not working yet // As discussed lately, don't mess with the rendering from inside data structures //mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PointSet::UpdateOutputInformation() { if ( this->GetSource( ) ) { this->GetSource( )->UpdateOutputInformation( ); } // // first make sure, that the associated time sliced geometry has // the same number of geometry 3d's as PointSets are present // TimeGeometry* timeGeometry = GetTimeGeometry(); if ( timeGeometry->CountTimeSteps() != m_PointSetSeries.size() ) { itkExceptionMacro(<<"timeGeometry->CountTimeSteps() != m_PointSetSeries.size() -- use Initialize(timeSteps) with correct number of timeSteps!"); } // This is needed to detect zero objects mitk::ScalarType nullpoint[]={0,0,0,0,0,0}; BoundingBox::BoundsArrayType itkBoundsNull(nullpoint); // // Iterate over the PointSets and update the Geometry // information of each of the items. // if (m_CalculateBoundingBox) { for ( unsigned int i = 0 ; i < m_PointSetSeries.size() ; ++i ) { const DataType::BoundingBoxType *bb = m_PointSetSeries[i]->GetBoundingBox(); BoundingBox::BoundsArrayType itkBounds = bb->GetBounds(); if ( m_PointSetSeries[i].IsNull() || (m_PointSetSeries[i]->GetNumberOfPoints() == 0) || (itkBounds == itkBoundsNull) ) { itkBounds = itkBoundsNull; continue; } // Ensure minimal bounds of 1.0 in each dimension for ( unsigned int j = 0; j < 3; ++j ) { if ( itkBounds[j*2+1] - itkBounds[j*2] < 1.0 ) { BoundingBox::CoordRepType center = (itkBounds[j*2] + itkBounds[j*2+1]) / 2.0; itkBounds[j*2] = center - 0.5; itkBounds[j*2+1] = center + 0.5; } } this->GetGeometry(i)->SetBounds(itkBounds); } m_CalculateBoundingBox = false; } this->GetTimeGeometry()->Update(); } void mitk::PointSet::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::PointSet::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::PointSet::VerifyRequestedRegion() { return true; } void mitk::PointSet::SetRequestedRegion(const DataObject * ) { } void mitk::PointSet::PrintSelf( std::ostream& os, itk::Indent indent ) const { Superclass::PrintSelf(os, indent); os << indent << "Number timesteps: " << m_PointSetSeries.size() << "\n"; unsigned int i = 0; for (auto it = m_PointSetSeries.begin(); it != m_PointSetSeries.end(); ++it) { os << indent << "Timestep " << i++ << ": \n"; MeshType::Pointer ps = *it; itk::Indent nextIndent = indent.GetNextIndent(); ps->Print(os, nextIndent); MeshType::PointsContainer* points = ps->GetPoints(); MeshType::PointDataContainer* datas = ps->GetPointData(); MeshType::PointDataContainer::Iterator dataIterator = datas->Begin(); for (MeshType::PointsContainer::Iterator pointIterator = points->Begin(); pointIterator != points->End(); ++pointIterator, ++dataIterator) { os << nextIndent << "Point " << pointIterator->Index() << ": ["; os << pointIterator->Value().GetElement(0); for (unsigned int i = 1; i < PointType::GetPointDimension(); ++i) { os << ", " << pointIterator->Value().GetElement(i); } os << "]"; os << ", selected: " << dataIterator->Value().selected << ", point spec: " << dataIterator->Value().pointSpec << "\n"; } } } bool mitk::PointSet::SwapPointContents(PointIdentifier id1, PointIdentifier id2, int timeStep) { /* search and cache contents */ PointType p1; if (m_PointSetSeries[timeStep]->GetPoint(id1, &p1) == false) return false; PointDataType data1; if (m_PointSetSeries[timeStep]->GetPointData(id1, &data1) == false) return false; PointType p2; if (m_PointSetSeries[timeStep]->GetPoint(id2, &p2) == false) return false; PointDataType data2; if (m_PointSetSeries[timeStep]->GetPointData(id2, &data2) == false) return false; /* now swap contents */ m_PointSetSeries[timeStep]->SetPoint(id1, p2); m_PointSetSeries[timeStep]->SetPointData(id1, data2); m_PointSetSeries[timeStep]->SetPoint(id2, p1); m_PointSetSeries[timeStep]->SetPointData(id2, data1); return true; } bool mitk::PointSet::PointDataType::operator ==(const mitk::PointSet::PointDataType &other) const { return id == other.id && selected == other.selected && pointSpec == other.pointSpec; } bool mitk::Equal( const mitk::PointSet* leftHandSide, const mitk::PointSet* rightHandSide, mitk::ScalarType eps, bool verbose ) { if((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal( const mitk::PointSet* leftHandSide, const mitk::PointSet* rightHandSide, mitk::ScalarType eps, bool verbose ) does not work with NULL pointer input."; return false; } return Equal( *leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal( const mitk::PointSet& leftHandSide, const mitk::PointSet& rightHandSide, mitk::ScalarType eps, bool verbose ) { bool result = true; if( !mitk::Equal( *leftHandSide.GetGeometry(), *rightHandSide.GetGeometry(), eps, verbose) ) { if(verbose) MITK_INFO << "[( PointSet )] Geometries differ."; result = false; } if ( leftHandSide.GetSize() != rightHandSide.GetSize()) { if(verbose) MITK_INFO << "[( PointSet )] Number of points differ."; result = false; } else { //if the size is equal, we compare the point values mitk::Point3D pointLeftHandSide; mitk::Point3D pointRightHandSide; int numberOfIncorrectPoints = 0; //Iterate over both pointsets in order to compare all points pair-wise mitk::PointSet::PointsConstIterator end = leftHandSide.End(); for( mitk::PointSet::PointsConstIterator pointSetIteratorLeft = leftHandSide.Begin(), pointSetIteratorRight = rightHandSide.Begin(); pointSetIteratorLeft != end; ++pointSetIteratorLeft, ++pointSetIteratorRight) //iterate simultaneously over both sets { pointLeftHandSide = pointSetIteratorLeft.Value(); pointRightHandSide = pointSetIteratorRight.Value(); if( !mitk::Equal( pointLeftHandSide, pointRightHandSide, eps, verbose ) ) { if(verbose) MITK_INFO << "[( PointSet )] Point values are different."; result = false; numberOfIncorrectPoints++; } } if((numberOfIncorrectPoints > 0) && verbose) { MITK_INFO << numberOfIncorrectPoints <<" of a total of " << leftHandSide.GetSize() << " points are different."; } } return result; } diff --git a/Modules/Core/src/IO/mitkGeometry3DToXML.cpp b/Modules/Core/src/IO/mitkGeometry3DToXML.cpp new file mode 100644 index 0000000000..2855711f6f --- /dev/null +++ b/Modules/Core/src/IO/mitkGeometry3DToXML.cpp @@ -0,0 +1,193 @@ +/*=================================================================== + +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 "mitkGeometry3DToXML.h" + +#include + +TiXmlElement* mitk::Geometry3DToXML::ToXML(const Geometry3D* geom3D) +{ + assert(geom3D); + + // really serialize + const AffineTransform3D* transform = geom3D->GetIndexToWorldTransform(); + + // get transform parameters that would need to be serialized + AffineTransform3D::MatrixType matrix = transform->GetMatrix(); + AffineTransform3D::OffsetType offset = transform->GetOffset(); + + bool isImageGeometry = geom3D->GetImageGeometry(); + BaseGeometry::BoundsArrayType bounds = geom3D->GetBounds(); + + // create XML file + // construct XML tree describing the geometry + TiXmlElement* geomElem = new TiXmlElement("Geometry3D"); + geomElem->SetAttribute("ImageGeometry", isImageGeometry ? "true" : "false" ); + geomElem->SetAttribute("FrameOfReferenceID", geom3D->GetFrameOfReferenceID()); + + // coefficients are matrix[row][column]! + TiXmlElement* matrixElem = new TiXmlElement("IndexToWorld"); + matrixElem->SetAttribute("type", "Matrix3x3"); + matrixElem->SetDoubleAttribute("m_0_0", matrix[0][0]); + matrixElem->SetDoubleAttribute("m_0_1", matrix[0][1]); + matrixElem->SetDoubleAttribute("m_0_2", matrix[0][2]); + matrixElem->SetDoubleAttribute("m_1_0", matrix[1][0]); + matrixElem->SetDoubleAttribute("m_1_1", matrix[1][1]); + matrixElem->SetDoubleAttribute("m_1_2", matrix[1][2]); + matrixElem->SetDoubleAttribute("m_2_0", matrix[2][0]); + matrixElem->SetDoubleAttribute("m_2_1", matrix[2][1]); + matrixElem->SetDoubleAttribute("m_2_2", matrix[2][2]); + geomElem->LinkEndChild(matrixElem); + + TiXmlElement* offsetElem = new TiXmlElement("Offset"); + offsetElem->SetAttribute("type", "Vector3D"); + offsetElem->SetDoubleAttribute("x", offset[0]); + offsetElem->SetDoubleAttribute("y", offset[1]); + offsetElem->SetDoubleAttribute("z", offset[2]); + geomElem->LinkEndChild(offsetElem); + + TiXmlElement* boundsElem = new TiXmlElement("Bounds"); + TiXmlElement* boundsMinElem = new TiXmlElement("Min"); + boundsMinElem->SetAttribute("type", "Vector3D"); + boundsMinElem->SetDoubleAttribute("x", bounds[0]); + boundsMinElem->SetDoubleAttribute("y", bounds[2]); + boundsMinElem->SetDoubleAttribute("z", bounds[4]); + boundsElem->LinkEndChild(boundsMinElem); + TiXmlElement* boundsMaxElem = new TiXmlElement("Max"); + boundsMaxElem->SetAttribute("type", "Vector3D"); + boundsMaxElem->SetDoubleAttribute("x", bounds[1]); + boundsMaxElem->SetDoubleAttribute("y", bounds[3]); + boundsMaxElem->SetDoubleAttribute("z", bounds[5]); + boundsElem->LinkEndChild(boundsMaxElem); + geomElem->LinkEndChild(boundsElem); + + return geomElem; +} + +mitk::Geometry3D::Pointer mitk::Geometry3DToXML::FromXML( TiXmlElement* geometryElement ) +{ + if (!geometryElement) + { + MITK_ERROR << "Cannot deserialize Geometry3D from nullptr."; + return nullptr; + } + + AffineTransform3D::MatrixType matrix; + AffineTransform3D::OffsetType offset; + bool isImageGeometry(false); + unsigned int frameOfReferenceID(0); + BaseGeometry::BoundsArrayType bounds; + + if ( TIXML_SUCCESS != geometryElement->QueryUnsignedAttribute("FrameOfReferenceID", &frameOfReferenceID) ) + { + MITK_WARN << "Missing FrameOfReference for Geometry3D."; + } + + if ( TIXML_SUCCESS != geometryElement->QueryBoolAttribute("ImageGeometry", &isImageGeometry) ) + { + MITK_WARN << "Missing bool ImageGeometry for Geometry3D."; + } + + // matrix + if ( TiXmlElement* matrixElem = geometryElement->FirstChildElement("IndexToWorld")->ToElement() ) + { + bool matrixComplete = true; + matrixComplete &= TIXML_SUCCESS == matrixElem->QueryDoubleAttribute("m_0_0", &matrix[0][0]); + matrixComplete &= TIXML_SUCCESS == matrixElem->QueryDoubleAttribute("m_0_1", &matrix[0][1]); + matrixComplete &= TIXML_SUCCESS == matrixElem->QueryDoubleAttribute("m_0_2", &matrix[0][2]); + matrixComplete &= TIXML_SUCCESS == matrixElem->QueryDoubleAttribute("m_1_0", &matrix[1][0]); + matrixComplete &= TIXML_SUCCESS == matrixElem->QueryDoubleAttribute("m_1_1", &matrix[1][1]); + matrixComplete &= TIXML_SUCCESS == matrixElem->QueryDoubleAttribute("m_1_2", &matrix[1][2]); + matrixComplete &= TIXML_SUCCESS == matrixElem->QueryDoubleAttribute("m_2_0", &matrix[2][0]); + matrixComplete &= TIXML_SUCCESS == matrixElem->QueryDoubleAttribute("m_2_1", &matrix[2][1]); + matrixComplete &= TIXML_SUCCESS == matrixElem->QueryDoubleAttribute("m_2_2", &matrix[2][2]); + + if ( !matrixComplete ) + { + MITK_ERROR << "Could not parse all Geometry3D matrix coefficients!"; + return nullptr; + } + } else + { + MITK_ERROR << "Parse error: expected Matrix3x3 child below Geometry3D node"; + return nullptr; + } + + // offset + if ( TiXmlElement* offsetElem = geometryElement->FirstChildElement("Offset")->ToElement() ) + { + bool vectorComplete = true; + vectorComplete &= TIXML_SUCCESS == offsetElem->QueryDoubleAttribute("x", &offset[0]); + vectorComplete &= TIXML_SUCCESS == offsetElem->QueryDoubleAttribute("y", &offset[1]); + vectorComplete &= TIXML_SUCCESS == offsetElem->QueryDoubleAttribute("z", &offset[2]); + + if ( !vectorComplete ) + { + MITK_ERROR << "Could not parse complete Geometry3D offset!"; + return nullptr; + } + } else + { + MITK_ERROR << "Parse error: expected Offset3D child below Geometry3D node"; + return nullptr; + } + + // bounds + if ( TiXmlElement* boundsElem = geometryElement->FirstChildElement("Bounds")->ToElement() ) + { + bool vectorsComplete(true); + if ( TiXmlElement* minElem = boundsElem->FirstChildElement("Min")->ToElement() ) + { + vectorsComplete &= TIXML_SUCCESS == minElem->QueryDoubleAttribute("x", &bounds[0]); + vectorsComplete &= TIXML_SUCCESS == minElem->QueryDoubleAttribute("y", &bounds[2]); + vectorsComplete &= TIXML_SUCCESS == minElem->QueryDoubleAttribute("z", &bounds[4]); + } else + { + vectorsComplete = false; + } + + if ( TiXmlElement* maxElem = boundsElem->FirstChildElement("Max")->ToElement() ) + { + vectorsComplete &= TIXML_SUCCESS == maxElem->QueryDoubleAttribute("x", &bounds[1]); + vectorsComplete &= TIXML_SUCCESS == maxElem->QueryDoubleAttribute("y", &bounds[3]); + vectorsComplete &= TIXML_SUCCESS == maxElem->QueryDoubleAttribute("z", &bounds[5]); + } else + { + vectorsComplete = false; + } + + if ( !vectorsComplete ) + { + MITK_ERROR << "Could not parse complete Geometry3D bounds!"; + return nullptr; + } + } + + // build GeometryData from matrix/offset + AffineTransform3D::Pointer newTransform = AffineTransform3D::New(); + newTransform->SetMatrix(matrix); + newTransform->SetOffset(offset); + + Geometry3D::Pointer newGeometry = Geometry3D::New(); + newGeometry->SetFrameOfReferenceID(frameOfReferenceID); + newGeometry->SetImageGeometry(isImageGeometry); + + newGeometry->SetIndexToWorldTransform(newTransform); + + newGeometry->SetBounds(bounds); + + return newGeometry; +} diff --git a/Modules/Core/src/IO/mitkGeometry3DToXML.h b/Modules/Core/src/IO/mitkGeometry3DToXML.h new file mode 100644 index 0000000000..865cbe78ff --- /dev/null +++ b/Modules/Core/src/IO/mitkGeometry3DToXML.h @@ -0,0 +1,58 @@ +/*=================================================================== + +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. + +===================================================================*/ + +#ifndef mitkPointSetToXML_h +#define mitkPointSetToXML_h + +// MITK +#include "mitkGeometry3D.h" + +class TiXmlElement; + +namespace mitk +{ + +/** + * @internal + * + * @brief Helper for Geometry3D related I/O classes. + * + * Creates TinyXML elements (blocks) that describe a Geometry3D. + * Also offers a method to read such blocks and create a corresponding Geometry3D. + * + * @sa PointSetWriterService, PointSetReaderService + * + * @ingroup IO + */ +class Geometry3DToXML +{ +public: + + /** + * @brief Serialize given geometry to XML. + */ + static TiXmlElement* ToXML( const Geometry3D* geometry); + + /** + * @brief Create a Geometry3D from XML. + * Interprets only the format created by ToXML(). + */ + static Geometry3D::Pointer FromXML( TiXmlElement* node ); +}; + +} + +#endif diff --git a/Modules/Core/src/IO/mitkPointSetReaderService.cpp b/Modules/Core/src/IO/mitkPointSetReaderService.cpp index 955d587de2..ea0f130916 100644 --- a/Modules/Core/src/IO/mitkPointSetReaderService.cpp +++ b/Modules/Core/src/IO/mitkPointSetReaderService.cpp @@ -1,130 +1,273 @@ /*=================================================================== 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. ===================================================================*/ // MITK #include "mitkPointSetReaderService.h" +#include "mitkGeometry3DToXML.h" +#include "mitkProportionalTimeGeometry.h" #include "mitkIOMimeTypes.h" // STL #include #include #include #include mitk::PointSetReaderService::PointSetReaderService() : AbstractFileReader(CustomMimeType(IOMimeTypes::POINTSET_MIMETYPE()), "MITK Point Set Reader") { RegisterService(); } mitk::PointSetReaderService::~PointSetReaderService() {} std::vector< itk::SmartPointer > mitk::PointSetReaderService::Read() { // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); std::vector< itk::SmartPointer > result; InputStream stream(this); TiXmlDocument doc; stream >> doc; if (!doc.Error()) { TiXmlHandle docHandle( &doc ); //unsigned int pointSetCounter(0); for( TiXmlElement* currentPointSetElement = docHandle.FirstChildElement("point_set_file").FirstChildElement("point_set").ToElement(); currentPointSetElement != NULL; currentPointSetElement = currentPointSetElement->NextSiblingElement()) { mitk::PointSet::Pointer newPointSet = mitk::PointSet::New(); + + // time geometry assembled for addition after all points + // else the SetPoint method would already transform the points that we provide it + mitk::ProportionalTimeGeometry::Pointer timeGeometry = mitk::ProportionalTimeGeometry::New(); + if(currentPointSetElement->FirstChildElement("time_series") != NULL) { for( TiXmlElement* currentTimeSeries = currentPointSetElement->FirstChildElement("time_series")->ToElement(); currentTimeSeries != NULL; currentTimeSeries = currentTimeSeries->NextSiblingElement()) { unsigned int currentTimeStep(0); TiXmlElement* currentTimeSeriesID = currentTimeSeries->FirstChildElement("time_series_id"); currentTimeStep = atoi(currentTimeSeriesID->GetText()); - newPointSet = this->ReadPoint(newPointSet, currentTimeSeries, currentTimeStep); + timeGeometry->Expand( currentTimeStep + 1 ); // expand (default to identity) in any case + TiXmlElement* geometryElem = currentTimeSeries->FirstChildElement("Geometry3D"); + if ( geometryElem ) + { + Geometry3D::Pointer geometry = Geometry3DToXML::FromXML(geometryElem); + if (geometry.IsNotNull()) + { + timeGeometry->SetTimeStepGeometry(geometry,currentTimeStep); + } + else + { + MITK_ERROR << "Could not deserialize Geometry3D element."; + } + } + else + { + MITK_WARN << "Fallback to legacy behavior: defining PointSet geometry as identity"; + } + + newPointSet = this->ReadPoints(newPointSet, currentTimeSeries, currentTimeStep); } } else { - newPointSet = this->ReadPoint(newPointSet, currentPointSetElement, 0); + newPointSet = this->ReadPoints(newPointSet, currentPointSetElement, 0); } + + newPointSet->SetTimeGeometry(timeGeometry); + result.push_back( newPointSet.GetPointer() ); } } else { mitkThrow() << "Parsing error at line " << doc.ErrorRow() << ", col " << doc.ErrorCol() << ": " << doc.ErrorDesc(); } return result; } -mitk::PointSet::Pointer mitk::PointSetReaderService::ReadPoint(mitk::PointSet::Pointer newPointSet, +mitk::BaseGeometry::Pointer mitk::PointSetReaderService::ReadGeometry(TiXmlElement* parentElement) +{ + TiXmlElement* geometryElem = parentElement->FirstChildElement("geometry3d"); + if (!geometryElem) return nullptr; + + // data to generate + AffineTransform3D::MatrixType matrix; + AffineTransform3D::OffsetType offset; + bool isImageGeometry(false); + unsigned int frameOfReferenceID(0); + BaseGeometry::BoundsArrayType bounds; + + bool somethingMissing(false); + + // find data in xml structure + TiXmlElement* imageGeometryElem = geometryElem->FirstChildElement("image_geometry"); + if (imageGeometryElem) + { + std::string igs = imageGeometryElem->GetText(); + isImageGeometry = igs == "true" || igs == "TRUE" || igs =="1"; + } + else + somethingMissing = true; + + TiXmlElement* frameOfReferenceElem = geometryElem->FirstChildElement("frame_of_reference_id"); + if (frameOfReferenceElem) + { + frameOfReferenceID = atoi(frameOfReferenceElem->GetText()); + } + else + somethingMissing = true; + + TiXmlElement* indexToWorldElem = geometryElem->FirstChildElement("index_to_world"); + if (indexToWorldElem) + { + TiXmlElement* matrixElem = indexToWorldElem->FirstChildElement("matrix3x3"); + TiXmlElement* offsetElem = indexToWorldElem->FirstChildElement("offset"); + if (indexToWorldElem && offsetElem) + { + TiXmlElement* col0 = matrixElem->FirstChildElement("column_0"); + TiXmlElement* col1 = matrixElem->FirstChildElement("column_1"); + TiXmlElement* col2 = matrixElem->FirstChildElement("column_2"); + + if (col0 && col1 && col2) + { + somethingMissing |= TIXML_SUCCESS != col0->QueryDoubleAttribute("x", &matrix[0][0]); + somethingMissing |= TIXML_SUCCESS != col0->QueryDoubleAttribute("y", &matrix[1][0]); + somethingMissing |= TIXML_SUCCESS != col0->QueryDoubleAttribute("z", &matrix[2][0]); + + somethingMissing |= TIXML_SUCCESS != col1->QueryDoubleAttribute("x", &matrix[0][1]); + somethingMissing |= TIXML_SUCCESS != col1->QueryDoubleAttribute("y", &matrix[1][1]); + somethingMissing |= TIXML_SUCCESS != col1->QueryDoubleAttribute("z", &matrix[2][1]); + + somethingMissing |= TIXML_SUCCESS != col2->QueryDoubleAttribute("x", &matrix[0][2]); + somethingMissing |= TIXML_SUCCESS != col2->QueryDoubleAttribute("y", &matrix[1][2]); + somethingMissing |= TIXML_SUCCESS != col2->QueryDoubleAttribute("z", &matrix[2][2]); + } + else + somethingMissing = true; + + somethingMissing |= TIXML_SUCCESS != offsetElem->QueryDoubleAttribute("x", &offset[0]); + somethingMissing |= TIXML_SUCCESS != offsetElem->QueryDoubleAttribute("y", &offset[1]); + somethingMissing |= TIXML_SUCCESS != offsetElem->QueryDoubleAttribute("z", &offset[2]); + } + else + somethingMissing = true; + + TiXmlElement* boundsElem = geometryElem->FirstChildElement("bounds"); + if (boundsElem) + { + TiXmlElement* minBoundsElem = boundsElem->FirstChildElement("min"); + TiXmlElement* maxBoundsElem = boundsElem->FirstChildElement("max"); + + if (minBoundsElem && maxBoundsElem) + { + somethingMissing |= TIXML_SUCCESS != minBoundsElem->QueryDoubleAttribute("x", &bounds[0]); + somethingMissing |= TIXML_SUCCESS != minBoundsElem->QueryDoubleAttribute("y", &bounds[2]); + somethingMissing |= TIXML_SUCCESS != minBoundsElem->QueryDoubleAttribute("z", &bounds[4]); + + somethingMissing |= TIXML_SUCCESS != maxBoundsElem->QueryDoubleAttribute("x", &bounds[1]); + somethingMissing |= TIXML_SUCCESS != maxBoundsElem->QueryDoubleAttribute("y", &bounds[3]); + somethingMissing |= TIXML_SUCCESS != maxBoundsElem->QueryDoubleAttribute("z", &bounds[5]); + } + else + somethingMissing = true; + } + else + somethingMissing = true; + } + else + somethingMissing = true; + + if (somethingMissing) + { + MITK_ERROR << "XML structure of geometry inside a PointSet file broken. Refusing to build Geometry3D"; + return nullptr; + } + else + { + Geometry3D::Pointer g = Geometry3D::New(); + g->SetImageGeometry( isImageGeometry ); + g->SetFrameOfReferenceID( frameOfReferenceID ); + g->SetBounds(bounds); + + AffineTransform3D::Pointer transform = AffineTransform3D::New(); + transform->SetMatrix(matrix); + transform->SetOffset(offset); + + g->SetIndexToWorldTransform( transform ); + + return g.GetPointer(); + } +} + +mitk::PointSet::Pointer mitk::PointSetReaderService::ReadPoints(mitk::PointSet::Pointer newPointSet, TiXmlElement* currentTimeSeries, unsigned int currentTimeStep) { if(currentTimeSeries->FirstChildElement("point") != NULL) { for( TiXmlElement* currentPoint = currentTimeSeries->FirstChildElement("point")->ToElement(); currentPoint != NULL; currentPoint = currentPoint->NextSiblingElement()) { unsigned int id(0); mitk::PointSpecificationType spec((mitk::PointSpecificationType) 0); double x(0.0); double y(0.0); double z(0.0); id = atoi(currentPoint->FirstChildElement("id")->GetText()); if(currentPoint->FirstChildElement("specification") != NULL) { spec = (mitk::PointSpecificationType) atoi(currentPoint->FirstChildElement("specification")->GetText()); } x = atof(currentPoint->FirstChildElement("x")->GetText()); y = atof(currentPoint->FirstChildElement("y")->GetText()); z = atof(currentPoint->FirstChildElement("z")->GetText()); mitk::Point3D point; mitk::FillVector3D(point, x, y, z); newPointSet->SetPoint(id, point, spec, currentTimeStep); } } else { if(currentTimeStep != newPointSet->GetTimeSteps()+1) { newPointSet->Expand(currentTimeStep+1); // expand time step series with empty time step } } return newPointSet; } mitk::PointSetReaderService::PointSetReaderService(const mitk::PointSetReaderService& other) : mitk::AbstractFileReader(other) { } mitk::PointSetReaderService* mitk::PointSetReaderService::Clone() const { return new mitk::PointSetReaderService(*this); } diff --git a/Modules/Core/src/IO/mitkPointSetReaderService.h b/Modules/Core/src/IO/mitkPointSetReaderService.h index df87265a71..786288ad49 100644 --- a/Modules/Core/src/IO/mitkPointSetReaderService.h +++ b/Modules/Core/src/IO/mitkPointSetReaderService.h @@ -1,64 +1,66 @@ /*=================================================================== 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. ===================================================================*/ #ifndef _MITK_POINT_SET_READER_SERVICE__H_ #define _MITK_POINT_SET_READER_SERVICE__H_ // MITK #include #include class TiXmlElement; namespace mitk { /** * @internal * * @brief reads xml representations of mitk::PointSets from a file * * Reader for xml files containing one or multiple xml represenations of * mitk::PointSet. If multiple mitk::PointSet objects are stored in one file, * these are assigned to multiple BaseData objects. * * The reader is able to read the old 3D Pointsets without the "specification" and "timeseries" tags and the new 4D Pointsets. * * @ingroup IO */ class PointSetReaderService: public AbstractFileReader { public: PointSetReaderService(); virtual ~PointSetReaderService(); using AbstractFileReader::Read; virtual std::vector< itk::SmartPointer > Read() override; private: PointSetReaderService(const PointSetReaderService& other); - mitk::PointSet::Pointer ReadPoint(mitk::PointSet::Pointer newPointSet, + mitk::BaseGeometry::Pointer ReadGeometry(TiXmlElement* parentElement); + + mitk::PointSet::Pointer ReadPoints(mitk::PointSet::Pointer newPointSet, TiXmlElement* currentTimeSeries, unsigned int currentTimeStep); virtual PointSetReaderService* Clone() const override; }; } #endif diff --git a/Modules/Core/src/IO/mitkPointSetWriterService.cpp b/Modules/Core/src/IO/mitkPointSetWriterService.cpp index f70c461c16..b153a6798f 100644 --- a/Modules/Core/src/IO/mitkPointSetWriterService.cpp +++ b/Modules/Core/src/IO/mitkPointSetWriterService.cpp @@ -1,194 +1,189 @@ /*=================================================================== 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 "mitkPointSetWriterService.h" +#include "mitkGeometry3DToXML.h" #include "mitkIOMimeTypes.h" +#include "mitkLocaleSwitch.h" + +#include "mitkGeometry3D.h" + +#include #include #include #include // // Initialization of the xml tags. // const std::string mitk::PointSetWriterService::XML_POINT_SET_FILE = "point_set_file" ; const std::string mitk::PointSetWriterService::XML_FILE_VERSION = "file_version" ; const std::string mitk::PointSetWriterService::XML_POINT_SET = "point_set" ; const std::string mitk::PointSetWriterService::XML_TIME_SERIES = "time_series"; const std::string mitk::PointSetWriterService::XML_TIME_SERIES_ID = "time_series_id"; const std::string mitk::PointSetWriterService::XML_POINT = "point" ; const std::string mitk::PointSetWriterService::XML_ID = "id" ; const std::string mitk::PointSetWriterService::XML_SPEC = "specification" ; const std::string mitk::PointSetWriterService::XML_X = "x" ; const std::string mitk::PointSetWriterService::XML_Y = "y" ; const std::string mitk::PointSetWriterService::XML_Z = "z" ; const std::string mitk::PointSetWriterService::VERSION_STRING = "0.1" ; mitk::PointSetWriterService::PointSetWriterService() : AbstractFileWriter(PointSet::GetStaticNameOfClass(), CustomMimeType(IOMimeTypes::POINTSET_MIMETYPE()), "MITK Point Set Writer") - , m_IndentDepth(0) - , m_Indent(2) { RegisterService(); } mitk::PointSetWriterService::PointSetWriterService(const mitk::PointSetWriterService& other) : AbstractFileWriter(other) - , m_IndentDepth(other.m_IndentDepth) - , m_Indent(other.m_Indent) { } mitk::PointSetWriterService::~PointSetWriterService() -{} +{ + +} void mitk::PointSetWriterService::Write() { - OutputStream out(this); + mitk::LocaleSwitch localeC("C"); - if ( !out.good() ) - { - mitkThrow() << "Stream not good."; - } + TiXmlDocument doc; - std::locale previousLocale(out.getloc()); - std::locale I("C"); - out.imbue(I); + TiXmlDeclaration* decl = new TiXmlDeclaration("1.0", "UTF-8", ""); // TODO what to write here? encoding? standalone would mean that we provide a DTD somewhere... + doc.LinkEndChild(decl); + + TiXmlElement* rootNode = new TiXmlElement(XML_POINT_SET_FILE); + doc.LinkEndChild(rootNode); - // - // Here the actual xml writing begins - // - WriteXMLHeader( out ); - WriteStartElement( XML_POINT_SET_FILE, out ); - WriteStartElement( XML_FILE_VERSION, out ); - out << VERSION_STRING; - WriteEndElement( XML_FILE_VERSION, out, false ); + TiXmlElement* versionNode = new TiXmlElement(XML_FILE_VERSION); + TiXmlText* versionText = new TiXmlText(VERSION_STRING ); + versionNode->LinkEndChild(versionText); + rootNode->LinkEndChild( versionNode ); - WriteXML( static_cast(this->GetInput()), out ); + TiXmlElement* pointSetNode = ToXML( static_cast(this->GetInput()) ); + if (!pointSetNode) + { + mitkThrow() << "Serialization error during PointSet writing."; + } + rootNode->LinkEndChild(pointSetNode); - WriteEndElement( XML_POINT_SET_FILE, out ); + //out << doc; // streaming of TinyXML write no new-lines, + // rendering XML files unreadable (for humans) - out.imbue(previousLocale); - if ( !out.good() ) // some error during output + LocalFile f(this); + if ( !doc.SaveFile( f.GetFileName() ) ) { mitkThrow() << "Some error during point set writing."; } } mitk::PointSetWriterService*mitk::PointSetWriterService::Clone() const { return new PointSetWriterService(*this); } -void mitk::PointSetWriterService::WriteXML( const mitk::PointSet* pointSet, std::ostream& out ) +TiXmlElement* mitk::PointSetWriterService::ToXML( const mitk::PointSet* pointSet ) { - WriteStartElement( XML_POINT_SET, out ); + // the following is rather bloated and could be expressed in more compact XML + // (e.g. using attributes instead of tags for x/y/z). The current format is + // kept to be compatible with the previous writer. + TiXmlElement* pointSetElement = new TiXmlElement( XML_POINT_SET ); unsigned int timecount = pointSet->GetTimeSteps(); for(unsigned int i=0; i< timecount; i++) { - WriteStartElement( XML_TIME_SERIES, out ); + TiXmlElement* timeSeriesElement = new TiXmlElement( XML_TIME_SERIES ); + pointSetElement->LinkEndChild(timeSeriesElement); - WriteStartElement( XML_TIME_SERIES_ID, out ); - out << ConvertToString( i ); - WriteEndElement( XML_TIME_SERIES_ID, out, false ); + TiXmlElement* timeSeriesIDElement = new TiXmlElement( XML_TIME_SERIES_ID ); + timeSeriesElement->LinkEndChild(timeSeriesIDElement); + TiXmlText* timeSeriesIDText = new TiXmlText(ConvertToString(i)); + timeSeriesIDElement->LinkEndChild(timeSeriesIDText); - mitk::PointSet::PointsContainer* pointsContainer = pointSet->GetPointSet(i)->GetPoints(); - mitk::PointSet::PointsContainer::Iterator it; + PointSet::PointsContainer* pointsContainer = pointSet->GetPointSet(i)->GetPoints(); + PointSet::PointsContainer::Iterator it; + + Geometry3D* geometry = dynamic_cast( pointSet->GetGeometry(i) ); + if (geometry == nullptr) + { + MITK_WARN << "Writing a PointSet with something other that a Geometry3D. This is not foreseen and not handled."; + // we'll continue anyway, this imitates previous behavior + } + else + { + TiXmlElement* geometryElement = Geometry3DToXML::ToXML( geometry ); + timeSeriesElement->LinkEndChild(geometryElement); + } for ( it = pointsContainer->Begin(); it != pointsContainer->End(); ++it ) { - WriteStartElement( XML_POINT, out ); + TiXmlElement* pointElement = new TiXmlElement(XML_POINT); + timeSeriesElement->LinkEndChild(pointElement); - WriteStartElement( XML_ID, out ); - out << ConvertToString( it->Index() ); - WriteEndElement( XML_ID, out, false ); + TiXmlElement* pointIDElement = new TiXmlElement(XML_ID); + TiXmlText* pointIDText = new TiXmlText( ConvertToString( it->Index() ) ); + pointIDElement->LinkEndChild(pointIDText); + pointElement->LinkEndChild(pointIDElement); mitk::PointSet::PointType point = it->Value(); - WriteStartElement( XML_SPEC, out ); - out << ConvertToString( pointSet->GetSpecificationTypeInfo(it->Index(), i) ); - WriteEndElement( XML_SPEC, out, false ); - - WriteStartElement( XML_X, out ); - out << ConvertToString( point[ 0 ] ); - WriteEndElement( XML_X, out, false ); - - WriteStartElement( XML_Y, out ); - out << ConvertToString( point[ 1 ] ); - WriteEndElement( XML_Y, out, false ); - - WriteStartElement( XML_Z, out ); - out << ConvertToString( point[ 2 ] ); - WriteEndElement( XML_Z, out, false ); - - WriteEndElement( XML_POINT, out ); + TiXmlElement* pointSpecElement = new TiXmlElement(XML_SPEC); + TiXmlText* pointSpecText = new TiXmlText( ConvertToString( pointSet->GetSpecificationTypeInfo(it->Index(), i) ) ); + pointSpecElement->LinkEndChild(pointSpecText); + pointElement->LinkEndChild(pointSpecElement); + + TiXmlElement* pointXElement = new TiXmlElement(XML_X); + TiXmlText* pointXText = new TiXmlText( ConvertToString( point[0] ) ); + pointXElement->LinkEndChild(pointXText); + pointElement->LinkEndChild(pointXElement); + + TiXmlElement* pointYElement = new TiXmlElement(XML_Y); + TiXmlText* pointYText = new TiXmlText( ConvertToString( point[1] ) ); + pointYElement->LinkEndChild(pointYText); + pointElement->LinkEndChild(pointYElement); + + TiXmlElement* pointZElement = new TiXmlElement(XML_Z); + TiXmlText* pointZText = new TiXmlText( ConvertToString( point[2] ) ); + pointZElement->LinkEndChild(pointZText); + pointElement->LinkEndChild(pointZElement); } - WriteEndElement( XML_TIME_SERIES, out ); } - WriteEndElement( XML_POINT_SET, out ); + return pointSetElement; } template < typename T> std::string mitk::PointSetWriterService::ConvertToString( T value ) { std::ostringstream o; std::locale I("C"); o.imbue(I); - if ( o << value ) + if ( o << std::setprecision(12) << value ) { return o.str(); } else { return "conversion error"; } -} - -void mitk::PointSetWriterService::WriteXMLHeader( std::ostream &file ) -{ - file << ""; -} - -void mitk::PointSetWriterService::WriteStartElement( const std::string& tag, std::ostream &file ) -{ - file << std::endl; - WriteIndent( file ); - file << '<' << tag << '>'; - m_IndentDepth++; -} - -void mitk::PointSetWriterService::WriteEndElement( const std::string& tag, std::ostream &file, const bool& indent ) -{ - m_IndentDepth--; - if ( indent ) - { - file << std::endl; - WriteIndent( file ); - } - file << '<' << '/' << tag << '>'; -} - -void mitk::PointSetWriterService::WriteIndent( std::ostream& file ) -{ - std::string spaces( m_IndentDepth * m_Indent, ' ' ); - file << spaces; -} +} \ No newline at end of file diff --git a/Modules/Core/src/IO/mitkPointSetWriterService.h b/Modules/Core/src/IO/mitkPointSetWriterService.h index 9ad8a5a58b..98cd5aa8a6 100644 --- a/Modules/Core/src/IO/mitkPointSetWriterService.h +++ b/Modules/Core/src/IO/mitkPointSetWriterService.h @@ -1,112 +1,81 @@ /*=================================================================== 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. ===================================================================*/ #ifndef _MITK_POINT_SET_WRITER_SERVICE__H_ #define _MITK_POINT_SET_WRITER_SERVICE__H_ #include #include +class TiXmlElement; + namespace mitk { +class Geometry3D; + /** * @internal * * @brief XML-based writer for mitk::PointSets * * XML-based writer for mitk::PointSet. Multiple PointSets can be written in * a single XML file by simply setting multiple inputs to the filter. * + * @todo This class would merit a XML library for maintainability or a denser format for performance. + * * @ingroup IO */ class PointSetWriterService : public AbstractFileWriter { public: PointSetWriterService(); virtual ~PointSetWriterService(); using AbstractFileWriter::Write; virtual void Write() override; private: PointSetWriterService(const PointSetWriterService& other); virtual mitk::PointSetWriterService* Clone() const override; - /** - * Converts an arbitrary type to a string. The type has to - * support the << operator. This works fine at least for integral - * data types as float, int, long etc. - * @param value the value to convert - * @returns the string representation of value - */ template < typename T> std::string ConvertToString( T value ); - /** - * Writes an XML representation of the given point set to - * an outstream. The XML-Header an root node is not included! - * @param pointSet the point set to be converted to xml - * @param out the stream to write to. - */ - void WriteXML( const mitk::PointSet* pointSet, std::ostream& out ); - - /** - * Writes an standard xml header to the given stream. - * @param file the stream in which the header is written. - */ - void WriteXMLHeader( std::ostream &file ); - - /** - * Write an end element tag - * End-Elements following character data should pass indent = false. - */ - void WriteEndElement( const std::string& tag, std::ostream &file, const bool& indent = true ); - - /** Write a start element tag */ - void WriteStartElement( const std::string &tag, std::ostream &file ); - - /** Write character data inside a tag. */ - void WriteCharacterData( const std::string &data, std::ostream &file ); - - /** Writes empty spaces to the stream according to m_IndentDepth and m_Indent */ - void WriteIndent( std::ostream& file ); - - unsigned int m_IndentDepth; - const unsigned int m_Indent; + TiXmlElement* ToXML( const mitk::PointSet* pointSet ); static const std::string XML_POINT_SET; static const std::string XML_TIME_SERIES; static const std::string XML_TIME_SERIES_ID; static const std::string XML_POINT_SET_FILE; static const std::string XML_FILE_VERSION; static const std::string XML_POINT; static const std::string XML_SPEC; static const std::string XML_ID; static const std::string XML_X; static const std::string XML_Y; static const std::string XML_Z; static const std::string VERSION_STRING; }; } #endif diff --git a/Modules/Core/src/Interactions/mitkPointSetDataInteractor.cpp b/Modules/Core/src/Interactions/mitkPointSetDataInteractor.cpp index 1e1f1c34b0..aa5417247a 100644 --- a/Modules/Core/src/Interactions/mitkPointSetDataInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkPointSetDataInteractor.cpp @@ -1,671 +1,671 @@ /*=================================================================== 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 "mitkPointSetDataInteractor.h" #include "mitkMouseMoveEvent.h" #include "mitkOperationEvent.h" #include #include "mitkInteractionConst.h" // TODO: refactor file #include "mitkRenderingManager.h" #include "mitkInternalEvent.h" // #include "mitkDispatcher.h" #include "mitkBaseRenderer.h" #include "mitkUndoController.h" void mitk::PointSetDataInteractor::ConnectActionsAndFunctions() { // Condition which is evaluated before transition is taken // following actions in the statemachine are only executed if it returns TRUE CONNECT_CONDITION("isoverpoint", CheckSelection); CONNECT_FUNCTION("addpoint", AddPoint); CONNECT_FUNCTION("selectpoint", SelectPoint); CONNECT_FUNCTION("unselect", UnSelectPointAtPosition); CONNECT_FUNCTION("unselectAll", UnSelectAll); CONNECT_FUNCTION("initMove", InitMove); CONNECT_FUNCTION("movePoint", MovePoint); CONNECT_FUNCTION("finishMovement", FinishMove); CONNECT_FUNCTION("removePoint", RemovePoint); } bool mitk::PointSetDataInteractor::AddPoint(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); // disallow adding of new points if maximum number of points is reached if (m_MaxNumberOfPoints > 1 && m_PointSet->GetSize(timeStep) >= m_MaxNumberOfPoints) { return false; } // To add a point the minimal information is the position, this method accepts all InteractionsPositionEvents InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { // 1) Undo/Redo Supports Grouping of Operations that are undone as a set, // this statement indicates that a new Operation starts here mitk::OperationEvent::IncCurrObjectEventId(); mitk::Point3D itkPoint = positionEvent->GetPositionInWorld(); this->UnselectAll( timeStep, timeInMs); int lastPosition = 0; mitk::PointSet::PointsIterator it, end; - it = m_PointSet->Begin(); - end = m_PointSet->End(); + it = m_PointSet->Begin(timeStep); + end = m_PointSet->End(timeStep); while( it != end ) { if (!m_PointSet->IndexExists(lastPosition,timeStep)) break; ++it; ++lastPosition; } // Insert a Point to the PointSet // 2) Create the Operation inserting the point PointOperation* doOp = new mitk::PointOperation(OpINSERT,timeInMs, itkPoint, lastPosition); // 3) If Undo is enabled, also create the inverse Operation if (m_UndoEnabled) { PointOperation *undoOp = new mitk::PointOperation( OpREMOVE,timeInMs, itkPoint, lastPosition); // 4) Do and Undo Operations are combined in an Operation event which also contains the target of the operations (here m_PointSet) OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Add point"); // 5) Store the Operation in the UndoController m_UndoController->SetOperationEvent(operationEvent); } // 6) Execute the Operation performs the actual insertion of the point into the PointSet m_PointSet->ExecuteOperation(doOp); // 7) If Undo is not enabled the Do-Operation is to be dropped to prevent memory leaks. if ( !m_UndoEnabled ) delete doOp; // Request update interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); // Check if points form a closed contour now, if so fire an InternalEvent IsClosedContour(stateMachineAction, interactionEvent); if (m_MaxNumberOfPoints > 0 && m_PointSet->GetSize(timeStep) >= m_MaxNumberOfPoints) { // Signal that DataNode is fully filled this->NotifyResultReady(); // Send internal event that can be used by StateMachines to switch in a different state InternalEvent::Pointer event = InternalEvent::New(NULL, this, "MaximalNumberOfPoints"); positionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); } return true; } else { return false; } } bool mitk::PointSetDataInteractor::SelectPoint(StateMachineAction*, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected int index = GetPointIndexByPosition(point, timeStep); if (index != -1) { //first deselect the other points //undoable deselect of all points in the DataList this->UnselectAll( timeStep, timeInMs); PointOperation* doOp = new mitk::PointOperation(OpSELECTPOINT,timeInMs, point, index); //Undo if (m_UndoEnabled) { PointOperation* undoOp = new mitk::PointOperation(OpDESELECTPOINT,timeInMs,point, index); OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation m_PointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return true; } } return false; } mitk::PointSetDataInteractor::PointSetDataInteractor() : m_MaxNumberOfPoints(0),m_SelectionAccuracy(3.5) { } mitk::PointSetDataInteractor::~PointSetDataInteractor() { } bool mitk::PointSetDataInteractor::RemovePoint(StateMachineAction*, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { mitk::OperationEvent::IncCurrObjectEventId(); mitk::Point3D itkPoint = positionEvent->GetPositionInWorld(); //search the point in the list int position = m_PointSet->SearchPoint( itkPoint , m_SelectionAccuracy, timeStep); if (position>=0)//found a point { PointSet::PointType pt = m_PointSet->GetPoint(position, timeStep); itkPoint[0] = pt[0]; itkPoint[1] = pt[1]; itkPoint[2] = pt[2]; PointOperation* doOp = new mitk::PointOperation(OpREMOVE,timeInMs, itkPoint, position); if (m_UndoEnabled) //write to UndoMechanism { PointOperation* undoOp = new mitk::PointOperation(OpINSERT,timeInMs, itkPoint, position); OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Remove point"); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation m_PointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; /*now select the point "position-1", and if it is the first in list, then continue at the last in list*/ //only then a select of a point is possible! if (m_PointSet->GetSize( timeStep ) > 0) { this->SelectPoint( m_PointSet->Begin( timeStep )->Index(), timeStep, timeInMs ); } } interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); } else { return false; } return true; } bool mitk::PointSetDataInteractor::IsClosedContour(StateMachineAction*, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected if (GetPointIndexByPosition(point, timeStep) != -1 && m_PointSet->GetSize(timeStep) >= 3) { InternalEvent::Pointer event = InternalEvent::New(NULL, this, "ClosedContour"); positionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); return true; } } return false; } bool mitk::PointSetDataInteractor::MovePoint(StateMachineAction* stateMachineAction, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { IsClosedContour(stateMachineAction, interactionEvent); mitk::Point3D newPoint, resultPoint; newPoint = positionEvent->GetPositionInWorld(); // search the elements in the list that are selected then calculate the // vector, because only with the vector we can move several elements in // the same direction // newPoint - lastPoint = vector // then move all selected and set the lastPoint = newPoint. // then add all vectors to a summeryVector (to be able to calculate the // startpoint for undoOperation) mitk::Vector3D dirVector = newPoint - m_LastPoint; //sum up all Movement for Undo in FinishMovement m_SumVec = m_SumVec + dirVector; mitk::PointSet::PointsIterator it, end; - it = m_PointSet->Begin(); - end = m_PointSet->End(); + it = m_PointSet->Begin(timeStep); + end = m_PointSet->End(timeStep); while( it != end ) { int position = it->Index(); if ( m_PointSet->GetSelectInfo(position, timeStep) )//if selected { PointSet::PointType pt = m_PointSet->GetPoint(position, timeStep); mitk::Point3D sumVec; sumVec[0] = pt[0]; sumVec[1] = pt[1]; sumVec[2] = pt[2]; resultPoint = sumVec + dirVector; PointOperation* doOp = new mitk::PointOperation(OpMOVE,timeInMs, resultPoint, position); //execute the Operation //here no undo is stored, because the movement-steps aren't interesting. // only the start and the end is interisting to store for undo. m_PointSet->ExecuteOperation(doOp); delete doOp; } ++it; } m_LastPoint = newPoint;//for calculation of the direction vector // Update the display interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); IsClosedContour(stateMachineAction,interactionEvent); return true; } else { return false; } } bool mitk::PointSetDataInteractor::UnSelectPointAtPosition(StateMachineAction*, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected int index = GetPointIndexByPosition(point, timeStep); // here it is ensured that we don't switch from one point being selected to another one being selected, // without accepting the unselect of the current point if (index != -1) { PointOperation* doOp = new mitk::PointOperation(OpDESELECTPOINT,timeInMs, point, index); if (m_UndoEnabled) //write to UndoMechanism { PointOperation* undoOp = new mitk::PointOperation(OpSELECTPOINT,timeInMs, point, index); OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation m_PointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; return true; } } return false; } bool mitk::PointSetDataInteractor::UnSelectAll(mitk::StateMachineAction *, mitk::InteractionEvent *interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { Point3D positioninWorld = positionEvent->GetPositionInWorld(); PointSet::PointsContainer::Iterator it, end; PointSet::DataType *itkPointSet = m_PointSet->GetPointSet( timeStep ); end = itkPointSet->GetPoints()->End(); for (it = itkPointSet->GetPoints()->Begin(); it != end; it++) { int position = it->Index(); //then declare an operation which unselects this point; //UndoOperation as well! if ( m_PointSet->GetSelectInfo(position,timeStep) ) { float distance = sqrt(positioninWorld.SquaredEuclideanDistanceTo(m_PointSet->GetPoint(position, timeStep))); if (distance > m_SelectionAccuracy) { mitk::Point3D noPoint; noPoint.Fill( 0 ); mitk::PointOperation* doOp = new mitk::PointOperation(OpDESELECTPOINT,timeInMs, noPoint, position); if ( m_UndoEnabled ) { mitk::PointOperation* undoOp = new mitk::PointOperation(OpSELECTPOINT, timeInMs, noPoint, position); OperationEvent *operationEvent = new OperationEvent( m_PointSet, doOp, undoOp ); m_UndoController->SetOperationEvent( operationEvent ); } m_PointSet->ExecuteOperation( doOp ); if ( !m_UndoEnabled ) delete doOp; } } } } else { this->UnselectAll(timeStep,timeInMs); } return true; } bool mitk::PointSetDataInteractor::UpdatePointSet(mitk::StateMachineAction*, mitk::InteractionEvent*) { mitk::PointSet* pointSet = dynamic_cast(this->GetDataNode()->GetData()); if ( pointSet == NULL ) { return false; MITK_ERROR << "PointSetDataInteractor:: No valid point set ."; } m_PointSet = pointSet; return true; } bool mitk::PointSetDataInteractor::Abort(StateMachineAction*, InteractionEvent* interactionEvent) { InternalEvent::Pointer event = InternalEvent::New(NULL, this, IntDeactivateMe); interactionEvent->GetSender()->GetDispatcher()->QueueEvent(event.GetPointer()); return true; } /* * Check whether the DataNode contains a pointset, if not create one and add it. */ void mitk::PointSetDataInteractor::DataNodeChanged() { if (GetDataNode().IsNotNull()) { PointSet* points = dynamic_cast(GetDataNode()->GetData()); if (points == NULL) { m_PointSet = PointSet::New(); GetDataNode()->SetData(m_PointSet); } else { m_PointSet = points; } // load config file parameter: maximal number of points mitk::PropertyList::Pointer properties = GetAttributes(); std::string strNumber; if (properties->GetStringProperty("MaxPoints", strNumber)) { m_MaxNumberOfPoints = atoi(strNumber.c_str()); } } } bool mitk::PointSetDataInteractor::InitMove(StateMachineAction*, InteractionEvent* interactionEvent) { InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent == NULL) return false; mitk::OperationEvent::IncCurrObjectEventId(); // start of the Movement is stored to calculate the undoKoordinate // in FinishMovement m_LastPoint = positionEvent->GetPositionInWorld(); // initialize a value to calculate the movement through all // MouseMoveEvents from MouseClick to MouseRelease m_SumVec.Fill(0); GetDataNode()->SetProperty("contourcolor", ColorProperty::New(1.0, 1.0, 1.0)); return true; } bool mitk::PointSetDataInteractor::FinishMove(StateMachineAction*, InteractionEvent* interactionEvent) { unsigned int timeStep = interactionEvent->GetSender()->GetTimeStep(GetDataNode()->GetData()); ScalarType timeInMs = interactionEvent->GetSender()->GetTime(); InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { //finish the movement: //the final point is m_LastPoint //m_SumVec stores the movement in a vector //the operation would not be necessary, but we need it for the undo Operation. //m_LastPoint is for the Operation //the point for undoOperation calculates from all selected //elements (point) - m_SumVec //search all selected elements and move them with undo-functionality. mitk::PointSet::PointsIterator it, end; - it = m_PointSet->Begin(); - end = m_PointSet->End(); + it = m_PointSet->Begin(timeStep); + end = m_PointSet->End(timeStep); while( it != end ) { int position = it->Index(); if ( m_PointSet->GetSelectInfo(position, timeStep) )//if selected { PointSet::PointType pt = m_PointSet->GetPoint(position, timeStep); Point3D itkPoint; itkPoint[0] = pt[0]; itkPoint[1] = pt[1]; itkPoint[2] = pt[2]; PointOperation* doOp = new mitk::PointOperation(OpMOVE,timeInMs,itkPoint, position); if ( m_UndoEnabled )//&& (posEvent->GetType() == mitk::Type_MouseButtonRelease) { //set the undo-operation, so the final position is undo-able //calculate the old Position from the already moved position - m_SumVec mitk::Point3D undoPoint = ( itkPoint - m_SumVec ); PointOperation* undoOp = new mitk::PointOperation(OpMOVE,timeInMs, undoPoint, position); OperationEvent *operationEvent = new OperationEvent(m_PointSet, doOp, undoOp, "Move point"); m_UndoController->SetOperationEvent(operationEvent); } //execute the Operation m_PointSet->ExecuteOperation(doOp); if ( !m_UndoEnabled ) delete doOp; } ++it; } // Update the display interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); OperationEvent::IncCurrGroupEventId(); } else {return false; } this->NotifyResultReady(); return true; } void mitk::PointSetDataInteractor::SetAccuracy(float accuracy) { m_SelectionAccuracy = accuracy; } void mitk::PointSetDataInteractor::SetMaxPoints(unsigned int maxNumber) { m_MaxNumberOfPoints = maxNumber; } int mitk::PointSetDataInteractor::GetPointIndexByPosition(Point3D position, unsigned int time, float accuracy) { // iterate over point set and check if it contains a point close enough to the pointer to be selected PointSet* points = dynamic_cast(GetDataNode()->GetData()); int index = -1; if (points == NULL) { return index; } if (points->GetPointSet(time) == nullptr) return -1; PointSet::PointsContainer* pointsContainer = points->GetPointSet(time)->GetPoints(); float minDistance = m_SelectionAccuracy; if (accuracy != -1 ) minDistance = accuracy; for (PointSet::PointsIterator it = pointsContainer->Begin(); it != pointsContainer->End(); it++) { float distance = sqrt(position.SquaredEuclideanDistanceTo(points->GetPoint(it->Index(), time))); if (distance < minDistance) // if several points fall within the margin, choose the one with minimal distance to position { index = it->Index(); } } return index; } bool mitk::PointSetDataInteractor::CheckSelection(const mitk::InteractionEvent *interactionEvent) { const InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); if (positionEvent != NULL) { int timeStep = positionEvent->GetSender()->GetTimeStep(); Point3D point = positionEvent->GetPositionInWorld(); // iterate over point set and check if it contains a point close enough to the pointer to be selected int index = GetPointIndexByPosition(point, timeStep); if (index != -1) return true; } return false; } void mitk::PointSetDataInteractor::UnselectAll(unsigned int timeStep, ScalarType timeInMs) { mitk::PointSet *pointSet = dynamic_cast( GetDataNode()->GetData() ); if ( pointSet == NULL ) { return; } mitk::PointSet::DataType *itkPointSet = pointSet->GetPointSet( timeStep ); if ( itkPointSet == NULL ) { return; } mitk::PointSet::PointsContainer::Iterator it, end; end = itkPointSet->GetPoints()->End(); for (it = itkPointSet->GetPoints()->Begin(); it != end; it++) { int position = it->Index(); PointSet::PointDataType pointData = {0, false, PTUNDEFINED}; itkPointSet->GetPointData( position, &pointData ); //then declare an operation which unselects this point; //UndoOperation as well! if ( pointData.selected ) { mitk::Point3D noPoint; noPoint.Fill( 0 ); mitk::PointOperation *doOp = new mitk::PointOperation(OpDESELECTPOINT,timeInMs, noPoint, position); if ( m_UndoEnabled ) { mitk::PointOperation *undoOp = new mitk::PointOperation(OpSELECTPOINT, timeInMs, noPoint, position); OperationEvent *operationEvent = new OperationEvent( pointSet, doOp, undoOp ); m_UndoController->SetOperationEvent( operationEvent ); } pointSet->ExecuteOperation( doOp ); if ( !m_UndoEnabled ) delete doOp; } } } void mitk::PointSetDataInteractor::SelectPoint(int position, unsigned int timeStep, ScalarType timeInMS) { mitk::PointSet *pointSet = dynamic_cast< mitk::PointSet * >( this->GetDataNode()->GetData() ); //if List is empty, then no selection of a point can be done! if ( (pointSet == NULL) || (pointSet->GetSize( timeStep ) <= 0) ) { return; } //dummyPoint... not needed anyway mitk::Point3D noPoint; noPoint.Fill(0); mitk::PointOperation *doOp = new mitk::PointOperation( OpSELECTPOINT,timeInMS, noPoint, position); if ( m_UndoEnabled ) { mitk::PointOperation* undoOp = new mitk::PointOperation(OpDESELECTPOINT,timeInMS, noPoint, position); OperationEvent *operationEvent = new OperationEvent(pointSet, doOp, undoOp); m_UndoController->SetOperationEvent(operationEvent); } pointSet->ExecuteOperation( doOp ); if ( !m_UndoEnabled ) delete doOp; } diff --git a/Modules/Core/test/CMakeLists.txt b/Modules/Core/test/CMakeLists.txt index 8676f4097f..742b45f2a1 100644 --- a/Modules/Core/test/CMakeLists.txt +++ b/Modules/Core/test/CMakeLists.txt @@ -1,226 +1,223 @@ # The core tests need relaxed compiler flags... # TODO fix core tests to compile without these additional no-error flags if(MSVC_VERSION) # disable deprecated warnings (they would lead to errors) mitkFunctionCheckCAndCXXCompilerFlags("/wd4996" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) else() mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=deprecated" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=deprecated-declarations" CMAKE_C_FLAGS CMAKE_CXX_FLAGS) endif() MITK_CREATE_MODULE_TESTS() if(TARGET ${TESTDRIVER}) mitk_use_modules(TARGET ${TESTDRIVER} PACKAGES ITK|ITKThresholding+ITKTestKernel VTK|vtkTestingRendering tinyxml) mitkAddCustomModuleTest(mitkVolumeCalculatorTest_Png2D-bw mitkVolumeCalculatorTest ${MITK_DATA_DIR}/Png2D-bw.png ${MITK_DATA_DIR}/Pic2DplusT.nrrd ) mitkAddCustomModuleTest(mitkEventMapperTest_Test1And2 mitkEventMapperTest ${MITK_DATA_DIR}/TestStateMachine1.xml ${MITK_DATA_DIR}/TestStateMachine2.xml ) mitkAddCustomModuleTest(mitkEventConfigTest_CreateObjectInDifferentWays mitkEventConfigTest ${MITK_SOURCE_DIR}/Modules/Core/test/resource/Interactions/StatemachineConfigTest.xml ) mitkAddCustomModuleTest(mitkNodeDependentPointSetInteractorTest mitkNodeDependentPointSetInteractorTest ${MITK_DATA_DIR}/Pic3D.nrrd ${MITK_DATA_DIR}/BallBinary30x30x30.nrrd ) mitkAddCustomModuleTest(mitkDataStorageTest_US4DCyl mitkDataStorageTest ${MITK_DATA_DIR}/US4DCyl.nrrd ) mitkAddCustomModuleTest(mitkStateMachineFactoryTest_TestStateMachine1_2 mitkStateMachineFactoryTest ${MITK_DATA_DIR}/TestStateMachine1.xml ${MITK_DATA_DIR}/TestStateMachine2.xml ) mitkAddCustomModuleTest(mitkDicomSeriesReaderTest_CTImage mitkDicomSeriesReaderTest ${MITK_DATA_DIR}/TinyCTAbdomen ${MITK_DATA_DIR}/DICOMReader/Broken-Series ) mitkAddCustomModuleTest(mitkPointSetReaderTest mitkPointSetReaderTest ${MITK_DATA_DIR}/PointSetReaderTestData.mps ) mitkAddCustomModuleTest(mitkImageTest_4DImageData mitkImageTest ${MITK_DATA_DIR}/US4DCyl.nrrd ) mitkAddCustomModuleTest(mitkImageTest_2D+tImageData mitkImageTest ${MITK_DATA_DIR}/Pic2DplusT.nrrd ) mitkAddCustomModuleTest(mitkImageTest_3DImageData mitkImageTest ${MITK_DATA_DIR}/Pic3D.nrrd ) mitkAddCustomModuleTest(mitkImageEqualTest mitkImageEqualTest) mitkAddCustomModuleTest(mitkImageTest_brainImage mitkImageTest ${MITK_DATA_DIR}/brain.mhd ) mitkAddCustomModuleTest(mitkImageTest_3DImageData mitkImageGeneratorTest ${MITK_DATA_DIR}/Pic3D.nrrd ) mitkAddCustomModuleTest(mitkLevelWindowManagerTest mitkLevelWindowManagerTest ${MITK_DATA_DIR}/Pic3D.nrrd ) mitkAddCustomModuleTest(mitkMultiComponentImageDataComparisonFilterTest mitkMultiComponentImageDataComparisonFilterTest ${MITK_DATA_DIR}/NrrdWritingTestImage.jpg ) mitkAddCustomModuleTest(mitkImageToItkTest mitkImageToItkTest ${MITK_DATA_DIR}/Pic3D.nrrd ) mitkAddCustomModuleTest(mitkImageSliceSelectorTest mitkImageSliceSelectorTest ${MITK_DATA_DIR}/Pic2DplusT.nrrd ) mitkAddCustomModuleTest(mitkRotatedSlice4DTest mitkRotatedSlice4DTest ${MITK_DATA_DIR}/UltrasoundImages/4D_TEE_Data_MV.dcm ) if(MITK_ENABLE_RENDERING_TESTING) ### since the rendering test's do not run in ubuntu, yet, we build them only for other systems or if the user explicitly sets the variable MITK_ENABLE_RENDERING_TESTING mitkAddCustomModuleTest(mitkImageVtkMapper2D_rgbaImage640x480 mitkImageVtkMapper2DTest ${MITK_DATA_DIR}/RenderingTestData/rgbaImage.png #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/rgbaImage640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3d640x480 mitkImageVtkMapper2DTest #test for standard Pic3D axial slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3d640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dColorBlue640x480 mitkImageVtkMapper2DColorTest #test for color property (=blue) Pic3D sagittal slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dColorBlue640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dLevelWindow640x480 mitkImageVtkMapper2DLevelWindowTest #test for levelwindow property (=blood) #Pic3D sagittal slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dLevelWindowBlood640x480REF.png #corresponding reference #screenshot ) #mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dOpacity640x480 mitkImageVtkMapper2DOpacityTest #test for opacity (=0.5) Pic3D coronal slice # ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage # -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dOpacity640x480REF.png corresponding reference screenshot #) mitkAddCustomModuleTest(mitkImageVtkMapper2D_pic3dSwivel640x480 mitkImageVtkMapper2DSwivelTest #test for a randomly chosen Pic3D swivelled slice ${MITK_DATA_DIR}/Pic3D.nrrd #input image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dSwivel640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkPointSetVtkMapper2D_openMeAlone640x480 mitkPointSetVtkMapper2DTest ${MITK_DATA_DIR}/RenderingTestData/openMeAlone.mps #input point set to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/openMeAlone640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkPointSetVtkMapper2D_Pic3DPointSetForPic3D640x480 mitkPointSetVtkMapper2DImageTest ${MITK_DATA_DIR}/Pic3D.nrrd ${MITK_DATA_DIR}/RenderingTestData/PointSetForPic3D.mps #input point set and image to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Pic3DPointSetForPic3D640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkPointSetVtkMapper2D_openMeAloneGlyphType640x480 mitkPointSetVtkMapper2DGlyphTypeTest ${MITK_DATA_DIR}/RenderingTestData/openMeAlone.mps #input point set to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/openMeAloneGlyphType640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkPointSetVtkMapper2D_openMeAloneTransformed640x480 mitkPointSetVtkMapper2DTransformedPointsTest ${MITK_DATA_DIR}/RenderingTestData/openMeAlone.mps #input point set to load in data storage -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/openMeAloneTransformedPoints640x480REF.png #corresponding reference screenshot ) # Currently not working on windows because of a rendering timing issue # see bug 18083 for details if(NOT WIN32) mitkAddCustomModuleTest(mitkSurfaceDepthSortingTransparency_StanfordBunnySTL640x480 mitkSurfaceDepthSortingTest ${MITK_DATA_DIR}/RenderingTestData/Stanford_bunny.stl -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Stanford_bunnySTLDepthSorting640x480REF.png ) endif() if(NOT APPLE) mitkAddCustomModuleTest(mitkSurfaceDepthPeelingTransparency_StanfordBunnySTL640x480 mitkSurfaceDepthPeelingTest ${MITK_DATA_DIR}/RenderingTestData/Stanford_bunny.stl -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Stanford_bunnySTLDepthPeeling640x480REF.png #corresponding reference screenshot ) endif() #Test reslice interpolation #note: nearest mode is already tested by swivel test mitkAddCustomModuleTest(ResliceInterpolationIsLinear mitkImageVtkMapper2DResliceInterpolationPropertyTest 1 #linear ${MITK_DATA_DIR}/Pic3D.nrrd -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dRefLinear.png #corresponding reference screenshot LINEAR ) mitkAddCustomModuleTest(ResliceInterpolationIsCubic mitkImageVtkMapper2DResliceInterpolationPropertyTest 3 #cubic ${MITK_DATA_DIR}/Pic3D.nrrd -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/pic3dRefCubic.png #corresponding reference screenshot CUBIC ) #End test reslice interpolation # Testing of the rendering of binary images #mitkAddCustomModuleTest(mitkImageVtkMapper2D_binaryTestImage640x480 mitkImageVtkMapper2DTest #test for standard Pic3D axial slice # ${MITK_DATA_DIR}/RenderingTestData/binaryImage.nrrd #input image to load in data storage # -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/binaryImage640x480REF.png #corresponding reference screenshot #) #mitkAddCustomModuleTest(mitkImageVtkMapper2D_binaryTestImageWithRef640x480 mitkImageVtkMapper2DTest #test for standard Pic3D axial slice # ${MITK_DATA_DIR}/Pic3D.nrrd ${MITK_DATA_DIR}/RenderingTestData/binaryImage.nrrd #input image to load in data storage # -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/binaryImageWithRef640x480REF.png #corresponding reference screenshot #) # End of binary image tests mitkAddCustomModuleTest(mitkSurfaceVtkMapper3DTest_TextureProperty mitkSurfaceVtkMapper3DTest ${MITK_DATA_DIR}/ToF-Data/Kinect_LiverPhantom.vtp ${MITK_DATA_DIR}/ToF-Data/Kinect_LiverPhantom_RGBImage.nrrd -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/texturedLiver640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkImageVtkMapper2DTransferFunctionTest_Png2D-bw mitkImageVtkMapper2DTransferFunctionTest ${MITK_DATA_DIR}/Png2D-bw.png -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Png2D-bw-TransferFunctionRGBImage640x480REF.png #corresponding reference screenshot ) mitkAddCustomModuleTest(mitkImageVtkMapper2DOpacityTransferFunctionTest_Png2D-bw mitkImageVtkMapper2DOpacityTransferFunctionTest ${MITK_DATA_DIR}/Png2D-bw.png -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Png2D-bw-OpacityTransferFunctionRGBImage640x480REF.png #corresponding reference screenshot ) ############################## DISABLED TESTS #Removed due to high rendering error. #mitkAddCustomModuleTest(mitkSurfaceVtkMapper3DTexturedSphereTest_Football mitkSurfaceVtkMapper3DTexturedSphereTest # ${MITK_DATA_DIR}/RenderingTestData/texture.jpg #input texture # -V # ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/texturedSphere640x480REF.png corresponding reference screenshot #) mitkAddCustomModuleTest(mitkImageVtkMapper2DLookupTableTest_Png2D-bw mitkImageVtkMapper2DLookupTableTest ${MITK_DATA_DIR}/Png2D-bw.png -V ${MITK_DATA_DIR}/RenderingTestData/ReferenceScreenshots/Png2D-bw-LookupTableRGBImage640x480REF.png #corresponding reference screenshot ) #mitkAddCustomModuleTest(mitkImageTest_color2DImage mitkImageTest # ${MITK_DATA_DIR}/NrrdWritingTestImage.jpg #) #mitkAddCustomModuleTest(mitkNodeDependentPointSetInteractorTest mitkNodeDependentPointSetInteractorTest # ${MITK_DATA_DIR}/Pic3D.pic.gz ${MITK_DATA_DIR}/BallBinary30x30x30.pic.gz #) SET_PROPERTY(TEST mitkRotatedSlice4DTest mitkImageVtkMapper2D_rgbaImage640x480 mitkImageVtkMapper2D_pic3d640x480 mitkImageVtkMapper2D_pic3dColorBlue640x480 mitkImageVtkMapper2D_pic3dLevelWindow640x480 mitkImageVtkMapper2D_pic3dSwivel640x480 mitkImageVtkMapper2DTransferFunctionTest_Png2D-bw # mitkImageVtkMapper2D_pic3dOpacity640x480 mitkSurfaceVtkMapper2DTest mitkSurfaceVtkMapper3DTest_TextureProperty mitkPointSetVtkMapper2D_Pic3DPointSetForPic3D640x480 mitkPointSetVtkMapper2D_openMeAlone640x480 mitkPointSetVtkMapper2D_openMeAloneGlyphType640x480 mitkPointSetVtkMapper2D_openMeAloneTransformed640x480 #mitkSurfaceVtkMapper3DTexturedSphereTest_Football PROPERTY RUN_SERIAL TRUE) endif() - add_test(mitkPointSetLocaleTest ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} mitkPointSetLocaleTest ${MITK_DATA_DIR}/pointSet.mps) - set_property(TEST mitkPointSetLocaleTest PROPERTY LABELS MITK-Core) - endif() # TARGET ${TESTDRIVER} diff --git a/Modules/Core/test/files.cmake b/Modules/Core/test/files.cmake index 0a398c0f8b..1e7773a72e 100644 --- a/Modules/Core/test/files.cmake +++ b/Modules/Core/test/files.cmake @@ -1,195 +1,196 @@ # tests with no extra command line parameter set(MODULE_TESTS # IMPORTANT: If you plan to deactivate / comment out a test please write a bug number to the commented out line of code. # # Example: #mitkMyTest #this test is commented out because of bug 12345 # # It is important that the bug is open and that the test will be activated again before the bug is closed. This assures that # no test is forgotten after it was commented out. If there is no bug for your current problem, please add a new one and # mark it as critical. ################## DISABLED TESTS ################################################# #mitkAbstractTransformGeometryTest.cpp #seems as tested class mitkExternAbstractTransformGeometry doesnt exist any more #mitkStateMachineContainerTest.cpp #rewrite test, indirect since no longer exported Bug 14529 #mitkRegistrationBaseTest.cpp #tested class mitkRegistrationBase doesn't exist any more #mitkSegmentationInterpolationTest.cpp #file doesn't exist! #mitkPipelineSmartPointerCorrectnessTest.cpp #file doesn't exist! #mitkITKThreadingTest.cpp #test outdated because itk::Semaphore was removed from ITK #mitkAbstractTransformPlaneGeometryTest.cpp #mitkVtkAbstractTransformPlaneGeometry doesn't exist any more #mitkTestUtilSharedLibrary.cpp #Linker problem with this test... #mitkTextOverlay2DSymbolsRenderingTest.cpp #Implementation of the tested feature is not finished yet. Ask Christoph or see bug 15104 for details. ################# RUNNING TESTS ################################################### mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkDataNodeTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkDispatcherTest.cpp mitkEnumerationPropertyTest.cpp mitkEventTest.cpp mitkFileReaderRegistryTest.cpp #mitkFileWriterRegistryTest.cpp mitkFocusManagerTest.cpp mitkGenericPropertyTest.cpp mitkGeometry3DTest.cpp mitkGeometry3DEqualTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkGlobalInteractionTest.cpp mitkImageCastTest.cpp mitkImageEqualTest.cpp mitkImageDataItemTest.cpp mitkImageGeneratorTest.cpp mitkIOUtilTest.cpp mitkBaseDataTest.cpp mitkImportItkImageTest.cpp mitkGrabItkImageMemoryTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkInteractorTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetTest.cpp mitkPointSetEqualTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetOnEmptyTest.cpp + mitkPointSetLocaleTest.cpp mitkPointSetWriterTest.cpp mitkPointSetReaderTest.cpp mitkPointSetInteractorTest.cpp mitkPointSetPointOperationsTest.cpp mitkProgressBarTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkStateMachineTest.cpp mitkStateTest.cpp mitkSurfaceTest.cpp mitkSurfaceEqualTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeGeometryTest.cpp mitkProportionalTimeGeometryTest.cpp mitkTransitionTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp mitkStepperTest.cpp mitkRenderingManagerTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp mitkExceptionTest.cpp mitkExtractSliceFilterTest.cpp mitkLogTest.cpp mitkImageDimensionConverterTest.cpp mitkLoggingAdapterTest.cpp mitkUIDGeneratorTest.cpp mitkShaderRepositoryTest.cpp mitkPlanePositionManagerTest.cpp mitkAffineTransformBaseTest.cpp mitkPropertyAliasesTest.cpp mitkPropertyDescriptionsTest.cpp mitkPropertyExtensionsTest.cpp mitkPropertyFiltersTest.cpp mitkTinyXMLTest.cpp mitkRawImageFileReaderTest.cpp mitkInteractionEventTest.cpp mitkLookupTableTest.cpp mitkSTLFileReaderTest.cpp mitkPointTypeConversionTest.cpp mitkVectorTypeConversionTest.cpp mitkMatrixTypeConversionTest.cpp mitkArrayTypeConversionTest.cpp mitkSurfaceToImageFilterTest.cpp mitkBaseGeometryTest.cpp mitkImageToSurfaceFilterTest.cpp mitkEqualTest.cpp mitkLineTest.cpp mitkItkImageIOTest.cpp mitkRotatedSlice4DTest.cpp ) if(MITK_ENABLE_RENDERING_TESTING) set(MODULE_TESTS ${MODULE_TESTS} mitkPointSetDataInteractorTest.cpp #since mitkInteractionTestHelper is currently creating a vtkRenderWindow mitkSurfaceVtkMapper2DTest.cpp #new rendering test in CppUnit style ) endif() # test with image filename as an extra command line parameter set(MODULE_IMAGE_TESTS mitkImageTimeSelectorTest.cpp #only runs on images mitkImageAccessorTest.cpp #only runs on images ) set(MODULE_SURFACE_TESTS mitkSurfaceVtkWriterTest.cpp #only runs on surfaces ) # list of images for which the tests are run set(MODULE_TESTIMAGES US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png ) set(MODULE_TESTSURFACES binary.stl ball.stl ) set(MODULE_CUSTOM_TESTS mitkDataStorageTest.cpp mitkDicomSeriesReaderTest.cpp mitkDICOMLocaleTest.cpp mitkDataNodeTest.cpp mitkEventMapperTest.cpp mitkEventConfigTest.cpp mitkNodeDependentPointSetInteractorTest.cpp mitkStateMachineFactoryTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageVtkMapper2DTest.cpp mitkImageVtkMapper2DLevelWindowTest.cpp mitkImageVtkMapper2DOpacityTest.cpp mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp mitkImageVtkMapper2DColorTest.cpp mitkImageVtkMapper2DSwivelTest.cpp mitkImageVtkMapper2DTransferFunctionTest.cpp mitkImageVtkMapper2DOpacityTransferFunctionTest.cpp mitkImageVtkMapper2DLookupTableTest.cpp mitkSurfaceVtkMapper3DTest mitkSurfaceVtkMapper3DTexturedSphereTest.cpp mitkVolumeCalculatorTest.cpp mitkLevelWindowManagerTest.cpp mitkPointSetVtkMapper2DTest.cpp mitkPointSetVtkMapper2DImageTest.cpp mitkPointSetVtkMapper2DGlyphTypeTest.cpp mitkPointSetVtkMapper2DTransformedPointsTest.cpp mitkVTKRenderWindowSizeTest.cpp mitkMultiComponentImageDataComparisonFilterTest.cpp mitkImageToItkTest.cpp mitkImageSliceSelectorTest.cpp mitkSurfaceDepthPeelingTest.cpp ) # Currently not working on windows because of a rendering timing issue # see bug 18083 for details if(NOT WIN32) set(MODULE_CUSTOM_TESTS ${MODULE_CUSTOM_TESTS} mitkSurfaceDepthSortingTest.cpp) endif() set(RESOURCE_FILES Interactions/AddAndRemovePoints.xml Interactions/globalConfig.xml Interactions/StatemachineTest.xml Interactions/StatemachineConfigTest.xml ) diff --git a/Modules/Core/test/mitkPointSetFileIOTest.cpp b/Modules/Core/test/mitkPointSetFileIOTest.cpp index a925e6a772..dfc1aac847 100644 --- a/Modules/Core/test/mitkPointSetFileIOTest.cpp +++ b/Modules/Core/test/mitkPointSetFileIOTest.cpp @@ -1,156 +1,204 @@ /*=================================================================== 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 "mitkPointSet.h" #include "mitkTestingMacros.h" #include "mitkFileWriterRegistry.h" #include "mitkIOUtil.h" +#include "mitkProportionalTimeGeometry.h" #include #include #include +#include //unsigned int numberOfTestPointSets = 1; unsigned int numberOfTimeSeries = 5; // create one test PointSet class mitkPointSetFileIOTestClass { public: mitk::PointSet::Pointer m_SavedPointSet; std::string m_FilePath; mitkPointSetFileIOTestClass() { } ~mitkPointSetFileIOTestClass() { if (!m_FilePath.empty()) { std::remove(m_FilePath.c_str()); } } - mitk::PointSet::Pointer CreateTestPointSet() + mitk::PointSet::Pointer CreateTestPointSet(mitk::BaseGeometry* geometry = nullptr) { mitk::PointSet::Pointer pointSet = mitk::PointSet::New(); + std::uniform_real_distribution<> r(0, 1); + std::mt19937 gen; for (unsigned int t = 0; t < numberOfTimeSeries; t++) { - unsigned int position(0); - mitk::Point3D point; - mitk::FillVector3D(point, (rand()%1000) /1000.0 , (rand()%1000) /1000.0, (rand()%1000)/1000.0); - pointSet->SetPoint(position, point, t); - - mitk::FillVector3D(point, (rand()%1000) /1000.0 , (rand()%1000) /1000.0, (rand()%1000)/1000.0); - ++position; - pointSet->SetPoint(position, point, t); - - mitk::FillVector3D(point, (rand()%1000) /1000.0 , (rand()%1000) /1000.0, (rand()%1000)/1000.0); - ++position; - pointSet->SetPoint(position, point, t); + for (unsigned int position = 0; position < 3; ++position) + { + mitk::Point3D point; + mitk::FillVector3D(point, r(gen), r(gen), r(gen)); + pointSet->SetPoint(position, point, t); + } } m_SavedPointSet = pointSet; - return pointSet; + // we set the geometry AFTER adding points. Else all points added via SetPoint would already bee transformed + if (geometry != nullptr) + { + mitk::ProportionalTimeGeometry::Pointer timeGeometry = mitk::ProportionalTimeGeometry::New(); + timeGeometry->Initialize( geometry, numberOfTimeSeries ); + pointSet->SetTimeGeometry( timeGeometry ); + } + return pointSet; } void PointSetCompare(mitk::PointSet::Pointer pointSet2, mitk::PointSet::Pointer pointSet1, bool& /*identical*/) { - MITK_TEST_CONDITION(pointSet1->GetSize() == pointSet2->GetSize(), "Testing if PointSet size is correct" ); for (unsigned int t = 0; t < numberOfTimeSeries; t++) { for (unsigned int i = 0; i < (unsigned int) pointSet1->GetSize(t); ++i) { mitk::Point3D p1 = pointSet1->GetPoint(i); mitk::Point3D p2 = pointSet2->GetPoint(i); //test std::cout << "r point: " << p2 << std::endl; std::cout << "w point: " << p1 << std::endl; //test end MITK_TEST_CONDITION((p1[0] - p2[0]) <= 0.0001, "Testing if X coordinates of the Point are at the same Position" ); MITK_TEST_CONDITION((p1[1] - p2[1]) <= 0.0001, "Testing if Y coordinates of the Point are at the same Position" ); MITK_TEST_CONDITION((p1[2] - p2[2]) <= 0.0001, "Testing if Z coordinates of the Point are at the same Position" ); } } + // testing geometry + MITK_TEST_CONDITION( mitk::Equal( *(pointSet1->GetGeometry()), *(pointSet2->GetGeometry()), 0.000001, true), + "Restored geometry must equal original one."); } - bool PointSetWrite() + bool PointSetWrite(mitk::BaseGeometry* geometry = nullptr) { try { m_SavedPointSet = NULL; std::ofstream tmpStream; - m_FilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream); - mitk::IOUtil::Save(CreateTestPointSet(), m_FilePath); + m_FilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream) + ".mps"; + MITK_INFO << "PointSet test file at " << m_FilePath; + mitk::IOUtil::Save(CreateTestPointSet(geometry), m_FilePath); } - catch (std::exception& /*e*/) + catch (std::exception& e) { + MITK_ERROR << "Error during pointset creation: " << e.what(); return false; } return true; } void PointSetLoadAndCompareTest() { try { mitk::PointSet::Pointer pointSet = mitk::IOUtil::LoadPointSet(m_FilePath); MITK_TEST_CONDITION(pointSet.IsNotNull(), "Testing if the loaded Data are NULL" ); bool identical(true); PointSetCompare(pointSet.GetPointer(), m_SavedPointSet.GetPointer(), identical); - } catch (std::exception& /*e*/) + } catch (std::exception& e) { + MITK_ERROR << "Error during pointset creation: " << e.what(); } } }; //mitkPointSetFileIOTestClass int mitkPointSetFileIOTest(int, char*[]) { MITK_TEST_BEGIN("PointSet"); - mitkPointSetFileIOTestClass* test = new mitkPointSetFileIOTestClass(); - - // write - MITK_TEST_CONDITION(test->PointSetWrite(), "Testing if the PointSetWriter writes Data" ); + // minimum test w/ identity geometry + { + mitkPointSetFileIOTestClass test; + MITK_TEST_CONDITION(test.PointSetWrite(), "Testing if the PointSetWriter writes Data" ); + test.PointSetLoadAndCompareTest(); // load - compare + } - // load - compare - test->PointSetLoadAndCompareTest(); + // case with a more complex geometry + { + mitkPointSetFileIOTestClass test; + + mitk::Geometry3D::Pointer g = mitk::Geometry3D::New(); + + // define arbitrary transformation matrix + // the number don't have much meaning - we just want them reproduced + // by the writer/reader cycle + mitk::BaseGeometry::BoundsArrayType bounds; + bounds[0] = -918273645.18293746; + bounds[1] = -52.723; + bounds[2] = -1.002; + bounds[3] = 918273645.18293746; + bounds[4] = +1.002; + bounds[5] = +52.723; + g->SetBounds(bounds); + + mitk::ScalarType matrixCoeffs[9] = { + 0.0, 1.1, 2.2, + 3.3, 4.4, 5.5, + 6.6, 7.7, 8.8 + }; + + mitk::AffineTransform3D::MatrixType matrix; + matrix.GetVnlMatrix().set(matrixCoeffs); + + mitk::AffineTransform3D::OffsetType offset; + offset[0] = -43.1829374; + offset[1] = 0.0; + offset[2] = +43.1829374; + + mitk::AffineTransform3D::Pointer transform = mitk::AffineTransform3D::New(); + transform->SetMatrix(matrix); + transform->SetOffset(offset); + g->SetIndexToWorldTransform(transform); + + MITK_TEST_CONDITION( test.PointSetWrite(g), "Testing if the PointSetWriter writes Data _with_ geometry" ); + test.PointSetLoadAndCompareTest(); // load - compare + } - //Delete correctly - delete test; MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkPointSetLocaleTest.cpp b/Modules/Core/test/mitkPointSetLocaleTest.cpp index 1e4af92832..9c41c4c9d9 100644 --- a/Modules/Core/test/mitkPointSetLocaleTest.cpp +++ b/Modules/Core/test/mitkPointSetLocaleTest.cpp @@ -1,170 +1,170 @@ /*=================================================================== 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 "mitkPointSet.h" #include "mitkStandardFileLocations.h" #include "mitkIOUtil.h" #include "mitkTestingMacros.h" #include #include #include #include bool ChangeLocale(const std::string& locale) { try { MITK_TEST_OUTPUT(<< "\n** Changing locale from " << setlocale(LC_ALL, NULL) << " to '" << locale << "'"); setlocale(LC_ALL, locale.c_str()); std::locale l( locale.c_str() ); std::cin.imbue(l); std::cout.imbue(l); return true; } catch(...) { - MITK_TEST_OUTPUT(<< "Could not activate locale" << locale << "\n"); + MITK_TEST_OUTPUT(<< "Could not activate locale " << locale << "\n"); return false; } } void ReaderLocaleTest(mitk::Point3D & refPoint, std::string filename) { MITK_TEST_OUTPUT(<< "---- Reader Test ---- "); mitk::PointSet::Pointer pointSet = mitk::IOUtil::LoadPointSet(filename); mitk::Point3D point; if (pointSet->GetPointIfExists(0, &point)) { MITK_TEST_CONDITION_REQUIRED(fabs(refPoint[0] - point[0]) < 0.00001, "read x correct"); MITK_TEST_CONDITION_REQUIRED(fabs(refPoint[1] - point[1]) < 0.00001, "read y correct"); MITK_TEST_CONDITION_REQUIRED(fabs(refPoint[2] - point[2]) < 0.00001, "read z correct"); }else { MITK_TEST_FAILED_MSG(<< "File "<< filename << " can not be read - test will not applied." ); return; } } void WriterLocaleTest(mitk::Point3D & refPoint, std::string filename) { MITK_TEST_OUTPUT(<< "---- Writer Test---- "); //create pointset mitk::PointSet::Pointer refPointSet = mitk::PointSet::New(); refPointSet->InsertPoint(0,refPoint); //SetPoint(0, refPoint); - std::ofstream tmpStream; - std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream); + std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile("testPointSet_XXXXXX.mps"); // write point set mitk::IOUtil::Save(refPointSet, tmpFilePath); - tmpStream.close(); std::ifstream stream(tmpFilePath.c_str()); //compare two .mps files std::ifstream refStream (filename.c_str()); MITK_TEST_CONDITION_REQUIRED(refStream,"Read reference point set"); MITK_TEST_CONDITION_REQUIRED(stream,"Read point set"); bool differ = false; if (stream.is_open() && refStream.is_open()) { std::string streamLine; std::string refStreamLine; while(!stream.eof() && ! refStream.eof()) { getline(stream, streamLine); getline(refStream, refStreamLine); if(streamLine.compare(refStreamLine) != 0) { differ = true; break; } } stream.close(); refStream.close(); } MITK_TEST_CONDITION_REQUIRED(!differ, "Write point set correct"); } -int mitkPointSetLocaleTest(int argc, char* argv[]) +int mitkPointSetLocaleTest(int, char*[]) { MITK_TEST_BEGIN("PointSetLocaleTest"); - if (argc<2) {MITK_TEST_FAILED_MSG(<<"Error: test file name is needed as second argument.");} - - - std::string filename = argv[1]; - - MITK_INFO << filename; - //create reference point set mitk::PointSet::Pointer refPointSet = mitk::PointSet::New(); mitk::Point3D refPoint; refPoint[0] = 32.2946; refPoint[1] = -17.7359; refPoint[2] = 29.6502; refPointSet->SetPoint(0, refPoint); //create locale list std::ofstream stream; std::locale previousLocale(stream.getloc()); typedef std::list StringList; StringList alllocales; alllocales.push_back("de_DE"); alllocales.push_back("de_DE.utf8"); alllocales.push_back("de_DE.UTF-8"); alllocales.push_back("de_DE@euro"); alllocales.push_back("German_Germany"); // QuickFix for MAC OS X // See for more the Bug #3894 comments #if defined (__APPLE__) || defined(MACOSX) alllocales.push_back("C"); #endif + // write a reference file using the "C" locale once + ChangeLocale("C"); + std::string referenceFilePath = mitk::IOUtil::CreateTemporaryFile("refPointSet_XXXXXX.mps"); + MITK_INFO << "Reference PointSet in " << referenceFilePath; + + // write point set + mitk::IOUtil::Save(refPointSet, referenceFilePath); + + unsigned int numberOfTestedGermanLocales(0); for (StringList::iterator iter = alllocales.begin(); iter != alllocales.end(); ++iter) { if ( ChangeLocale(*iter) ) { ++numberOfTestedGermanLocales; - WriterLocaleTest(refPoint,filename); - ReaderLocaleTest(refPoint,filename); + WriterLocaleTest(refPoint, referenceFilePath); + ReaderLocaleTest(refPoint, referenceFilePath); } } if(numberOfTestedGermanLocales == 0) { MITK_TEST_OUTPUT(<< "Warning: No German locale was found on the system."); } //MITK_TEST_CONDITION_REQUIRED( numberOfTestedGermanLocales > 0, "Verify that at least one German locale has been tested."); MITK_TEST_END(); } diff --git a/Modules/DiffusionImaging/MiniApps/CMakeLists.txt b/Modules/DiffusionImaging/MiniApps/CMakeLists.txt index 84dd09cf5b..a90546cd82 100755 --- a/Modules/DiffusionImaging/MiniApps/CMakeLists.txt +++ b/Modules/DiffusionImaging/MiniApps/CMakeLists.txt @@ -1,128 +1,128 @@ option(BUILD_DiffusionMiniApps "Build commandline tools for diffusion" OFF) if(BUILD_DiffusionMiniApps OR MITK_BUILD_ALL_APPS) find_package(OpenMP) if(NOT OPENMP_FOUND) message("OpenMP is not available.") endif() if(OPENMP_FOUND) message(STATUS "Found OpenMP.") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") endif() # needed include directories include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) # list of diffusion miniapps # if an app requires additional dependencies # they are added after a "^^" and separated by "_" set( diffusionminiapps DwiDenoising^^ ImageResampler^^ NetworkCreation^^MitkFiberTracking_MitkConnectomics NetworkStatistics^^MitkConnectomics ExportShImage^^ Fiberfox^^MitkFiberTracking MultishellMethods^^MitkFiberTracking PeaksAngularError^^MitkFiberTracking PeakExtraction^^MitkFiberTracking FiberExtraction^^MitkFiberTracking FiberProcessing^^MitkFiberTracking FiberDirectionExtraction^^MitkFiberTracking LocalDirectionalFiberPlausibility^^MitkFiberTracking StreamlineTracking^^MitkFiberTracking GibbsTracking^^MitkFiberTracking CopyGeometry^^ DiffusionIndices^^ TractometerMetrics^^MitkFiberTracking QballReconstruction^^ Registration^^ FileFormatConverter^^MitkFiberTracking TensorReconstruction^^ TensorDerivedMapsExtraction^^ DICOMLoader^^ DFTraining^^MitkFiberTracking DFTracking^^MitkFiberTracking ) foreach(diffusionminiapp ${diffusionminiapps}) # extract mini app name and dependencies string(REPLACE "^^" "\\;" miniapp_info ${diffusionminiapp}) set(miniapp_info_list ${miniapp_info}) list(GET miniapp_info_list 0 appname) list(GET miniapp_info_list 1 raw_dependencies) string(REPLACE "_" "\\;" dependencies "${raw_dependencies}") set(dependencies_list ${dependencies}) mitk_create_executable(${appname} - DEPENDS MitkCore MitkDiffusionCore ${dependencies_list} + DEPENDS MitkCore MitkDiffusionCore MitkCommandLine ${dependencies_list} PACKAGE_DEPENDS ITK - CPP_FILES ${appname}.cpp mitkCommandLineParser.cpp + CPP_FILES ${appname}.cpp ) if(EXECUTABLE_IS_ENABLED) # On Linux, create a shell script to start a relocatable application if(UNIX AND NOT APPLE) install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${EXECUTABLE_TARGET}.sh) endif() get_target_property(_is_bundle ${EXECUTABLE_TARGET} MACOSX_BUNDLE) if(APPLE) if(_is_bundle) set(_target_locations ${EXECUTABLE_TARGET}.app) set(${_target_locations}_qt_plugins_install_dir ${EXECUTABLE_TARGET}.app/Contents/MacOS) set(_bundle_dest_dir ${EXECUTABLE_TARGET}.app/Contents/MacOS) set(_qt_plugins_for_current_bundle ${EXECUTABLE_TARGET}.app/Contents/MacOS) set(_qt_conf_install_dirs ${EXECUTABLE_TARGET}.app/Contents/Resources) install(TARGETS ${EXECUTABLE_TARGET} BUNDLE DESTINATION . ) else() if(NOT MACOSX_BUNDLE_NAMES) set(_qt_conf_install_dirs bin) set(_target_locations bin/${EXECUTABLE_TARGET}) set(${_target_locations}_qt_plugins_install_dir bin) install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) else() foreach(bundle_name ${MACOSX_BUNDLE_NAMES}) list(APPEND _qt_conf_install_dirs ${bundle_name}.app/Contents/Resources) set(_current_target_location ${bundle_name}.app/Contents/MacOS/${EXECUTABLE_TARGET}) list(APPEND _target_locations ${_current_target_location}) set(${_current_target_location}_qt_plugins_install_dir ${bundle_name}.app/Contents/MacOS) message( " set(${_current_target_location}_qt_plugins_install_dir ${bundle_name}.app/Contents/MacOS) ") install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION ${bundle_name}.app/Contents/MacOS/) endforeach() endif() endif() else() set(_target_locations bin/${EXECUTABLE_TARGET}${CMAKE_EXECUTABLE_SUFFIX}) set(${_target_locations}_qt_plugins_install_dir bin) set(_qt_conf_install_dirs bin) install(TARGETS ${EXECUTABLE_TARGET} RUNTIME DESTINATION bin) endif() endif() endforeach() # This mini app does not depend on mitkDiffusionImaging at all mitk_create_executable(Dicom2Nrrd - DEPENDS MitkCore - CPP_FILES Dicom2Nrrd.cpp mitkCommandLineParser.cpp + DEPENDS MitkCore MitkCommandLine + CPP_FILES Dicom2Nrrd.cpp ) # On Linux, create a shell script to start a relocatable application if(UNIX AND NOT APPLE) install(PROGRAMS "${MITK_SOURCE_DIR}/CMake/RunInstalledApp.sh" DESTINATION "." RENAME ${EXECUTABLE_TARGET}.sh) endif() if(EXECUTABLE_IS_ENABLED) MITK_INSTALL_TARGETS(EXECUTABLES ${EXECUTABLE_TARGET}) endif() endif() diff --git a/Modules/DiffusionImaging/MiniApps/mitkCommandLineParser.cpp b/Modules/DiffusionImaging/MiniApps/mitkCommandLineParser.cpp deleted file mode 100755 index 35502e1266..0000000000 --- a/Modules/DiffusionImaging/MiniApps/mitkCommandLineParser.cpp +++ /dev/null @@ -1,900 +0,0 @@ -/*=================================================================== - -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. - -===================================================================*/ -/*========================================================================= - - Library: CTK - - Copyright (c) Kitware Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -// STL includes -#include -#include - - -// MITK includes -#include "mitkCommandLineParser.h" - - -using namespace std; - -namespace -{ -// -------------------------------------------------------------------------- -class CommandLineParserArgumentDescription -{ -public: - - - CommandLineParserArgumentDescription( - const string& longArg, const string& longArgPrefix, - const string& shortArg, const string& shortArgPrefix, - mitkCommandLineParser::Type type, const string& argHelp, const string& argLabel, - const us::Any& defaultValue, bool ignoreRest, - bool deprecated, bool optional, string& argGroup, string& groupDescription) - : LongArg(longArg), LongArgPrefix(longArgPrefix), - ShortArg(shortArg), ShortArgPrefix(shortArgPrefix), - ArgHelp(argHelp), ArgLabel(argLabel), IgnoreRest(ignoreRest), NumberOfParametersToProcess(0), - Deprecated(deprecated), DefaultValue(defaultValue), Value(type), ValueType(type), Optional(optional), ArgGroup(argGroup), ArgGroupDescription(groupDescription) - { - Value = defaultValue; - - switch (type) - { - case mitkCommandLineParser::String: - { - NumberOfParametersToProcess = 1; - } - break; - case mitkCommandLineParser::Bool: - { - NumberOfParametersToProcess = 0; - } - break; - case mitkCommandLineParser::StringList: - { - NumberOfParametersToProcess = -1; - } - break; - case mitkCommandLineParser::Int: - { - NumberOfParametersToProcess = 1; - } - break; - case mitkCommandLineParser::Float: - { - NumberOfParametersToProcess = 1; - } - break; - - case mitkCommandLineParser::OutputDirectory: - case mitkCommandLineParser::InputDirectory: - { - NumberOfParametersToProcess = 1; - } - break; - - case mitkCommandLineParser::OutputFile: - case mitkCommandLineParser::InputFile: - { - NumberOfParametersToProcess = 1; - } - break; - case mitkCommandLineParser::InputImage: - { - NumberOfParametersToProcess = 1; - } - break; - - default: - std::cout << "Type not supported: " << static_cast(type); - } - - } - - ~CommandLineParserArgumentDescription(){} - - bool addParameter(const string& value); - - string helpText(); - - string LongArg; - string LongArgPrefix; - string ShortArg; - string ShortArgPrefix; - string ArgHelp; - string ArgLabel; - string ArgGroup; - string ArgGroupDescription; - bool IgnoreRest; - int NumberOfParametersToProcess; - bool Deprecated; - bool Optional; - - us::Any DefaultValue; - us::Any Value; - mitkCommandLineParser::Type ValueType; -}; - -// -------------------------------------------------------------------------- -bool CommandLineParserArgumentDescription::addParameter(const string &value) -{ - switch (ValueType) - { - case mitkCommandLineParser::String: - { - Value = value; - } - break; - case mitkCommandLineParser::Bool: - { - if (value.compare("true")==0) - Value = true; - else - Value = false; - } - break; - case mitkCommandLineParser::StringList: - { - try - { - mitkCommandLineParser::StringContainerType list = us::any_cast(Value); - list.push_back(value); - Value = list; - } - catch(...) - { - mitkCommandLineParser::StringContainerType list; - list.push_back(value); - Value = list; - } - } - break; - case mitkCommandLineParser::Int: - { - stringstream ss(value); - int i; - ss >> i; - Value = i; - } - break; - case mitkCommandLineParser::Float: - { - stringstream ss(value); - float f; - ss >> f; - Value = f; - } - break; - - case mitkCommandLineParser::InputDirectory: - case mitkCommandLineParser::OutputDirectory: - { - Value = value; - } - break; - - case mitkCommandLineParser::InputFile: - case mitkCommandLineParser::InputImage: - case mitkCommandLineParser::OutputFile: - { - Value = value; - } - break; - - default: - return false; - } - - return true; -} - -// -------------------------------------------------------------------------- -string CommandLineParserArgumentDescription::helpText() -{ - string text; - - string shortAndLongArg; - if (!this->ShortArg.empty()) - { - shortAndLongArg = " "; - shortAndLongArg += this->ShortArgPrefix; - shortAndLongArg += this->ShortArg; - } - - if (!this->LongArg.empty()) - { - if (this->ShortArg.empty()) - shortAndLongArg.append(" "); - else - shortAndLongArg.append(", "); - - shortAndLongArg += this->LongArgPrefix; - shortAndLongArg += this->LongArg; - } - - text = text + shortAndLongArg + ", " + this->ArgHelp; - - if (this->Optional) - text += " (optional)"; - - if (!this->DefaultValue.Empty()) - { - text = text + ", (default: " + this->DefaultValue.ToString() + ")"; - } - text += "\n"; - return text; -} - -} - -// -------------------------------------------------------------------------- -// ctkCommandLineParser::ctkInternal class - -// -------------------------------------------------------------------------- -class mitkCommandLineParser::ctkInternal -{ -public: - ctkInternal() - : Debug(false), FieldWidth(0), StrictMode(false) - {} - - ~ctkInternal() { } - - CommandLineParserArgumentDescription* argumentDescription(const string& argument); - - vector ArgumentDescriptionList; - map ArgNameToArgumentDescriptionMap; - map > GroupToArgumentDescriptionListMap; - - StringContainerType UnparsedArguments; - StringContainerType ProcessedArguments; - string ErrorString; - bool Debug; - int FieldWidth; - string LongPrefix; - string ShortPrefix; - string CurrentGroup; - string DisableQSettingsLongArg; - string DisableQSettingsShortArg; - bool StrictMode; -}; - -// -------------------------------------------------------------------------- -// ctkCommandLineParser::ctkInternal methods - -// -------------------------------------------------------------------------- -CommandLineParserArgumentDescription* -mitkCommandLineParser::ctkInternal::argumentDescription(const string& argument) -{ - string unprefixedArg = argument; - - if (!LongPrefix.empty() && argument.compare(0, LongPrefix.size(), LongPrefix)==0) - { - // Case when (ShortPrefix + UnPrefixedArgument) matches LongPrefix - if (argument == LongPrefix && !ShortPrefix.empty() && argument.compare(0, ShortPrefix.size(), ShortPrefix)==0) - { - unprefixedArg = argument.substr(ShortPrefix.size(),argument.size()); - } - else - { - unprefixedArg = argument.substr(LongPrefix.size(),argument.size()); - } - } - else if (!ShortPrefix.empty() && argument.compare(0, ShortPrefix.size(), ShortPrefix)==0) - { - unprefixedArg = argument.substr(ShortPrefix.size(),argument.size()); - } - else if (!LongPrefix.empty() && !ShortPrefix.empty()) - { - return nullptr; - } - - if (ArgNameToArgumentDescriptionMap.count(unprefixedArg)) - { - return this->ArgNameToArgumentDescriptionMap[unprefixedArg]; - } - return nullptr; -} - -// -------------------------------------------------------------------------- -// ctkCommandLineParser methods - -// -------------------------------------------------------------------------- -mitkCommandLineParser::mitkCommandLineParser() -{ - this->Internal = new ctkInternal(); - this->Category = string(); - this->Title = string(); - this->Contributor = string(); - this->Description = string(); - this->ParameterGroupName = "Parameters"; - this->ParameterGroupDescription = "Parameters"; -} - -// -------------------------------------------------------------------------- -mitkCommandLineParser::~mitkCommandLineParser() -{ - delete this->Internal; -} - -// -------------------------------------------------------------------------- -map mitkCommandLineParser::parseArguments(const StringContainerType& arguments, - bool* ok) -{ - // Reset - this->Internal->UnparsedArguments.clear(); - this->Internal->ProcessedArguments.clear(); - this->Internal->ErrorString.clear(); - // foreach (CommandLineParserArgumentDescription* desc, this->Internal->ArgumentDescriptionList) - for (unsigned int i=0; iArgumentDescriptionList.size(); i++) - { - CommandLineParserArgumentDescription* desc = Internal->ArgumentDescriptionList.at(i); - desc->Value = us::Any(desc->ValueType); - if (!desc->DefaultValue.Empty()) - { - desc->Value = desc->DefaultValue; - } - } - bool error = false; - bool ignoreRest = false; - CommandLineParserArgumentDescription * currentArgDesc = nullptr; - vector parsedArgDescriptions; - for(unsigned int i = 1; i < arguments.size(); ++i) - { - string argument = arguments.at(i); - - if (this->Internal->Debug) { std::cout << "Processing" << argument; } - if (!argument.compare("--xml") || !argument.compare("-xml") || !argument.compare("--XML") || !argument.compare("-XML")) - { - this->generateXmlOutput(); - return map(); - } - - // should argument be ignored ? - if (ignoreRest) - { - if (this->Internal->Debug) - { - std::cout << " Skipping: IgnoreRest flag was been set"; - } - this->Internal->UnparsedArguments.push_back(argument); - continue; - } - - // Skip if the argument does not start with the defined prefix - if (!(argument.compare(0, Internal->LongPrefix.size(), Internal->LongPrefix)==0 - || argument.compare(0, Internal->ShortPrefix.size(), Internal->ShortPrefix)==0)) - { - if (this->Internal->StrictMode) - { - this->Internal->ErrorString = "Unknown argument "; - this->Internal->ErrorString += argument; - error = true; - break; - } - if (this->Internal->Debug) - { - std::cout << " Skipping: It does not start with the defined prefix"; - } - this->Internal->UnparsedArguments.push_back(argument); - continue; - } - - // Skip if argument has already been parsed ... - bool alreadyProcessed = false; - for (unsigned int i=0; iProcessedArguments.size(); i++) - if (argument.compare(Internal->ProcessedArguments.at(i))==0) - { - alreadyProcessed = true; - break; - } - - if (alreadyProcessed) - { - if (this->Internal->StrictMode) - { - this->Internal->ErrorString = "Argument "; - this->Internal->ErrorString += argument; - this->Internal->ErrorString += " already processed !"; - error = true; - break; - } - if (this->Internal->Debug) - { - std::cout << " Skipping: Already processed !"; - } - continue; - } - - // Retrieve corresponding argument description - currentArgDesc = this->Internal->argumentDescription(argument); - - // Is there a corresponding argument description ? - if (currentArgDesc) - { - // If the argument is deprecated, print the help text but continue processing - if (currentArgDesc->Deprecated) - { - std::cout << "Deprecated argument " << argument << ": " << currentArgDesc->ArgHelp; - } - else - { - parsedArgDescriptions.push_back(currentArgDesc); - } - - this->Internal->ProcessedArguments.push_back(currentArgDesc->ShortArg); - this->Internal->ProcessedArguments.push_back(currentArgDesc->LongArg); - int numberOfParametersToProcess = currentArgDesc->NumberOfParametersToProcess; - ignoreRest = currentArgDesc->IgnoreRest; - if (this->Internal->Debug && ignoreRest) - { - std::cout << " IgnoreRest flag is True"; - } - - // Is the number of parameters associated with the argument being processed known ? - if (numberOfParametersToProcess == 0) - { - currentArgDesc->addParameter("true"); - } - else if (numberOfParametersToProcess > 0) - { - string missingParameterError = - "Argument %1 has %2 value(s) associated whereas exacly %3 are expected."; - for(int j=1; j <= numberOfParametersToProcess; ++j) - { - if (i + j >= arguments.size()) - { -// this->Internal->ErrorString = -// missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess); -// if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; } - if (ok) { *ok = false; } - return map(); - } - string parameter = arguments.at(i + j); - if (this->Internal->Debug) - { - std::cout << " Processing parameter" << j << ", value:" << parameter; - } - if (this->argumentAdded(parameter)) - { -// this->Internal->ErrorString = -// missingParameterError.arg(argument).arg(j-1).arg(numberOfParametersToProcess); -// if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; } - if (ok) { *ok = false; } - return map(); - } - if (!currentArgDesc->addParameter(parameter)) - { -// this->Internal->ErrorString = string( -// "Value(s) associated with argument %1 are incorrect. %2"). -// arg(argument).arg(currentArgDesc->ExactMatchFailedMessage); -// if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; } - if (ok) { *ok = false; } - return map(); - } - } - // Update main loop increment - i = i + numberOfParametersToProcess; - } - else if (numberOfParametersToProcess == -1) - { - if (this->Internal->Debug) - { - std::cout << " Proccessing StringList ..."; - } - int j = 1; - while(j + i < arguments.size()) - { - if (this->argumentAdded(arguments.at(j + i))) - { - if (this->Internal->Debug) - { - std::cout << " No more parameter for" << argument; - } - break; - } - string parameter = arguments.at(j + i); - - if (parameter.compare(0, Internal->LongPrefix.size(), Internal->LongPrefix)==0 - || parameter.compare(0, Internal->ShortPrefix.size(), Internal->ShortPrefix)==0) - { - j--; - break; - } - - if (this->Internal->Debug) - { - std::cout << " Processing parameter" << j << ", value:" << parameter; - } - if (!currentArgDesc->addParameter(parameter)) - { -// this->Internal->ErrorString = string( -// "Value(s) associated with argument %1 are incorrect. %2"). -// arg(argument).arg(currentArgDesc->ExactMatchFailedMessage); -// if (this->Internal->Debug) { std::cout << this->Internal->ErrorString; } - if (ok) { *ok = false; } - return map(); - } - j++; - } - // Update main loop increment - i = i + j; - } - } - else - { - if (this->Internal->StrictMode) - { - this->Internal->ErrorString = "Unknown argument "; - this->Internal->ErrorString += argument; - error = true; - break; - } - if (this->Internal->Debug) - { - std::cout << " Skipping: Unknown argument"; - } - this->Internal->UnparsedArguments.push_back(argument); - } - } - - if (ok) - { - *ok = !error; - } - - map parsedArguments; - - int obligatoryArgs = 0; - vector::iterator it; - for(it = Internal->ArgumentDescriptionList.begin(); it != Internal->ArgumentDescriptionList.end();++it) - { - CommandLineParserArgumentDescription* desc = *it; - - if(!desc->Optional) - obligatoryArgs++; - } - - int parsedObligatoryArgs = 0; - for(it = parsedArgDescriptions.begin(); it != parsedArgDescriptions.end();++it) - { - CommandLineParserArgumentDescription* desc = *it; - - string key; - if (!desc->LongArg.empty()) - { - key = desc->LongArg; - } - else - { - key = desc->ShortArg; - } - - if(!desc->Optional) - parsedObligatoryArgs++; - - std::pair elem; elem.first = key; elem.second = desc->Value; - parsedArguments.insert(elem); - } - - if (obligatoryArgs>parsedObligatoryArgs) - { - parsedArguments.clear(); - cout << helpText(); - } - - return parsedArguments; -} - -// ------------------------------------------------------------------------- -map mitkCommandLineParser::parseArguments(int argc, char** argv, bool* ok) -{ - StringContainerType arguments; - - // Create a StringContainerType of arguments - for(int i = 0; i < argc; ++i) - arguments.push_back(argv[i]); - - return this->parseArguments(arguments, ok); -} - -// ------------------------------------------------------------------------- -string mitkCommandLineParser::errorString() const -{ - return this->Internal->ErrorString; -} - -// ------------------------------------------------------------------------- -const mitkCommandLineParser::StringContainerType& mitkCommandLineParser::unparsedArguments() const -{ - return this->Internal->UnparsedArguments; -} - -// -------------------------------------------------------------------------- -void mitkCommandLineParser::addArgument(const string& longarg, const string& shortarg, - Type type, const string& argLabel, const string& argHelp, - const us::Any &defaultValue, bool optional, bool ignoreRest, - bool deprecated) -{ - if (longarg.empty() && shortarg.empty()) { return; } - - /* Make sure it's not already added */ - bool added = this->Internal->ArgNameToArgumentDescriptionMap.count(longarg); - if (added) { return; } - - added = this->Internal->ArgNameToArgumentDescriptionMap.count(shortarg); - if (added) { return; } - - auto argDesc = - new CommandLineParserArgumentDescription(longarg, this->Internal->LongPrefix, - shortarg, this->Internal->ShortPrefix, type, - argHelp, argLabel, defaultValue, ignoreRest, deprecated, optional, ParameterGroupName, ParameterGroupDescription); - - int argWidth = 0; - if (!longarg.empty()) - { - this->Internal->ArgNameToArgumentDescriptionMap[longarg] = argDesc; - argWidth += longarg.size() + this->Internal->LongPrefix.size(); - } - if (!shortarg.empty()) - { - this->Internal->ArgNameToArgumentDescriptionMap[shortarg] = argDesc; - argWidth += shortarg.size() + this->Internal->ShortPrefix.size() + 2; - } - argWidth += 5; - - // Set the field width for the arguments - if (argWidth > this->Internal->FieldWidth) - { - this->Internal->FieldWidth = argWidth; - } - - this->Internal->ArgumentDescriptionList.push_back(argDesc); - this->Internal->GroupToArgumentDescriptionListMap[this->Internal->CurrentGroup].push_back(argDesc); -} - -// -------------------------------------------------------------------------- -void mitkCommandLineParser::addDeprecatedArgument( - const string& longarg, const string& shortarg, const string& argLabel, const string& argHelp) -{ - addArgument(longarg, shortarg, StringList, argLabel, argHelp, us::Any(), false, true, false); -} - -// -------------------------------------------------------------------------- -int mitkCommandLineParser::fieldWidth() const -{ - return this->Internal->FieldWidth; -} - -// -------------------------------------------------------------------------- -void mitkCommandLineParser::beginGroup(const string& description) -{ - this->Internal->CurrentGroup = description; -} - -// -------------------------------------------------------------------------- -void mitkCommandLineParser::endGroup() -{ - this->Internal->CurrentGroup.clear(); -} - -// -------------------------------------------------------------------------- -string mitkCommandLineParser::helpText() const -{ - string text; - vector deprecatedArgs; - - // Loop over grouped argument descriptions - map >::iterator it; - for(it = Internal->GroupToArgumentDescriptionListMap.begin(); it != Internal->GroupToArgumentDescriptionListMap.end();++it) - { - if (!(*it).first.empty()) - { - text = text + "\n" + (*it).first + "\n"; - } - - vector::iterator it2; - for(it2 = (*it).second.begin(); it2 != (*it).second.end(); ++it2) - { - CommandLineParserArgumentDescription* argDesc = *it2; - if (argDesc->Deprecated) - { - deprecatedArgs.push_back(argDesc); - } - else - { - text += argDesc->helpText(); - } - } - } - - if (!deprecatedArgs.empty()) - { - text += "\nDeprecated arguments:\n"; - vector::iterator it2; - for(it2 = deprecatedArgs.begin(); it2 != deprecatedArgs.end(); ++it2) - { - CommandLineParserArgumentDescription* argDesc = *it2; - text += argDesc->helpText(); - } - } - - return text; -} - -// -------------------------------------------------------------------------- -bool mitkCommandLineParser::argumentAdded(const string& argument) const -{ - return this->Internal->ArgNameToArgumentDescriptionMap.count(argument); -} - -// -------------------------------------------------------------------------- -bool mitkCommandLineParser::argumentParsed(const string& argument) const -{ - for (unsigned int i=0; iProcessedArguments.size(); i++) - if (argument.compare(Internal->ProcessedArguments.at(i))==0) - return true; - return false; -} - -// -------------------------------------------------------------------------- -void mitkCommandLineParser::setArgumentPrefix(const string& longPrefix, const string& shortPrefix) -{ - this->Internal->LongPrefix = longPrefix; - this->Internal->ShortPrefix = shortPrefix; -} - -// -------------------------------------------------------------------------- -void mitkCommandLineParser::setStrictModeEnabled(bool strictMode) -{ - this->Internal->StrictMode = strictMode; -} - -void mitkCommandLineParser::generateXmlOutput() -{ - std::stringstream xml; - - xml << "" << endl; - xml << "" << Category << "" << endl; - xml << "" << Title <<"" << endl; - xml << "" << Description << "" << endl; - xml << "" << Contributor << "" << endl; - xml << "" << endl; - - std::vector::iterator it; - - std::string lastParameterGroup = ""; - for (it = this->Internal->ArgumentDescriptionList.begin(); it != this->Internal->ArgumentDescriptionList.end(); it++) - { - std::string type; - switch ((*it)->ValueType) - { - case mitkCommandLineParser::String: - type = "string"; - break; - - case mitkCommandLineParser::Bool: - type = "boolean"; - break; - - case mitkCommandLineParser::StringList: - type = "string-vector"; - break; - - case mitkCommandLineParser::Int: - type = "integer"; - break; - - case mitkCommandLineParser::Float: - type = "float"; - break; - - case mitkCommandLineParser::OutputDirectory: - case mitkCommandLineParser::InputDirectory: - type = "directory"; - break; - - case mitkCommandLineParser::InputImage: - type = "image"; - break; - - case mitkCommandLineParser::OutputFile: - case mitkCommandLineParser::InputFile: - type = "file"; - break; - } - - if (lastParameterGroup.compare((*it)->ArgGroup)) - { - if (it != this->Internal->ArgumentDescriptionList.begin()) - { - xml << "" << endl; - xml << "" << endl; - } - xml << "" << endl; - xml << "" << (*it)->ArgGroupDescription << "" << endl; - lastParameterGroup = (*it)->ArgGroup; - } - - // Skip help item, as it's no use in GUI - if ((*it)->ShortArg == "h") - continue; - - xml << "<" << type << ">" << endl; - xml << "" << (*it)->LongArg << "" << endl; - xml << "" << (*it)->ArgHelp << "" << endl; - xml << "" << endl; - if (!(*it)->DefaultValue.Empty()) - xml << "" << (*it)->DefaultValue.ToString() << "" << endl; - - xml << "" << (*it)->LongArg << "" << endl; - xml << "" << (*it)->ShortArg << "" << endl; - - if ((*it)->ValueType == mitkCommandLineParser::InputDirectory || (*it)->ValueType == mitkCommandLineParser::InputFile || (*it)->ValueType == mitkCommandLineParser::InputImage) - { - xml << "input" << endl; - } - else if ((*it)->ValueType == mitkCommandLineParser::OutputDirectory || (*it)->ValueType == mitkCommandLineParser::OutputFile) - { - xml << "output" << endl; - } - xml << "" << endl; - } - - xml << "" << endl; - xml << "" << endl; - - cout << xml.str(); -} - -void mitkCommandLineParser::setTitle(string title) -{ - Title = title; -} -void mitkCommandLineParser::setContributor(string contributor) -{ - Contributor = contributor; -} - -void mitkCommandLineParser::setCategory(string category) -{ - Category = category; -} - -void mitkCommandLineParser::setDescription(string description) -{ - Description = description; -} - -void mitkCommandLineParser::changeParameterGroup(string name, string tooltip) -{ - ParameterGroupName = name; - ParameterGroupDescription = tooltip; -} diff --git a/Modules/DiffusionImaging/MiniApps/mitkCommandLineParser.h b/Modules/DiffusionImaging/MiniApps/mitkCommandLineParser.h deleted file mode 100755 index 96e63d9a1d..0000000000 --- a/Modules/DiffusionImaging/MiniApps/mitkCommandLineParser.h +++ /dev/null @@ -1,475 +0,0 @@ -/*=================================================================== - -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. - -===================================================================*/ -/*========================================================================= - - Library: CTK - - Copyright (c) Kitware Inc. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0.txt - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -=========================================================================*/ - -#ifndef __mitkCommandLineParser_h -#define __mitkCommandLineParser_h - -#include -#include - - -/** - * \ingroup Core - * - * The CTK command line parser. - * - * Use this class to add information about the command line arguments - * your program understands and to easily parse them from a given list - * of strings. - * - * This parser provides the following features: - * - *
    - *
  • Add arguments by supplying a long name and/or a short name. - * Arguments are validated using a regular expression. They can have - * a default value and a help string.
  • - *
  • Deprecated arguments.
  • - *
  • Custom regular expressions for argument validation.
  • - *
  • Set different argument name prefixes for native platform look and feel.
  • - *
  • QSettings support. Default values for arguments can be read from - * a QSettings object.
  • - *
  • Create a help text for the command line arguments with support for - * grouping arguments.
  • - *
- * - * Here is an example how to use this class inside a main function: - * - * \code - * #include - * #include - * #include - * - * int main(int argc, char** argv) - * { - * QCoreApplication app(argc, argv); - * // This is used by QSettings - * QCoreApplication::setOrganizationName("MyOrg"); - * QCoreApplication::setApplicationName("MyApp"); - * - * ctkCommandLineParser parser; - * // Use Unix-style argument names - * parser.setArgumentPrefix("--", "-"); - * // Enable QSettings support - * parser.enableSettings("disable-settings"); - * - * // Add command line argument names - * parser.addArgument("disable-settings", "", us::Any::Bool, "Do not use QSettings"); - * parser.addArgument("help", "h", us::Any::Bool, "Show this help text"); - * parser.addArgument("search-paths", "s", us::Any::StringList, "A list of paths to search"); - * - * // Parse the command line arguments - * bool ok = false; - * map parsedArgs = parser.parseArguments(QCoreApplication::arguments(), &ok); - * if (!ok) - * { - * QTextStream(stderr, QIODevice::WriteOnly) << "Error parsing arguments: " - * << parser.errorString() << "\n"; - * return EXIT_FAILURE; - * } - * - * // Show a help message - * if (parsedArgs.contains("help") || parsedArgs.contains("h")) - * { - * QTextStream(stdout, QIODevice::WriteOnly) << parser.helpText(); - * return EXIT_SUCCESS; - * } - * - * // Do something - * - * return EXIT_SUCCESS; - * } - * \endcode - */ - -using namespace std; - -class mitkCommandLineParser -{ - -public: - - enum Type { - String = 0, - Bool = 1, - StringList = 2, - Int = 3, - Float = 4, - InputDirectory = 5, - InputFile = 6, - OutputDirectory = 7, - OutputFile = 8, - InputImage = 9 - }; - - typedef std::vector< std::string > StringContainerType; - - /** - * Constructs a parser instance. - * - * If QSettings support is enabled by a call to enableSettings() - * a default constructed QSettings instance will be used when parsing - * the command line arguments. Make sure to call QCoreApplication::setOrganizationName() - * and QCoreApplication::setApplicationName() before using default - * constructed QSettings objects. - * - * @param newParent The QObject parent. - */ - mitkCommandLineParser(); - - ~mitkCommandLineParser(); - - /** - * Parse a given list of command line arguments. - * - * This method parses a list of string elements considering the known arguments - * added by calls to addArgument(). If any one of the argument - * values does not match the corresponding regular expression, - * ok is set to false and an empty map object is returned. - * - * The keys in the returned map object correspond to the long argument string, - * if it is not empty. Otherwise, the short argument string is used as key. The - * us::Any values can safely be converted to the type specified in the - * addArgument() method call. - * - * @param arguments A StringContainerType containing command line arguments. Usually - * given by QCoreApplication::arguments(). - * @param ok A pointer to a boolean variable. Will be set to true - * if all regular expressions matched, false otherwise. - * @return A map object mapping the long argument (if empty, the short one) - * to a us::Any containing the value. - */ - - map parseArguments(const StringContainerType &arguments, bool* ok = nullptr); - - /** - * Convenient method allowing to parse a given list of command line arguments. - * @see parseArguments(const StringContainerType &, bool*) - */ - map parseArguments(int argc, char** argv, bool* ok = nullptr); - - /** - * Returns a detailed error description if a call to parseArguments() - * failed. - * - * @return The error description, empty if no error occured. - * @see parseArguments(const StringContainerType&, bool*) - */ - string errorString() const; - - /** - * This method returns all unparsed arguments, i.e. all arguments - * for which no long or short name has been registered via a call - * to addArgument(). - * - * @see addArgument() - * - * @return A list containing unparsed arguments. - */ - const StringContainerType& unparsedArguments() const; - - /** - * Checks if the given argument has been added via a call - * to addArgument(). - * - * @see addArgument() - * - * @param argument The argument to be checked. - * @return true if the argument was added, false - * otherwise. - */ - bool argumentAdded(const string& argument) const; - - /** - * Checks if the given argument has been parsed successfully by a previous - * call to parseArguments(). - * - * @param argument The argument to be checked. - * @return true if the argument was parsed, false - * otherwise. - */ - bool argumentParsed(const string& argument) const; - - /** - * Adds a command line argument. An argument can have a long name - * (like --long-argument-name), a short name (like -l), or both. The type - * of the argument can be specified by using the type parameter. - * The following types are supported: - * - * - * - * - * - * - * - * - *
Type# of parametersDefault regular exprExample
us::Any::String1.*--test-string StringParameter
us::Any::Bool0does not apply--enable-something
us::Any::StringList-1.*--test-list string1 string2
us::Any::Int1-?[0-9]+--test-int -5
- * - * The regular expressions are used to validate the parameters of command line - * arguments. You can restrict the valid set of parameters by calling - * setExactMatchRegularExpression() for your argument. - * - * Optionally, a help string and a default value can be provided for the argument. If - * the us::Any type of the default value does not match type, an - * exception is thrown. Arguments with default values are always returned by - * parseArguments(). - * - * You can also declare an argument deprecated, by setting deprecated - * to true. Alternatively you can add a deprecated argument by calling - * addDeprecatedArgument(). - * - * If the long or short argument has already been added, or if both are empty strings, - * the method call has no effect. - * - * @param longarg The long argument name. - * @param shortarg The short argument name. - * @param type The argument type (see the list above for supported types). - * @param argLabel The label of this argument, when auto generated interface is used. - * @param argHelp A help string describing the argument. - * @param defaultValue A default value for the argument. - * @param ignoreRest All arguments after the current one will be ignored. - * @param deprecated Declares the argument deprecated. - * - * @see setExactMatchRegularExpression() - * @see addDeprecatedArgument() - * @throws std::logic_error If the us::Any type of defaultValue - * does not match type, a std::logic_error is thrown. - */ - void addArgument(const string& longarg, const string& shortarg, - Type type, const string& argLabel, const string& argHelp = string(), - const us::Any& defaultValue = us::Any(), bool optional=true, - bool ignoreRest = false, bool deprecated = false); - - /** - * Adds a deprecated command line argument. If a deprecated argument is provided - * on the command line, argHelp is displayed in the console and - * processing continues with the next argument. - * - * Deprecated arguments are grouped separately at the end of the help text - * returned by helpText(). - * - * @param longarg The long argument name. - * @param shortarg The short argument name. - * @param argHelp A help string describing alternatives to the deprecated argument. - */ - void addDeprecatedArgument(const string& longarg, const string& shortarg, const string& argLabel, - const string& argHelp); - - /** - * Sets a custom regular expression for validating argument parameters. The method - * errorString() can be used the get the last error description. - * - * @param argument The previously added long or short argument name. - * @param expression A regular expression which the arugment parameters must match. - * @param exactMatchFailedMessage An error message explaining why the parameter did - * not match. - * - * @return true if the argument was found and the regular expression was set, - * false otherwise. - * - * @see errorString() - */ - bool setExactMatchRegularExpression(const string& argument, const string& expression, - const string& exactMatchFailedMessage); - - /** - * The field width for the argument names without the help text. - * - * @return The argument names field width in the help text. - */ - int fieldWidth() const; - - /** - * Creates a help text containing properly formatted argument names and help strings - * provided by calls to addArgument(). The arguments can be grouped by - * using beginGroup() and endGroup(). - * - * @param charPad The padding character. - * @return The formatted help text. - */ - string helpText() const; - - /** - * Sets the argument prefix for long and short argument names. This can be used - * to create native command line arguments without changing the calls to - * addArgument(). For example on Unix-based systems, long argument - * names start with "--" and short names with "-", while on Windows argument names - * always start with "/". - * - * Note that all methods in ctkCommandLineParser which take an argument name - * expect the name as it was supplied to addArgument. - * - * Example usage: - * - * \code - * ctkCommandLineParser parser; - * parser.setArgumentPrefix("--", "-"); - * parser.addArgument("long-argument", "l", us::Any::String); - * StringContainerType args; - * args << "program name" << "--long-argument Hi"; - * parser.parseArguments(args); - * \endcode - * - * @param longPrefix The prefix for long argument names. - * @param shortPrefix The prefix for short argument names. - */ - void setArgumentPrefix(const string& longPrefix, const string& shortPrefix); - - /** - * Begins a new group for documenting arguments. All newly added arguments via - * addArgument() will be put in the new group. You can close the - * current group by calling endGroup() or be opening a new group. - * - * Note that groups cannot be nested and all arguments which do not belong to - * a group will be listed at the top of the text created by helpText(). - * - * @param description The description of the group - */ - void beginGroup(const string& description); - - /** - * Ends the current group. - * - * @see beginGroup(const string&) - */ - void endGroup(); - - /** - * Enables QSettings support in ctkCommandLineParser. If an argument name is found - * in the QSettings instance with a valid us::Any, the value is considered as - * a default value and overwrites default values registered with - * addArgument(). User supplied values on the command line overwrite - * values in the QSettings instance, except for arguments with multiple parameters - * which are merged with QSettings values. Call mergeSettings(false) - * to disable merging. - * - * See ctkCommandLineParser(QSettings*) for information about how to - * supply a QSettings instance. - * - * Additionally, a long and short argument name can be specified which will disable - * QSettings support if supplied on the command line. The argument name must be - * registered as a regular argument via addArgument(). - * - * @param disableLongArg Long argument name. - * @param disableShortArg Short argument name. - * - * @see ctkCommandLineParser(QSettings*) - */ - void enableSettings(const string& disableLongArg = "", - const string& disableShortArg = ""); - - /** - * Controlls the merging behavior of user values and QSettings values. - * - * If merging is on (the default), user supplied values for an argument - * which can take more than one parameter are merged with values stored - * in the QSettings instance. If merging is off, the user values overwrite - * the QSettings values. - * - * @param merge true enables QSettings merging, false - * disables it. - */ - void mergeSettings(bool merge); - - /** - * Can be used to check if QSettings support has been enabled by a call to - * enableSettings(). - * - * @return true if QSettings support is enabled, false - * otherwise. - */ - bool settingsEnabled() const; - - - /** - * Can be used to teach the parser to stop parsing the arguments and return False when - * an unknown argument is encountered. By default StrictMode is disabled. - * - * @see parseArguments(const StringContainerType &, bool*) - */ - void setStrictModeEnabled(bool strictMode); - - /** - * Is used to generate an XML output for any commandline program. - */ - void generateXmlOutput(); - - /** - * Is used to set the title of the auto generated interface. - * - * @param title The title of the app. - */ - void setTitle(std::string title); - /** - * Is used to set the contributor for the help view in the auto generated interface. - * - * @param contributor Contributor of the app. - */ - void setContributor(std::string contributor); - /** - * Is used to categorize the apps in the commandline module. - * - * @param category The category of the app. - */ - void setCategory(std::string category); - /** - * Is used as the help text in the auto generated interface. - * - * @param description A short description for the app. - */ - void setDescription(std::string description); - /** - * Is used to group several Parameters in one groupbox in the auto generated interface. - * Default name is "Parameters", with the tooltip: "Groupbox containing parameters." - * - * To change the group of several arguments, call this method before the arguments are added. - * - * @param name The name of the groupbox. - * @param tooltip The tooltip of the groupbox. - */ - void changeParameterGroup(std::string name, std::string tooltip); - -private: - class ctkInternal; - ctkInternal * Internal; - - string Title; - string Contributor; - string Category; - string Description; - string ParameterGroupName; - string ParameterGroupDescription; -}; - -#endif diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 828edde878..c8e1cd5fd4 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -1,72 +1,73 @@ # The entries in the mitk_modules list must be # ordered according to their dependencies. set(mitk_modules Core + CommandLine AppUtil DCMTesting RDF LegacyIO DataTypesExt Overlays LegacyGL AlgorithmsExt MapperExt DICOMReader DICOMTesting Qt4Qt5TestModule SceneSerializationBase PlanarFigure ImageDenoising ImageExtraction ImageStatistics LegacyAdaptors SceneSerialization GraphAlgorithms Multilabel ContourModel SurfaceInterpolation Segmentation PlanarFigureSegmentation OpenViewCore QmlItems QtWidgets QtWidgetsExt SegmentationUI DiffusionImaging GPGPU OpenIGTLink IGTBase IGT CameraCalibration RigidRegistration RigidRegistrationUI DeformableRegistration DeformableRegistrationUI OpenCL OpenCVVideoSupport QtOverlays InputDevices ToFHardware ToFProcessing ToFUI US USUI DicomUI Simulation Remeshing Python Persistence OpenIGTLinkUI IGTUI VtkShaders DicomRT RTUI IOExt XNAT TubeGraph ) if(MITK_ENABLE_PIC_READER) list(APPEND mitk_modules IpPicSupportIO) endif() diff --git a/Modules/PlanarFigure/include/mitkPlanarFigure.h b/Modules/PlanarFigure/include/mitkPlanarFigure.h index bcc4fa6d62..c9746d0fc5 100644 --- a/Modules/PlanarFigure/include/mitkPlanarFigure.h +++ b/Modules/PlanarFigure/include/mitkPlanarFigure.h @@ -1,411 +1,411 @@ /*=================================================================== 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. ===================================================================*/ #ifndef _MITK_PLANAR_FIGURE_H_ #define _MITK_PLANAR_FIGURE_H_ #include #include "mitkBaseData.h" #include "mitkCommon.h" #include namespace mitk { class PlaneGeometry; /** * \brief Base-class for geometric planar (2D) figures, such as * lines, circles, rectangles, polygons, etc. * * \warning Currently does not support time-resolved data handling * * Behavior and appearance of PlanarFigures are controlled by various properties; for a detailed * list of appearance properties see mitk::PlanarFigureMapper2D * * The following properties control general PlanarFigure behavior: * *
    *
  • "selected": true if the planar figure is selected *
  • "planarfigure.ishovering": true if the mouse "hovers" over the planar figure *
  • "planarfigure.iseditable": true if the planar figure can be edited (otherwise, * it can only be picked/selected, but its control points cannot be edited); default is true *
  • "planarfigure.isextendable": true if new control points can be inserted into the list of control points; * default is false *
* * * TODO: Implement local 2D transform (including center of rotation...) * */ class MITKPLANARFIGURE_EXPORT PlanarFigure : public BaseData { public: mitkClassMacro( PlanarFigure, BaseData ) itkCloneMacro( Self ) typedef Point2D PolyLineElement; typedef itk::VectorContainer< unsigned long, bool> BoolContainerType; typedef std::deque< Point2D > ControlPointListType; typedef std::vector< PolyLineElement > PolyLineType; /** \brief Sets the 2D geometry on which this figure will be placed. * * In most cases, this is a Geometry already owned by another object, e.g. * describing the slice of the image on which measurements will be * performed. */ virtual void SetPlaneGeometry( mitk::PlaneGeometry *geometry ); /** \brief Returns (previously set) 2D geometry of this figure. */ virtual const PlaneGeometry *GetPlaneGeometry() const; /** \brief True if the planar figure is closed. * * Default is false. The "closed" boolean property must be set in sub-classes. */ virtual bool IsClosed() const; /** \brief True if the planar figure has been placed (and can be * displayed/interacted with). */ virtual bool IsPlaced() const { return m_FigurePlaced; }; /** \brief Place figure at the given point (in 2D index coordinates) onto * the given 2D geometry. * * By default, the first two control points of the figure are set to the * passed point. Further points can be set via AddControlPoint(), if the * current number of control points is below the maximum number of control * points. * * Can be re-implemented in sub-classes as needed. */ virtual void PlaceFigure( const Point2D& point ); /** * \brief Adds / inserts new control-points * * This method adds a new control-point with the coordinates defined by point at the given index. * If 'index' == -1 or index is greater than the number of control-points the new point is appended * to the back of the list of control points. * If a control-point already exists for 'index', an additional point is inserted at that position. * It is not possible to add more points if the maximum number of control-points (GetMaximumNumberOfControlPoints()) * has been reached. */ virtual bool AddControlPoint( const Point2D& point, int index = -1 ); virtual bool SetControlPoint( unsigned int index, const Point2D& point, bool createIfDoesNotExist = false); virtual bool SetCurrentControlPoint( const Point2D& point ); /** \brief Returns the current number of 2D control points defining this figure. */ unsigned int GetNumberOfControlPoints() const; /** \brief Returns the minimum number of control points needed to represent * this figure. * * Must be implemented in sub-classes. */ virtual unsigned int GetMinimumNumberOfControlPoints() const = 0; /** \brief Returns the maximum number of control points allowed for * this figure (e.g. 3 for triangles). * * Must be implemented in sub-classes. */ virtual unsigned int GetMaximumNumberOfControlPoints() const = 0; /** \brief Selects currently active control points. */ virtual bool SelectControlPoint( unsigned int index ); /** \brief Deselect control point; no control point active. */ virtual bool DeselectControlPoint(); /** \brief Return currently selected control point. */ virtual int GetSelectedControlPoint() const { return m_SelectedControlPoint; } /** \brief Returns specified control point in 2D world coordinates. */ Point2D GetControlPoint( unsigned int index ) const; /** * \brief Returns the id of the control-point that corresponds to the given * polyline-point. */ - int GetControlPointForPolylinePoint( int indexOfPolylinePoint, int polyLineIndex ) const; + virtual int GetControlPointForPolylinePoint( int indexOfPolylinePoint, int polyLineIndex ) const; /** \brief Returns specified control point in world coordinates. */ Point3D GetWorldControlPoint( unsigned int index ) const; /** \brief Returns the polyline representing the planar figure * (for rendering, measurements, etc.). */ const PolyLineType GetPolyLine(unsigned int index); /** \brief Returns the polyline representing the planar figure * (for rendering, measurments, etc.). */ const PolyLineType GetPolyLine(unsigned int index) const; /** \brief Returns the polyline that should be drawn the same size at every scale * (for text, angles, etc.). */ const PolyLineType GetHelperPolyLine( unsigned int index, double mmPerDisplayUnit, unsigned int displayHeight ); /** \brief Sets the position of the PreviewControlPoint. Automatically sets it visible.*/ void SetPreviewControlPoint( const Point2D& point ); /** \brief Marks the PreviewControlPoint as invisible.*/ void ResetPreviewContolPoint(); /** \brief Returns whether or not the PreviewControlPoint is visible.*/ bool IsPreviewControlPointVisible(); /** \brief Returns the coordinates of the PreviewControlPoint. */ Point2D GetPreviewControlPoint(); /** \brief Returns the number of features available for this PlanarFigure * (such as, radius, area, ...). */ virtual unsigned int GetNumberOfFeatures() const; /** \brief Returns the name (identifier) of the specified features. */ const char *GetFeatureName( unsigned int index ) const; /** \brief Returns the physical unit of the specified features. */ const char *GetFeatureUnit( unsigned int index ) const; /** Returns quantity of the specified feature (e.g., length, radius, * area, ... ) */ double GetQuantity( unsigned int index ) const; /** \brief Returns true if the feature with the specified index exists and * is active (an inactive feature may e.g. be the area of a non-closed * polygon. */ bool IsFeatureActive( unsigned int index ) const; /** \brief Returns true if the feature with the specified index exists and is set visible */ bool IsFeatureVisible( unsigned int index ) const; /** \brief Defines if the feature with the specified index will be shown as an * overlay in the RenderWindow */ void SetFeatureVisible( unsigned int index, bool visible ); /** \brief Calculates quantities of all features of this planar figure. */ virtual void EvaluateFeatures(); /** \brief Intherited from parent */ virtual void UpdateOutputInformation() override; /** \brief Intherited from parent */ virtual void SetRequestedRegionToLargestPossibleRegion() override; /** \brief Intherited from parent */ virtual bool RequestedRegionIsOutsideOfTheBufferedRegion() override; /** \brief Intherited from parent */ virtual bool VerifyRequestedRegion() override; /** \brief Intherited from parent */ virtual void SetRequestedRegion( const itk::DataObject *data) override; /** \brief Returns the current number of polylines */ virtual unsigned short GetPolyLinesSize(); /** \brief Returns the current number of helperpolylines */ virtual unsigned short GetHelperPolyLinesSize(); /** \brief Returns whether a helper polyline should be painted or not */ virtual bool IsHelperToBePainted(unsigned int index); /** \brief Returns true if the planar figure is reset to "add points" mode * when a point is selected. * * Default return value is false. Subclasses can overwrite this method and * execute any reset / initialization statements required. */ virtual bool ResetOnPointSelect(); /** \brief removes the point with the given index from the list of controlpoints. */ virtual void RemoveControlPoint( unsigned int index ); /** \brief Removes last control point */ virtual void RemoveLastControlPoint(); /** \brief Allow sub-classes to apply constraints on control points. * * Sub-classes can define spatial constraints to certain control points by * overwriting this method and returning a constrained point. By default, * the points are constrained by the image bounds. */ virtual Point2D ApplyControlPointConstraints( unsigned int /*index*/, const Point2D& point ); /** * \brief Compare two PlanarFigure objects * Note: all subclasses have to implement the method on their own. */ virtual bool Equals(const mitk::PlanarFigure& other) const; protected: PlanarFigure(); PlanarFigure(const Self& other); /** \brief Set the initial number of control points of the planar figure */ void ResetNumberOfControlPoints( int numberOfControlPoints ); /** Adds feature (e.g., circumference, radius, angle, ...) to feature vector * of a planar figure object and returns integer ID for the feature element. * Should be called in sub-class constructors. */ virtual unsigned int AddFeature( const char *featureName, const char *unitName ); /** Sets the name of the specified feature. INTERNAL METHOD. */ void SetFeatureName( unsigned int index, const char *featureName ); /** Sets the physical unit of the specified feature. INTERNAL METHOD. */ void SetFeatureUnit( unsigned int index, const char *unitName ); /** Sets quantity of the specified feature. INTERNAL METHOD. */ void SetQuantity( unsigned int index, double quantity ); /** Sets the specified feature as active. INTERAL METHOD. */ void ActivateFeature( unsigned int index ); /** Sets the specified feature as active. INTERAL METHOD. */ void DeactivateFeature( unsigned int index ); /** \brief Generates the poly-line representation of the planar figure. * Must be implemented in sub-classes. */ virtual void GeneratePolyLine() = 0; /** \brief Generates the poly-lines that should be drawn the same size regardless of zoom. * Must be implemented in sub-classes. */ virtual void GenerateHelperPolyLine(double mmPerDisplayUnit, unsigned int displayHeight) = 0; /** \brief Calculates quantities of all features of this planar figure. * Must be implemented in sub-classes. */ virtual void EvaluateFeaturesInternal() = 0; /** \brief Initializes the TimeGeometry describing the (time-resolved) * geometry of this figure. Note that each time step holds one PlaneGeometry. */ virtual void InitializeTimeGeometry( unsigned int timeSteps = 1 ) override; /** \brief defines the number of PolyLines that will be available */ void SetNumberOfPolyLines( unsigned int numberOfPolyLines ); /** \brief Append a point to the PolyLine # index */ void AppendPointToPolyLine( unsigned int index, PolyLineElement element ); /** \brief clears the list of PolyLines. Call before re-calculating a new Polyline. */ void ClearPolyLines(); /** \brief defines the number of HelperPolyLines that will be available */ void SetNumberOfHelperPolyLines( unsigned int numberOfHelperPolyLines ); /** \brief Append a point to the HelperPolyLine # index */ void AppendPointToHelperPolyLine( unsigned int index, PolyLineElement element ); /** \brief clears the list of HelperPolyLines. Call before re-calculating a new HelperPolyline. */ void ClearHelperPolyLines(); virtual void PrintSelf( std::ostream& os, itk::Indent indent ) const override; ControlPointListType m_ControlPoints; unsigned int m_NumberOfControlPoints; // Currently selected control point; -1 means no point selected int m_SelectedControlPoint; std::vector m_PolyLines; std::vector m_HelperPolyLines; BoolContainerType::Pointer m_HelperPolyLinesToBePainted; // this point is used to store the coordiantes an additional 'ControlPoint' that is rendered // when the mouse cursor is above the figure (and not a control-point) and when the // property 'planarfigure.isextendable' is set to true Point2D m_PreviewControlPoint; bool m_PreviewControlPointVisible; bool m_FigurePlaced; private: // not implemented to prevent PlanarFigure::New() calls which would create an itk::Object. static Pointer New(); struct Feature { Feature( const char *name, const char *unit ) : Name( name ), Unit( unit ), Quantity( 0.0 ), Active( true ), Visible( true ) { } std::string Name; std::string Unit; double Quantity; bool Active; bool Visible; }; virtual itk::LightObject::Pointer InternalClone() const override = 0; PlaneGeometry *m_PlaneGeometry; bool m_PolyLineUpToDate; bool m_HelperLinesUpToDate; bool m_FeaturesUpToDate; // Vector of features available for this geometric figure typedef std::vector< Feature > FeatureVectorType; FeatureVectorType m_Features; unsigned long m_FeaturesMTime; // this pair is used to store the mmInDisplayUnits (m_DisplaySize.first) and the displayHeight (m_DisplaySize.second) // that the helperPolyLines have been calculated for. // It's used to determine whether or not GetHelperPolyLine() needs to recalculate the HelperPolyLines. std::pair m_DisplaySize; }; MITKPLANARFIGURE_EXPORT bool Equal( const mitk::PlanarFigure& leftHandSide, const mitk::PlanarFigure& rightHandSide, ScalarType eps, bool verbose ); } // namespace mitk #endif //_MITK_PLANAR_FIGURE_H_ diff --git a/Modules/PlanarFigure/src/DataManagement/mitkPlanarBezierCurve.cpp b/Modules/PlanarFigure/src/DataManagement/mitkPlanarBezierCurve.cpp index 62cb9e8139..552c60ba0a 100644 --- a/Modules/PlanarFigure/src/DataManagement/mitkPlanarBezierCurve.cpp +++ b/Modules/PlanarFigure/src/DataManagement/mitkPlanarBezierCurve.cpp @@ -1,162 +1,160 @@ /*=================================================================== 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 "mitkPlanarBezierCurve.h" #include #include mitk::PlanarBezierCurve::PlanarBezierCurve() : FEATURE_ID_LENGTH(Superclass::AddFeature("Length", "mm")), m_NumberOfSegments(100) { this->ResetNumberOfControlPoints(2); this->SetNumberOfPolyLines(1); this->SetNumberOfHelperPolyLines(1); } void mitk::PlanarBezierCurve::EvaluateFeaturesInternal() { double length = 0.0; for (unsigned int i = 0; i < m_NumberOfSegments; ++i) length += static_cast(m_PolyLines[0][i]).EuclideanDistanceTo(static_cast(m_PolyLines[0][i + 1])); this->SetQuantity(FEATURE_ID_LENGTH, length); } unsigned int mitk::PlanarBezierCurve::GetNumberOfSegments() const { return m_NumberOfSegments; } void mitk::PlanarBezierCurve::SetNumberOfSegments(unsigned int numSegments) { m_NumberOfSegments = std::max(1U, numSegments); if (this->IsPlaced()) { this->GeneratePolyLine(); this->Modified(); } } void mitk::PlanarBezierCurve::GenerateHelperPolyLine(double, unsigned int) { this->ClearHelperPolyLines(); unsigned int numHelperPolyLinePoints = m_ControlPoints.size(); for (unsigned int i = 0; i < numHelperPolyLinePoints; ++i) this->AppendPointToHelperPolyLine(0, m_ControlPoints[i]); } void mitk::PlanarBezierCurve::GeneratePolyLine() { this->ClearPolyLines(); const unsigned int numPolyLinePoints = m_NumberOfSegments + 1; for (unsigned int i = 0; i < numPolyLinePoints; ++i) this->AppendPointToPolyLine(0, this->ComputeDeCasteljauPoint(i / static_cast(m_NumberOfSegments))); } mitk::Point2D mitk::PlanarBezierCurve::ComputeDeCasteljauPoint(mitk::ScalarType t) { unsigned int n = m_ControlPoints.size() - 1; if (m_DeCasteljauPoints.size() != n) m_DeCasteljauPoints.resize(n); for (unsigned int i = 0; i < n; ++i) { m_DeCasteljauPoints[i][0] = (1 - t) * m_ControlPoints[i][0] + t * m_ControlPoints[i + 1][0]; m_DeCasteljauPoints[i][1] = (1 - t) * m_ControlPoints[i][1] + t * m_ControlPoints[i + 1][1]; } for (--n; n > 0; --n) { for (unsigned int i = 0; i < n; ++i) { m_DeCasteljauPoints[i][0] = (1 - t) * m_DeCasteljauPoints[i][0] + t * m_DeCasteljauPoints[i + 1][0]; m_DeCasteljauPoints[i][1] = (1 - t) * m_DeCasteljauPoints[i][1] + t * m_DeCasteljauPoints[i + 1][1]; } } return m_DeCasteljauPoints[0]; } int mitk::PlanarBezierCurve::GetControlPointForPolylinePoint( int indexOfPolylinePoint, int polyLineIndex ) const { - mitk::PlanarFigure::PolyLineType polyLine = GetPolyLine( polyLineIndex ); - if ( indexOfPolylinePoint > static_cast(polyLine.size()) ) - { + mitk::PlanarFigure::PolyLineType polyLine = GetPolyLine(polyLineIndex); + + if (indexOfPolylinePoint < 0 || indexOfPolylinePoint > static_cast(polyLine.size())) return -1; - } mitk::PlanarFigure::ControlPointListType::const_iterator elem; mitk::PlanarFigure::ControlPointListType::const_iterator first = m_ControlPoints.cbegin(); mitk::PlanarFigure::ControlPointListType::const_iterator end = m_ControlPoints.cend(); mitk::PlanarFigure::PolyLineType::const_iterator polyLineIter; mitk::PlanarFigure::PolyLineType::const_iterator polyLineEnd = polyLine.cend(); mitk::PlanarFigure::PolyLineType::const_iterator polyLineStart = polyLine.cbegin(); polyLineStart += indexOfPolylinePoint; - for ( polyLineIter = polyLineStart; polyLineIter != polyLineEnd; ++polyLineIter ) + for (polyLineIter = polyLineStart; polyLineIter != polyLineEnd; ++polyLineIter) { - elem = std::find( first, end, *polyLineIter ); - if ( elem != end ) - { - return std::distance( first, elem ); - } + elem = std::find(first, end, *polyLineIter); + + if (elem != end) + return std::distance(first, elem); } return GetNumberOfControlPoints(); } unsigned int mitk::PlanarBezierCurve::GetMaximumNumberOfControlPoints() const { return std::numeric_limits::max(); } unsigned int mitk::PlanarBezierCurve::GetMinimumNumberOfControlPoints() const { return 2; } bool mitk::PlanarBezierCurve::IsHelperToBePainted(unsigned int index) { return index == 0 && m_ControlPoints.size() > 2; } bool mitk::PlanarBezierCurve::Equals(const PlanarFigure &other) const { const mitk::PlanarBezierCurve* otherBezierCurve = dynamic_cast(&other); if ( otherBezierCurve ) { if( this->m_NumberOfSegments != otherBezierCurve->m_NumberOfSegments ) return false; if( this->m_DeCasteljauPoints != otherBezierCurve->m_DeCasteljauPoints ) return false; return Superclass::Equals(other); } else { return false; } } diff --git a/Modules/PlanarFigure/src/DataManagement/mitkPlanarSubdivisionPolygon.cpp b/Modules/PlanarFigure/src/DataManagement/mitkPlanarSubdivisionPolygon.cpp index 80d8626b77..a5ca8ed6b5 100644 --- a/Modules/PlanarFigure/src/DataManagement/mitkPlanarSubdivisionPolygon.cpp +++ b/Modules/PlanarFigure/src/DataManagement/mitkPlanarSubdivisionPolygon.cpp @@ -1,173 +1,171 @@ /*=================================================================== 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 "mitkPlanarSubdivisionPolygon.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" // stl related includes #include mitk::PlanarSubdivisionPolygon::PlanarSubdivisionPolygon(): m_TensionParameter(0.0625), m_SubdivisionRounds(5) { // Polygon is subdivision (in contrast to parent class PlanarPolygon this->SetProperty( "closed", mitk::BoolProperty::New( true ) ); this->SetProperty( "subdivision", mitk::BoolProperty::New( true ) ); // Other properties are inherited / already initialized by parent class PlanarPolygon } void mitk::PlanarSubdivisionPolygon::GeneratePolyLine() { this->ClearPolyLines(); ControlPointListType subdivisionPoints; ControlPointListType newSubdivisionPoints; subdivisionPoints.clear(); subdivisionPoints = m_ControlPoints; if( m_ControlPoints.size() >= GetMinimumNumberOfControlPoints() ) { for( unsigned int i=0; i < GetSubdivisionRounds(); i++ ) { // Indices unsigned int index, indexPrev, indexNext, indexNextNext; unsigned int numberOfPoints = subdivisionPoints.size(); Point2D newPoint; // Keep cycling our array indices forward until they wrap around at the end for ( index = 0; index < numberOfPoints; ++index ) { // Create new subdivision point according to formula // p_new = (0.5 + tension) * (p_here + p_next) - tension * (p_prev + p_nextnext) indexPrev = (numberOfPoints + index - 1) % numberOfPoints; indexNext = (index + 1) % numberOfPoints; indexNextNext = (index + 2) % numberOfPoints; newPoint[0] = (0.5 + GetTensionParameter()) * (double)( subdivisionPoints[index][0] + subdivisionPoints[indexNext][0] ) - GetTensionParameter() * (double)( subdivisionPoints[indexPrev][0] + subdivisionPoints[indexNextNext][0]); newPoint[1] = (0.5 + GetTensionParameter()) * (double)( subdivisionPoints[index][1] + subdivisionPoints[indexNext][1] ) - GetTensionParameter() * (double)( subdivisionPoints[indexPrev][1] + subdivisionPoints[indexNextNext][1]); newSubdivisionPoints.push_back( newPoint ); } ControlPointListType mergedSubdivisionPoints; ControlPointListType::iterator it, itNew; for ( it = subdivisionPoints.begin() , itNew = newSubdivisionPoints.begin(); it != subdivisionPoints.end(); ++it, ++itNew ) { mergedSubdivisionPoints.push_back( *it ); mergedSubdivisionPoints.push_back( *itNew ); } subdivisionPoints = mergedSubdivisionPoints; newSubdivisionPoints.clear(); } } bool isInitiallyPlaced = this->GetProperty("initiallyplaced"); unsigned int i; ControlPointListType::iterator it; for ( it = subdivisionPoints.begin(), i = 0; it != subdivisionPoints.end(); ++it, ++i ) { // Determine the index of the control point FOLLOWING this poly-line element // (this is needed by PlanarFigureInteractor to insert new points at the correct position, // namely BEFORE the next control point) unsigned int nextIndex; if ( i == 0 ) { // For the FIRST polyline point, use the index of the LAST control point // (it will used to check if the mouse is near the very last polyline element) nextIndex = m_ControlPoints.size() - 1; } else { // For all other polyline points, use the index of the control point succeeding it // (for polyline points lying on control points, the index of the previous control point // is used) nextIndex = (((i - 1) >> this->GetSubdivisionRounds()) + 1) % m_ControlPoints.size(); if(!isInitiallyPlaced && nextIndex > m_ControlPoints.size()-2) { this->AppendPointToPolyLine( 0, m_ControlPoints[m_ControlPoints.size()-1] ); break; } } this->AppendPointToPolyLine( 0, *it ); } subdivisionPoints.clear(); } bool mitk::PlanarSubdivisionPolygon::Equals(const mitk::PlanarFigure& other) const { const mitk::PlanarSubdivisionPolygon* otherSubDivPoly = dynamic_cast(&other); if ( otherSubDivPoly ) { if ( this->m_SubdivisionRounds != otherSubDivPoly->m_SubdivisionRounds) return false; if ( std::abs(this->m_TensionParameter - otherSubDivPoly->m_TensionParameter) > mitk::eps) return false; return Superclass::Equals(other); } else { return false; } } int mitk::PlanarSubdivisionPolygon::GetControlPointForPolylinePoint( int indexOfPolylinePoint, int polyLineIndex ) const { mitk::PlanarFigure::PolyLineType polyLine = GetPolyLine( polyLineIndex ); - if ( indexOfPolylinePoint > static_cast(polyLine.size()) ) - { + + if (indexOfPolylinePoint < 0 || indexOfPolylinePoint > static_cast(polyLine.size())) return -1; - } mitk::PlanarFigure::ControlPointListType::const_iterator elem; mitk::PlanarFigure::ControlPointListType::const_iterator first = m_ControlPoints.cbegin(); mitk::PlanarFigure::ControlPointListType::const_iterator end = m_ControlPoints.cend(); mitk::PlanarFigure::PolyLineType::const_iterator polyLineIter; mitk::PlanarFigure::PolyLineType::const_iterator polyLineEnd = polyLine.cend(); mitk::PlanarFigure::PolyLineType::const_iterator polyLineStart = polyLine.cbegin(); polyLineStart += indexOfPolylinePoint; - for ( polyLineIter = polyLineStart; polyLineIter != polyLineEnd; ++polyLineIter ) + for (polyLineIter = polyLineStart; polyLineIter != polyLineEnd; ++polyLineIter) { - elem = std::find( first, end, *polyLineIter ); - if ( elem != end ) - { - return std::distance( first, elem ); - } + elem = std::find(first, end, *polyLineIter); + + if (elem != end) + return std::distance(first, elem); } return GetNumberOfControlPoints(); } diff --git a/Modules/PlanarFigure/src/Interactions/mitkPlanarFigureInteractor.cpp b/Modules/PlanarFigure/src/Interactions/mitkPlanarFigureInteractor.cpp index 8c91a1b4c5..853e2dc6fe 100644 --- a/Modules/PlanarFigure/src/Interactions/mitkPlanarFigureInteractor.cpp +++ b/Modules/PlanarFigure/src/Interactions/mitkPlanarFigureInteractor.cpp @@ -1,994 +1,1004 @@ /*=================================================================== 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. ===================================================================*/ #define PLANARFIGUREINTERACTOR_DBG MITK_DEBUG("PlanarFigureInteractor") << __LINE__ << ": " #include "mitkPlanarFigureInteractor.h" #include "mitkPlanarFigure.h" #include "mitkPlanarPolygon.h" #include "mitkPlanarCircle.h" +#include "mitkPlanarBezierCurve.h" #include "mitkInteractionPositionEvent.h" #include "mitkInternalEvent.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include "mitkPlaneGeometry.h" #include "mitkAbstractTransformGeometry.h" //how precise must the user pick the point //default value mitk::PlanarFigureInteractor::PlanarFigureInteractor() : DataInteractor() , m_Precision( 6.5 ) , m_MinimumPointDistance( 25.0 ) , m_IsHovering( false ) , m_LastPointWasValid( false ) { } mitk::PlanarFigureInteractor::~PlanarFigureInteractor() { } void mitk::PlanarFigureInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("figure_is_on_current_slice", CheckFigureOnRenderingGeometry); CONNECT_CONDITION("figure_is_placed", CheckFigurePlaced); CONNECT_CONDITION("minimal_figure_is_finished", CheckMinimalFigureFinished); CONNECT_CONDITION("hovering_above_figure", CheckFigureHovering); CONNECT_CONDITION("hovering_above_point", CheckControlPointHovering); CONNECT_CONDITION("figure_is_selected", CheckSelection); CONNECT_CONDITION("point_is_valid", CheckPointValidity); CONNECT_CONDITION("figure_is_finished", CheckFigureFinished); CONNECT_CONDITION("reset_on_point_select_needed", CheckResetOnPointSelect); CONNECT_CONDITION("points_can_be_added_or_removed", CheckFigureIsExtendable); CONNECT_FUNCTION( "finalize_figure", FinalizeFigure); CONNECT_FUNCTION( "hide_preview_point", HidePreviewPoint ) CONNECT_FUNCTION( "hide_control_points", HideControlPoints ) CONNECT_FUNCTION( "set_preview_point_position", SetPreviewPointPosition ) CONNECT_FUNCTION( "move_current_point", MoveCurrentPoint); CONNECT_FUNCTION( "deselect_point", DeselectPoint); CONNECT_FUNCTION( "add_new_point", AddPoint); CONNECT_FUNCTION( "add_initial_point", AddInitialPoint); CONNECT_FUNCTION( "remove_selected_point", RemoveSelectedPoint); CONNECT_FUNCTION( "request_context_menu", RequestContextMenu); CONNECT_FUNCTION( "select_figure", SelectFigure ); CONNECT_FUNCTION( "select_point", SelectPoint ); CONNECT_FUNCTION( "end_interaction", EndInteraction ); CONNECT_FUNCTION( "start_hovering", StartHovering ) CONNECT_FUNCTION( "end_hovering", EndHovering ); CONNECT_FUNCTION( "delete_figure", DeleteFigure ); } bool mitk::PlanarFigureInteractor::CheckFigurePlaced( const InteractionEvent* /*interactionEvent*/ ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); bool isFigureFinished = false; planarFigure->GetPropertyList()->GetBoolProperty( "initiallyplaced", isFigureFinished ); return planarFigure->IsPlaced() && isFigureFinished; } bool mitk::PlanarFigureInteractor::MoveCurrentPoint(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; bool isEditable = true; GetDataNode()->GetBoolProperty( "planarfigure.iseditable", isEditable ); mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::PlaneGeometry *planarFigureGeometry = dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); mitk::AbstractTransformGeometry *abstractTransformGeometry = dynamic_cast< AbstractTransformGeometry * >( planarFigure->GetGeometry( 0 ) ); if ( abstractTransformGeometry != NULL ) return false; // Extract point in 2D world coordinates (relative to PlaneGeometry of // PlanarFigure) Point2D point2D; if ( !this->TransformPositionEventToPoint2D( positionEvent, planarFigureGeometry, point2D ) || !isEditable ) { return false; } planarFigure->InvokeEvent( StartInteractionPlanarFigureEvent() ); // check if the control points shall be hidden during interaction bool hidecontrolpointsduringinteraction = false; GetDataNode()->GetBoolProperty( "planarfigure.hidecontrolpointsduringinteraction", hidecontrolpointsduringinteraction ); // hide the control points if necessary //interactionEvent->GetSender()->GetDataStorage()->BlockNodeModifiedEvents( true ); GetDataNode()->SetBoolProperty( "planarfigure.drawcontrolpoints", !hidecontrolpointsduringinteraction ); //interactionEvent->GetSender()->GetDataStorage()->BlockNodeModifiedEvents( false ); // Move current control point to this point planarFigure->SetCurrentControlPoint( point2D ); // Re-evaluate features planarFigure->EvaluateFeatures(); // Update rendered scene interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::PlanarFigureInteractor::FinalizeFigure( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); planarFigure->Modified(); planarFigure->DeselectControlPoint(); planarFigure->RemoveLastControlPoint(); planarFigure->SetProperty( "initiallyplaced", mitk::BoolProperty::New( true ) ); GetDataNode()->SetBoolProperty( "planarfigure.drawcontrolpoints", true ); GetDataNode()->Modified(); planarFigure->InvokeEvent( EndPlacementPlanarFigureEvent() ); planarFigure->InvokeEvent( EndInteractionPlanarFigureEvent() ); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return false; } bool mitk::PlanarFigureInteractor::EndInteraction( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); GetDataNode()->SetBoolProperty( "planarfigure.drawcontrolpoints", true ); planarFigure->Modified(); planarFigure->InvokeEvent( EndInteractionPlanarFigureEvent() ); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return false; } bool mitk::PlanarFigureInteractor::EndHovering( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); planarFigure->ResetPreviewContolPoint(); // Invoke end-hover event once the mouse is exiting the figure area m_IsHovering = false; planarFigure->InvokeEvent( EndHoverPlanarFigureEvent() ); // Set bool property to indicate that planar figure is no longer in "hovering" mode GetDataNode()->SetBoolProperty( "planarfigure.ishovering", false ); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return false; } bool mitk::PlanarFigureInteractor::DeleteFigure( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); planarFigure->RemoveAllObservers(); GetDataNode()->RemoveAllObservers(); interactionEvent->GetSender()->GetDataStorage()->Remove( GetDataNode() ); interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); return false; } bool mitk::PlanarFigureInteractor::CheckMinimalFigureFinished( const InteractionEvent* /*interactionEvent*/ ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); return ( planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMinimumNumberOfControlPoints() ); } bool mitk::PlanarFigureInteractor::CheckFigureFinished( const InteractionEvent* /*interactionEvent*/ ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); return ( planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMaximumNumberOfControlPoints() ); } bool mitk::PlanarFigureInteractor::CheckFigureIsExtendable( const InteractionEvent* /*interactionEvent*/ ) { bool isExtendable = false; GetDataNode()->GetBoolProperty("planarfigure.isextendable", isExtendable); return isExtendable; } bool mitk::PlanarFigureInteractor::DeselectPoint(StateMachineAction*, InteractionEvent* /*interactionEvent*/) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); bool wasSelected = planarFigure->DeselectControlPoint(); if ( wasSelected ) { // Issue event so that listeners may update themselves planarFigure->Modified(); planarFigure->InvokeEvent( EndInteractionPlanarFigureEvent() ); GetDataNode()->SetBoolProperty( "planarfigure.drawcontrolpoints", true ); // GetDataNode()->SetBoolProperty( "planarfigure.ishovering", false ); GetDataNode()->Modified(); } return true; } bool mitk::PlanarFigureInteractor::AddPoint(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; + DataNode::Pointer node = this->GetDataNode(); + BaseData::Pointer data = node->GetData(); + + /* + * Added check for "initiallyplaced" due to bug 13097: + * + * There are two possible cases in which a point can be inserted into a PlanarPolygon: + * + * 1. The figure is currently drawn -> the point will be appended at the end of the figure + * 2. A point is inserted at a userdefined position after the initial placement of the figure is finished + * + * In the second case we need to determine the proper insertion index. In the first case the index always has + * to be -1 so that the point is appended to the end. + * + * These changes are necessary because of a mac os x specific issue: If a users draws a PlanarPolygon then the + * next point to be added moves according to the mouse position. If then the user left clicks in order to add + * a point one would assume the last move position is identical to the left click position. This is actually the + * case for windows and linux but somehow NOT for mac. Because of the insertion logic of a new point in the + * PlanarFigure then for mac the wrong current selected point is determined. + * + * With this check here this problem can be avoided. However a redesign of the insertion logic should be considered + */ + + bool isFigureFinished = false; + data->GetPropertyList()->GetBoolProperty("initiallyplaced", isFigureFinished); + bool selected = false; bool isEditable = true; - GetDataNode()->GetBoolProperty("selected", selected); - GetDataNode()->GetBoolProperty( "planarfigure.iseditable", isEditable ); + + node->GetBoolProperty("selected", selected); + node->GetBoolProperty("planarfigure.iseditable", isEditable); if ( !selected || !isEditable ) { return false; } - mitk::PlanarFigure *planarFigure = dynamic_cast( - GetDataNode()->GetData() ); + mitk::PlanarFigure *planarFigure = dynamic_cast(data.GetPointer()); + + // We can't derive a new control point from a polyline of a Bezier curve + // as all control points contribute to each polyline point. + if (dynamic_cast(planarFigure) != nullptr && isFigureFinished) + return false; const mitk::PlaneGeometry *planarFigureGeometry = planarFigure->GetPlaneGeometry(); mitk::AbstractTransformGeometry *abstractTransformGeometry = dynamic_cast< AbstractTransformGeometry * >( planarFigure->GetGeometry( 0 ) ); if ( abstractTransformGeometry != NULL) return false; // If the planarFigure already has reached the maximum number if ( planarFigure->GetNumberOfControlPoints() >= planarFigure->GetMaximumNumberOfControlPoints() ) { return false; } // Extract point in 2D world coordinates (relative to PlaneGeometry of // PlanarFigure) Point2D point2D, projectedPoint; if ( !this->TransformPositionEventToPoint2D( positionEvent, planarFigureGeometry, point2D ) ) { return false; } // TODO: check segment of polyline we clicked in int nextIndex = -1; // We only need to check which position to insert the control point // when interacting with a PlanarPolygon. For all other types // new control points will always be appended - /* - * Added check for "initiallyplaced" due to bug 13097: - * - * There are two possible cases in which a point can be inserted into a PlanarPolygon: - * - * 1. The figure is currently drawn -> the point will be appended at the end of the figure - * 2. A point is inserted at a userdefined position after the initial placement of the figure is finished - * - * In the second case we need to determine the proper insertion index. In the first case the index always has - * to be -1 so that the point is appended to the end. - * - * These changes are necessary because of a mac os x specific issue: If a users draws a PlanarPolygon then the - * next point to be added moves according to the mouse position. If then the user left clicks in order to add - * a point one would assume the last move position is identical to the left click position. This is actually the - * case for windows and linux but somehow NOT for mac. Because of the insertion logic of a new point in the - * PlanarFigure then for mac the wrong current selected point is determined. - * - * With this check here this problem can be avoided. However a redesign of the insertion logic should be considered - */ - bool isFigureFinished = false; - planarFigure->GetPropertyList()->GetBoolProperty( "initiallyplaced", isFigureFinished ); - mitk::BaseRenderer *renderer = interactionEvent->GetSender(); const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry(); - if ( dynamic_cast( planarFigure ) && isFigureFinished) + if (dynamic_cast(planarFigure) && isFigureFinished) { nextIndex = this->IsPositionOverFigure( positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer->GetDisplayGeometry(), projectedPoint ); } // Add point as new control point renderer->GetDisplayGeometry()->DisplayToWorld( projectedPoint, projectedPoint ); if ( planarFigure->IsPreviewControlPointVisible() ) { point2D = planarFigure->GetPreviewControlPoint(); } planarFigure->AddControlPoint( point2D, planarFigure->GetControlPointForPolylinePoint( nextIndex, 0 ) ); if ( planarFigure->IsPreviewControlPointVisible() ) { planarFigure->SelectControlPoint( nextIndex ); planarFigure->ResetPreviewContolPoint(); } // Re-evaluate features planarFigure->EvaluateFeatures(); //this->LogPrintPlanarFigureQuantities( planarFigure ); // Update rendered scene renderer->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::PlanarFigureInteractor::AddInitialPoint(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); mitk::PlaneGeometry *planarFigureGeometry = dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); mitk::AbstractTransformGeometry *abstractTransformGeometry = dynamic_cast< AbstractTransformGeometry * >( planarFigure->GetGeometry( 0 ) ); // Invoke event to notify listeners that placement of this PF starts now planarFigure->InvokeEvent( StartPlacementPlanarFigureEvent() ); // Use PlaneGeometry of the renderer clicked on for this PlanarFigure mitk::PlaneGeometry *planeGeometry = const_cast< mitk::PlaneGeometry * >( dynamic_cast< const mitk::PlaneGeometry * >( renderer->GetSliceNavigationController()->GetCurrentPlaneGeometry() ) ); if ( planeGeometry != NULL && abstractTransformGeometry == NULL) { planarFigureGeometry = planeGeometry; planarFigure->SetPlaneGeometry( planeGeometry ); } else { return false; } // Extract point in 2D world coordinates (relative to PlaneGeometry of // PlanarFigure) Point2D point2D; if ( !this->TransformPositionEventToPoint2D( positionEvent, planarFigureGeometry, point2D ) ) { return false; } // Place PlanarFigure at this point planarFigure->PlaceFigure( point2D ); // Re-evaluate features planarFigure->EvaluateFeatures(); //this->LogPrintPlanarFigureQuantities( planarFigure ); // Set a bool property indicating that the figure has been placed in // the current RenderWindow. This is required so that the same render // window can be re-aligned to the PlaneGeometry of the PlanarFigure later // on in an application. GetDataNode()->SetBoolProperty( "PlanarFigureInitializedWindow", true, renderer ); // Update rendered scene renderer->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::PlanarFigureInteractor::StartHovering( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); if ( !m_IsHovering ) { // Invoke hover event once when the mouse is entering the figure area m_IsHovering = true; planarFigure->InvokeEvent( StartHoverPlanarFigureEvent() ); // Set bool property to indicate that planar figure is currently in "hovering" mode GetDataNode()->SetBoolProperty( "planarfigure.ishovering", true ); renderer->GetRenderingManager()->RequestUpdateAll(); } return true; } bool mitk::PlanarFigureInteractor::SetPreviewPointPosition( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); planarFigure->DeselectControlPoint(); mitk::Point2D pointProjectedOntoLine = positionEvent->GetPointerPositionOnScreen(); bool selected(false); bool isExtendable(false); bool isEditable(true); GetDataNode()->GetBoolProperty("selected", selected); GetDataNode()->GetBoolProperty("planarfigure.isextendable", isExtendable); GetDataNode()->GetBoolProperty("planarfigure.iseditable", isEditable ); if ( selected && isExtendable && isEditable ) { renderer->GetDisplayGeometry()->DisplayToWorld( pointProjectedOntoLine, pointProjectedOntoLine ); planarFigure->SetPreviewControlPoint( pointProjectedOntoLine ); } renderer->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::PlanarFigureInteractor::HideControlPoints( StateMachineAction*, InteractionEvent* /*interactionEvent*/ ) { GetDataNode()->SetBoolProperty( "planarfigure.drawcontrolpoints", false ); return true; } bool mitk::PlanarFigureInteractor::HidePreviewPoint( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); planarFigure->ResetPreviewContolPoint(); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); renderer->GetRenderingManager()->RequestUpdateAll(); return true; } bool mitk::PlanarFigureInteractor::CheckFigureHovering( const InteractionEvent* interactionEvent ) { const mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); const mitk::PlaneGeometry *planarFigureGeometry = planarFigure->GetPlaneGeometry(); mitk::AbstractTransformGeometry *abstractTransformGeometry = dynamic_cast< AbstractTransformGeometry * >( planarFigure->GetGeometry( 0 ) ); const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry(); if ( abstractTransformGeometry != NULL ) return false; mitk::Point2D pointProjectedOntoLine; int previousControlPoint = this->IsPositionOverFigure( positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer->GetDisplayGeometry(), pointProjectedOntoLine ); bool isHovering = (previousControlPoint != -1); if ( isHovering ) { return true; } else { return false; } return false; } bool mitk::PlanarFigureInteractor::CheckControlPointHovering( const InteractionEvent* interactionEvent ) { const mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); mitk::PlaneGeometry *planarFigureGeometry = dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); mitk::AbstractTransformGeometry *abstractTransformGeometry = dynamic_cast< AbstractTransformGeometry * >( planarFigure->GetGeometry( 0 ) ); const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry(); if (abstractTransformGeometry != NULL) return false; int pointIndex = -1; pointIndex = mitk::PlanarFigureInteractor::IsPositionInsideMarker( positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer->GetDisplayGeometry() ); if ( pointIndex >= 0 ) { return true; } else { return false; } } bool mitk::PlanarFigureInteractor::CheckSelection( const InteractionEvent* /*interactionEvent*/ ) { bool selected = false; GetDataNode()->GetBoolProperty("selected", selected); return selected; } bool mitk::PlanarFigureInteractor::SelectFigure( StateMachineAction*, InteractionEvent* /*interactionEvent*/ ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); planarFigure->InvokeEvent( SelectPlanarFigureEvent() ); return false; } bool mitk::PlanarFigureInteractor::SelectPoint( StateMachineAction*, InteractionEvent* interactionEvent ) { mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); mitk::PlaneGeometry *planarFigureGeometry = dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); mitk::AbstractTransformGeometry *abstractTransformGeometry = dynamic_cast< AbstractTransformGeometry * >( planarFigure->GetGeometry( 0 ) ); const PlaneGeometry *projectionPlane = renderer->GetCurrentWorldPlaneGeometry(); if (abstractTransformGeometry != NULL) return false; int pointIndex = -1; pointIndex = mitk::PlanarFigureInteractor::IsPositionInsideMarker( positionEvent, planarFigure, planarFigureGeometry, projectionPlane, renderer->GetDisplayGeometry() ); if ( pointIndex >= 0 ) { // If mouse is above control point, mark it as selected planarFigure->SelectControlPoint( pointIndex ); } else { planarFigure->DeselectControlPoint(); } return false; } bool mitk::PlanarFigureInteractor::CheckPointValidity( const InteractionEvent* interactionEvent ) { // Check if the distance of the current point to the previously set point in display coordinates // is sufficient (if a previous point exists) // Extract display position const mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); if ( positionEvent == NULL ) return false; mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); m_LastPointWasValid = IsMousePositionAcceptableAsNewControlPoint( positionEvent, planarFigure ); return m_LastPointWasValid; } bool mitk::PlanarFigureInteractor::RemoveSelectedPoint(StateMachineAction*, InteractionEvent* interactionEvent) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::BaseRenderer *renderer = interactionEvent->GetSender(); int selectedControlPoint = planarFigure->GetSelectedControlPoint(); planarFigure->RemoveControlPoint( selectedControlPoint ); // Re-evaluate features planarFigure->EvaluateFeatures(); planarFigure->Modified(); GetDataNode()->SetBoolProperty( "planarfigure.drawcontrolpoints", true ); planarFigure->InvokeEvent( EndInteractionPlanarFigureEvent() ); renderer->GetRenderingManager()->RequestUpdateAll(); HandleEvent( mitk::InternalEvent::New( renderer, this, "Dummy-Event" ), GetDataNode() ); return true; } bool mitk::PlanarFigureInteractor::RequestContextMenu(StateMachineAction*, InteractionEvent* /*interactionEvent*/) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); bool selected = false; GetDataNode()->GetBoolProperty("selected", selected); // no need to invoke this if the figure is already selected if ( !selected ) { planarFigure->InvokeEvent( SelectPlanarFigureEvent() ); } planarFigure->InvokeEvent( ContextMenuPlanarFigureEvent() ); return true; } bool mitk::PlanarFigureInteractor::CheckResetOnPointSelect( const InteractionEvent* /*interactionEvent*/ ) { mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); // Reset the PlanarFigure if required return planarFigure->ResetOnPointSelect(); } bool mitk::PlanarFigureInteractor::CheckFigureOnRenderingGeometry( const InteractionEvent* interactionEvent ) { const mitk::InteractionPositionEvent* posEvent = dynamic_cast(interactionEvent); if ( posEvent == NULL ) return false; mitk::Point3D worldPoint3D = posEvent->GetPositionInWorld(); mitk::PlanarFigure *planarFigure = dynamic_cast( GetDataNode()->GetData() ); mitk::PlaneGeometry *planarFigurePlaneGeometry = dynamic_cast< PlaneGeometry * >( planarFigure->GetGeometry( 0 ) ); mitk::AbstractTransformGeometry *abstractTransformGeometry = dynamic_cast< AbstractTransformGeometry * >( planarFigure->GetGeometry( 0 ) ); if ( abstractTransformGeometry != NULL) return false; double planeThickness = planarFigurePlaneGeometry->GetExtentInMM( 2 ); if ( planarFigurePlaneGeometry->Distance( worldPoint3D ) > planeThickness ) { // don't react, when interaction is too far away return false; } return true; } void mitk::PlanarFigureInteractor::SetPrecision( mitk::ScalarType precision ) { m_Precision = precision; } void mitk::PlanarFigureInteractor::SetMinimumPointDistance( ScalarType minimumDistance ) { m_MinimumPointDistance = minimumDistance; } bool mitk::PlanarFigureInteractor::TransformPositionEventToPoint2D( const InteractionPositionEvent *positionEvent, const PlaneGeometry *planarFigureGeometry, Point2D &point2D ) { mitk::Point3D worldPoint3D = positionEvent->GetPositionInWorld(); // TODO: proper handling of distance tolerance if ( planarFigureGeometry->Distance( worldPoint3D ) > 0.1 ) { return false; } // Project point onto plane of this PlanarFigure planarFigureGeometry->Map( worldPoint3D, point2D ); return true; } bool mitk::PlanarFigureInteractor::TransformObjectToDisplay( const mitk::Point2D &point2D, mitk::Point2D &displayPoint, const mitk::PlaneGeometry *objectGeometry, const mitk::PlaneGeometry *rendererGeometry, const mitk::DisplayGeometry *displayGeometry ) const { mitk::Point3D point3D; // Map circle point from local 2D geometry into 3D world space objectGeometry->Map( point2D, point3D ); double planeThickness = objectGeometry->GetExtentInMM( 2 ); // TODO: proper handling of distance tolerance if ( rendererGeometry->Distance( point3D ) < planeThickness / 3.0 ) { // Project 3D world point onto display geometry rendererGeometry->Map( point3D, displayPoint ); displayGeometry->WorldToDisplay( displayPoint, displayPoint ); return true; } return false; } bool mitk::PlanarFigureInteractor::IsPointNearLine( const mitk::Point2D& point, const mitk::Point2D& startPoint, const mitk::Point2D& endPoint, mitk::Point2D& projectedPoint ) const { mitk::Vector2D n1 = endPoint - startPoint; n1.Normalize(); // Determine dot products between line vector and startpoint-point / endpoint-point vectors double l1 = n1 * (point - startPoint); double l2 = -n1 * (point - endPoint); // Determine projection of specified point onto line defined by start / end point mitk::Point2D crossPoint = startPoint + n1 * l1; projectedPoint = crossPoint; float dist1 = crossPoint.SquaredEuclideanDistanceTo(point); float dist2 = endPoint.SquaredEuclideanDistanceTo(point); float dist3 = startPoint.SquaredEuclideanDistanceTo(point); // Point is inside encompassing rectangle IF // - its distance to its projected point is small enough // - it is not further outside of the line than the defined tolerance if (((dist1 < 20.0) && (l1 > 0.0) && (l2 > 0.0)) || dist2 < 20.0 || dist3 < 20.0) { return true; } return false; } int mitk::PlanarFigureInteractor::IsPositionOverFigure( const InteractionPositionEvent *positionEvent, PlanarFigure *planarFigure, const PlaneGeometry *planarFigureGeometry, const PlaneGeometry *rendererGeometry, const DisplayGeometry *displayGeometry, Point2D& pointProjectedOntoLine ) const { mitk::Point2D displayPosition = positionEvent->GetPointerPositionOnScreen(); // Iterate over all polylines of planar figure, and check if // any one is close to the current display position typedef mitk::PlanarFigure::PolyLineType VertexContainerType; Point2D polyLinePoint; Point2D firstPolyLinePoint; Point2D previousPolyLinePoint; for ( unsigned short loop=0; loopGetPolyLinesSize(); ++loop ) { const VertexContainerType polyLine = planarFigure->GetPolyLine( loop ); bool firstPoint( true ); for ( VertexContainerType::const_iterator it = polyLine.begin(); it != polyLine.end(); ++it ) { // Get plane coordinates of this point of polyline (if possible) if ( !this->TransformObjectToDisplay( *it, polyLinePoint, planarFigureGeometry, rendererGeometry, displayGeometry ) ) { break; // Poly line invalid (not on current 2D plane) --> skip it } if ( firstPoint ) { firstPolyLinePoint = polyLinePoint; firstPoint = false; } else if ( this->IsPointNearLine( displayPosition, previousPolyLinePoint, polyLinePoint, pointProjectedOntoLine ) ) { // Point is close enough to line segment --> Return index of the segment return std::distance(polyLine.begin(), it); } previousPolyLinePoint = polyLinePoint; } // For closed figures, also check last line segment if ( planarFigure->IsClosed() && this->IsPointNearLine( displayPosition, polyLinePoint, firstPolyLinePoint, pointProjectedOntoLine ) ) { return 0; // Return index of first control point } } return -1; } int mitk::PlanarFigureInteractor::IsPositionInsideMarker( const InteractionPositionEvent* positionEvent, const PlanarFigure *planarFigure, const PlaneGeometry *planarFigureGeometry, const PlaneGeometry *rendererGeometry, const DisplayGeometry *displayGeometry ) const { mitk::Point2D displayPosition = positionEvent->GetPointerPositionOnScreen(); // Iterate over all control points of planar figure, and check if // any one is close to the current display position mitk::Point2D displayControlPoint; int numberOfControlPoints = planarFigure->GetNumberOfControlPoints(); for ( int i=0; iTransformObjectToDisplay( planarFigure->GetControlPoint(i), displayControlPoint, planarFigureGeometry, rendererGeometry, displayGeometry ) ) { // TODO: variable size of markers if ( displayPosition.SquaredEuclideanDistanceTo( displayControlPoint ) < 20.0 ) { return i; } } } return -1; } void mitk::PlanarFigureInteractor::LogPrintPlanarFigureQuantities( const PlanarFigure *planarFigure ) { MITK_INFO << "PlanarFigure: " << planarFigure->GetNameOfClass(); for ( unsigned int i = 0; i < planarFigure->GetNumberOfFeatures(); ++i ) { MITK_INFO << "* " << planarFigure->GetFeatureName( i ) << ": " << planarFigure->GetQuantity( i ) << " " << planarFigure->GetFeatureUnit( i ); } } bool mitk::PlanarFigureInteractor::IsMousePositionAcceptableAsNewControlPoint( const mitk::InteractionPositionEvent* positionEvent, const PlanarFigure* planarFigure ) { assert(positionEvent && planarFigure); BaseRenderer* renderer = positionEvent->GetSender(); assert(renderer); // Get the timestep to support 3D+t int timeStep( renderer->GetTimeStep( planarFigure ) ); bool tooClose(false); const PlaneGeometry *renderingPlane = renderer->GetCurrentWorldPlaneGeometry(); mitk::PlaneGeometry *planarFigureGeometry = dynamic_cast< mitk::PlaneGeometry * >( planarFigure->GetGeometry( timeStep ) ); mitk::AbstractTransformGeometry *abstractTransformGeometry = dynamic_cast< mitk::AbstractTransformGeometry * >( planarFigure->GetGeometry( timeStep ) ); if ( abstractTransformGeometry != NULL ) return false; Point2D point2D, correctedPoint; // Get the point2D from the positionEvent if ( !this->TransformPositionEventToPoint2D( positionEvent, planarFigureGeometry, point2D ) ) { return false; } // apply the controlPoint constraints of the planarFigure to get the // coordinates that would actually be used. correctedPoint = const_cast( planarFigure )->ApplyControlPointConstraints( 0, point2D ); // map the 2D coordinates of the new point to world-coordinates // and transform those to display-coordinates mitk::Point3D newPoint3D; planarFigureGeometry->Map( correctedPoint, newPoint3D ); mitk::Point2D newDisplayPosition; renderingPlane->Map( newPoint3D, newDisplayPosition ); renderer->GetDisplayGeometry()->WorldToDisplay( newDisplayPosition, newDisplayPosition ); for( int i=0; i < (int)planarFigure->GetNumberOfControlPoints(); i++ ) { if ( i != planarFigure->GetSelectedControlPoint() ) { // Try to convert previous point to current display coordinates mitk::Point3D previousPoint3D; // map the 2D coordinates of the control-point to world-coordinates planarFigureGeometry->Map( planarFigure->GetControlPoint( i ), previousPoint3D ); if ( renderer->GetDisplayGeometry()->Distance( previousPoint3D ) < 0.1 ) // ugly, but assert makes this work { mitk::Point2D previousDisplayPosition; // transform the world-coordinates into display-coordinates renderingPlane->Map( previousPoint3D, previousDisplayPosition ); renderer->GetDisplayGeometry()->WorldToDisplay( previousDisplayPosition, previousDisplayPosition ); //Calculate the distance. We use display-coordinates here to make // the check independent of the zoom-level of the rendering scene. double a = newDisplayPosition[0] - previousDisplayPosition[0]; double b = newDisplayPosition[1] - previousDisplayPosition[1]; // If point is to close, do not set a new point tooClose = (a * a + b * b < m_MinimumPointDistance ); } if ( tooClose ) return false; // abort loop early } } return !tooClose; // default } void mitk::PlanarFigureInteractor::ConfigurationChanged() { mitk::PropertyList::Pointer properties = GetAttributes(); std::string precision = ""; if (properties->GetStringProperty("precision", precision)) { m_Precision = atof(precision.c_str()); } else { m_Precision = (ScalarType) 6.5; } std::string minPointDistance = ""; if (properties->GetStringProperty("minPointDistance", minPointDistance)) { m_MinimumPointDistance = atof(minPointDistance.c_str()); } else { m_MinimumPointDistance = (ScalarType) 25.0; } } diff --git a/Modules/QtWidgetsExt/src/QmitkPointListView.cpp b/Modules/QtWidgetsExt/src/QmitkPointListView.cpp index 0fb63cb7b1..2828e4e8b7 100644 --- a/Modules/QtWidgetsExt/src/QmitkPointListView.cpp +++ b/Modules/QtWidgetsExt/src/QmitkPointListView.cpp @@ -1,414 +1,414 @@ /*=================================================================== 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 "QmitkPointListView.h" #include "QmitkPointListModel.h" #include "QmitkStdMultiWidget.h" #include "QmitkEditPointDialog.h" #include "mitkRenderingManager.h" #include #include #include #include #include QmitkPointListView::QmitkPointListView( QWidget* parent ) : QListView( parent ), m_Snc1(NULL), m_Snc2(NULL), m_Snc3(NULL), m_PointListModel( new QmitkPointListModel() ), m_SelfCall( false ), m_showFading(false), m_MultiWidget( NULL) { QListView::setAlternatingRowColors( true ); QListView::setSelectionBehavior( QAbstractItemView::SelectRows ); QListView::setSelectionMode( QAbstractItemView::SingleSelection ); QListView::setModel( m_PointListModel ); QString tooltip = QString("Use the F2/F3 keys to move a point up/down, the Del key to remove a point\nand the mouse wheel to change the timestep.\n\nTimeStep:\t%1").arg(0); QListView::setToolTip(tooltip); this->setContextMenuPolicy(Qt::CustomContextMenu); m_TimeStepFaderLabel = new QLabel(this); QFont font("Arial", 17); m_TimeStepFaderLabel->setFont(font); this->setMinimumHeight(40); this->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); connect( m_PointListModel, SIGNAL(SignalUpdateSelection()), this, SLOT(OnPointSetSelectionChanged()) ); connect( this, SIGNAL(doubleClicked ( const QModelIndex & )), this, SLOT(OnPointDoubleClicked( const QModelIndex & )) ); connect( QListView::selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)), this, SLOT(OnListViewSelectionChanged(const QItemSelection& , const QItemSelection&)) ); connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(ctxMenu(const QPoint &))); } QmitkPointListView::~QmitkPointListView() { delete m_PointListModel; } void QmitkPointListView::SetPointSetNode( mitk::DataNode* pointSetNode ) { m_PointListModel->SetPointSetNode( pointSetNode); } const mitk::PointSet* QmitkPointListView::GetPointSet() const { return m_PointListModel->GetPointSet(); } void QmitkPointListView::SetMultiWidget( QmitkStdMultiWidget* multiWidget ) { m_MultiWidget = multiWidget; } QmitkStdMultiWidget* QmitkPointListView::GetMultiWidget() const { return m_MultiWidget; } void QmitkPointListView::OnPointDoubleClicked(const QModelIndex & index) { mitk::PointSet::PointType p; mitk::PointSet::PointIdentifier id; m_PointListModel->GetPointForModelIndex(index, p, id); QmitkEditPointDialog _EditPointDialog(this); _EditPointDialog.SetPoint(m_PointListModel->GetPointSet(), id, m_PointListModel->GetTimeStep()); _EditPointDialog.exec(); } void QmitkPointListView::OnPointSetSelectionChanged() { const mitk::PointSet* pointSet = m_PointListModel->GetPointSet(); if (pointSet == NULL) return; // update this view's selection status as a result to changes in the point set data structure m_SelfCall = true; int timeStep = m_PointListModel->GetTimeStep(); if ( pointSet->GetNumberOfSelected( timeStep ) > 1 ) { MITK_ERROR << "Point set has multiple selected points. This view is not designed for more than one selected point."; } int selectedIndex = pointSet->SearchSelectedPoint( timeStep ); if (selectedIndex == -1) // no selected point is found { m_SelfCall = false; return; } QModelIndex index; bool modelIndexOkay = m_PointListModel->GetModelIndexForPointID(selectedIndex, index); if (modelIndexOkay == true) QListView::selectionModel()->select( index , QItemSelectionModel::ClearAndSelect ); emit SignalPointSelectionChanged(); m_SelfCall = false; } void QmitkPointListView::OnListViewSelectionChanged(const QItemSelection& selected, const QItemSelection& /*deselected*/) { if (m_SelfCall) return; mitk::PointSet* pointSet = const_cast( m_PointListModel->GetPointSet() ); if (pointSet == NULL) return; // (take care that this widget doesn't react to self-induced changes by setting m_SelfCall) m_SelfCall = true; // update selection of all points in pointset: select the one(s) that are selected in the view, deselect all others QModelIndexList selectedIndexes = selected.indexes(); for (mitk::PointSet::PointsContainer::Iterator it = pointSet->GetPointSet(m_PointListModel->GetTimeStep())->GetPoints()->Begin(); it != pointSet->GetPointSet(m_PointListModel->GetTimeStep())->GetPoints()->End(); ++it) { QModelIndex index; if (m_PointListModel->GetModelIndexForPointID(it->Index(), index)) { if (selectedIndexes.indexOf(index) != -1) // index is found in the selected indices list { pointSet->SetSelectInfo(it->Index(), true, m_PointListModel->GetTimeStep()); // Use Multiwidget or SliceNavigationControllers to set crosshair to selected point if ( m_MultiWidget != NULL) { m_MultiWidget->MoveCrossToPosition(pointSet->GetPoint(it->Index(), m_PointListModel->GetTimeStep())); } mitk::Point3D p = pointSet->GetPoint(it->Index(), m_PointListModel->GetTimeStep()); // remove the three ifs below after the SetSnc* methods have been removed if (m_Snc1 != NULL) { m_Snc1->SelectSliceByPoint(p); } if (m_Snc2 != NULL) { m_Snc2->SelectSliceByPoint(p); } if (m_Snc3 != NULL) { m_Snc3->SelectSliceByPoint(p); } for (std::set::const_iterator i = m_Sncs.begin(); i != m_Sncs.end(); ++i) { (*i)->SelectSliceByPoint(p); } } else { pointSet->SetSelectInfo(it->Index(), false, m_PointListModel->GetTimeStep()); } } } m_SelfCall = false; emit SignalPointSelectionChanged(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkPointListView::keyPressEvent( QKeyEvent * e ) { if (m_PointListModel == NULL) return; int key = e->key(); switch (key) { case Qt::Key_F2: m_PointListModel->MoveSelectedPointUp(); break; case Qt::Key_F3: m_PointListModel->MoveSelectedPointDown(); break; case Qt::Key_Delete: m_PointListModel->RemoveSelectedPoint(); break; default: break; } } void QmitkPointListView::wheelEvent(QWheelEvent *event) { if (!m_PointListModel || !m_PointListModel->GetPointSet() || (int)(m_PointListModel->GetPointSet()->GetTimeSteps()) == 1) return; int whe = event->delta(); mitk::PointSet::Pointer ps = dynamic_cast(m_PointListModel->GetPointSet()); unsigned int numberOfTS = ps->GetTimeSteps(); if(numberOfTS == 1) return; int currentTS = this->m_PointListModel->GetTimeStep(); if(whe > 0) { - if((currentTS >= (int)(m_PointListModel->GetPointSet()->GetTimeSteps()))) + if((currentTS+1 >= (int)numberOfTS)) return; this->m_PointListModel->SetTimeStep(++currentTS); } else { if((currentTS <= 0)) return; this->m_PointListModel->SetTimeStep(--currentTS); } QString tooltip = QString("Use the F2/F3 keys to move a point up/down, the Del key to remove a point\nand the mouse wheel to change the timestep.\n\nTimeStep:\t%1").arg(currentTS); this->setToolTip(tooltip); fadeTimeStepIn(); } void QmitkPointListView::fadeTimeStepIn() { //Setup Widget QWidget *m_TimeStepFader = new QWidget(this); QHBoxLayout *layout = new QHBoxLayout(m_TimeStepFader); int x = (int)(this->geometry().x()+this->width()*0.6); int y = (int)(this->geometry().y()+this->height()*0.8); m_TimeStepFader->move(x,y); m_TimeStepFader->resize(60, 55); m_TimeStepFader->setLayout(layout); m_TimeStepFader->setAttribute(Qt::WA_DeleteOnClose); layout->addWidget(m_TimeStepFaderLabel); m_TimeStepFaderLabel->setAlignment(Qt::AlignCenter); m_TimeStepFaderLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Raised); m_TimeStepFaderLabel->setLineWidth(2); m_TimeStepFaderLabel->setText(QString("%1").arg(this->m_PointListModel->GetTimeStep())); //give the widget opacity and some colour QPalette pal = m_TimeStepFaderLabel->palette(); QColor semiTransparentColor(139, 192, 223, 50); QColor labelTransparentColor(0,0,0,200); pal.setColor(m_TimeStepFaderLabel->backgroundRole(), semiTransparentColor); pal.setColor(m_TimeStepFaderLabel->foregroundRole(), labelTransparentColor); m_TimeStepFaderLabel->setAutoFillBackground(true); m_TimeStepFaderLabel->setPalette(pal); //show the widget m_TimeStepFader->show(); //and start the timer m_TimeStepFaderLabel->setVisible(true); QTimer::singleShot(2000, this, SLOT(fadeTimeStepOut())); } void QmitkPointListView::fadeTimeStepOut() { m_TimeStepFaderLabel->hide(); } void QmitkPointListView::ctxMenu(const QPoint &pos) { QMenu *menu = new QMenu; //add Fading check QAction *showFading = new QAction(this); showFading->setCheckable(false); //TODO: reset when fading is working showFading->setEnabled(false); //TODO: reset when fading is working showFading->setText("Fade TimeStep"); connect(showFading, SIGNAL(triggered(bool)), this, SLOT(SetFading(bool))); menu->addAction(showFading); //add Clear action QAction *clearList = new QAction(this); clearList->setText("Clear List"); connect(clearList, SIGNAL(triggered()), this, SLOT(ClearPointList())); menu->addAction(clearList); //add Clear TimeStep action QAction *clearTS = new QAction(this); clearTS->setText("Clear current time step"); connect(clearTS, SIGNAL(triggered()), this, SLOT(ClearPointListTS())); menu->addAction(clearTS); menu->exec(this->mapToGlobal(pos)); } void QmitkPointListView::SetFading(bool onOff) { m_showFading = onOff; } void QmitkPointListView::ClearPointList() { if(!m_PointListModel->GetPointSet()) return; mitk::PointSet::Pointer curPS = m_PointListModel->GetPointSet(); if ( curPS->GetSize() == 0) return; switch( QMessageBox::question( this, tr("Clear Points"), tr("Remove all points from the displayed list?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) { case QMessageBox::Yes: { mitk::PointSet::PointsIterator it; mitk::PointSet::PointsContainer *curPsPoints; while( !curPS->IsEmptyTimeStep(0)) { curPsPoints = curPS->GetPointSet()->GetPoints(); it = curPsPoints->Begin(); curPS->SetSelectInfo(it->Index(),true); m_PointListModel->RemoveSelectedPoint(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); break; } case QMessageBox::No: default: break; } } void QmitkPointListView::ClearPointListTS() { } void QmitkPointListView::SetSnc1(mitk::SliceNavigationController* snc) { m_Snc1 = snc; } void QmitkPointListView::SetSnc2(mitk::SliceNavigationController* snc) { m_Snc2 = snc; } void QmitkPointListView::SetSnc3(mitk::SliceNavigationController* snc) { m_Snc3 = snc; } void QmitkPointListView::AddSliceNavigationController(mitk::SliceNavigationController* snc) { if (snc == NULL) return; m_Sncs.insert(snc); } void QmitkPointListView::RemoveSliceNavigationController(mitk::SliceNavigationController* snc) { if (snc == NULL) return; m_Sncs.erase(snc); } diff --git a/Plugins/org.mitk.gui.qt.application/src/QmitkPreferencesDialog.cpp b/Plugins/org.mitk.gui.qt.application/src/QmitkPreferencesDialog.cpp index cc32e4deaf..116cd96e0d 100644 --- a/Plugins/org.mitk.gui.qt.application/src/QmitkPreferencesDialog.cpp +++ b/Plugins/org.mitk.gui.qt.application/src/QmitkPreferencesDialog.cpp @@ -1,410 +1,454 @@ /*=================================================================== 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 "QmitkPreferencesDialog.h" #include "berryPlatform.h" #include "berryPlatformUI.h" #include "berryIWorkbench.h" #include "berryIConfigurationElement.h" #include "berryIExtensionRegistry.h" #include "berryIExtension.h" #include #include #include "internal/org_mitk_gui_qt_application_Activator.h" #include #include #include #include #include #include using namespace std; class QmitkPreferencesDialogPrivate : public Ui::QmitkPreferencesDialog { public: /// /// Just a stub class for holding information on prefpages (metadata) /// struct PrefPage { PrefPage(QString _id, QString _name, QString _category , QString _className, QString _keywords, berry::IConfigurationElement::Pointer _confElem) : id(_id), name(_name), category(_category), className(_className), keywords(_keywords), prefPage(nullptr), confElem(_confElem), treeWidgetItem(nullptr) {} bool operator==(const PrefPage& other) { return id == other.id; } bool operator<(const PrefPage& other) { return name < other.name; } QString id; QString name; QString category; QString className; QString keywords; berry::IQtPreferencePage* prefPage; berry::IConfigurationElement::Pointer confElem; QTreeWidgetItem* treeWidgetItem; }; QmitkPreferencesDialogPrivate() : m_CurrentPage(0) { berry::IExtensionRegistry* xpService = berry::Platform::GetExtensionRegistry(); // m_PrefPages QList prefPages(xpService->GetConfigurationElementsFor("org.blueberry.ui.preferencePages")); QList keywordExts(xpService->GetConfigurationElementsFor("org.blueberry.ui.keywords")); QList::iterator prefPagesIt; QList::iterator keywordRefsIt; for (prefPagesIt = prefPages.begin(); prefPagesIt != prefPages.end(); ++prefPagesIt) { QString id = (*prefPagesIt)->GetAttribute("id"); QString name = (*prefPagesIt)->GetAttribute("name"); QString className = (*prefPagesIt)->GetAttribute("class"); if(!id.isEmpty() && !name.isEmpty() && !className.isEmpty()) { QString keywordLabels; QString category = (*prefPagesIt)->GetAttribute("category"); //# collect keywords QList keywordRefs = (*prefPagesIt)->GetChildren("keywordreference"); // get all keyword references for (keywordRefsIt = keywordRefs.begin() ; keywordRefsIt != keywordRefs.end(); ++keywordRefsIt) // iterate over all refs { QString keywordRefId = (*keywordRefsIt)->GetAttribute("id"); // get referenced id for (QList::iterator keywordExtsIt = keywordExts.begin(); keywordExtsIt != keywordExts.end(); ++keywordExtsIt) // iterate over all keywords { QString keywordId = (*keywordExtsIt)->GetAttribute("id"); // get keyword id if(keywordId == keywordRefId) // if referenced id is equals the current keyword id { //# collect all keywords from label attribute with a tokenizer QString currLabel = (*keywordExtsIt)->GetAttribute("label"); currLabel = currLabel.toLower(); if (!currLabel.isEmpty()) keywordLabels += QString(" ") + currLabel; //break; // break here; possibly search for other referenced keywords } } } // add information as PrefPage m_PrefPages.push_back(PrefPage(id, name, category, className, keywordLabels, berry::IConfigurationElement::Pointer(*prefPagesIt))); } } } /// /// Saves all treewidgetitems in a map, the key is the id of the preferencepage. /// QList m_PrefPages; int m_CurrentPage; }; QmitkPreferencesDialog::QmitkPreferencesDialog(QWidget * parent, Qt::WindowFlags f) : QDialog(parent, f), d(new QmitkPreferencesDialogPrivate) { d->setupUi(this); QObject::connect(d->m_Keyword, SIGNAL(editingFinished()), this, SLOT(OnKeywordEditingFinished())); QObject::connect(d->m_Keyword, SIGNAL(textChanged(QString)), this, SLOT(OnKeywordTextChanged(QString))); QObject::connect(d->m_PreferencesTree, SIGNAL(itemSelectionChanged()), this, SLOT(OnPreferencesTreeItemSelectionChanged())); QPushButton* importButton = d->buttonBox->addButton("Import...", QDialogButtonBox::ActionRole); QObject::connect(importButton, SIGNAL(clicked()), this, SLOT(OnImportButtonClicked())); QPushButton* exportButton = d->buttonBox->addButton("Export...", QDialogButtonBox::ActionRole); QObject::connect(exportButton, SIGNAL(clicked()), this, SLOT(OnExportButtonClicked())); QObject::connect(this, SIGNAL(accepted()), this, SLOT(OnDialogAccepted())); QObject::connect(this, SIGNAL(rejected()), this, SLOT(OnDialogRejected())); d->buttonBox->button(QDialogButtonBox::Cancel)->setDefault(true); this->UpdateTree(); } QmitkPreferencesDialog::~QmitkPreferencesDialog() { } void QmitkPreferencesDialog::SetSelectedPage(const QString& id) { for(QList::iterator it = d->m_PrefPages.begin(); it != d->m_PrefPages.end(); ++it) { if(it->id == id) { d->m_PreferencesTree->setCurrentItem(it->treeWidgetItem); break; } } } void QmitkPreferencesDialog::OnImportButtonClicked() { int answer = QMessageBox::question(this, "Importing Preferences" , "All existing preferences will be overwritten!\nAre you sure that you want to import other preferences?", QMessageBox::Yes | QMessageBox::No ); if(answer == QMessageBox::No) return; try { berry::IBerryPreferencesService* berryPrefService = dynamic_cast(berry::Platform::GetPreferencesService()); if(berryPrefService != nullptr) { static QString importDir = ""; QString fileName = QFileDialog::getOpenFileName(this, tr("Choose file to import preferences"), importDir, tr("XML files (*.xml)")); if(!fileName.isEmpty()) { importDir = QFileInfo(fileName).absoluteDir().path(); berryPrefService->ImportPreferences(fileName, ""); berry::IQtPreferencePage* prefPage = d->m_PrefPages[d->m_CurrentPage].prefPage; if(prefPage) prefPage->Update(); MITK_INFO("QmitkPreferencesDialog") << "Preferences successfully imported from " << fileName; } } } catch (Poco::Exception& pe) { QMessageBox::critical(this, "Error Importing", pe.message().c_str()); MITK_ERROR("QmitkPreferencesDialog") << pe.what(); } catch (std::exception& e) { QMessageBox::critical(this, "Error Importing", e.what()); MITK_ERROR("QmitkPreferencesDialog") << e.what(); } } void QmitkPreferencesDialog::OnExportButtonClicked() { try { berry::IBerryPreferencesService* berryPrefService = dynamic_cast(berry::Platform::GetPreferencesService()); if(berryPrefService != nullptr) { SavePreferences(); static QString exportDir = ""; QString fileName = QFileDialog::getSaveFileName(this, tr("Choose file to export preferences"), exportDir, tr("XML files (*.xml)")); if(!fileName.isEmpty()) { if(QFileInfo(fileName).suffix() != ".xml") { fileName += ".xml"; } exportDir = QFileInfo(fileName).absoluteDir().path(); berryPrefService->ExportPreferences(fileName, ""); MITK_INFO("QmitkPreferencesDialog") << "Preferences successfully exported to " << fileName; } } } catch (Poco::Exception& pe) { QMessageBox::critical(this, "Error Exporting", pe.message().c_str()); MITK_ERROR("QmitkPreferencesDialog") << pe.what(); } catch (std::exception& e) { QMessageBox::critical(this, "Error Exporting", e.what()); MITK_ERROR("QmitkPreferencesDialog") << e.what(); } } void QmitkPreferencesDialog::SavePreferences() { berry::IQtPreferencePage* prefPage = nullptr; for(QList::iterator it = d->m_PrefPages.begin(); it != d->m_PrefPages.end(); ++it) { prefPage = it->prefPage; if(prefPage) { prefPage->PerformOk(); } } /** * Every preference page has its own preferences, which should stay the same after a system restart.
* Therefore this method flushes all the preferences, every time a change in the preferences is
* performed and confirmed. * */ berry::Platform::GetPreferencesService()->GetSystemPreferences()->Flush(); } void QmitkPreferencesDialog::OnDialogAccepted() { this->SavePreferences(); } void QmitkPreferencesDialog::OnDialogRejected() { berry::IQtPreferencePage* prefPage = d->m_PrefPages[d->m_CurrentPage].prefPage; if(prefPage) prefPage->PerformCancel(); } void QmitkPreferencesDialog::OnKeywordTextChanged(const QString & /*s*/) { // search for text this->UpdateTree(); } void QmitkPreferencesDialog::OnKeywordEditingFinished() { } //bool QmitkPreferencesDialog::eventFilter( QObject *obj, QEvent *event ) //{ // if(obj == d->m_Keyword) // { // if(event->type() == QEvent::FocusIn && d->m_Keyword->text() == "search ...") // { // d->m_Keyword->setText(""); // d->m_Keyword->setStyleSheet("color: black;"); // } // else if(event->type() == QEvent::FocusOut && d->m_Keyword->text() == "") // { // d->m_Keyword->setText("search ..."); // d->m_Keyword->setStyleSheet("color: gray;"); // } // } // return true; //} void QmitkPreferencesDialog::OnPreferencesTreeItemSelectionChanged() { if(d->m_PreferencesTree == nullptr) return; // TODO: create page and show it QList selectedItems = d->m_PreferencesTree->selectedItems(); if(selectedItems.size()>0) { d->m_CurrentPage = 0; berry::IWorkbench* workbench = berry::PlatformUI::GetWorkbench(); for(QList::iterator it = d->m_PrefPages.begin(); it != d->m_PrefPages.end(); ++it, ++d->m_CurrentPage) { if(it->treeWidgetItem == selectedItems.at(0)) { d->m_Headline->setText(it->name); if(it->prefPage == nullptr) { berry::IPreferencePage* page = it->confElem->CreateExecutableExtension("class"); it->prefPage = dynamic_cast(page); it->prefPage->Init(berry::IWorkbench::Pointer(workbench)); it->prefPage->CreateQtControl(d->m_PreferencesPanel); d->m_PreferencesPanel->addWidget(it->prefPage->GetQtControl()); } d->m_PreferencesPanel->setCurrentWidget(it->prefPage->GetQtControl()); break; } } } } void QmitkPreferencesDialog::UpdateTree() { if(d->m_PreferencesTree == nullptr) return; //m_PreferencesTree->clear(); QString keyword = d->m_Keyword->text().toLower(); map items; + std::list< QList::iterator > deferredItems; - for(QList::iterator it = d->m_PrefPages.begin(); - it != d->m_PrefPages.end(); ++it) + for (QList::iterator it = d->m_PrefPages.begin(); + it != d->m_PrefPages.end(); ++it) { - if(it->treeWidgetItem == nullptr) + if (it->treeWidgetItem == nullptr) { - - if(it->category.isEmpty()) + if (it->category.isEmpty()) { it->treeWidgetItem = new QTreeWidgetItem(d->m_PreferencesTree); + it->treeWidgetItem->setText(0, it->name); + items[it->id] = it->treeWidgetItem; } else { - it->treeWidgetItem = new QTreeWidgetItem(items[it->category]); + // the parent might not be created yet + deferredItems.push_back(it); } - it->treeWidgetItem->setText(0, it->name); - items[it->id] = it->treeWidgetItem; } + } + + // deal with deferred items. We do not know how many levels + // of parents need to be created + auto currentItem = deferredItems.begin(); + + while (currentItem != deferredItems.end()) + { + auto currentItemContent = *currentItem; + if (items[currentItemContent->category] != nullptr) + { + currentItemContent->treeWidgetItem = new QTreeWidgetItem(items[currentItemContent->category]); + currentItemContent->treeWidgetItem->setText(0, currentItemContent->name); + items[currentItemContent->id] = currentItemContent->treeWidgetItem; + + deferredItems.erase(currentItem); + currentItem = deferredItems.begin(); + } + else + { + ++currentItem; + } + } + + if (!deferredItems.empty()) + { + MITK_ERROR << "Unknown preference category. They are added top-level."; + + for (const auto ¤tItemContent : deferredItems) + { + currentItemContent->treeWidgetItem = new QTreeWidgetItem(d->m_PreferencesTree); + currentItemContent->treeWidgetItem->setText(0, currentItemContent->name); + items[currentItemContent->id] = currentItemContent->treeWidgetItem; + } + } + + // we have to iterate over the list a second time, as only + // now all parents and items are guaranteed to be created + for (QList::iterator it = d->m_PrefPages.begin(); + it != d->m_PrefPages.end(); ++it) + { // hide treeWidgetItem if keyword not matches if(!keyword.isEmpty()) { - if( it->keywords.indexOf(keyword) == -1 ) + if (it->keywords.indexOf(keyword) == -1) + { it->treeWidgetItem->setHidden(true); + } else { //#make the whole branch visible QTreeWidgetItem* treeWidgetParent = it->treeWidgetItem->parent(); while(treeWidgetParent!=nullptr) { treeWidgetParent->setHidden(false); treeWidgetParent->setExpanded(true); treeWidgetParent = treeWidgetParent->parent(); } it->treeWidgetItem->setHidden(false); QFont f = it->treeWidgetItem->font(0); f.setBold(true); it->treeWidgetItem->setFont(0, f); } } else { QFont f = it->treeWidgetItem->font(0); f.setBold(false); it->treeWidgetItem->setFont(0, f); it->treeWidgetItem->setHidden(false); } } if(d->m_PrefPages.size()>0) { if(d->m_PrefPages.front().treeWidgetItem != nullptr) d->m_PrefPages.front().treeWidgetItem->setSelected(true); } } diff --git a/Plugins/org.mitk.gui.qt.datamanager/src/QmitkDataManagerView.cpp b/Plugins/org.mitk.gui.qt.datamanager/src/QmitkDataManagerView.cpp index 663ba8db7d..25eac895f8 100644 --- a/Plugins/org.mitk.gui.qt.datamanager/src/QmitkDataManagerView.cpp +++ b/Plugins/org.mitk.gui.qt.datamanager/src/QmitkDataManagerView.cpp @@ -1,995 +1,996 @@ /*=================================================================== 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 "QmitkDataManagerView.h" //# Own Includes //## mitk #include "mitkDataStorageEditorInput.h" #include "mitkIDataStorageReference.h" #include "mitkNodePredicateDataType.h" #include "mitkCoreObjectFactory.h" #include "mitkColorProperty.h" #include "mitkCommon.h" #include "mitkNodePredicateData.h" #include "mitkNodePredicateNot.h" #include "mitkNodePredicateOr.h" #include "mitkNodePredicateProperty.h" #include "mitkEnumerationProperty.h" #include "mitkLookupTableProperty.h" #include "mitkProperties.h" #include #include #include #include #include //## Qmitk #include #include #include #include #include #include #include #include "src/internal/QmitkNodeTableViewKeyFilter.h" #include "src/internal/QmitkInfoDialog.h" #include "src/internal/QmitkDataManagerItemDelegate.h" //## Berry #include #include #include #include #include #include //# Toolkit Includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkDataNodeObject.h" #include "mitkIContextMenuAction.h" #include "berryIExtensionRegistry.h" #include "mitkRenderingModeProperty.h" const QString QmitkDataManagerView::VIEW_ID = "org.mitk.views.datamanager"; QmitkDataManagerView::QmitkDataManagerView() : m_GlobalReinitOnNodeDelete(true), m_ItemDelegate(NULL) { } QmitkDataManagerView::~QmitkDataManagerView() { //Remove all registered actions from each descriptor for (std::vector< std::pair< QmitkNodeDescriptor*, QAction* > >::iterator it = m_DescriptorActionList.begin();it != m_DescriptorActionList.end(); it++) { // first== the NodeDescriptor; second== the registered QAction (it->first)->RemoveAction(it->second); } } void QmitkDataManagerView::CreateQtPartControl(QWidget* parent) { m_CurrentRowCount = 0; m_Parent = parent; //# Preferences berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService(); berry::IBerryPreferences::Pointer prefs = (prefService->GetSystemPreferences()->Node(VIEW_ID)) .Cast(); assert( prefs ); prefs->OnChanged.AddListener( berry::MessageDelegate1( this , &QmitkDataManagerView::OnPreferencesChanged ) ); //# GUI m_NodeTreeModel = new QmitkDataStorageTreeModel(this->GetDataStorage()); m_NodeTreeModel->setParent( parent ); m_NodeTreeModel->SetPlaceNewNodesOnTop( prefs->GetBool("Place new nodes on top", true) ); m_SurfaceDecimation = prefs->GetBool("Use surface decimation", false); // Prepare filters m_HelperObjectFilterPredicate = mitk::NodePredicateOr::New( mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true)), mitk::NodePredicateProperty::New("hidden object", mitk::BoolProperty::New(true))); m_NodeWithNoDataFilterPredicate = mitk::NodePredicateData::New(0); m_FilterModel = new QmitkDataStorageFilterProxyModel(); m_FilterModel->setSourceModel(m_NodeTreeModel); m_FilterModel->AddFilterPredicate(m_HelperObjectFilterPredicate); m_FilterModel->AddFilterPredicate(m_NodeWithNoDataFilterPredicate); //# Tree View (experimental) m_NodeTreeView = new QTreeView; m_NodeTreeView->setHeaderHidden(true); m_NodeTreeView->setSelectionMode( QAbstractItemView::ExtendedSelection ); m_NodeTreeView->setSelectionBehavior( QAbstractItemView::SelectRows ); m_NodeTreeView->setAlternatingRowColors(true); m_NodeTreeView->setDragEnabled(true); m_NodeTreeView->setDropIndicatorShown(true); m_NodeTreeView->setAcceptDrops(true); m_NodeTreeView->setContextMenuPolicy(Qt::CustomContextMenu); m_NodeTreeView->setModel(m_FilterModel); m_NodeTreeView->setTextElideMode(Qt::ElideMiddle); m_NodeTreeView->installEventFilter(new QmitkNodeTableViewKeyFilter(this)); m_ItemDelegate = new QmitkDataManagerItemDelegate(m_NodeTreeView); m_NodeTreeView->setItemDelegate(m_ItemDelegate); QObject::connect( m_NodeTreeView, SIGNAL(customContextMenuRequested(const QPoint&)) , this, SLOT(NodeTableViewContextMenuRequested(const QPoint&)) ); QObject::connect( m_NodeTreeModel, SIGNAL(rowsInserted (const QModelIndex&, int, int)) , this, SLOT(NodeTreeViewRowsInserted ( const QModelIndex&, int, int )) ); QObject::connect( m_NodeTreeModel, SIGNAL(rowsRemoved (const QModelIndex&, int, int)) , this, SLOT(NodeTreeViewRowsRemoved( const QModelIndex&, int, int )) ); QObject::connect( m_NodeTreeView->selectionModel() , SIGNAL( selectionChanged ( const QItemSelection &, const QItemSelection & ) ) , this , SLOT( NodeSelectionChanged ( const QItemSelection &, const QItemSelection & ) ) ); //# m_NodeMenu m_NodeMenu = new QMenu(m_NodeTreeView); // # Actions berry::IEditorRegistry* editorRegistry = berry::PlatformUI::GetWorkbench()->GetEditorRegistry(); QList editors = editorRegistry->GetEditors("*.mitk"); if (editors.size() > 1) { m_ShowInMapper = new QSignalMapper(this); foreach(berry::IEditorDescriptor::Pointer descriptor, editors) { QAction* action = new QAction(descriptor->GetLabel(), this); m_ShowInActions << action; m_ShowInMapper->connect(action, SIGNAL(triggered()), m_ShowInMapper, SLOT(map())); m_ShowInMapper->setMapping(action, descriptor->GetId()); } connect(m_ShowInMapper, SIGNAL(mapped(QString)), this, SLOT(ShowIn(QString))); } QmitkNodeDescriptor* unknownDataNodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetUnknownDataNodeDescriptor(); QmitkNodeDescriptor* imageDataNodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor("Image"); QmitkNodeDescriptor* diffusionImageDataNodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor("DiffusionImage"); QmitkNodeDescriptor* surfaceDataNodeDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor("Surface"); QAction* globalReinitAction = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/Refresh_48.png"), "Global Reinit", this); QObject::connect( globalReinitAction, SIGNAL( triggered(bool) ) , this, SLOT( GlobalReinit(bool) ) ); unknownDataNodeDescriptor->AddAction(globalReinitAction); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor, globalReinitAction)); QAction* saveAction = new QmitkFileSaveAction(QIcon(":/org.mitk.gui.qt.datamanager/Save_48.png"), this->GetSite()->GetWorkbenchWindow()); unknownDataNodeDescriptor->AddAction(saveAction); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,saveAction)); QAction* removeAction = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/Remove_48.png"), "Remove", this); QObject::connect( removeAction, SIGNAL( triggered(bool) ) , this, SLOT( RemoveSelectedNodes(bool) ) ); unknownDataNodeDescriptor->AddAction(removeAction); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,removeAction)); QAction* reinitAction = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/Refresh_48.png"), "Reinit", this); QObject::connect( reinitAction, SIGNAL( triggered(bool) ) , this, SLOT( ReinitSelectedNodes(bool) ) ); unknownDataNodeDescriptor->AddAction(reinitAction); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,reinitAction)); // find contextMenuAction extension points and add them to the node descriptor berry::IExtensionRegistry* extensionPointService = berry::Platform::GetExtensionRegistry(); QList cmActions( extensionPointService->GetConfigurationElementsFor("org.mitk.gui.qt.datamanager.contextMenuActions") ); QList::iterator cmActionsIt; QmitkNodeDescriptor* tmpDescriptor; QAction* contextMenuAction; QVariant cmActionDataIt; m_ConfElements.clear(); int i=1; for (cmActionsIt = cmActions.begin() ; cmActionsIt != cmActions.end() ; ++cmActionsIt) { QString cmNodeDescriptorName = (*cmActionsIt)->GetAttribute("nodeDescriptorName"); QString cmLabel = (*cmActionsIt)->GetAttribute("label"); QString cmClass = (*cmActionsIt)->GetAttribute("class"); if(!cmNodeDescriptorName.isEmpty() && !cmLabel.isEmpty() && !cmClass.isEmpty()) { QString cmIcon = (*cmActionsIt)->GetAttribute("icon"); // create context menu entry here tmpDescriptor = QmitkNodeDescriptorManager::GetInstance()->GetDescriptor(cmNodeDescriptorName); if(!tmpDescriptor) { MITK_WARN << "cannot add action \"" << cmLabel << "\" because descriptor " << cmNodeDescriptorName << " does not exist"; continue; } // check if the user specified an icon attribute if ( !cmIcon.isEmpty() ) { if (cmLabel == "Upload to XNAT") contextMenuAction = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/xnat-icon.png"), cmLabel, parent); else contextMenuAction = new QAction( QIcon(cmIcon), cmLabel, parent); } else { contextMenuAction = new QAction( cmLabel, parent); } tmpDescriptor->AddAction(contextMenuAction); m_DescriptorActionList.push_back(std::pair(tmpDescriptor,contextMenuAction)); m_ConfElements[contextMenuAction] = *cmActionsIt; cmActionDataIt.setValue(i); contextMenuAction->setData( cmActionDataIt ); connect( contextMenuAction, SIGNAL( triggered(bool) ) , this, SLOT( ContextMenuActionTriggered(bool) ) ); ++i; } } m_OpacitySlider = new QSlider; m_OpacitySlider->setMinimum(0); m_OpacitySlider->setMaximum(100); m_OpacitySlider->setOrientation(Qt::Horizontal); QObject::connect( m_OpacitySlider, SIGNAL( valueChanged(int) ) , this, SLOT( OpacityChanged(int) ) ); QLabel* _OpacityLabel = new QLabel("Opacity: "); QHBoxLayout* _OpacityWidgetLayout = new QHBoxLayout; _OpacityWidgetLayout->setContentsMargins(4,4,4,4); _OpacityWidgetLayout->addWidget(_OpacityLabel); _OpacityWidgetLayout->addWidget(m_OpacitySlider); QWidget* _OpacityWidget = new QWidget; _OpacityWidget->setLayout(_OpacityWidgetLayout); QWidgetAction* opacityAction = new QWidgetAction(this); opacityAction ->setDefaultWidget(_OpacityWidget); QObject::connect( opacityAction , SIGNAL( changed() ) , this, SLOT( OpacityActionChanged() ) ); unknownDataNodeDescriptor->AddAction(opacityAction , false); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,opacityAction)); m_ColorButton = new QPushButton; m_ColorButton->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Minimum); //m_ColorButton->setText("Change color"); QObject::connect( m_ColorButton, SIGNAL( clicked() ) , this, SLOT( ColorChanged() ) ); QLabel* _ColorLabel = new QLabel("Color: "); _ColorLabel->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum); QHBoxLayout* _ColorWidgetLayout = new QHBoxLayout; _ColorWidgetLayout->setContentsMargins(4,4,4,4); _ColorWidgetLayout->addWidget(_ColorLabel); _ColorWidgetLayout->addWidget(m_ColorButton); QWidget* _ColorWidget = new QWidget; _ColorWidget->setLayout(_ColorWidgetLayout); QWidgetAction* colorAction = new QWidgetAction(this); colorAction->setDefaultWidget(_ColorWidget); QObject::connect( colorAction, SIGNAL( changed() ) , this, SLOT( ColorActionChanged() ) ); unknownDataNodeDescriptor->AddAction(colorAction, false); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,colorAction)); m_ComponentSlider = new QmitkNumberPropertySlider; m_ComponentSlider->setOrientation(Qt::Horizontal); //QObject::connect( m_OpacitySlider, SIGNAL( valueChanged(int) ) // , this, SLOT( OpacityChanged(int) ) ); QLabel* _ComponentLabel = new QLabel("Component: "); QHBoxLayout* _ComponentWidgetLayout = new QHBoxLayout; _ComponentWidgetLayout->setContentsMargins(4,4,4,4); _ComponentWidgetLayout->addWidget(_ComponentLabel); _ComponentWidgetLayout->addWidget(m_ComponentSlider); QLabel* _ComponentValueLabel = new QLabel(); _ComponentWidgetLayout->addWidget(_ComponentValueLabel); connect(m_ComponentSlider, SIGNAL(valueChanged(int)), _ComponentValueLabel, SLOT(setNum(int))); QWidget* _ComponentWidget = new QWidget; _ComponentWidget->setLayout(_ComponentWidgetLayout); QWidgetAction* componentAction = new QWidgetAction(this); componentAction->setDefaultWidget(_ComponentWidget); QObject::connect( componentAction , SIGNAL( changed() ) , this, SLOT( ComponentActionChanged() ) ); imageDataNodeDescriptor->AddAction(componentAction, false); m_DescriptorActionList.push_back(std::pair(imageDataNodeDescriptor,componentAction)); if (diffusionImageDataNodeDescriptor!=NULL) { diffusionImageDataNodeDescriptor->AddAction(componentAction, false); m_DescriptorActionList.push_back(std::pair(diffusionImageDataNodeDescriptor,componentAction)); } m_TextureInterpolation = new QAction("Texture Interpolation", this); m_TextureInterpolation->setCheckable ( true ); QObject::connect( m_TextureInterpolation, SIGNAL( changed() ) , this, SLOT( TextureInterpolationChanged() ) ); QObject::connect( m_TextureInterpolation, SIGNAL( toggled(bool) ) , this, SLOT( TextureInterpolationToggled(bool) ) ); imageDataNodeDescriptor->AddAction(m_TextureInterpolation, false); m_DescriptorActionList.push_back(std::pair(imageDataNodeDescriptor,m_TextureInterpolation)); if (diffusionImageDataNodeDescriptor!=NULL) { diffusionImageDataNodeDescriptor->AddAction(m_TextureInterpolation, false); m_DescriptorActionList.push_back(std::pair(diffusionImageDataNodeDescriptor,m_TextureInterpolation)); } m_ColormapAction = new QAction("Colormap", this); m_ColormapAction->setMenu(new QMenu); QObject::connect( m_ColormapAction->menu(), SIGNAL( aboutToShow() ) , this, SLOT( ColormapMenuAboutToShow() ) ); imageDataNodeDescriptor->AddAction(m_ColormapAction, false); m_DescriptorActionList.push_back(std::pair(imageDataNodeDescriptor, m_ColormapAction)); if (diffusionImageDataNodeDescriptor!=NULL) { diffusionImageDataNodeDescriptor->AddAction(m_ColormapAction, false); m_DescriptorActionList.push_back(std::pair(diffusionImageDataNodeDescriptor, m_ColormapAction)); } m_SurfaceRepresentation = new QAction("Surface Representation", this); m_SurfaceRepresentation->setMenu(new QMenu(m_NodeTreeView)); QObject::connect( m_SurfaceRepresentation->menu(), SIGNAL( aboutToShow() ) , this, SLOT( SurfaceRepresentationMenuAboutToShow() ) ); surfaceDataNodeDescriptor->AddAction(m_SurfaceRepresentation, false); m_DescriptorActionList.push_back(std::pair(surfaceDataNodeDescriptor, m_SurfaceRepresentation)); QAction* showOnlySelectedNodes = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/ShowSelectedNode_48.png") , "Show only selected nodes", this); QObject::connect( showOnlySelectedNodes, SIGNAL( triggered(bool) ) , this, SLOT( ShowOnlySelectedNodes(bool) ) ); unknownDataNodeDescriptor->AddAction(showOnlySelectedNodes); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor, showOnlySelectedNodes)); QAction* toggleSelectedVisibility = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/InvertShowSelectedNode_48.png") , "Toggle visibility", this); QObject::connect( toggleSelectedVisibility, SIGNAL( triggered(bool) ) , this, SLOT( ToggleVisibilityOfSelectedNodes(bool) ) ); unknownDataNodeDescriptor->AddAction(toggleSelectedVisibility); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,toggleSelectedVisibility)); QAction* actionShowInfoDialog = new QAction(QIcon(":/org.mitk.gui.qt.datamanager/ShowDataInfo_48.png") , "Details...", this); QObject::connect( actionShowInfoDialog, SIGNAL( triggered(bool) ) , this, SLOT( ShowInfoDialogForSelectedNodes(bool) ) ); unknownDataNodeDescriptor->AddAction(actionShowInfoDialog); m_DescriptorActionList.push_back(std::pair(unknownDataNodeDescriptor,actionShowInfoDialog)); QGridLayout* _DndFrameWidgetLayout = new QGridLayout; _DndFrameWidgetLayout->addWidget(m_NodeTreeView, 0, 0); _DndFrameWidgetLayout->setContentsMargins(0,0,0,0); m_DndFrameWidget = new QmitkDnDFrameWidget(m_Parent); m_DndFrameWidget->setLayout(_DndFrameWidgetLayout); QVBoxLayout* layout = new QVBoxLayout(parent); layout->addWidget(m_DndFrameWidget); layout->setContentsMargins(0,0,0,0); m_Parent->setLayout(layout); } void QmitkDataManagerView::SetFocus() { } void QmitkDataManagerView::ContextMenuActionTriggered( bool ) { QAction* action = qobject_cast ( sender() ); std::map::iterator it = m_ConfElements.find( action ); if( it == m_ConfElements.end() ) { MITK_WARN << "associated conf element for action " << action->text().toStdString() << " not found"; return; } berry::IConfigurationElement::Pointer confElem = it->second; mitk::IContextMenuAction* contextMenuAction = confElem->CreateExecutableExtension("class"); QString className = confElem->GetAttribute("class"); QString smoothed = confElem->GetAttribute("smoothed"); if(className == "QmitkCreatePolygonModelAction") { contextMenuAction->SetDataStorage(this->GetDataStorage()); if(smoothed == "false") { contextMenuAction->SetSmoothed(false); } else { contextMenuAction->SetSmoothed(true); } contextMenuAction->SetDecimated(m_SurfaceDecimation); } else if(className == "QmitkStatisticsAction") { contextMenuAction->SetFunctionality(this); } else if(className == "QmitkCreateSimulationAction") { contextMenuAction->SetDataStorage(this->GetDataStorage()); } contextMenuAction->Run( this->GetCurrentSelection() ); // run the action } void QmitkDataManagerView::OnPreferencesChanged(const berry::IBerryPreferences* prefs) { if( m_NodeTreeModel->GetPlaceNewNodesOnTopFlag() != prefs->GetBool("Place new nodes on top", true) ) m_NodeTreeModel->SetPlaceNewNodesOnTop( !m_NodeTreeModel->GetPlaceNewNodesOnTopFlag() ); bool hideHelperObjects = !prefs->GetBool("Show helper objects", false); if (m_FilterModel->HasFilterPredicate(m_HelperObjectFilterPredicate) != hideHelperObjects) { if (hideHelperObjects) { m_FilterModel->AddFilterPredicate(m_HelperObjectFilterPredicate); } else { m_FilterModel->RemoveFilterPredicate(m_HelperObjectFilterPredicate); } } bool hideNodesWithNoData = !prefs->GetBool("Show nodes containing no data", false); if (m_FilterModel->HasFilterPredicate(m_NodeWithNoDataFilterPredicate) != hideNodesWithNoData) { if (hideNodesWithNoData) { m_FilterModel->AddFilterPredicate(m_NodeWithNoDataFilterPredicate); } else { m_FilterModel->RemoveFilterPredicate(m_NodeWithNoDataFilterPredicate); } } m_GlobalReinitOnNodeDelete = prefs->GetBool("Call global reinit if node is deleted", true); m_NodeTreeView->expandAll(); m_SurfaceDecimation = prefs->GetBool("Use surface decimation", false); this->GlobalReinit(); } void QmitkDataManagerView::NodeTableViewContextMenuRequested( const QPoint & pos ) { QModelIndex selectedProxy = m_NodeTreeView->indexAt ( pos ); QModelIndex selected = m_FilterModel->mapToSource(selectedProxy); mitk::DataNode::Pointer node = m_NodeTreeModel->GetNode(selected); QList selectedNodes = this->GetCurrentSelection(); if(!selectedNodes.isEmpty()) { m_NodeMenu->clear(); QList actions; if(selectedNodes.size() == 1 ) { actions = QmitkNodeDescriptorManager::GetInstance()->GetActions(node); for(QList::iterator it = actions.begin(); it != actions.end(); ++it) { (*it)->setData(QVariant::fromValue(node.GetPointer())); } } else actions = QmitkNodeDescriptorManager::GetInstance()->GetActions(selectedNodes); if (!m_ShowInActions.isEmpty()) { QMenu* showInMenu = m_NodeMenu->addMenu("Show In"); showInMenu->addActions(m_ShowInActions); } m_NodeMenu->addActions(actions); m_NodeMenu->popup(QCursor::pos()); } } void QmitkDataManagerView::OpacityChanged(int value) { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_FilterModel->mapToSource(m_NodeTreeView->selectionModel()->currentIndex())); if(node) { float opacity = static_cast(value)/100.0f; node->SetFloatProperty("opacity", opacity); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkDataManagerView::OpacityActionChanged() { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_FilterModel->mapToSource(m_NodeTreeView->selectionModel()->currentIndex())); if(node) { float opacity = 0.0; if(node->GetFloatProperty("opacity", opacity)) { m_OpacitySlider->setValue(static_cast(opacity*100)); } } } void QmitkDataManagerView::ComponentActionChanged() { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_FilterModel->mapToSource(m_NodeTreeView->selectionModel()->currentIndex())); mitk::IntProperty* componentProperty = NULL; int numComponents = 0; if(node) { componentProperty = dynamic_cast(node->GetProperty("Image.Displayed Component")); mitk::Image* img = dynamic_cast(node->GetData()); if (img != NULL) { numComponents = img->GetPixelType().GetNumberOfComponents(); } } if (componentProperty && numComponents > 1) { m_ComponentSlider->SetProperty(componentProperty); m_ComponentSlider->setMinValue(0); m_ComponentSlider->setMaxValue(numComponents-1); } else { m_ComponentSlider->SetProperty(static_cast(NULL)); } } void QmitkDataManagerView::ColorChanged() { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_FilterModel->mapToSource(m_NodeTreeView->selectionModel()->currentIndex())); if(node) { mitk::Color color; mitk::ColorProperty::Pointer colorProp; node->GetProperty(colorProp,"color"); if(colorProp.IsNull()) return; color = colorProp->GetValue(); QColor initial(color.GetRed()*255,color.GetGreen()*255,color.GetBlue()*255); QColor qcolor = QColorDialog::getColor(initial,0,QString("Change color")); if (!qcolor.isValid()) return; m_ColorButton->setAutoFillBackground(true); node->SetProperty("color",mitk::ColorProperty::New(qcolor.red()/255.0,qcolor.green()/255.0,qcolor.blue()/255.0)); if (node->GetProperty("binaryimage.selectedcolor")) { node->SetProperty("binaryimage.selectedcolor",mitk::ColorProperty::New(qcolor.red()/255.0,qcolor.green()/255.0,qcolor.blue()/255.0)); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkDataManagerView::ColorActionChanged() { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_FilterModel->mapToSource(m_NodeTreeView->selectionModel()->currentIndex())); if(node) { mitk::Color color; mitk::ColorProperty::Pointer colorProp; node->GetProperty(colorProp,"color"); if(colorProp.IsNull()) return; color = colorProp->GetValue(); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0]*255)); styleSheet.append(","); styleSheet.append(QString::number(color[1]*255)); styleSheet.append(","); styleSheet.append(QString::number(color[2]*255)); styleSheet.append(")"); m_ColorButton->setStyleSheet(styleSheet); } } void QmitkDataManagerView::TextureInterpolationChanged() { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_FilterModel->mapToSource(m_NodeTreeView->selectionModel()->currentIndex())); if(node) { bool textureInterpolation = false; node->GetBoolProperty("texture interpolation", textureInterpolation); m_TextureInterpolation->setChecked(textureInterpolation); } } void QmitkDataManagerView::TextureInterpolationToggled( bool checked ) { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_FilterModel->mapToSource(m_NodeTreeView->selectionModel()->currentIndex())); if(node) { node->SetBoolProperty("texture interpolation", checked); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkDataManagerView::ColormapActionToggled( bool /*checked*/ ) { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_FilterModel->mapToSource(m_NodeTreeView->selectionModel()->currentIndex())); if(!node) return; mitk::LookupTableProperty::Pointer lookupTableProperty = dynamic_cast(node->GetProperty("LookupTable")); if (!lookupTableProperty) return; QAction* senderAction = qobject_cast(QObject::sender()); if(!senderAction) return; std::string activatedItem = senderAction->text().toStdString(); mitk::LookupTable::Pointer lookupTable = lookupTableProperty->GetValue(); if (!lookupTable) return; lookupTable->SetType(activatedItem); lookupTableProperty->SetValue(lookupTable); mitk::RenderingModeProperty::Pointer renderingMode = dynamic_cast(node->GetProperty("Image Rendering.Mode")); renderingMode->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataManagerView::ColormapMenuAboutToShow() { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_FilterModel->mapToSource(m_NodeTreeView->selectionModel()->currentIndex())); if(!node) return; mitk::LookupTableProperty::Pointer lookupTableProperty = dynamic_cast(node->GetProperty("LookupTable")); if (!lookupTableProperty) { mitk::LookupTable::Pointer mitkLut = mitk::LookupTable::New(); lookupTableProperty = mitk::LookupTableProperty::New(); lookupTableProperty->SetLookupTable(mitkLut); node->SetProperty("LookupTable", lookupTableProperty); } mitk::LookupTable::Pointer lookupTable = lookupTableProperty->GetValue(); if (!lookupTable) return; m_ColormapAction->menu()->clear(); QAction* tmp; int i = 0; std::string lutType = lookupTable->typenameList[i]; while (lutType != "END_OF_ARRAY") { tmp = m_ColormapAction->menu()->addAction(QString::fromStdString(lutType)); tmp->setCheckable(true); if (lutType == lookupTable->GetActiveTypeAsString()) { tmp->setChecked(true); } QObject::connect(tmp, SIGNAL(triggered(bool)), this, SLOT(ColormapActionToggled(bool))); lutType = lookupTable->typenameList[++i]; } } void QmitkDataManagerView::SurfaceRepresentationMenuAboutToShow() { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_FilterModel->mapToSource(m_NodeTreeView->selectionModel()->currentIndex())); if(!node) return; mitk::EnumerationProperty* representationProp = dynamic_cast (node->GetProperty("material.representation")); if(!representationProp) return; // clear menu m_SurfaceRepresentation->menu()->clear(); QAction* tmp; // create menu entries for(mitk::EnumerationProperty::EnumConstIterator it=representationProp->Begin(); it!=representationProp->End() ; it++) { tmp = m_SurfaceRepresentation->menu()->addAction(QString::fromStdString(it->second)); tmp->setCheckable(true); if(it->second == representationProp->GetValueAsString()) { tmp->setChecked(true); } QObject::connect( tmp, SIGNAL( triggered(bool) ) , this, SLOT( SurfaceRepresentationActionToggled(bool) ) ); } } void QmitkDataManagerView::SurfaceRepresentationActionToggled( bool /*checked*/ ) { mitk::DataNode* node = m_NodeTreeModel->GetNode(m_FilterModel->mapToSource(m_NodeTreeView->selectionModel()->currentIndex())); if(!node) return; mitk::EnumerationProperty* representationProp = dynamic_cast (node->GetProperty("material.representation")); if(!representationProp) return; QAction* senderAction = qobject_cast ( QObject::sender() ); if(!senderAction) return; std::string activatedItem = senderAction->text().toStdString(); if ( activatedItem != representationProp->GetValueAsString() ) { if ( representationProp->IsValidEnumerationValue( activatedItem ) ) { representationProp->SetValue( activatedItem ); representationProp->InvokeEvent( itk::ModifiedEvent() ); representationProp->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } } void QmitkDataManagerView::ReinitSelectedNodes( bool ) { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow == NULL) renderWindow = this->OpenRenderWindowPart(false); QList selectedNodes = this->GetCurrentSelection(); foreach(mitk::DataNode::Pointer node, selectedNodes) { mitk::BaseData::Pointer basedata = node->GetData(); if ( basedata.IsNotNull() && basedata->GetTimeGeometry()->IsValid() ) { renderWindow->GetRenderingManager()->InitializeViews( basedata->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, true ); renderWindow->GetRenderingManager()->RequestUpdateAll(); } } } void QmitkDataManagerView::RemoveSelectedNodes( bool ) { QModelIndexList indexesOfSelectedRowsFiltered = m_NodeTreeView->selectionModel()->selectedRows(); QModelIndexList indexesOfSelectedRows; for (int i = 0; i < indexesOfSelectedRowsFiltered.size(); ++i) { indexesOfSelectedRows.push_back(m_FilterModel->mapToSource(indexesOfSelectedRowsFiltered[i])); } if(indexesOfSelectedRows.size() < 1) { return; } std::vector selectedNodes; mitk::DataNode::Pointer node = 0; QString question = tr("Do you really want to remove "); for (QModelIndexList::iterator it = indexesOfSelectedRows.begin() ; it != indexesOfSelectedRows.end(); it++) { node = m_NodeTreeModel->GetNode(*it); // if node is not defined or if the node contains geometry data do not remove it if ( node.IsNotNull() /*& strcmp(node->GetData()->GetNameOfClass(), "PlaneGeometryData") != 0*/ ) { selectedNodes.push_back(node); question.append(QString::fromStdString(node->GetName())); question.append(", "); } } // remove the last two characters = ", " question = question.remove(question.size()-2, 2); question.append(" from data storage?"); QMessageBox::StandardButton answerButton = QMessageBox::question( m_Parent , tr("DataManager") , question , QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if(answerButton == QMessageBox::Yes) { for (std::vector::iterator it = selectedNodes.begin() ; it != selectedNodes.end(); it++) { node = *it; this->GetDataStorage()->Remove(node); if (m_GlobalReinitOnNodeDelete) this->GlobalReinit(false); } } } void QmitkDataManagerView::MakeAllNodesInvisible( bool ) { QList nodes = m_NodeTreeModel->GetNodeSet(); foreach(mitk::DataNode::Pointer node, nodes) { node->SetVisibility(false); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataManagerView::ShowOnlySelectedNodes( bool ) { QList selectedNodes = this->GetCurrentSelection(); QList allNodes = m_NodeTreeModel->GetNodeSet(); foreach(mitk::DataNode::Pointer node, allNodes) { node->SetVisibility(selectedNodes.contains(node)); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataManagerView::ToggleVisibilityOfSelectedNodes( bool ) { QList selectedNodes = this->GetCurrentSelection(); bool isVisible = false; foreach(mitk::DataNode::Pointer node, selectedNodes) { isVisible = false; node->GetBoolProperty("visible", isVisible); node->SetVisibility(!isVisible); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataManagerView::ShowInfoDialogForSelectedNodes( bool ) { QList selectedNodes = this->GetCurrentSelection(); QmitkInfoDialog _QmitkInfoDialog(selectedNodes, this->m_Parent); _QmitkInfoDialog.exec(); } void QmitkDataManagerView::NodeChanged(const mitk::DataNode* /*node*/) { // m_FilterModel->invalidate(); // fix as proposed by R. Khlebnikov in the mitk-users mail from 02.09.2014 QMetaObject::invokeMethod( m_FilterModel, "invalidate", Qt::QueuedConnection ); } QItemSelectionModel *QmitkDataManagerView::GetDataNodeSelectionModel() const { return m_NodeTreeView->selectionModel(); } void QmitkDataManagerView::GlobalReinit( bool ) { mitk::IRenderWindowPart* renderWindow = this->GetRenderWindowPart(); if (renderWindow == NULL) renderWindow = this->OpenRenderWindowPart(false); // no render window available if (renderWindow == NULL) return; mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(this->GetDataStorage()); } void QmitkDataManagerView::NodeTreeViewRowsRemoved ( const QModelIndex & /*parent*/, int /*start*/, int /*end*/ ) { m_CurrentRowCount = m_NodeTreeModel->rowCount(); } void QmitkDataManagerView::NodeTreeViewRowsInserted( const QModelIndex & parent, int, int ) { - m_NodeTreeView->setExpanded(parent, true); + QModelIndex viewIndex = m_FilterModel->mapFromSource(parent); + m_NodeTreeView->setExpanded(viewIndex, true); // a new row was inserted if( m_CurrentRowCount == 0 && m_NodeTreeModel->rowCount() == 1 ) { this->OpenRenderWindowPart(); m_CurrentRowCount = m_NodeTreeModel->rowCount(); } } void QmitkDataManagerView::NodeSelectionChanged( const QItemSelection & /*selected*/, const QItemSelection & /*deselected*/ ) { QList nodes = m_NodeTreeModel->GetNodeSet(); foreach(mitk::DataNode::Pointer node, nodes) { if ( node.IsNotNull() ) node->SetBoolProperty("selected", false); } nodes.clear(); nodes = this->GetCurrentSelection(); foreach(mitk::DataNode::Pointer node, nodes) { if ( node.IsNotNull() ) node->SetBoolProperty("selected", true); } //changing the selection does NOT require any rendering processes! //mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkDataManagerView::ShowIn(const QString &editorId) { berry::IWorkbenchPage::Pointer page = this->GetSite()->GetPage(); berry::IEditorInput::Pointer input(new mitk::DataStorageEditorInput(this->GetDataStorageReference())); page->OpenEditor(input, editorId, false, berry::IWorkbenchPage::MATCH_ID); } mitk::IRenderWindowPart* QmitkDataManagerView::OpenRenderWindowPart(bool activatedEditor) { if (activatedEditor) { return this->GetRenderWindowPart(QmitkAbstractView::ACTIVATE | QmitkAbstractView::OPEN); } else { return this->GetRenderWindowPart(QmitkAbstractView::BRING_TO_FRONT | QmitkAbstractView::OPEN); } } diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/document-print.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/document-print.svg index 9f5fbc1d14..6f97bd1204 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/document-print.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/document-print.svg @@ -1,57 +1,50 @@ image/svg+xml - \ No newline at end of file + inkscape:current-layer="Layer_1" /> \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-down.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-down.svg index e3e3d3ba02..bfc158b29b 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-down.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-down.svg @@ -1,68 +1,65 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="go-down.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-home.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-home.svg index f8d8968a0f..3d72be76a9 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-home.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-home.svg @@ -1,68 +1,64 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="go-home.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-next.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-next.svg index b9a61c83ae..7cdabfcbad 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-next.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-next.svg @@ -1,68 +1,64 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="go-next.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-previous.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-previous.svg index 313a575dc5..0fdce8863d 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-previous.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-previous.svg @@ -1,69 +1,65 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="go-previous.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-up.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-up.svg index 69c7bd01e5..4382bc2ba1 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-up.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/go-up.svg @@ -1,67 +1,64 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="go-up.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/system-log-out.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/system-log-out.svg index 270bef7652..de5744c5ba 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/system-log-out.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/system-log-out.svg @@ -1,67 +1,64 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="system-log-out.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/view-list-details.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/view-list-details.svg index deb608cd98..849c496b5c 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/view-list-details.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/view-list-details.svg @@ -1,67 +1,64 @@ image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/view-list-icons.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/view-list-icons.svg index 679547096d..1770b1be59 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/view-list-icons.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/view-list-icons.svg @@ -1,68 +1,65 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="view-list-icons.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/view-refresh.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/view-refresh.svg index 4eb0ddef9f..0faab59eed 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/view-refresh.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/actions/view-refresh.svg @@ -1,751 +1,747 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="view-refresh.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/places/folder.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/places/folder.svg index 39b5c1e5d7..84afe1571c 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/places/folder.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/places/folder.svg @@ -1,68 +1,64 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="folder.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-error.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-error.svg index b7fa48afd4..e4ee7ffaac 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-error.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-error.svg @@ -1,67 +1,64 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="dialog-error.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-information.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-information.svg index 8047b23a5b..b260c1b1f7 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-information.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-information.svg @@ -1,67 +1,64 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="dialog-information.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-question.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-question.svg index 9087700663..788c1ef821 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-question.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-question.svg @@ -1,67 +1,64 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="dialog-question.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-warning.svg b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-warning.svg index fa8ce9a8d9..107336eee2 100644 --- a/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-warning.svg +++ b/Plugins/org.mitk.gui.qt.ext/resources/icons/awesome/scalable/status/dialog-warning.svg @@ -1,67 +1,64 @@ + inkscape:version="0.91 r13725" + sodipodi:docname="dialog-warning.svg"> image/svg+xml - + - + + + diff --git a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp index 57a14ffd4b..bb95712aeb 100644 --- a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp +++ b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp @@ -1,1370 +1,1372 @@ /*=================================================================== 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 "QmitkExtWorkbenchWindowAdvisor.h" #include "QmitkExtActionBarAdvisor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // UGLYYY #include "internal/QmitkExtWorkbenchWindowAdvisorHack.h" #include "internal/QmitkCommonExtPlugin.h" #include "mitkUndoController.h" #include "mitkVerboseLimitedLinearUndo.h" #include #include #include #include QmitkExtWorkbenchWindowAdvisorHack * QmitkExtWorkbenchWindowAdvisorHack::undohack = new QmitkExtWorkbenchWindowAdvisorHack(); QString QmitkExtWorkbenchWindowAdvisor::QT_SETTINGS_FILENAME = "QtSettings.ini"; static bool USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS = false; class PartListenerForTitle: public berry::IPartListener { public: PartListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa) : windowAdvisor(wa) { } Events::Types GetPartEventTypes() const override { return Events::ACTIVATED | Events::BROUGHT_TO_TOP | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartActivated(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref.Cast ()) { windowAdvisor->UpdateTitle(false); } } void PartBroughtToTop(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref.Cast ()) { windowAdvisor->UpdateTitle(false); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& /*ref*/) override { windowAdvisor->UpdateTitle(false); } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { if (!windowAdvisor->lastActiveEditor.Expired() && ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock()) { windowAdvisor->UpdateTitle(true); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { if (!windowAdvisor->lastActiveEditor.Expired() && ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock()) { windowAdvisor->UpdateTitle(false); } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; }; class PartListenerForViewNavigator: public berry::IPartListener { public: PartListenerForViewNavigator(QAction* act) : viewNavigatorAction(act) { } Events::Types GetPartEventTypes() const override { return Events::OPENED | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigatorview") { viewNavigatorAction->setChecked(true); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigatorview") { viewNavigatorAction->setChecked(false); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigatorview") { viewNavigatorAction->setChecked(true); } } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigatorview") { viewNavigatorAction->setChecked(false); } } private: QAction* viewNavigatorAction; }; class PartListenerForImageNavigator: public berry::IPartListener { public: PartListenerForImageNavigator(QAction* act) : imageNavigatorAction(act) { } Events::Types GetPartEventTypes() const override { return Events::OPENED | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(true); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(false); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(true); } } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(false); } } private: QAction* imageNavigatorAction; }; class PerspectiveListenerForTitle: public berry::IPerspectiveListener { public: PerspectiveListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa) : windowAdvisor(wa), perspectivesClosed(false) { } Events::Types GetPerspectiveEventTypes() const override { if (USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS) { return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED; } else { return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED | Events::CLOSED | Events::OPENED; } } void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveSavedAs(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*oldPerspective*/, const berry::IPerspectiveDescriptor::Pointer& /*newPerspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveOpened(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { if (perspectivesClosed) { QListIterator i(windowAdvisor->viewActions); while (i.hasNext()) { i.next()->setEnabled(true); } //GetViewRegistry()->Find("org.mitk.views.imagenavigator"); if(windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) { windowAdvisor->openDicomEditorAction->setEnabled(true); } windowAdvisor->fileSaveProjectAction->setEnabled(true); windowAdvisor->closeProjectAction->setEnabled(true); windowAdvisor->undoAction->setEnabled(true); windowAdvisor->redoAction->setEnabled(true); windowAdvisor->imageNavigatorAction->setEnabled(true); windowAdvisor->viewNavigatorAction->setEnabled(true); windowAdvisor->resetPerspAction->setEnabled(true); if( windowAdvisor->GetShowClosePerspectiveMenuItem() ) { windowAdvisor->closePerspAction->setEnabled(true); } } perspectivesClosed = false; } void PerspectiveClosed(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { berry::IWorkbenchWindow::Pointer wnd = windowAdvisor->GetWindowConfigurer()->GetWindow(); bool allClosed = true; if (wnd->GetActivePage()) { QList perspectives(wnd->GetActivePage()->GetOpenPerspectives()); allClosed = perspectives.empty(); } if (allClosed) { perspectivesClosed = true; QListIterator i(windowAdvisor->viewActions); while (i.hasNext()) { i.next()->setEnabled(false); } if(windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) { windowAdvisor->openDicomEditorAction->setEnabled(false); } windowAdvisor->fileSaveProjectAction->setEnabled(false); windowAdvisor->closeProjectAction->setEnabled(false); windowAdvisor->undoAction->setEnabled(false); windowAdvisor->redoAction->setEnabled(false); windowAdvisor->imageNavigatorAction->setEnabled(false); windowAdvisor->viewNavigatorAction->setEnabled(false); windowAdvisor->resetPerspAction->setEnabled(false); if( windowAdvisor->GetShowClosePerspectiveMenuItem() ) { windowAdvisor->closePerspAction->setEnabled(false); } } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; bool perspectivesClosed; }; class PerspectiveListenerForMenu: public berry::IPerspectiveListener { public: PerspectiveListenerForMenu(QmitkExtWorkbenchWindowAdvisor* wa) : windowAdvisor(wa) { } Events::Types GetPerspectiveEventTypes() const override { return Events::ACTIVATED | Events::DEACTIVATED; } void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& perspective) override { QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()]; if (action) { action->setChecked(true); } } void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& perspective) override { QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()]; if (action) { action->setChecked(false); } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; }; QmitkExtWorkbenchWindowAdvisor::QmitkExtWorkbenchWindowAdvisor(berry::WorkbenchAdvisor* wbAdvisor, berry::IWorkbenchWindowConfigurer::Pointer configurer) : berry::WorkbenchWindowAdvisor(configurer), lastInput(nullptr), wbAdvisor(wbAdvisor), showViewToolbar(true), showPerspectiveToolbar(false), showVersionInfo(true), showMitkVersionInfo(true), showViewMenuItem(true), showNewWindowMenuItem(false), showClosePerspectiveMenuItem(true), viewNavigatorFound(false), showMemoryIndicator(true), dropTargetListener(new QmitkDefaultDropTargetListener) { productName = QCoreApplication::applicationName(); viewExcludeList.push_back("org.mitk.views.viewnavigatorview"); } QmitkExtWorkbenchWindowAdvisor::~QmitkExtWorkbenchWindowAdvisor() { } berry::ActionBarAdvisor::Pointer QmitkExtWorkbenchWindowAdvisor::CreateActionBarAdvisor( berry::IActionBarConfigurer::Pointer configurer) { if (USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS) { berry::ActionBarAdvisor::Pointer actionBarAdvisor( new QmitkExtActionBarAdvisor(configurer)); return actionBarAdvisor; } else { return berry::WorkbenchWindowAdvisor::CreateActionBarAdvisor(configurer); } } QWidget* QmitkExtWorkbenchWindowAdvisor::CreateEmptyWindowContents(QWidget* parent) { QWidget* parentWidget = static_cast(parent); auto label = new QLabel(parentWidget); label->setText("No perspectives are open. Open a perspective in the Window->Open Perspective menu."); label->setContentsMargins(10,10,10,10); label->setAlignment(Qt::AlignTop); label->setEnabled(false); parentWidget->layout()->addWidget(label); return label; } void QmitkExtWorkbenchWindowAdvisor::ShowClosePerspectiveMenuItem(bool show) { showClosePerspectiveMenuItem = show; } bool QmitkExtWorkbenchWindowAdvisor::GetShowClosePerspectiveMenuItem() { return showClosePerspectiveMenuItem; } void QmitkExtWorkbenchWindowAdvisor::ShowMemoryIndicator(bool show) { showMemoryIndicator = show; } bool QmitkExtWorkbenchWindowAdvisor::GetShowMemoryIndicator() { return showMemoryIndicator; } void QmitkExtWorkbenchWindowAdvisor::ShowNewWindowMenuItem(bool show) { showNewWindowMenuItem = show; } void QmitkExtWorkbenchWindowAdvisor::ShowViewToolbar(bool show) { showViewToolbar = show; } void QmitkExtWorkbenchWindowAdvisor::ShowViewMenuItem(bool show) { showViewMenuItem = show; } void QmitkExtWorkbenchWindowAdvisor::ShowPerspectiveToolbar(bool show) { showPerspectiveToolbar = show; } void QmitkExtWorkbenchWindowAdvisor::ShowVersionInfo(bool show) { showVersionInfo = show; } void QmitkExtWorkbenchWindowAdvisor::ShowMitkVersionInfo(bool show) { showMitkVersionInfo = show; } void QmitkExtWorkbenchWindowAdvisor::SetProductName(const QString& product) { productName = product; } void QmitkExtWorkbenchWindowAdvisor::SetWindowIcon(const QString& wndIcon) { windowIcon = wndIcon; } void QmitkExtWorkbenchWindowAdvisor::PostWindowCreate() { // very bad hack... berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow(); QMainWindow* mainWindow = qobject_cast (window->GetShell()->GetControl()); if (!windowIcon.isEmpty()) { mainWindow->setWindowIcon(QIcon(windowIcon)); } mainWindow->setContextMenuPolicy(Qt::PreventContextMenu); /*mainWindow->setStyleSheet("color: white;" "background-color: #808080;" "selection-color: #659EC7;" "selection-background-color: #808080;" " QMenuBar {" "background-color: #808080; }");*/ // Load selected icon theme QStringList searchPaths = QIcon::themeSearchPaths(); searchPaths.push_front( QString(":/org_mitk_icons/icons/") ); QIcon::setThemeSearchPaths( searchPaths ); berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService(); berry::IPreferences::Pointer stylePref = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE); QString iconTheme = stylePref->Get(berry::QtPreferences::QT_ICON_THEME, "<>"); if( iconTheme == QString( "<>" ) ) { iconTheme = QString( "tango" ); } QIcon::setThemeName( iconTheme ); // ==== Application menu ============================ QMenuBar* menuBar = mainWindow->menuBar(); menuBar->setContextMenuPolicy(Qt::PreventContextMenu); #ifdef __APPLE__ menuBar->setNativeMenuBar(true); #else menuBar->setNativeMenuBar(false); #endif QAction* fileOpenAction = new QmitkFileOpenAction(QIcon::fromTheme("document-open",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/document-open.svg")), window); fileOpenAction->setShortcut(QKeySequence::Open); QAction* fileSaveAction = new QmitkFileSaveAction(QIcon(":/org.mitk.gui.qt.ext/Save_48.png"), window); fileSaveAction->setShortcut(QKeySequence::Save); fileSaveProjectAction = new QmitkExtFileSaveProjectAction(window); fileSaveProjectAction->setIcon(QIcon::fromTheme("document-save",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/document-save.svg"))); closeProjectAction = new QmitkCloseProjectAction(window); closeProjectAction->setIcon(QIcon::fromTheme("edit-delete",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/edit-delete.svg"))); auto perspGroup = new QActionGroup(menuBar); std::map VDMap; // sort elements (converting vector to map...) QList::const_iterator iter; berry::IViewRegistry* viewRegistry = berry::PlatformUI::GetWorkbench()->GetViewRegistry(); const QList viewDescriptors = viewRegistry->GetViews(); bool skip = false; for (iter = viewDescriptors.begin(); iter != viewDescriptors.end(); ++iter) { // if viewExcludeList is set, it contains the id-strings of view, which // should not appear as an menu-entry in the menu if (viewExcludeList.size() > 0) { for (int i=0; iGetId()) { skip = true; break; } } if (skip) { skip = false; continue; } } if ((*iter)->GetId() == "org.blueberry.ui.internal.introview") continue; if ((*iter)->GetId() == "org.mitk.views.imagenavigator") continue; if ((*iter)->GetId() == "org.mitk.views.viewnavigatorview") continue; std::pair p( (*iter)->GetLabel(), (*iter)); VDMap.insert(p); } std::map::const_iterator MapIter; for (MapIter = VDMap.begin(); MapIter != VDMap.end(); ++MapIter) { berry::QtShowViewAction* viewAction = new berry::QtShowViewAction(window, (*MapIter).second); viewActions.push_back(viewAction); } if (!USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS) { QMenu* fileMenu = menuBar->addMenu("&File"); fileMenu->setObjectName("FileMenu"); fileMenu->addAction(fileOpenAction); fileMenu->addAction(fileSaveAction); fileMenu->addAction(fileSaveProjectAction); fileMenu->addAction(closeProjectAction); fileMenu->addSeparator(); QAction* fileExitAction = new QmitkFileExitAction(window); fileExitAction->setIcon(QIcon::fromTheme("system-log-out",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/system-log-out.svg"))); fileExitAction->setShortcut(QKeySequence::Quit); fileExitAction->setObjectName("QmitkFileExitAction"); fileMenu->addAction(fileExitAction); // another bad hack to get an edit/undo menu... QMenu* editMenu = menuBar->addMenu("&Edit"); undoAction = editMenu->addAction(QIcon::fromTheme("edit-undo",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/edit-undo.svg")), "&Undo", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onUndo()), QKeySequence("CTRL+Z")); undoAction->setToolTip("Undo the last action (not supported by all modules)"); redoAction = editMenu->addAction(QIcon::fromTheme("edit-redo",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/edit-redo.svg")) , "&Redo", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onRedo()), QKeySequence("CTRL+Y")); redoAction->setToolTip("execute the last action that was undone again (not supported by all modules)"); // ==== Window Menu ========================== QMenu* windowMenu = menuBar->addMenu("Window"); if (showNewWindowMenuItem) { windowMenu->addAction("&New Window", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onNewWindow())); windowMenu->addSeparator(); } QMenu* perspMenu = windowMenu->addMenu("&Open Perspective"); QMenu* viewMenu; if (showViewMenuItem) { viewMenu = windowMenu->addMenu("Show &View"); viewMenu->setObjectName("Show View"); } windowMenu->addSeparator(); resetPerspAction = windowMenu->addAction("&Reset Perspective", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onResetPerspective())); if(showClosePerspectiveMenuItem) closePerspAction = windowMenu->addAction("&Close Perspective", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onClosePerspective())); windowMenu->addSeparator(); windowMenu->addAction("&Preferences...", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onEditPreferences()), QKeySequence("CTRL+P")); // fill perspective menu berry::IPerspectiveRegistry* perspRegistry = window->GetWorkbench()->GetPerspectiveRegistry(); QList perspectives( perspRegistry->GetPerspectives()); skip = false; for (QList::iterator perspIt = perspectives.begin(); perspIt != perspectives.end(); ++perspIt) { // if perspectiveExcludeList is set, it contains the id-strings of perspectives, which // should not appear as an menu-entry in the perspective menu if (perspectiveExcludeList.size() > 0) { for (int i=0; iGetId()) { skip = true; break; } } if (skip) { skip = false; continue; } } QAction* perspAction = new berry::QtOpenPerspectiveAction(window, *perspIt, perspGroup); mapPerspIdToAction.insert((*perspIt)->GetId(), perspAction); } perspMenu->addActions(perspGroup->actions()); if (showViewMenuItem) { for (auto viewAction : viewActions) { viewMenu->addAction(viewAction); } } // ===== Help menu ==================================== QMenu* helpMenu = menuBar->addMenu("&Help"); helpMenu->addAction("&Welcome",this, SLOT(onIntro())); helpMenu->addAction("&Open Help Perspective", this, SLOT(onHelpOpenHelpPerspective())); helpMenu->addAction("&Context Help",this, SLOT(onHelp()), QKeySequence("F1")); helpMenu->addAction("&About",this, SLOT(onAbout())); // ===================================================== } else { //undoAction = new QAction(QIcon::fromTheme("edit-undo",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/edit-undo.svg")), // "&Undo", nullptr); undoAction = new QmitkUndoAction(QIcon::fromTheme("edit-undo",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/edit-undo.svg")), nullptr); undoAction->setShortcut(QKeySequence::Undo); redoAction = new QmitkRedoAction(QIcon::fromTheme("edit-redo",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/edit-redo.svg")), nullptr); redoAction->setShortcut(QKeySequence::Redo); } // toolbar for showing file open, undo, redo and other main actions auto mainActionsToolBar = new QToolBar; mainActionsToolBar->setObjectName("mainActionsToolBar"); mainActionsToolBar->setContextMenuPolicy(Qt::PreventContextMenu); #ifdef __APPLE__ mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextUnderIcon ); #else mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextBesideIcon ); #endif imageNavigatorAction = new QAction(QIcon(":/org.mitk.gui.qt.ext/Slider.png"), "&Image Navigator", nullptr); bool imageNavigatorViewFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.imagenavigator"); if(this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) { openDicomEditorAction = new QmitkOpenDicomEditorAction(QIcon(":/org.mitk.gui.qt.ext/dcm-icon.png"),window); } if (imageNavigatorViewFound) { QObject::connect(imageNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onImageNavigator())); imageNavigatorAction->setCheckable(true); // add part listener for image navigator imageNavigatorPartListener.reset(new PartListenerForImageNavigator(imageNavigatorAction)); window->GetPartService()->AddPartListener(imageNavigatorPartListener.data()); berry::IViewPart::Pointer imageNavigatorView = window->GetActivePage()->FindView("org.mitk.views.imagenavigator"); imageNavigatorAction->setChecked(false); if (imageNavigatorView) { bool isImageNavigatorVisible = window->GetActivePage()->IsPartVisible(imageNavigatorView); if (isImageNavigatorVisible) imageNavigatorAction->setChecked(true); } imageNavigatorAction->setToolTip("Toggle image navigator for navigating through image"); } viewNavigatorAction = new QAction(QIcon(":/org.mitk.gui.qt.ext/view-manager_48.png"),"&View Navigator", nullptr); viewNavigatorFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.viewnavigatorview"); if (viewNavigatorFound) { QObject::connect(viewNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onViewNavigator())); viewNavigatorAction->setCheckable(true); // add part listener for view navigator viewNavigatorPartListener.reset(new PartListenerForViewNavigator(viewNavigatorAction)); window->GetPartService()->AddPartListener(viewNavigatorPartListener.data()); berry::IViewPart::Pointer viewnavigatorview = window->GetActivePage()->FindView("org.mitk.views.viewnavigatorview"); viewNavigatorAction->setChecked(false); if (viewnavigatorview) { bool isViewNavigatorVisible = window->GetActivePage()->IsPartVisible(viewnavigatorview); if (isViewNavigatorVisible) viewNavigatorAction->setChecked(true); } viewNavigatorAction->setToolTip("Toggle View Navigator"); } mainActionsToolBar->addAction(fileOpenAction); mainActionsToolBar->addAction(fileSaveProjectAction); mainActionsToolBar->addAction(closeProjectAction); mainActionsToolBar->addAction(undoAction); mainActionsToolBar->addAction(redoAction); if(this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) { mainActionsToolBar->addAction(openDicomEditorAction); } if (imageNavigatorViewFound) { mainActionsToolBar->addAction(imageNavigatorAction); } if (viewNavigatorFound) { mainActionsToolBar->addAction(viewNavigatorAction); } mainWindow->addToolBar(mainActionsToolBar); // ==== Perspective Toolbar ================================== auto qPerspectiveToolbar = new QToolBar; qPerspectiveToolbar->setObjectName("perspectiveToolBar"); if (showPerspectiveToolbar) { qPerspectiveToolbar->addActions(perspGroup->actions()); mainWindow->addToolBar(qPerspectiveToolbar); } else delete qPerspectiveToolbar; // ==== View Toolbar ================================== auto qToolbar = new QToolBar; qToolbar->setObjectName("viewToolBar"); if (showViewToolbar) { mainWindow->addToolBar(qToolbar); for (auto viewAction : viewActions) { qToolbar->addAction(viewAction); } } else delete qToolbar; QSettings settings(GetQSettingsFile(), QSettings::IniFormat); mainWindow->restoreState(settings.value("ToolbarPosition").toByteArray()); -#ifdef __APPLE__ -#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) - mainWindow->setUnifiedTitleAndToolBarOnMac(true); - // default is false -#endif -#endif - auto qStatusBar = new QStatusBar(); //creating a QmitkStatusBar for Output on the QStatusBar and connecting it with the MainStatusBar auto statusBar = new QmitkStatusBar(qStatusBar); //disabling the SizeGrip in the lower right corner statusBar->SetSizeGripEnabled(false); auto progBar = new QmitkProgressBar(); qStatusBar->addPermanentWidget(progBar, 0); progBar->hide(); // progBar->AddStepsToDo(2); // progBar->Progress(1); mainWindow->setStatusBar(qStatusBar); if (showMemoryIndicator) { auto memoryIndicator = new QmitkMemoryUsageIndicatorView(); qStatusBar->addPermanentWidget(memoryIndicator, 0); } } void QmitkExtWorkbenchWindowAdvisor::PreWindowOpen() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); // show the shortcut bar and progress indicator, which are hidden by // default //configurer->SetShowPerspectiveBar(true); //configurer->SetShowFastViewBars(true); //configurer->SetShowProgressIndicator(true); // // add the drag and drop support for the editor area // configurer.addEditorAreaTransfer(EditorInputTransfer.getInstance()); // configurer.addEditorAreaTransfer(ResourceTransfer.getInstance()); // configurer.addEditorAreaTransfer(FileTransfer.getInstance()); // configurer.addEditorAreaTransfer(MarkerTransfer.getInstance()); // configurer.configureEditorAreaDropListener(new EditorAreaDropAdapter( // configurer.getWindow())); this->HookTitleUpdateListeners(configurer); menuPerspectiveListener.reset(new PerspectiveListenerForMenu(this)); configurer->GetWindow()->AddPerspectiveListener(menuPerspectiveListener.data()); configurer->AddEditorAreaTransfer(QStringList("text/uri-list")); configurer->ConfigureEditorAreaDropListener(dropTargetListener.data()); } void QmitkExtWorkbenchWindowAdvisor::PostWindowOpen() { berry::WorkbenchWindowAdvisor::PostWindowOpen(); // Force Rendering Window Creation on startup. berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); ctkPluginContext* context = QmitkCommonExtPlugin::getContext(); ctkServiceReference serviceRef = context->getServiceReference(); if (serviceRef) { mitk::IDataStorageService *dsService = context->getService(serviceRef); if (dsService) { mitk::IDataStorageReference::Pointer dsRef = dsService->GetDataStorage(); mitk::DataStorageEditorInput::Pointer dsInput(new mitk::DataStorageEditorInput(dsRef)); mitk::WorkbenchUtil::OpenEditor(configurer->GetWindow()->GetActivePage(),dsInput); } } } void QmitkExtWorkbenchWindowAdvisor::onIntro() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onIntro(); } void QmitkExtWorkbenchWindowAdvisor::onHelp() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelp(); } void QmitkExtWorkbenchWindowAdvisor::onHelpOpenHelpPerspective() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelpOpenHelpPerspective(); } void QmitkExtWorkbenchWindowAdvisor::onAbout() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onAbout(); } //-------------------------------------------------------------------------------- // Ugly hack from here on. Feel free to delete when command framework // and undo buttons are done. //-------------------------------------------------------------------------------- QmitkExtWorkbenchWindowAdvisorHack::QmitkExtWorkbenchWindowAdvisorHack() : QObject() { } QmitkExtWorkbenchWindowAdvisorHack::~QmitkExtWorkbenchWindowAdvisorHack() { } void QmitkExtWorkbenchWindowAdvisorHack::onUndo() { mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel(); if (model) { if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast( model )) { mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetUndoDescriptions(); if (descriptions.size() >= 1) { MITK_INFO << "Undo " << descriptions.front().second; } } model->Undo(); } else { MITK_ERROR << "No undo model instantiated"; } } void QmitkExtWorkbenchWindowAdvisorHack::onRedo() { mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel(); if (model) { if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast( model )) { mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetRedoDescriptions(); if (descriptions.size() >= 1) { MITK_INFO << "Redo " << descriptions.front().second; } } model->Redo(); } else { MITK_ERROR << "No undo model instantiated"; } } -void QmitkExtWorkbenchWindowAdvisorHack::onImageNavigator() +// safe calls to the complete chain +// berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.imagenavigator"); +// to cover for all possible cases of closed pages etc. +static void SafeHandleNavigatorView(QString view_query_name) { - // get ImageNavigatorView - berry::IViewPart::Pointer imageNavigatorView = - berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.imagenavigator"); - if (imageNavigatorView) + berry::IWorkbench* wbench = berry::PlatformUI::GetWorkbench(); + if( wbench == nullptr ) + return; + + berry::IWorkbenchWindow::Pointer wbench_window = wbench->GetActiveWorkbenchWindow(); + if( wbench_window.IsNull() ) + return; + + berry::IWorkbenchPage::Pointer wbench_page = wbench_window->GetActivePage(); + if( wbench_page.IsNull() ) + return; + + auto wbench_view = wbench_page->FindView( view_query_name ); + + if( wbench_view.IsNotNull() ) { - bool isImageNavigatorVisible = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->IsPartVisible(imageNavigatorView); - if (isImageNavigatorVisible) + bool isViewVisible = wbench_page->IsPartVisible( wbench_view ); + if( isViewVisible ) { - berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->HideView(imageNavigatorView); + wbench_page->HideView( wbench_view ); return; } + } - berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ShowView("org.mitk.views.imagenavigator"); - //berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ResetPerspective(); + + wbench_page->ShowView( view_query_name ); +} + +void QmitkExtWorkbenchWindowAdvisorHack::onImageNavigator() +{ + // show/hide ImageNavigatorView + SafeHandleNavigatorView("org.mitk.views.imagenavigator"); } void QmitkExtWorkbenchWindowAdvisorHack::onViewNavigator() { - // get viewnavigatorView - berry::IViewPart::Pointer viewnavigatorView = - berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.viewnavigatorview"); - if (viewnavigatorView) - { - bool isviewnavigatorVisible = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->IsPartVisible(viewnavigatorView); - if (isviewnavigatorVisible) - { - berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->HideView(viewnavigatorView); - return; - } - } - berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ShowView("org.mitk.views.viewnavigatorview"); - //berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ResetPerspective(); + // show/hide viewnavigatorView + SafeHandleNavigatorView("org.mitk.views.viewnavigatorview"); } void QmitkExtWorkbenchWindowAdvisorHack::onEditPreferences() { QmitkPreferencesDialog _PreferencesDialog(QApplication::activeWindow()); _PreferencesDialog.exec(); } void QmitkExtWorkbenchWindowAdvisorHack::onQuit() { berry::PlatformUI::GetWorkbench()->Close(); } void QmitkExtWorkbenchWindowAdvisorHack::onResetPerspective() { berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ResetPerspective(); } void QmitkExtWorkbenchWindowAdvisorHack::onClosePerspective() { berry::IWorkbenchPage::Pointer page = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage(); page->ClosePerspective(page->GetPerspective(), true, true); } void QmitkExtWorkbenchWindowAdvisorHack::onNewWindow() { berry::PlatformUI::GetWorkbench()->OpenWorkbenchWindow(nullptr); } void QmitkExtWorkbenchWindowAdvisorHack::onIntro() { bool hasIntro = berry::PlatformUI::GetWorkbench()->GetIntroManager()->HasIntro(); if (!hasIntro) { QRegExp reg("(.*)(\\n)*"); QRegExp reg2("(\\n)*(.*)"); QFile file(":/org.mitk.gui.qt.ext/index.html"); file.open(QIODevice::ReadOnly | QIODevice::Text); //text file only for reading QString text = QString(file.readAll()); file.close(); QString title = text; title.replace(reg, ""); title.replace(reg2, ""); std::cout << title.toStdString() << std::endl; QMessageBox::information(nullptr, title, text, "Close"); } else { berry::PlatformUI::GetWorkbench()->GetIntroManager()->ShowIntro( berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow(), false); } } void QmitkExtWorkbenchWindowAdvisorHack::onHelp() { ctkPluginContext* context = QmitkCommonExtPlugin::getContext(); if (context == nullptr) { MITK_WARN << "Plugin context not set, unable to open context help"; return; } // Check if the org.blueberry.ui.qt.help plug-in is installed and started QList > plugins = context->getPlugins(); foreach(QSharedPointer p, plugins) { if (p->getSymbolicName() == "org.blueberry.ui.qt.help") { if (p->getState() != ctkPlugin::ACTIVE) { // try to activate the plug-in explicitly try { p->start(ctkPlugin::START_TRANSIENT); } catch (const ctkPluginException& pe) { MITK_ERROR << "Activating org.blueberry.ui.qt.help failed: " << pe.what(); return; } } } } ctkServiceReference eventAdminRef = context->getServiceReference(); ctkEventAdmin* eventAdmin = nullptr; if (eventAdminRef) { eventAdmin = context->getService(eventAdminRef); } if (eventAdmin == nullptr) { MITK_WARN << "ctkEventAdmin service not found. Unable to open context help"; } else { ctkEvent ev("org/blueberry/ui/help/CONTEXTHELP_REQUESTED"); eventAdmin->postEvent(ev); } } void QmitkExtWorkbenchWindowAdvisorHack::onHelpOpenHelpPerspective() { berry::PlatformUI::GetWorkbench()->ShowPerspective("org.blueberry.perspectives.help", berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()); } void QmitkExtWorkbenchWindowAdvisorHack::onAbout() { auto aboutDialog = new QmitkAboutDialog(QApplication::activeWindow(),nullptr); aboutDialog->open(); } void QmitkExtWorkbenchWindowAdvisor::HookTitleUpdateListeners( berry::IWorkbenchWindowConfigurer::Pointer configurer) { // hook up the listeners to update the window title titlePartListener.reset(new PartListenerForTitle(this)); titlePerspectiveListener.reset(new PerspectiveListenerForTitle(this)); editorPropertyListener.reset(new berry::PropertyChangeIntAdapter< QmitkExtWorkbenchWindowAdvisor>(this, &QmitkExtWorkbenchWindowAdvisor::PropertyChange)); // configurer.getWindow().addPageListener(new IPageListener() { // public void pageActivated(IWorkbenchPage page) { // updateTitle(false); // } // // public void pageClosed(IWorkbenchPage page) { // updateTitle(false); // } // // public void pageOpened(IWorkbenchPage page) { // // do nothing // } // }); configurer->GetWindow()->AddPerspectiveListener(titlePerspectiveListener.data()); configurer->GetWindow()->GetPartService()->AddPartListener(titlePartListener.data()); } QString QmitkExtWorkbenchWindowAdvisor::ComputeTitle() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); berry::IWorkbenchPage::Pointer currentPage = configurer->GetWindow()->GetActivePage(); berry::IEditorPart::Pointer activeEditor; if (currentPage) { activeEditor = lastActiveEditor.Lock(); } QString title; berry::IProduct::Pointer product = berry::Platform::GetProduct(); if (product.IsNotNull()) { title = product->GetName(); } if (title.isEmpty()) { // instead of the product name, we use a custom variable for now title = productName; } if(showMitkVersionInfo) { title += QString(" ") + MITK_VERSION_STRING; } if (showVersionInfo) { // add version informatioin QString versions = QString(" (ITK %1.%2.%3 VTK %4.%5.%6 Qt %7 MITK %8)") .arg(ITK_VERSION_MAJOR).arg(ITK_VERSION_MINOR).arg(ITK_VERSION_PATCH) .arg(VTK_MAJOR_VERSION).arg(VTK_MINOR_VERSION).arg(VTK_BUILD_VERSION) .arg(QT_VERSION_STR) .arg(MITK_VERSION_STRING); title += versions; } if (currentPage) { if (activeEditor) { lastEditorTitle = activeEditor->GetTitleToolTip(); if (!lastEditorTitle.isEmpty()) title = lastEditorTitle + " - " + title; } berry::IPerspectiveDescriptor::Pointer persp = currentPage->GetPerspective(); QString label = ""; if (persp) { label = persp->GetLabel(); } berry::IAdaptable* input = currentPage->GetInput(); if (input && input != wbAdvisor->GetDefaultPageInput()) { label = currentPage->GetLabel(); } if (!label.isEmpty()) { title = label + " - " + title; } } title += " (Not for use in diagnosis or treatment of patients)"; return title; } void QmitkExtWorkbenchWindowAdvisor::RecomputeTitle() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); QString oldTitle = configurer->GetTitle(); QString newTitle = ComputeTitle(); if (newTitle != oldTitle) { configurer->SetTitle(newTitle); } } void QmitkExtWorkbenchWindowAdvisor::UpdateTitle(bool editorHidden) { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); berry::IWorkbenchWindow::Pointer window = configurer->GetWindow(); berry::IEditorPart::Pointer activeEditor; berry::IWorkbenchPage::Pointer currentPage = window->GetActivePage(); berry::IPerspectiveDescriptor::Pointer persp; berry::IAdaptable* input = nullptr; if (currentPage) { activeEditor = currentPage->GetActiveEditor(); persp = currentPage->GetPerspective(); input = currentPage->GetInput(); } if (editorHidden) { activeEditor = nullptr; } // Nothing to do if the editor hasn't changed if (activeEditor == lastActiveEditor.Lock() && currentPage == lastActivePage.Lock() && persp == lastPerspective.Lock() && input == lastInput) { return; } if (!lastActiveEditor.Expired()) { lastActiveEditor.Lock()->RemovePropertyListener(editorPropertyListener.data()); } lastActiveEditor = activeEditor; lastActivePage = currentPage; lastPerspective = persp; lastInput = input; if (activeEditor) { activeEditor->AddPropertyListener(editorPropertyListener.data()); } RecomputeTitle(); } void QmitkExtWorkbenchWindowAdvisor::PropertyChange(const berry::Object::Pointer& /*source*/, int propId) { if (propId == berry::IWorkbenchPartConstants::PROP_TITLE) { if (!lastActiveEditor.Expired()) { QString newTitle = lastActiveEditor.Lock()->GetPartName(); if (lastEditorTitle != newTitle) { RecomputeTitle(); } } } } void QmitkExtWorkbenchWindowAdvisor::SetPerspectiveExcludeList(const QList& v) { this->perspectiveExcludeList = v; } QList QmitkExtWorkbenchWindowAdvisor::GetPerspectiveExcludeList() { return this->perspectiveExcludeList; } void QmitkExtWorkbenchWindowAdvisor::SetViewExcludeList(const QList& v) { this->viewExcludeList = v; } QList QmitkExtWorkbenchWindowAdvisor::GetViewExcludeList() { return this->viewExcludeList; } void QmitkExtWorkbenchWindowAdvisor::PostWindowClose() { berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow(); QMainWindow* mainWindow = static_cast (window->GetShell()->GetControl()); QSettings settings(GetQSettingsFile(), QSettings::IniFormat); settings.setValue("ToolbarPosition", mainWindow->saveState()); } QString QmitkExtWorkbenchWindowAdvisor::GetQSettingsFile() const { QFileInfo settingsInfo = QmitkCommonExtPlugin::getContext()->getDataFile(QT_SETTINGS_FILENAME); return settingsInfo.canonicalFilePath(); }